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