Rename for better pylint coverage
[curses-crossword.git] / curses_crossword.py
diff --git a/curses_crossword.py b/curses_crossword.py
new file mode 100644 (file)
index 0000000..662fa17
--- /dev/null
@@ -0,0 +1,287 @@
+#!/usr/bin/python
+
+import curses
+import curses.ascii
+import locale
+import codecs
+import getopt
+import os
+import sys
+
+locale.setlocale(locale.LC_ALL, '')
+code = locale.getpreferredencoding()
+
+superscript_numbers = {}
+
+try:
+    superscript_numbers = {
+        "0": unichr(8304).encode(code),
+        "1": unichr(185).encode(code),
+        "2": unichr(178).encode(code),
+        "3": unichr(179).encode(code),
+        "4": unichr(8308).encode(code),
+        "5": unichr(8309).encode(code),
+        "6": unichr(8310).encode(code),
+        "7": unichr(8311).encode(code),
+        "8": unichr(8312).encode(code),
+        "9": unichr(8313).encode(code),
+    }
+except:
+    for number in range(0, 10):
+        superscript_numbers[str(number)] = str(number)
+
+filename = None
+
+try:
+    (options, args) = getopt.getopt(sys.argv[1:], "f:", "file=")
+except getopt.GetoptError, err:
+    print str(err)
+    sys.exit(2)
+
+for option in options:
+    if option[0] == "-f" or option[0] == "--file":
+        filename = option[1]
+
+if not filename and len(args) > 0:
+    filename = args[0]
+
+if not filename:
+    sys.stderr.write("No crossword file specified, exiting.\n")
+    sys.exit(0)
+
+if os.path.exists(filename) and os.path.isfile(filename):
+    crossworddata = codecs.open(filename, "r", "utf-8").read()
+else:
+    sys.stderr.write("Couldn't open file %s\n" %(filename))
+    sys.exit(0)
+
+def parsecrossword(crossworddata):
+    ingrid = False
+    inacross = False
+    indown = False
+    crossword = {"grid": [],
+        "across": {},
+        "down": {},
+        "grid_questions_start": [],
+        "grid_questions_end": []}
+
+    for line in crossworddata.split("\n"):
+        line = line.strip("\n")
+        if line == "GRID":
+            ingrid = True
+            inacross = False
+            indown = False
+        elif line == "ACROSS":
+            ingrid = False
+            inacross = True
+            indown = False
+        elif line == "DOWN":
+            ingrid = False
+            inacross = False
+            indown = True
+        else:
+            if ingrid:
+                if line != "":
+                    crossword["grid"].append(line)
+            if inacross:
+                if line != "":
+                    parts = line.split()
+                    question_number = int(parts[0])
+                    clue = " ".join(parts[1:])
+                    crossword["across"][int(question_number)] = \
+                        clue.encode(code)
+            if indown:
+                if line != "":
+                    parts = line.split()
+                    question_number = int(parts[0])
+                    clue = " ".join(parts[1:])
+                    crossword["down"][int(question_number)] = clue.encode(code)
+    num_cols = len(crossword["grid"][0])
+    num_rows = len(crossword["grid"])
+
+    current_clue_number = 1
+
+    for row in range(0, num_rows):
+        crossword["grid_questions_start"].append([])
+
+    for row in range(0, num_rows):
+        for col in range(0, num_cols):
+            have_clue = False
+            if col > 0 \
+                and crossword["grid"][row][col - 1] == "x" \
+                and crossword["grid"][row][col] != "x" \
+                and col < (num_cols - 1) \
+                and crossword["grid"][row][col + 1] != "x":
+                have_clue = True
+            if col == 0 and crossword["grid"][row][col] != "x" \
+                and crossword["grid"][row][col + 1] != "x":
+                have_clue = True
+            if row > 0 and crossword["grid"][row-1][col] == "x" \
+                and row < (num_rows - 1) \
+                and crossword["grid"][row][col] != "x" \
+                and crossword["grid"][row + 1][col] != "x":
+                have_clue = True
+            if row == 0 and crossword["grid"][row][col] != "x" \
+                and crossword["grid"][row + 1][col] != "x":
+                have_clue = True
+            if have_clue:
+                crossword["grid_questions_start"][row] \
+                    .append(current_clue_number)
+                current_clue_number += 1
+            else:
+                crossword["grid_questions_start"][row].append(0)
+
+    return crossword
+
+def crossword(stdscr, crossworddata):
+    crossword = parsecrossword(crossworddata)
+    cury = 0
+    grid_length = len(crossword["grid"][0])
+    curx = 0
+    stdscr.addch(cury, curx, curses.ACS_ULCORNER)
+    curx += 1
+    while ((curx - 1) / 4) < grid_length:
+        stdscr.addch(cury, curx, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+1, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+2, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+3, curses.ACS_TTEE)
+        curx += 4
+    curx -= 1
+    stdscr.addch(cury, curx, curses.ACS_URCORNER)
+    cury += 1
+    curgridy = 0
+    for line in crossword["grid"]:
+        datastr = ""
+        curx = 0
+        curgridx = 0
+        for c in line:
+            stdscr.addch(cury, curx, curses.ACS_VLINE)
+            if curx > 0:
+                stdscr.addch(cury+1, curx, curses.ACS_PLUS)
+            else:
+                stdscr.addch(cury+1, curx, curses.ACS_LTEE)
+            stdscr.addch(cury+1, curx + 1, curses.ACS_HLINE)
+            stdscr.addch(cury+1, curx + 2, curses.ACS_HLINE)
+            stdscr.addch(cury+1, curx + 3, curses.ACS_HLINE)
+            curx += 1
+            if c == "x":
+                stdscr.addch(cury, curx, curses.ACS_BLOCK)
+                stdscr.addch(cury, curx+1, curses.ACS_BLOCK)
+                stdscr.addch(cury, curx+2, curses.ACS_BLOCK)
+            elif crossword["grid_questions_start"][curgridy][curgridx] > 0:
+                stdscr.addstr(cury, curx, \
+                    ''.join( \
+                        [superscript_numbers[x].decode(code) \
+                        for x in str( \
+                            crossword["grid_questions_start"] \
+                            [curgridy][curgridx])]\
+                        ).encode(code))
+            curx += 3
+            curgridx += 1
+        else:
+            stdscr.addch(cury, curx, curses.ACS_VLINE)
+            stdscr.addch(cury + 1, curx, curses.ACS_RTEE)
+        cury += 2
+        curgridy += 1
+    cury -= 1
+    curx = 0
+    stdscr.addch(cury, curx, curses.ACS_LLCORNER)
+    curx += 1
+    while ((curx - 1) / 4) < grid_length:
+        stdscr.addch(cury, curx, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+1, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+2, curses.ACS_HLINE)
+        stdscr.addch(cury, curx+3, curses.ACS_BTEE)
+        curx += 4
+    curx -= 1
+    stdscr.addch(cury, curx, curses.ACS_LRCORNER)
+    # draw the clues in
+    cury = (len(crossword["grid"]) * 2) + 1
+    curx = 0
+    stdscr.addstr(cury, curx, "Across")
+    cury += 1
+    for cluenumber in crossword["across"].keys():
+        stdscr.addstr(cury, curx, "%3s: %s" \
+            %(str(cluenumber), crossword["across"][cluenumber]))
+        cury += 1
+
+    cury += 1
+    stdscr.addstr(cury, curx, "Down")
+    cury += 1
+    for cluenumber in crossword["down"].keys():
+        stdscr.addstr(cury, curx, "%3s: %s" \
+            %(str(cluenumber), crossword["down"][cluenumber]))
+        cury += 1
+
+    curx = 3
+    cury = 1
+    gridx = 0
+    gridy = 0
+
+    while crossword["grid"][gridy][gridx] == "x":
+        curx += 4
+        gridx += 1
+
+    stdscr.move(cury, curx)
+
+    while 1:
+        c = stdscr.getch()
+        if c == curses.ascii.ESC:
+            break
+        if c == curses.KEY_RIGHT:
+            if gridx < (len(crossword["grid"][0]) - 1):
+                gridx += 1
+                curx += 4
+                while gridx < (len(crossword["grid"][0]) -1) \
+                    and crossword["grid"][gridy][gridx] == "x":
+                    gridx += 1
+                    curx += 4
+                while crossword["grid"][gridy][gridx] == "x":
+                    gridx -= 1
+                    curx -= 4
+                stdscr.move(cury, curx)
+        if c == curses.KEY_LEFT:
+            if gridx > 0:
+                curx -= 4
+                gridx -= 1
+                while gridx > 0 \
+                    and crossword["grid"][gridy][gridx] == "x":
+                    gridx -= 1
+                    curx -= 4
+                while crossword["grid"][gridy][gridx] == "x":
+                    gridx += 1
+                    curx += 4
+                stdscr.move(cury, curx)
+        if c == curses.KEY_UP:
+            if gridy > 0:
+                gridy -= 1
+                cury -= 2
+                while gridy > 0 \
+                    and crossword["grid"][gridy][gridx] == "x":
+                    gridy -= 1
+                    cury -= 2
+                while crossword["grid"][gridy][gridx] == "x":
+                    gridy += 1
+                    cury += 2
+                stdscr.move(cury, curx)
+        if c == curses.KEY_DOWN:
+            if gridy < (len(crossword["grid"]) - 1):
+                gridy += 1
+                cury += 2
+                while gridy < (len(crossword["grid"]) - 1) \
+                    and crossword["grid"][gridy][gridx] == "x":
+                    gridy += 1
+                    cury += 2
+                while crossword["grid"][gridy][gridx] == "x":
+                    gridy -= 1
+                    cury -= 2
+                stdscr.move(cury, curx)
+        if curses.ascii.isalpha(c) or c == ord(" "):
+            stdscr.addch(cury, curx, c)
+            stdscr.move(cury, curx)
+        if c == curses.KEY_BACKSPACE or c == curses.KEY_DC:
+            stdscr.addch(cury, curx, ord(" "))
+            stdscr.move(cury, curx)
+
+
+curses.wrapper(crossword, crossworddata)