3 Play a crossword puzzle in a curses grid. Takes a command line argument of the
15 locale.setlocale(locale.LC_ALL, '')
16 code = locale.getpreferredencoding()
18 superscript_numbers = {}
21 superscript_numbers = {
22 "0": unichr(8304).encode(code),
23 "1": unichr(185).encode(code),
24 "2": unichr(178).encode(code),
25 "3": unichr(179).encode(code),
26 "4": unichr(8308).encode(code),
27 "5": unichr(8309).encode(code),
28 "6": unichr(8310).encode(code),
29 "7": unichr(8311).encode(code),
30 "8": unichr(8312).encode(code),
31 "9": unichr(8313).encode(code),
33 except UnicodeError, e:
34 for number in range(0, 10):
35 superscript_numbers[str(number)] = str(number)
40 (options, args) = getopt.getopt(sys.argv[1:], "f:", "file=")
41 except getopt.GetoptError, err:
45 for option in options:
46 if option[0] == "-f" or option[0] == "--file":
49 if not filename and len(args) > 0:
53 sys.stderr.write("No crossword file specified, exiting.\n")
56 if os.path.exists(filename) and os.path.isfile(filename):
57 crossworddata = codecs.open(filename, "r", "utf-8").read()
59 sys.stderr.write("Couldn't open file %s\n" %(filename))
62 def parsecrossword(crossworddata):
64 Read the content of a crossword file in to memory and rejig it in to the 3
65 seperate sections that we expect - namely the grid, the across clues and
71 crossword = {"grid": [],
74 "grid_questions_start": [],
75 "grid_questions_end": []}
77 for line in crossworddata.split("\n"):
78 line = line.strip("\n")
83 elif line == "ACROSS":
94 crossword["grid"].append(line)
98 question_number = int(parts[0].split(",")[0])
99 clue = " ".join(parts[1:])
100 crossword["across"][int(question_number)] = \
105 question_number = int(parts[0].split(",")[0])
106 clue = " ".join(parts[1:])
107 crossword["down"][int(question_number)] = clue.encode(code)
108 num_cols = len(crossword["grid"][0])
109 num_rows = len(crossword["grid"])
111 current_clue_number = 1
113 for row in range(0, num_rows):
114 crossword["grid_questions_start"].append([])
116 for row in range(0, num_rows):
117 for col in range(0, num_cols):
120 and crossword["grid"][row][col - 1] == "x" \
121 and crossword["grid"][row][col] != "x" \
122 and col < (num_cols - 1) \
123 and crossword["grid"][row][col + 1] != "x":
125 if col == 0 and crossword["grid"][row][col] != "x" \
126 and crossword["grid"][row][col + 1] != "x":
128 if row > 0 and crossword["grid"][row-1][col] == "x" \
129 and row < (num_rows - 1) \
130 and crossword["grid"][row][col] != "x" \
131 and crossword["grid"][row + 1][col] != "x":
133 if row == 0 and crossword["grid"][row][col] != "x" \
134 and crossword["grid"][row + 1][col] != "x":
137 crossword["grid_questions_start"][row] \
138 .append(current_clue_number)
139 current_clue_number += 1
141 crossword["grid_questions_start"][row].append(0)
145 def crossword(stdscr, crossworddata):
146 crossword = parsecrossword(crossworddata)
148 grid_length = len(crossword["grid"][0])
150 stdscr.addch(cury, curx, curses.ACS_ULCORNER)
152 while ((curx - 1) / 4) < grid_length:
153 stdscr.addch(cury, curx, curses.ACS_HLINE)
154 stdscr.addch(cury, curx+1, curses.ACS_HLINE)
155 stdscr.addch(cury, curx+2, curses.ACS_HLINE)
156 stdscr.addch(cury, curx+3, curses.ACS_TTEE)
159 stdscr.addch(cury, curx, curses.ACS_URCORNER)
162 for line in crossword["grid"]:
166 stdscr.addch(cury, curx, curses.ACS_VLINE)
168 stdscr.addch(cury+1, curx, curses.ACS_PLUS)
170 stdscr.addch(cury+1, curx, curses.ACS_LTEE)
171 stdscr.addch(cury+1, curx + 1, curses.ACS_HLINE)
172 stdscr.addch(cury+1, curx + 2, curses.ACS_HLINE)
173 stdscr.addch(cury+1, curx + 3, curses.ACS_HLINE)
176 stdscr.addch(cury, curx, curses.ACS_BLOCK)
177 stdscr.addch(cury, curx+1, curses.ACS_BLOCK)
178 stdscr.addch(cury, curx+2, curses.ACS_BLOCK)
179 elif crossword["grid_questions_start"][curgridy][curgridx] > 0:
180 stdscr.addstr(cury, curx, \
182 [superscript_numbers[x].decode(code) \
184 crossword["grid_questions_start"] \
185 [curgridy][curgridx])]\
190 stdscr.addch(cury, curx, curses.ACS_VLINE)
191 stdscr.addch(cury + 1, curx, curses.ACS_RTEE)
196 stdscr.addch(cury, curx, curses.ACS_LLCORNER)
198 while ((curx - 1) / 4) < grid_length:
199 stdscr.addch(cury, curx, curses.ACS_HLINE)
200 stdscr.addch(cury, curx+1, curses.ACS_HLINE)
201 stdscr.addch(cury, curx+2, curses.ACS_HLINE)
202 stdscr.addch(cury, curx+3, curses.ACS_BTEE)
205 stdscr.addch(cury, curx, curses.ACS_LRCORNER)
206 # draw the clues in (in their own pad)
207 cluespad = curses.newpad( \
208 len(crossword["across"].keys()) \
209 + len(crossword["down"].keys()) \
210 + 3, stdscr.getmaxyx()[1])
211 cury = (len(crossword["grid"]) * 2) + 1
216 (padbry, padbrx) = stdscr.getmaxyx()
217 if padbry > cluespad.getmaxyx()[0] + padtly:
218 padbry = cluespad.getmaxyx()[0] + padtly
220 cluespad.addstr(cury, curx, "Across")
222 for cluenumber in crossword["across"].keys():
223 cluespad.addstr(cury, curx, "%3s: %s" \
224 %(str(cluenumber), crossword["across"][cluenumber]))
228 cluespad.addstr(cury, curx, "Down")
230 for cluenumber in crossword["down"].keys():
231 cluespad.addstr(cury, curx, "%3s: %s" \
232 %(str(cluenumber), crossword["down"][cluenumber]))
240 while crossword["grid"][gridy][gridx] == "x":
244 stdscr.move(cury, curx)
247 cluespad.refresh(cluescury, 0, padtly, padtlx, padbry - 1, padbrx - 1)
248 cluespad.overlay(stdscr, cluescury, 0, padtly, padtlx, padbry - 1, padbrx - 1)
252 inch = stdscr.getch()
253 if inch == curses.ascii.ESC:
255 if inch == curses.KEY_NPAGE:
256 if cluescury < cluespad.getmaxyx()[0] - (padbry - padtly):
258 cluespad.refresh(cluescury, 0, padtly, padtlx, padbry - 1, padbrx - 1)
259 if inch == curses.KEY_PPAGE:
262 cluespad.refresh(cluescury, 0, padtly, padtlx, padbry - 1, padbrx - 1)
263 if inch == curses.KEY_RIGHT:
264 if gridx < (len(crossword["grid"][0]) - 1):
267 while gridx < (len(crossword["grid"][0]) -1) \
268 and crossword["grid"][gridy][gridx] == "x":
271 while crossword["grid"][gridy][gridx] == "x":
274 stdscr.move(cury, curx)
275 if inch == curses.KEY_LEFT:
280 and crossword["grid"][gridy][gridx] == "x":
283 while crossword["grid"][gridy][gridx] == "x":
286 stdscr.move(cury, curx)
287 if inch == curses.KEY_UP:
292 and crossword["grid"][gridy][gridx] == "x":
295 while crossword["grid"][gridy][gridx] == "x":
298 stdscr.move(cury, curx)
299 if inch == curses.KEY_DOWN:
300 if gridy < (len(crossword["grid"]) - 1):
303 while gridy < (len(crossword["grid"]) - 1) \
304 and crossword["grid"][gridy][gridx] == "x":
307 while crossword["grid"][gridy][gridx] == "x":
310 stdscr.move(cury, curx)
311 if curses.ascii.isalpha(inch) or inch == ord(" "):
312 stdscr.addch(cury, curx, inch)
313 stdscr.move(cury, curx)
314 if inch == curses.KEY_BACKSPACE or inch == curses.KEY_DC:
315 stdscr.addch(cury, curx, ord(" "))
316 stdscr.move(cury, curx)
319 curses.wrapper(crossword, crossworddata)