9617897fddada51883187489cdb2153e268ab6f6
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 class CrosswordPuzzle
:
54 def __init__ (self
, rows
, cols
):
55 # define number of rows and columns
59 # initialize the list to hold the grid
62 # initial state of the grid is unfrozen
63 self
.frozen_grid
= False
65 # create the grid data
66 for i
in range (rows
):
68 for j
in range (cols
):
69 self
.data
[i
].append (GridItem ())
71 # getting a down word at a position
72 def get_word_down (self
, row
, col
):
73 # if index is out of bounds
74 if row
>= self
.rows
or col
>= self
.cols
:
75 raise NoWordException (row
, col
)
77 # if there is no occupied down letter at that position
78 if self
.data
[row
][col
].occupied_down
is False:
79 raise NoWordException (row
, col
)
81 # now traverse the grid to find the beginning of the word
84 # if it is occupied down and is the beginning of the word
85 if (self
.data
[i
][col
].occupied_down
is True and
86 self
.data
[i
][col
].down_start
is True):
93 # now seek the end of the word
95 if self
.data
[i
][col
].occupied_down
is True:
96 word_chars
.append (self
.data
[i
][col
].char
)
101 word
= "".join (word_chars
)
103 # return the word, starting row, column and length as a tuple
104 return (word
, start_row
, col
, len(word
))
106 # getting an across word at a position
107 def get_word_across (self
, row
, col
):
108 # if index is out of bounds
109 if row
>= self
.rows
or col
>= self
.cols
:
110 raise NoWordException (row
, col
)
112 # if there is no occupied across letter at that position
113 if self
.data
[row
][col
].occupied_across
is False:
114 raise NoWordException (row
, col
)
116 # now traverse the grid to look for the beginning of the word
119 # if it is occupied across and is the beginning of the word
120 if (self
.data
[row
][i
].occupied_across
is True and
121 self
.data
[row
][i
].across_start
is True):
128 # now seek the end of the word
130 if self
.data
[row
][i
].occupied_across
is True:
131 word_chars
.append (self
.data
[row
][i
].char
)
136 word
= "".join (word_chars
)
138 # return the word, starting column, row and length as a tuple
139 return (word
, start_col
, row
, len(word
))
141 # setting a down word
142 def set_word_down (self
, row
, col
, word
):
143 # if the grid is frozen the abort
144 if self
.frozen_grid
is True:
145 raise FrozenGridException
147 # if the word length greater than totalrows - startrow
148 if len(word
) > self
.rows
- row
:
149 raise TooLongWordException (word
, len(word
))
151 # is the word intersecting any other word?
152 for i
in range (len(word
)):
154 if self
.data
[row
+i
][col
].occupied_down
is True:
155 raise IntersectWordException (word
, len(word
))
156 # on the previous column except first column
158 # except the first and last col
159 if i
> 0 and i
< len(word
) - 1:
160 if self
.data
[row
+i
][col
-1].occupied_down
is True:
161 raise IntersectWordException (word
, len(word
))
162 # on the next column except last column
163 if col
< len(word
) - 1:
164 # except the first and last row check if there is any
165 # down word in previous column
166 if i
> 0 and i
< len(word
) - 1:
167 if self
.data
[row
+i
][col
+1].occupied_down
is True:
168 raise IntersectWordException (word
, len(word
))
169 # check if there is any across word starting in the
171 if self
.data
[row
+i
][col
+1].across_start
is True:
172 raise IntersectWordException (word
, len(word
))
174 # also check the character before and after
175 if (row
> 0 and self
.data
[row
-1][col
].occupied_down
is True
176 and self
.data
[row
-1][col
].occupied_across
is True):
177 raise IntersectWordException (word
, len(word
))
178 if (row
+ len(word
) < self
.rows
and
179 self
.data
[row
+len(word
)][col
].occupied_across
is True and
180 self
.data
[row
+len(word
)][col
].occupied_down
is True):
181 raise IntersectWordException (word
, len(word
))
183 # set the down start to true
184 self
.data
[row
][col
].down_start
= True
186 for i
in range (len(word
)):
187 self
.data
[row
+i
][col
].occupied_down
= True
188 self
.data
[row
+i
][col
].char
= word
[i
].upper ()
191 # setting an across word
192 def set_word_across (self
, row
, col
, word
):
193 # if the grid is frozen the abort
194 if self
.frozen_grid
is True:
195 raise FrozenGridException
197 # is the word length greater than totalcols - startcol?
198 if len(word
) > self
.cols
- col
:
199 raise TooLongWordException (word
, len(word
))
201 # is the word intersecting any other word?
202 for i
in range (len(word
)):
204 if self
.data
[row
][col
+i
].occupied_across
is True:
205 raise IntersectWordException (word
, len(word
))
206 # on a previous row except first row
208 # if not the first or last col
209 if i
> 0 and i
< len(word
) - 1:
210 if self
.data
[row
-1][col
+i
].occupied_across
is True:
211 raise IntersectWordException (word
, len(word
))
213 if (row
< (self
.rows
- 1)):
214 # except the first and last letter check if there is
215 # any across intersection
216 if i
> 0 and i
< len (word
) - 1:
217 if self
.data
[row
+1][col
+i
].occupied_across
is True:
218 raise IntersectWordException (word
, len(word
))
219 # if a down word is starting at any column below the
221 if self
.data
[row
+1][col
+i
].down_start
is True:
222 raise IntersectWordException (word
, len(word
))
224 # also check the character beyond and before and after
225 if (col
> 0 and (self
.data
[row
][col
-1].occupied_across
is True or
226 self
.data
[row
][col
-1].occupied_down
is True)):
227 raise IntersectWordException (word
, len(word
))
228 if (col
+ len(word
) < self
.cols
and
229 (self
.data
[row
][col
+len(word
)].occupied_across
is True or
230 self
.data
[row
][col
+len(word
)].occupied_down
is True)):
231 raise IntersectWordException (word
, len(word
))
233 # set across start to true
234 self
.data
[row
][col
].across_start
= True
237 for i
in range (len(word
)):
238 self
.data
[row
][col
+i
].char
= word
[i
].upper ()
239 self
.data
[row
][col
+i
].occupied_across
= True
241 # freeze the grid numbers etc.
242 def freeze_grid (self
):
245 # run through the grid
246 for row
in range (self
.rows
):
247 for col
in range (self
.cols
):
248 # if grid is blank set the character to #
249 if (self
.data
[row
][col
].occupied_across
is False
250 and self
.data
[row
][col
].occupied_down
is False):
251 self
.data
[row
][col
].char
= "#"
252 elif (self
.data
[row
][col
].across_start
is True or
253 self
.data
[row
][col
].down_start
is True):
254 self
.data
[row
][col
].numbered
= numbering
257 self
.frozen_grid
= True
259 # unfreeze the grid numbers etc.
260 def unfreeze_grid (self
):
261 # run through the grid
262 for row
in range (self
.rows
):
263 for col
in range (self
.cols
):
264 self
.data
[row
][col
].numbered
= 0
265 if (self
.data
[row
][col
].occupied_across
is False and
266 self
.data
[row
][col
].occupied_down
is False):
267 self
.data
[row
][col
].char
= '.'
269 self
.frozen_grid
= False