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 class CrosswordPuzzle
:
49 # ansi color codes for grid display
50 BRICKRED
= '\033[44;1;31m'
60 def __init__ (self
, rows
, cols
):
61 # define number of rows and columns
65 # initialize the list to hold the grid
68 # initial state of the grid is unfrozen
69 self
.frozen_grid
= False
71 # create the grid data
72 for i
in range (rows
):
74 for j
in range (cols
):
75 self
.data
[i
].append (GridItem ())
78 def set_word_down (self
, row
, col
, word
):
79 # if the grid is frozen the abort
80 if self
.frozen_grid
is True:
81 raise FrozenGridException
83 # if the word length greater than totalrows - startrow
84 if len(word
) > self
.rows
- row
:
85 raise TooLongWordException (word
, len(word
))
87 # is the word intersecting any other word?
88 for i
in range (len(word
)):
90 if self
.data
[row
+i
][col
].occupied_down
is True:
91 raise IntersectWordException (word
, len(word
))
92 # on the previous column
93 if col
> 0 and self
.data
[row
+i
][col
-1].occupied_down
is True:
94 raise IntersectWordException (word
, len(word
))
96 if (col
< (len(word
) - 1) and
97 (self
.data
[row
+i
][col
+1].occupied_down
is True or
98 self
.data
[row
+i
][col
+1].across_start
is True)):
99 raise IntersectWordException (word
, len(word
))
101 # also check the character before and after
102 if (row
> 0 and self
.data
[row
-1][col
].occupied_down
is True
103 and self
.data
[row
-1][col
].occupied_across
is True):
104 raise IntersectWordException (word
, len(word
))
105 if (row
+ len(word
) < self
.rows
and
106 self
.data
[row
+len(word
)][col
].occupied_across
is True and
107 self
.data
[row
+len(word
)][col
].occupied_down
is True):
108 raise IntersectWordException (word
, len(word
))
110 # set the down start to true
111 self
.data
[row
][col
].down_start
= True
113 for i
in range (len(word
)):
114 self
.data
[row
+i
][col
].occupied_down
= True
115 self
.data
[row
+i
][col
].char
= word
[i
].upper ()
118 # setting an across word
119 def set_word_across (self
, row
, col
, word
):
120 # if the grid is frozen the abort
121 if self
.frozen_grid
is True:
122 raise FrozenGridException
124 # is the word length greater than totalcols - startcol?
125 if len(word
) > self
.cols
- col
:
126 raise TooLongWordException (word
, len(word
))
128 # is the word intersecting any other word?
129 for i
in range (len(word
)):
131 if self
.data
[row
][col
+i
].occupied_across
is True:
132 raise IntersectWordException (word
, len(word
))
133 # on a previous line except the last column
134 if row
> 0 and self
.data
[row
-1][col
+i
].occupied_across
is True:
135 raise IntersectWordException (word
, len(word
))
136 # on a next line except the last column
137 if (row
< (self
.rows
- 1) and
138 (self
.data
[row
+1][col
+i
].down_start
is True
139 or self
.data
[row
+1][col
+i
].occupied_across
is True)):
140 raise IntersectWordException (word
, len(word
))
142 # also check the character beyond and before and after
143 if (col
> 0 and (self
.data
[row
][col
-1].occupied_across
is True or
144 self
.data
[row
][col
-1].occupied_down
is True)):
145 raise IntersectWordException (word
, len(word
))
146 if (col
+ len(word
) < self
.cols
and
147 (self
.data
[row
][col
+len(word
)].occupied_across
is True or
148 self
.data
[row
][col
+len(word
)].occupied_down
is True)):
149 raise IntersectWordException (word
, len(word
))
151 # set across start to true
152 self
.data
[row
][col
].across_start
= True
155 for i
in range (len(word
)):
156 self
.data
[row
][col
+i
].char
= word
[i
].upper ()
157 self
.data
[row
][col
+i
].occupied_across
= True
159 # display the grid with words
160 def print_grid (self
, no_words
=False):
161 # get row, col and print them (with grid number if set)
162 for col
in range (self
.cols
):
163 # print the first row as column headers
164 print self
.BLUE
+ ' ' + str (col
) + self
.ENDCOL
,
167 for row
in range (self
.rows
):
168 for col
in range (self
.cols
):
170 # if the cell is numbered i.e. start of a word
171 if self
.data
[row
][col
].numbered
!= 0:
172 print self
.BRICKRED
+ str(self
.data
[row
][col
].numbered
) + self
.ENDCOL
,
176 # if the character is not a blank or a block
177 if self
.data
[row
][col
].char
<> "." and self
.data
[row
][col
].char
<> "#":
178 # if words are to be shown regardless of hidden/revealed state
179 if no_words
is False:
180 print self
.BOLD
+ self
.data
[row
][col
].char
+ self
.ENDCOL
,
182 # display only revealed
183 if self
.data
[row
][col
].revealed
is True:
184 print self
.BOLD
+ self
.data
[row
][col
].char
+ self
.ENDCOL
,
187 print self
.GREY
+ '.' + self
.ENDCOL
,
189 print self
.GREY
+ self
.data
[row
][col
].char
+ self
.ENDCOL
,
191 print ' ' + self
.BLUE
+ str(row
) + self
.ENDCOL
193 # freeze the grid numbers etc.
194 def freeze_grid (self
):
197 # run through the grid
198 for row
in range (self
.rows
):
199 for col
in range (self
.cols
):
200 # if grid is blank set the character to #
201 if (self
.data
[row
][col
].occupied_across
is False
202 and self
.data
[row
][col
].occupied_down
is False):
203 self
.data
[row
][col
].char
= "#"
204 elif (self
.data
[row
][col
].across_start
is True or
205 self
.data
[row
][col
].down_start
is True):
206 self
.data
[row
][col
].numbered
= numbering
209 self
.frozen_grid
= True