1e62315ecd42f6fc4fb71ca8962e412a55e39455
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")
164 # add a word to the puzzle
165 def on_add_word (self
, across
=True):
166 # first display the grid
168 # get the row and column
169 srow
= raw_input (self
.BRICKRED
+ "Start row: " + self
.ENDCOL
)
170 scol
= raw_input (self
.BRICKRED
+ "Start col: " + self
.ENDCOL
)
171 # try converting it to number
176 sys
.stderr
.write ("Invalid row or column\n")
179 word
= raw_input (self
.BRICKRED
+ "Word: " + self
.ENDCOL
)
181 # try to add the word to the puzzle grid
184 self
.puzzle
.set_word_across (row
, col
, word
)
186 self
.puzzle
.set_word_down (row
, col
, word
)
187 except crosswordpuzzle
.TooLongWordException
:
188 sys
.stderr
.write ("Word is too long to fit in the grid! Aborting.\n")
189 except crosswordpuzzle
.IntersectWordException
:
190 sys
.stderr
.write ("Word intersects badly with another word!\n")
191 except crosswordpuzzle
.FrozenGridException
:
192 sys
.stderr
.write ("Word cannot be added to a frozen puzzle.\n")
194 # Export to image/HTML
195 def on_export_image (self
, solution
=True):
197 sys
.stdout
.write (self
.BLUE
+ "Exporting puzzle to image/HTML\n")
198 pngfile
= raw_input (self
.BRICKRED
+ "Filename (PNG): " + self
.ENDCOL
)
199 if solution
is False:
200 htmlfile
= raw_input (self
.BRICKRED
+ "Filename (HTML): " +
202 puztitle
= raw_input (self
.BRICKRED
+ "Title of puzzle: " +
204 self
.puzzle
.export_image (pngfile
, htmlfile
, puztitle
, solution
)
206 self
.puzzle
.export_image (pngfile
)
208 sys
.stdout
.write (self
.BLUE
+ "Successfully exported!")
209 except crosswordpuzzle
.FrozenGridException
:
210 sys
.stderr
.write ("Cannot export as grid is not frozen/finalized\n")
211 except crosswordpuzzle
.NoWordsException
:
212 sys
.stderr
.write ("No words to export!\n")
214 # Export to across lite
215 def on_export_acrosslite (self
):
217 sys
.stdout
.write (self
.BLUE
+ "Exporting to AcrossLite(tm) Format\n" +
219 title
= raw_input (self
.BRICKRED
+ "Puzzle title: " + self
.ENDCOL
)
220 name
= raw_input (self
.BRICKRED
+ "Author name: " + self
.ENDCOL
)
221 copyright
= raw_input (self
.BRICKRED
+ "Copyright: " + self
.ENDCOL
)
222 exportfile
= raw_input (self
.BRICKRED
+ "Export to file: " + self
.ENDCOL
)
224 acrosslite_str
= self
.puzzle
.export_acrosslite (title
, name
, copyright
)
225 fexport
= open (exportfile
, "w")
226 fexport
.write (acrosslite_str
)
228 sys
.stdout
.write (self
.BLUE
+ "Exported AcrossLite(tm) File: " +
229 exportfile
+ "\n" + self
.ENDCOL
)
230 except crosswordpuzzle
.FrozenGridException
:
231 sys
.stderr
.write ("Cannot export as grid is not frozen/finalized\n")
232 except crosswordpuzzle
.NoWordsException
:
233 sys
.stderr
.write ("No words to export!\n")
236 def do_puzzle_loop (self
):
237 # there is a current file
238 if self
.current_file
and self
.puzzle
:
240 sys
.stdout
.write (self
.BOLD
+ "\n-----------------------------------\n")
241 sys
.stdout
.write ("Puzzle: " + self
.current_file
+ "\n")
242 sys
.stdout
.write ("-----------------------------------" + self
.ENDCOL
+ "\n")
243 sys
.stdout
.write (self
.BLUE
+ "1. Display grid\n")
244 sys
.stdout
.write ("2. Add across word\n")
245 sys
.stdout
.write ("3. Add down word\n")
246 sys
.stdout
.write ("4. Remove across word\n")
247 sys
.stdout
.write ("5. Remove down word\n")
248 sys
.stdout
.write ("6. Freeze grid\n")
249 sys
.stdout
.write ("7. Unfreeze grid\n")
250 sys
.stdout
.write ("8. Set clue for word\n")
251 sys
.stdout
.write ("9. Display clues\n")
252 sys
.stdout
.write ("R. Reset grid\n")
253 sys
.stdout
.write ("S. Save puzzle\n")
254 sys
.stdout
.write ("E. Export to AcrossLite(TM) format\n")
255 sys
.stdout
.write ("H. Export puzzle as image/HTML\n")
256 sys
.stdout
.write ("I. Export solution as image\n")
257 sys
.stdout
.write ("X. Exit to main menu\n" + self
.ENDCOL
)
258 ch
= raw_input (self
.BRICKRED
+ "Your choice: " + self
.ENDCOL
)
264 self
.on_add_word (False)
266 self
.puzzle
.freeze_grid ()
268 self
.puzzle
.unfreeze_grid ()
272 self
.on_display_clues ()
273 elif ch
== "R" or ch
== "r":
274 self
.on_reset_grid ()
275 elif ch
== "S" or ch
== "s":
277 elif ch
== "E" or ch
== "e":
278 self
.on_export_acrosslite ()
279 elif ch
== "H" or ch
== "h":
280 self
.on_export_image (False)
281 elif ch
== "I" or ch
== "i":
282 self
.on_export_image ()
283 elif ch
== "X" or ch
== "x":
286 # when user chooses new puzzle
287 def on_new_puzzle (self
):
288 self
.current_file
= raw_input (self
.BRICKRED
+ "New puzzle file name: "
290 srows
= raw_input (self
.BRICKRED
+ "Number of rows: " + self
.ENDCOL
)
291 scols
= raw_input (self
.BRICKRED
+ "Number of cols: " + self
.ENDCOL
)
296 sys
.stderr
.write ("Invalid number of rows/columns")
298 self
.puzzle
= crosswordpuzzle
.CrosswordPuzzle (rows
, cols
)
299 self
.do_puzzle_loop ()
301 # when user chooses to load puzzle
302 def on_load_puzzle (self
):
303 self
.current_file
= raw_input (self
.BRICKRED
+ "Puzzle to load: "
306 self
.do_puzzle_loop ()
308 # when user chooses to reset grid
309 def on_reset_grid (self
):
310 ans
= raw_input (self
.BRICKRED
+
311 "This will clear the entire grid! Are you sure (Y/N)? " + self
.ENDCOL
)
312 if ans
== "y" or ans
== "Y":
313 self
.puzzle
.reset_grid ()
314 sys
.stdout
.write (self
.BLUE
+ "Grid has been cleared of all data!"
315 + self
.ENDCOL
+ "\n")
317 # Main application loop
318 def do_main_loop (self
):
321 sys
.stdout
.write (self
.BOLD
+ "\n-----------------------------------\n")
322 sys
.stdout
.write ("Get A Clue - Crossword Puzzle Maker\n")
323 sys
.stdout
.write ("-----------------------------------\n" + self
.ENDCOL
)
324 sys
.stdout
.write (self
.BLUE
+ "1. Start a new puzzle\n")
325 sys
.stdout
.write ("2. Open an existing puzzle\n")
326 sys
.stdout
.write ("X. Exit\n" + self
.ENDCOL
)
327 ch
= raw_input (self
.BRICKRED
+ "Your choice: " + self
.ENDCOL
)
329 self
.on_new_puzzle ()
331 self
.on_load_puzzle ()
332 if ch
== 'x' or ch
== 'X':