a0676c799b17a438d43b0628a7135c5bf01d11a2
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
5 # Cross puzzle creator class
10 import crosswordpuzzle
12 class CrosswordPuzzleCreator
:
13 # ansi color codes for grid display
21 # Black background white text
22 BLACK_BG
= '\033[40;1;37m'
23 # white background black text
24 WHITE_BG
= '\033[47;1;30m'
25 # white background red text
26 REDWHITE_BG
= '\033[47;1;31m'
32 self
.current_file
= None
36 def save_puzzle (self
):
38 fpuzzle
= open (self
.current_file
, "wb")
39 cPickle
.dump (self
.puzzle
, fpuzzle
, cPickle
.HIGHEST_PROTOCOL
)
40 sys
.stdout
.write (self
.BLUE
+ "Puzzle saved to: " +
41 self
.current_file
+ "\n" + self
.ENDCOL
)
44 def load_puzzle (self
):
46 fpuzzle
= open (self
.current_file
, "rb")
47 self
.puzzle
= cPickle
.load (fpuzzle
)
49 # display the grid with words
50 def print_puzzle (self
, no_words
=False):
51 # if self.puzzle is not none
53 # get row, col and print them (with grid number if set)
54 for col
in range (self
.puzzle
.cols
):
55 # print the first row as column headers
56 sys
.stdout
.write (self
.BLUE
+ ' %02d' % col
+ self
.ENDCOL
)
57 sys
.stdout
.write ("\n")
59 for row
in range (self
.puzzle
.rows
):
60 for col
in range (self
.puzzle
.cols
):
62 # if the cell is numbered i.e. start of a word
63 if self
.puzzle
.data
[row
][col
].numbered
!= 0:
64 sys
.stdout
.write (self
.REDWHITE_BG
+
65 '%02d ' % self
.puzzle
.data
[row
][col
].numbered
+ self
.ENDCOL
)
68 if self
.puzzle
.data
[row
][col
].char
== "#":
69 sys
.stdout
.write (self
.BLACK_BG
+ ' ' + self
.ENDCOL
)
71 sys
.stdout
.write (self
.WHITE_BG
+ ' ' + self
.ENDCOL
)
72 # if the character is not a blank or a block
73 if (self
.puzzle
.data
[row
][col
].char
<> "." and
74 self
.puzzle
.data
[row
][col
].char
<> "#"):
75 # if words are to be shown regardless of hidden/revealed state
77 sys
.stdout
.write (self
.WHITE_BG
+
78 self
.puzzle
.data
[row
][col
].char
+ self
.ENDCOL
)
80 # display only revealed
81 if self
.puzzle
.data
[row
][col
].revealed
is True:
82 sys
.stdout
.write (self
.WHITE_BG
+
83 self
.puzzle
.data
[row
][col
].char
+ self
.ENDCOL
)
86 sys
.stdout
.write (self
.WHITE_BG
+ '.' + self
.ENDCOL
)
87 elif self
.puzzle
.data
[row
][col
].char
== '.':
88 sys
.stdout
.write (self
.WHITE_BG
+
89 self
.puzzle
.data
[row
][col
].char
+ self
.ENDCOL
)
90 elif self
.puzzle
.data
[row
][col
].char
== '#':
91 sys
.stdout
.write (self
.BLACK_BG
+ " " + self
.ENDCOL
)
93 sys
.stdout
.write (' ' + self
.BLUE
+ "%2d" % row
+ self
.ENDCOL
+ "\n")
94 raw_input (self
.BRICKRED
+ "Press <return> to continue" + self
.ENDCOL
)
96 # display existing clues
97 def on_display_clues (self
):
99 aclues
= self
.puzzle
.get_clues_across ()
100 sys
.stdout
.write (self
.BOLD
+ "\n------------\n")
101 sys
.stdout
.write ("Across words\n")
102 sys
.stdout
.write ("------------\n" + self
.ENDCOL
)
104 for word
, clue
in aclues
:
105 sys
.stdout
.write (self
.BOLD
+ word
[0] + ": " + self
.ENDCOL
)
107 sys
.stdout
.write (self
.BLUE
+ clue
+ "\n" + self
.ENDCOL
)
109 sys
.stdout
.write (self
.BLUE
+ "(No clue yet)\n" + self
.ENDCOL
)
110 except crosswordpuzzle
.NoWordsException
:
111 sys
.stderr
.write ("No words across\n")
114 dclues
= self
.puzzle
.get_clues_down ()
115 sys
.stdout
.write (self
.BOLD
+ "\n----------\n")
116 sys
.stdout
.write ("Down words\n")
117 sys
.stdout
.write ("----------\n" + self
.ENDCOL
)
119 for word
, clue
in dclues
:
120 sys
.stdout
.write (self
.BOLD
+ word
[0] + ": " + self
.ENDCOL
)
122 sys
.stdout
.write (self
.BLUE
+ clue
+ "\n" + self
.ENDCOL
)
124 sys
.stdout
.write (self
.BLUE
+ "(No clue yet)\n" + self
.ENDCOL
)
125 except crosswordpuzzle
.NoWordsException
:
126 sys
.stderr
.write ("No words down\n")
128 raw_input (self
.BRICKRED
+ "Press <return> to continue" + self
.ENDCOL
)
130 # set a clue to a word
131 def on_set_clue (self
):
133 # get the row and column
134 srow
= raw_input (self
.BRICKRED
+ "At row: " + self
.ENDCOL
)
135 scol
= raw_input (self
.BRICKRED
+ "At col: " + self
.ENDCOL
)
136 # try converting it to number
141 sys
.stderr
.write ("Invalid row or column\n")
145 # across word set the clue if found
146 aword
, arow
, acol
, alen
= self
.puzzle
.get_word_across (row
, col
)
147 sys
.stdout
.write (self
.BLUE
+ "Across word at position: " + aword
+ "\n" + self
.ENDCOL
)
148 clue
= raw_input (self
.BRICKRED
+ "Clue for across word: " + self
.ENDCOL
)
149 self
.puzzle
.data
[arow
][acol
].clue_across
= clue
150 sys
.stdout
.write (self
.BLUE
+ "Set the clue: \n" + self
.puzzle
.data
[arow
][acol
].clue_across
)
151 except crosswordpuzzle
.NoWordException
:
152 sys
.stderr
.write ("No across word found at that position\n")
155 # down word set the clue if found
156 dword
, drow
, dcol
, dlen
= self
.puzzle
.get_word_down (row
, col
)
157 sys
.stdout
.write (self
.BLUE
+ "Down word at position: " + dword
+ "\n" + self
.ENDCOL
)
158 clue
= raw_input (self
.BRICKRED
+ "Clue for down word: " + self
.ENDCOL
)
159 self
.puzzle
.data
[drow
][dcol
].clue_down
= clue
160 sys
.stdout
.write (self
.BLUE
+ "Set the clue: \n" + self
.puzzle
.data
[drow
][dcol
].clue_down
)
161 except crosswordpuzzle
.NoWordException
:
162 sys
.stderr
.write ("No down word found at that position\n")
165 def on_remove_down (self
):
168 srow
= raw_input (self
.BRICKRED
+ "At row: " + self
.ENDCOL
)
169 scol
= raw_input (self
.BRICKRED
+ "At col: " + self
.ENDCOL
)
174 sys
.stderr
.write ("Invalid row or column\n")
178 self
.puzzle
.remove_word_down (row
, col
)
179 sys
.stdout
.write (self
.BLUE
+ "Down word removed\n" + self
.ENDCOL
)
180 except crosswordpuzzle
.FrozenGridException
:
181 sys
.stderr
.write ("Word cannot be removed from a frozen puzzle\n")
182 except crosswordpuzzle
.NoWordException
:
183 sys
.stderr
.write ("No down word found at that position\n")
185 # remove an across word
186 def on_remove_across (self
):
189 srow
= raw_input (self
.BRICKRED
+ "At row: " + self
.ENDCOL
)
190 scol
= raw_input (self
.BRICKRED
+ "At col: " + self
.ENDCOL
)
195 sys
.stderr
.write ("Invalid row or column\n")
199 self
.puzzle
.remove_word_across (row
, col
)
200 sys
.stdout
.write (self
.BLUE
+ "Across word removed\n" + self
.ENDCOL
)
201 except crosswordpuzzle
.FrozenGridException
:
202 sys
.stderr
.write ("Word cannot be removed from a frozen puzzle\n")
203 except crosswordpuzzle
.NoWordException
:
204 sys
.stderr
.write ("No across word found at that position\n")
206 # add a word to the puzzle
207 def on_add_word (self
, across
=True):
208 # first display the grid
210 # get the row and column
211 srow
= raw_input (self
.BRICKRED
+ "Start row: " + self
.ENDCOL
)
212 scol
= raw_input (self
.BRICKRED
+ "Start col: " + self
.ENDCOL
)
213 # try converting it to number
218 sys
.stderr
.write ("Invalid row or column\n")
221 word
= raw_input (self
.BRICKRED
+ "Word: " + self
.ENDCOL
)
223 # try to add the word to the puzzle grid
226 self
.puzzle
.set_word_across (row
, col
, word
)
228 self
.puzzle
.set_word_down (row
, col
, word
)
229 except crosswordpuzzle
.TooLongWordException
:
230 sys
.stderr
.write ("Word is too long to fit in the grid! Aborting.\n")
231 except crosswordpuzzle
.IntersectWordException
:
232 sys
.stderr
.write ("Word intersects badly with another word!\n")
233 except crosswordpuzzle
.FrozenGridException
:
234 sys
.stderr
.write ("Word cannot be added to a frozen puzzle.\n")
236 # Export to image/HTML
237 def on_export_image (self
, solution
=True):
239 sys
.stdout
.write (self
.BLUE
+ "Exporting puzzle to image/HTML\n")
240 pngfile
= raw_input (self
.BRICKRED
+ "Filename (PNG): " + self
.ENDCOL
)
241 if solution
is False:
242 htmlfile
= raw_input (self
.BRICKRED
+ "Filename (HTML): " +
244 puztitle
= raw_input (self
.BRICKRED
+ "Title of puzzle: " +
246 self
.puzzle
.export_image (pngfile
, htmlfile
, puztitle
, solution
)
248 self
.puzzle
.export_image (pngfile
)
250 sys
.stdout
.write (self
.BLUE
+ "Successfully exported!")
251 except crosswordpuzzle
.FrozenGridException
:
252 sys
.stderr
.write ("Cannot export as grid is not frozen/finalized\n")
253 except crosswordpuzzle
.NoWordsException
:
254 sys
.stderr
.write ("No words to export!\n")
256 # Export to across lite
257 def on_export_acrosslite (self
):
259 sys
.stdout
.write (self
.BLUE
+ "Exporting to AcrossLite(tm) Format\n" +
261 title
= raw_input (self
.BRICKRED
+ "Puzzle title: " + self
.ENDCOL
)
262 name
= raw_input (self
.BRICKRED
+ "Author name: " + self
.ENDCOL
)
263 copyright
= raw_input (self
.BRICKRED
+ "Copyright: " + self
.ENDCOL
)
264 exportfile
= raw_input (self
.BRICKRED
+ "Export to file: " + self
.ENDCOL
)
266 acrosslite_str
= self
.puzzle
.export_acrosslite (title
, name
, copyright
)
267 fexport
= open (exportfile
, "w")
268 fexport
.write (acrosslite_str
)
270 sys
.stdout
.write (self
.BLUE
+ "Exported AcrossLite(tm) File: " +
271 exportfile
+ "\n" + self
.ENDCOL
)
272 except crosswordpuzzle
.FrozenGridException
:
273 sys
.stderr
.write ("Cannot export as grid is not frozen/finalized\n")
274 except crosswordpuzzle
.NoWordsException
:
275 sys
.stderr
.write ("No words to export!\n")
278 def do_puzzle_loop (self
):
279 # there is a current file
280 if self
.current_file
and self
.puzzle
:
282 sys
.stdout
.write (self
.BOLD
+ "\n-----------------------------------\n")
283 sys
.stdout
.write ("Puzzle: " + self
.current_file
+ "\n")
284 sys
.stdout
.write ("-----------------------------------" + self
.ENDCOL
+ "\n")
285 sys
.stdout
.write (self
.BLUE
+ "1. Display grid\n")
286 sys
.stdout
.write ("2. Add across word\n")
287 sys
.stdout
.write ("3. Add down word\n")
288 sys
.stdout
.write ("4. Remove across word\n")
289 sys
.stdout
.write ("5. Remove down word\n")
290 sys
.stdout
.write ("6. Freeze grid\n")
291 sys
.stdout
.write ("7. Unfreeze grid\n")
292 sys
.stdout
.write ("8. Set clue for word\n")
293 sys
.stdout
.write ("9. Display clues\n")
294 sys
.stdout
.write ("R. Reset grid\n")
295 sys
.stdout
.write ("S. Save puzzle\n")
296 sys
.stdout
.write ("E. Export to AcrossLite(TM) format\n")
297 sys
.stdout
.write ("H. Export puzzle as image/HTML\n")
298 sys
.stdout
.write ("I. Export solution as image\n")
299 sys
.stdout
.write ("X. Exit to main menu\n" + self
.ENDCOL
)
300 ch
= raw_input (self
.BRICKRED
+ "Your choice: " + self
.ENDCOL
)
306 self
.on_add_word (False)
308 self
.on_remove_across ()
310 self
.on_remove_down ()
312 self
.puzzle
.freeze_grid ()
314 self
.puzzle
.unfreeze_grid ()
318 self
.on_display_clues ()
319 elif ch
== "R" or ch
== "r":
320 self
.on_reset_grid ()
321 elif ch
== "S" or ch
== "s":
323 elif ch
== "E" or ch
== "e":
324 self
.on_export_acrosslite ()
325 elif ch
== "H" or ch
== "h":
326 self
.on_export_image (False)
327 elif ch
== "I" or ch
== "i":
328 self
.on_export_image ()
329 elif ch
== "X" or ch
== "x":
332 # when user chooses new puzzle
333 def on_new_puzzle (self
):
334 self
.current_file
= raw_input (self
.BRICKRED
+ "New puzzle file name: "
336 srows
= raw_input (self
.BRICKRED
+ "Number of rows: " + self
.ENDCOL
)
337 scols
= raw_input (self
.BRICKRED
+ "Number of cols: " + self
.ENDCOL
)
342 sys
.stderr
.write ("Invalid number of rows/columns")
344 self
.puzzle
= crosswordpuzzle
.CrosswordPuzzle (rows
, cols
)
345 self
.do_puzzle_loop ()
347 # when user chooses to load puzzle
348 def on_load_puzzle (self
):
349 self
.current_file
= raw_input (self
.BRICKRED
+ "Puzzle to load: "
352 self
.do_puzzle_loop ()
354 # when user chooses to reset grid
355 def on_reset_grid (self
):
356 ans
= raw_input (self
.BRICKRED
+
357 "This will clear the entire grid! Are you sure (Y/N)? " + self
.ENDCOL
)
358 if ans
== "y" or ans
== "Y":
359 self
.puzzle
.reset_grid ()
360 sys
.stdout
.write (self
.BLUE
+ "Grid has been cleared of all data!"
361 + self
.ENDCOL
+ "\n")
363 # Main application loop
364 def do_main_loop (self
):
367 sys
.stdout
.write (self
.BOLD
+ "\n-----------------------------------\n")
368 sys
.stdout
.write ("Get A Clue - Crossword Puzzle Maker\n")
369 sys
.stdout
.write ("-----------------------------------\n" + self
.ENDCOL
)
370 sys
.stdout
.write (self
.BLUE
+ "1. Start a new puzzle\n")
371 sys
.stdout
.write ("2. Open an existing puzzle\n")
372 sys
.stdout
.write ("X. Exit\n" + self
.ENDCOL
)
373 ch
= raw_input (self
.BRICKRED
+ "Your choice: " + self
.ENDCOL
)
375 self
.on_new_puzzle ()
377 self
.on_load_puzzle ()
378 if ch
== 'x' or ch
== 'X':