]> git.sommitrealweird.co.uk Git - curses-crossword.git/blob - curses_crossword.py
Make more lint compliant
[curses-crossword.git] / curses_crossword.py
1 #!/usr/bin/python
2 """
3 Play a crossword puzzle in a curses grid. Takes a command line argument of the
4 crossword puzzle file.
5 """
6
7 import curses
8 import curses.ascii
9 import locale
10 import codecs
11 import getopt
12 import os
13 import sys
14
15 locale.setlocale(locale.LC_ALL, '')
16 code = locale.getpreferredencoding()
17
18 superscript_numbers = {}
19
20 try:
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),
32     }
33 except UnicodeError, e:
34     for number in range(0, 10):
35         superscript_numbers[str(number)] = str(number)
36
37 filename = None
38
39 try:
40     (options, args) = getopt.getopt(sys.argv[1:], "f:", "file=")
41 except getopt.GetoptError, err:
42     print str(err)
43     sys.exit(2)
44
45 for option in options:
46     if option[0] == "-f" or option[0] == "--file":
47         filename = option[1]
48
49 if not filename and len(args) > 0:
50     filename = args[0]
51
52 if not filename:
53     sys.stderr.write("No crossword file specified, exiting.\n")
54     sys.exit(0)
55
56 if os.path.exists(filename) and os.path.isfile(filename):
57     crossworddata = codecs.open(filename, "r", "utf-8").read()
58 else:
59     sys.stderr.write("Couldn't open file %s\n" %(filename))
60     sys.exit(0)
61
62 def parsecrossword(crossworddata):
63     """
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
66     the down clues.
67     """
68     ingrid = False
69     inacross = False
70     indown = False
71     crossword = {"grid": [],
72         "across": {},
73         "down": {},
74         "grid_questions_start": [],
75         "grid_questions_end": []}
76
77     for line in crossworddata.split("\n"):
78         line = line.strip("\n")
79         if line == "GRID":
80             ingrid = True
81             inacross = False
82             indown = False
83         elif line == "ACROSS":
84             ingrid = False
85             inacross = True
86             indown = False
87         elif line == "DOWN":
88             ingrid = False
89             inacross = False
90             indown = True
91         else:
92             if ingrid:
93                 if line != "":
94                     crossword["grid"].append(line)
95             if inacross:
96                 if line != "":
97                     parts = line.split()
98                     question_number = int(parts[0])
99                     clue = " ".join(parts[1:])
100                     crossword["across"][int(question_number)] = \
101                         clue.encode(code)
102             if indown:
103                 if line != "":
104                     parts = line.split()
105                     question_number = int(parts[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"])
110
111     current_clue_number = 1
112
113     for row in range(0, num_rows):
114         crossword["grid_questions_start"].append([])
115
116     for row in range(0, num_rows):
117         for col in range(0, num_cols):
118             have_clue = False
119             if col > 0 \
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":
124                 have_clue = True
125             if col == 0 and crossword["grid"][row][col] != "x" \
126                 and crossword["grid"][row][col + 1] != "x":
127                 have_clue = True
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":
132                 have_clue = True
133             if row == 0 and crossword["grid"][row][col] != "x" \
134                 and crossword["grid"][row + 1][col] != "x":
135                 have_clue = True
136             if have_clue:
137                 crossword["grid_questions_start"][row] \
138                     .append(current_clue_number)
139                 current_clue_number += 1
140             else:
141                 crossword["grid_questions_start"][row].append(0)
142
143     return crossword
144
145 def crossword(stdscr, crossworddata):
146     crossword = parsecrossword(crossworddata)
147     cury = 0
148     grid_length = len(crossword["grid"][0])
149     curx = 0
150     stdscr.addch(cury, curx, curses.ACS_ULCORNER)
151     curx += 1
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)
157         curx += 4
158     curx -= 1
159     stdscr.addch(cury, curx, curses.ACS_URCORNER)
160     cury += 1
161     curgridy = 0
162     for line in crossword["grid"]:
163         curx = 0
164         curgridx = 0
165         for curch in line:
166             stdscr.addch(cury, curx, curses.ACS_VLINE)
167             if curx > 0:
168                 stdscr.addch(cury+1, curx, curses.ACS_PLUS)
169             else:
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)
174             curx += 1
175             if curch == "x":
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, \
181                     ''.join( \
182                         [superscript_numbers[x].decode(code) \
183                         for x in str( \
184                             crossword["grid_questions_start"] \
185                             [curgridy][curgridx])]\
186                         ).encode(code))
187             curx += 3
188             curgridx += 1
189         else:
190             stdscr.addch(cury, curx, curses.ACS_VLINE)
191             stdscr.addch(cury + 1, curx, curses.ACS_RTEE)
192         cury += 2
193         curgridy += 1
194     cury -= 1
195     curx = 0
196     stdscr.addch(cury, curx, curses.ACS_LLCORNER)
197     curx += 1
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)
203         curx += 4
204     curx -= 1
205     stdscr.addch(cury, curx, curses.ACS_LRCORNER)
206     # draw the clues in
207     cury = (len(crossword["grid"]) * 2) + 1
208     curx = 0
209     stdscr.addstr(cury, curx, "Across")
210     cury += 1
211     for cluenumber in crossword["across"].keys():
212         stdscr.addstr(cury, curx, "%3s: %s" \
213             %(str(cluenumber), crossword["across"][cluenumber]))
214         cury += 1
215
216     cury += 1
217     stdscr.addstr(cury, curx, "Down")
218     cury += 1
219     for cluenumber in crossword["down"].keys():
220         stdscr.addstr(cury, curx, "%3s: %s" \
221             %(str(cluenumber), crossword["down"][cluenumber]))
222         cury += 1
223
224     curx = 3
225     cury = 1
226     gridx = 0
227     gridy = 0
228
229     while crossword["grid"][gridy][gridx] == "x":
230         curx += 4
231         gridx += 1
232
233     stdscr.move(cury, curx)
234
235     while 1:
236         inch = stdscr.getch()
237         if inch == curses.ascii.ESC:
238             break
239         if inch == curses.KEY_RIGHT:
240             if gridx < (len(crossword["grid"][0]) - 1):
241                 gridx += 1
242                 curx += 4
243                 while gridx < (len(crossword["grid"][0]) -1) \
244                     and crossword["grid"][gridy][gridx] == "x":
245                     gridx += 1
246                     curx += 4
247                 while crossword["grid"][gridy][gridx] == "x":
248                     gridx -= 1
249                     curx -= 4
250                 stdscr.move(cury, curx)
251         if inch == curses.KEY_LEFT:
252             if gridx > 0:
253                 curx -= 4
254                 gridx -= 1
255                 while gridx > 0 \
256                     and crossword["grid"][gridy][gridx] == "x":
257                     gridx -= 1
258                     curx -= 4
259                 while crossword["grid"][gridy][gridx] == "x":
260                     gridx += 1
261                     curx += 4
262                 stdscr.move(cury, curx)
263         if inch == curses.KEY_UP:
264             if gridy > 0:
265                 gridy -= 1
266                 cury -= 2
267                 while gridy > 0 \
268                     and crossword["grid"][gridy][gridx] == "x":
269                     gridy -= 1
270                     cury -= 2
271                 while crossword["grid"][gridy][gridx] == "x":
272                     gridy += 1
273                     cury += 2
274                 stdscr.move(cury, curx)
275         if inch == curses.KEY_DOWN:
276             if gridy < (len(crossword["grid"]) - 1):
277                 gridy += 1
278                 cury += 2
279                 while gridy < (len(crossword["grid"]) - 1) \
280                     and crossword["grid"][gridy][gridx] == "x":
281                     gridy += 1
282                     cury += 2
283                 while crossword["grid"][gridy][gridx] == "x":
284                     gridy -= 1
285                     cury -= 2
286                 stdscr.move(cury, curx)
287         if curses.ascii.isalpha(inch) or inch == ord(" "):
288             stdscr.addch(cury, curx, inch)
289             stdscr.move(cury, curx)
290         if inch == curses.KEY_BACKSPACE or inch == curses.KEY_DC:
291             stdscr.addch(cury, curx, ord(" "))
292             stdscr.move(cury, curx)
293
294
295 curses.wrapper(crossword, crossworddata)