8091d3d8091f69e8df1ac378e842e39343c394f5
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
5 # Class for the puzzle data representation
9 def __init__ (self
, item_char
='.', across_start
= False, down_start
= False,
10 occupied_across
= False, occupied_down
= False, num
= 0,
11 clue_across
= None, clue_down
= None, revealed
= False):
12 # character in the cell
14 # is the cell the start of an across word?
15 self
.across_start
= across_start
16 # is the cell the start of a down word?
17 self
.down_start
= down_start
18 # is the cell occupied by a letter in an across word?
19 self
.occupied_across
= occupied_across
20 # is the cell occupied by a letter in a down word?
21 self
.occupied_down
= occupied_down
22 # numbering of the cell if it is the start of a word
24 # clue across if the cell is the start of an across word
25 self
.clue_across
= clue_across
26 # clue down if the cell is the start of a down word
27 self
.clue_down
= clue_down
28 # is the letter revealed or hidden?
29 self
.revealed
= revealed
31 # exception for too long words
32 class TooLongWordException (Exception):
33 def __init__ (self
, word
, length
):
37 # exception for intersecting words
38 class IntersectWordException (Exception):
39 def __init__ (self
, word
, length
):
43 # exception when grid is sought to be changed when frozen
44 class FrozenGridException (Exception):
46 self
.msg
= "Grid is frozen and cannot be edited"
48 # exception when no word is found at a position
49 class NoWordException (Exception):
50 def __init__ (self
, row
, col
):
53 # exception when no words are present in the grid
54 class NoWordsException (Exception):
56 self
.msg
= "No words in grid"
58 class CrosswordPuzzle
:
59 def __init__ (self
, rows
, cols
):
60 # define number of rows and columns
64 # initialize the list to hold the grid
67 # initial state of the grid is unfrozen
68 self
.frozen_grid
= False
70 # create the grid data
71 for i
in range (rows
):
73 for j
in range (cols
):
74 self
.data
[i
].append (GridItem ())
76 # get all the clues for across
77 def get_clues_across (self
):
80 for row
in range (self
.rows
):
81 for col
in range (self
.cols
):
82 if (self
.data
[row
][col
].occupied_across
is True and
83 self
.data
[row
][col
].across_start
is True):
84 word_across
= self
.get_word_across (row
, col
)
85 clues
.append ((word_across
, self
.data
[row
][col
].clue_across
))
86 # if no across words are found at all
88 raise NoWordsException
92 # get all the clues for down
93 def get_clues_down (self
):
96 for row
in range (self
.rows
):
97 for col
in range (self
.cols
):
98 if (self
.data
[row
][col
].occupied_down
is True and
99 self
.data
[row
][col
].down_start
is True):
100 word_down
= self
.get_word_down (row
, col
)
101 clues
.append ((word_down
, self
.data
[row
][col
].clue_down
))
102 # if no down words are found at all
104 raise NoWordsException
108 # getting a down word at a position
109 def get_word_down (self
, row
, col
):
110 # if index is out of bounds
111 if row
>= self
.rows
or col
>= self
.cols
:
112 raise NoWordException (row
, col
)
114 # if there is no occupied down letter at that position
115 if self
.data
[row
][col
].occupied_down
is False:
116 raise NoWordException (row
, col
)
118 # now traverse the grid to find the beginning of the word
121 # if it is occupied down and is the beginning of the word
122 if (self
.data
[i
][col
].occupied_down
is True and
123 self
.data
[i
][col
].down_start
is True):
130 # now seek the end of the word
132 if self
.data
[i
][col
].occupied_down
is True:
133 word_chars
.append (self
.data
[i
][col
].char
)
138 word
= "".join (word_chars
)
140 # return the word, starting row, column and length as a tuple
141 return (word
, start_row
, col
, len(word
))
143 # getting an across word at a position
144 def get_word_across (self
, row
, col
):
145 # if index is out of bounds
146 if row
>= self
.rows
or col
>= self
.cols
:
147 raise NoWordException (row
, col
)
149 # if there is no occupied across letter at that position
150 if self
.data
[row
][col
].occupied_across
is False:
151 raise NoWordException (row
, col
)
153 # now traverse the grid to look for the beginning of the word
156 # if it is occupied across and is the beginning of the word
157 if (self
.data
[row
][i
].occupied_across
is True and
158 self
.data
[row
][i
].across_start
is True):
165 # now seek the end of the word
167 if self
.data
[row
][i
].occupied_across
is True:
168 word_chars
.append (self
.data
[row
][i
].char
)
173 word
= "".join (word_chars
)
175 # return the word, starting column, row and length as a tuple
176 return (word
, start_col
, row
, len(word
))
178 # setting a down word
179 def set_word_down (self
, row
, col
, word
):
180 # if the grid is frozen the abort
181 if self
.frozen_grid
is True:
182 raise FrozenGridException
184 # if the word length greater than totalrows - startrow
185 if len(word
) > self
.rows
- row
:
186 raise TooLongWordException (word
, len(word
))
188 # is the word intersecting any other word?
189 for i
in range (len(word
)):
191 if self
.data
[row
+i
][col
].occupied_down
is True:
192 raise IntersectWordException (word
, len(word
))
193 # on the previous column except first column
195 # except the first and last col
196 if i
> 0 and i
< len(word
) - 1:
197 if self
.data
[row
+i
][col
-1].occupied_down
is True:
198 raise IntersectWordException (word
, len(word
))
199 # on the next column except last column
200 if col
< len(word
) - 1:
201 # except the first and last row check if there is any
202 # down word in previous column
203 if i
> 0 and i
< len(word
) - 1:
204 if self
.data
[row
+i
][col
+1].occupied_down
is True:
205 raise IntersectWordException (word
, len(word
))
206 # check if there is any across word starting in the
208 if self
.data
[row
+i
][col
+1].across_start
is True:
209 raise IntersectWordException (word
, len(word
))
211 # also check the character before and after
212 if (row
> 0 and self
.data
[row
-1][col
].occupied_down
is True
213 and self
.data
[row
-1][col
].occupied_across
is True):
214 raise IntersectWordException (word
, len(word
))
215 if (row
+ len(word
) < self
.rows
and
216 self
.data
[row
+len(word
)][col
].occupied_across
is True and
217 self
.data
[row
+len(word
)][col
].occupied_down
is True):
218 raise IntersectWordException (word
, len(word
))
220 # set the down start to true
221 self
.data
[row
][col
].down_start
= True
223 for i
in range (len(word
)):
224 self
.data
[row
+i
][col
].occupied_down
= True
225 self
.data
[row
+i
][col
].char
= word
[i
].upper ()
228 # setting an across word
229 def set_word_across (self
, row
, col
, word
):
230 # if the grid is frozen the abort
231 if self
.frozen_grid
is True:
232 raise FrozenGridException
234 # is the word length greater than totalcols - startcol?
235 if len(word
) > self
.cols
- col
:
236 raise TooLongWordException (word
, len(word
))
238 # is the word intersecting any other word?
239 for i
in range (len(word
)):
241 if self
.data
[row
][col
+i
].occupied_across
is True:
242 raise IntersectWordException (word
, len(word
))
243 # on a previous row except first row
245 # if not the first or last col
246 if i
> 0 and i
< len(word
) - 1:
247 if self
.data
[row
-1][col
+i
].occupied_across
is True:
248 raise IntersectWordException (word
, len(word
))
250 if (row
< (self
.rows
- 1)):
251 # except the first and last letter check if there is
252 # any across intersection
253 if i
> 0 and i
< len (word
) - 1:
254 if self
.data
[row
+1][col
+i
].occupied_across
is True:
255 raise IntersectWordException (word
, len(word
))
256 # if a down word is starting at any column below the
258 if self
.data
[row
+1][col
+i
].down_start
is True:
259 raise IntersectWordException (word
, len(word
))
261 # also check the character beyond and before and after
262 if (col
> 0 and (self
.data
[row
][col
-1].occupied_across
is True or
263 self
.data
[row
][col
-1].occupied_down
is True)):
264 raise IntersectWordException (word
, len(word
))
265 if (col
+ len(word
) < self
.cols
and
266 (self
.data
[row
][col
+len(word
)].occupied_across
is True or
267 self
.data
[row
][col
+len(word
)].occupied_down
is True)):
268 raise IntersectWordException (word
, len(word
))
270 # set across start to true
271 self
.data
[row
][col
].across_start
= True
274 for i
in range (len(word
)):
275 self
.data
[row
][col
+i
].char
= word
[i
].upper ()
276 self
.data
[row
][col
+i
].occupied_across
= True
278 # freeze the grid numbers etc.
279 def freeze_grid (self
):
282 # run through the grid
283 for row
in range (self
.rows
):
284 for col
in range (self
.cols
):
285 # if grid is blank set the character to #
286 if (self
.data
[row
][col
].occupied_across
is False
287 and self
.data
[row
][col
].occupied_down
is False):
288 self
.data
[row
][col
].char
= "#"
289 elif (self
.data
[row
][col
].across_start
is True or
290 self
.data
[row
][col
].down_start
is True):
291 self
.data
[row
][col
].numbered
= numbering
294 self
.frozen_grid
= True
296 # unfreeze the grid numbers etc.
297 def unfreeze_grid (self
):
298 # run through the grid
299 for row
in range (self
.rows
):
300 for col
in range (self
.cols
):
301 self
.data
[row
][col
].numbered
= 0
302 if (self
.data
[row
][col
].occupied_across
is False and
303 self
.data
[row
][col
].occupied_down
is False):
304 self
.data
[row
][col
].char
= '.'
306 self
.frozen_grid
= False