Made some logic changes to word intersection code
[getaclue.git] / crosswordpuzzle.py
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
4
5 # Class for the puzzle data representation
6
7 class GridItem:
8 # initialize the item
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
13 self.char = item_char
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
23 self.numbered = num
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
30
31 # exception for too long words
32 class TooLongWordException (Exception):
33 def __init__ (self, word, length):
34 self.word = word
35 self.length = length
36
37 # exception for intersecting words
38 class IntersectWordException (Exception):
39 def __init__ (self, word, length):
40 self.word = word
41 self.length = length
42
43 # exception when grid is sought to be changed when frozen
44 class FrozenGridException (Exception):
45 def __init__ (self):
46 self.msg = "Grid is frozen and cannot be edited"
47
48 class CrosswordPuzzle:
49 def __init__ (self, rows, cols):
50 # define number of rows and columns
51 self.rows = rows
52 self.cols = cols
53
54 # initialize the list to hold the grid
55 self.data = []
56
57 # initial state of the grid is unfrozen
58 self.frozen_grid = False
59
60 # create the grid data
61 for i in range (rows):
62 self.data.append ([])
63 for j in range (cols):
64 self.data[i].append (GridItem ())
65
66 # setting a down word
67 def set_word_down (self, row, col, word):
68 # if the grid is frozen the abort
69 if self.frozen_grid is True:
70 raise FrozenGridException
71
72 # if the word length greater than totalrows - startrow
73 if len(word) > self.rows - row:
74 raise TooLongWordException (word, len(word))
75
76 # is the word intersecting any other word?
77 for i in range (len(word)):
78 # on the same column
79 if self.data[row+i][col].occupied_down is True:
80 raise IntersectWordException (word, len(word))
81 # on the previous column except first column
82 if col > 0:
83 # except the first and last col
84 if i > 0 and i < len(word) - 1:
85 if self.data[row+i][col-1].occupied_down is True:
86 raise IntersectWordException (word, len(word))
87 # on the next column except last column
88 if col < len(word) - 1:
89 # except the first and last row check if there is any
90 # down word in previous column
91 if i > 0 and i < len(word) - 1:
92 if self.data[row+i][col+1].occupied_down is True:
93 raise IntersectWordException (word, len(word))
94 # check if there is any across word starting in the
95 # next column
96 if self.data[row+i][col+1].across_start is True:
97 raise IntersectWordException (word, len(word))
98
99 # also check the character before and after
100 if (row > 0 and self.data[row-1][col].occupied_down is True
101 and self.data[row-1][col].occupied_across is True):
102 raise IntersectWordException (word, len(word))
103 if (row + len(word) < self.rows and
104 self.data[row+len(word)][col].occupied_across is True and
105 self.data[row+len(word)][col].occupied_down is True):
106 raise IntersectWordException (word, len(word))
107
108 # set the down start to true
109 self.data[row][col].down_start = True
110 # set the word
111 for i in range (len(word)):
112 self.data[row+i][col].occupied_down = True
113 self.data[row+i][col].char = word[i].upper ()
114
115
116 # setting an across word
117 def set_word_across (self, row, col, word):
118 # if the grid is frozen the abort
119 if self.frozen_grid is True:
120 raise FrozenGridException
121
122 # is the word length greater than totalcols - startcol?
123 if len(word) > self.cols - col:
124 raise TooLongWordException (word, len(word))
125
126 # is the word intersecting any other word?
127 for i in range (len(word)):
128 # on the same row
129 if self.data[row][col+i].occupied_across is True:
130 raise IntersectWordException (word, len(word))
131 # on a previous row except first row
132 if row > 0:
133 # if not the first or last col
134 if i > 0 and i < len(word) - 1:
135 if self.data[row-1][col+i].occupied_across is True:
136 raise IntersectWordException (word, len(word))
137 # on a next row
138 if (row < (self.rows - 1)):
139 # except the first and last letter check if there is
140 # any across intersection
141 if i > 0 and i < len (word) - 1:
142 if self.data[row+1][col+i].occupied_across is True:
143 raise IntersectWordException (word, len(word))
144 # if a down word is starting at any column below the
145 # word
146 if self.data[row+1][col+i].down_start is True:
147 raise IntersectWordException (word, len(word))
148
149 # also check the character beyond and before and after
150 if (col > 0 and (self.data[row][col-1].occupied_across is True or
151 self.data[row][col-1].occupied_down is True)):
152 raise IntersectWordException (word, len(word))
153 if (col + len(word) < self.cols and
154 (self.data[row][col+len(word)].occupied_across is True or
155 self.data[row][col+len(word)].occupied_down is True)):
156 raise IntersectWordException (word, len(word))
157
158 # set across start to true
159 self.data[row][col].across_start = True
160
161 # set the word
162 for i in range (len(word)):
163 self.data[row][col+i].char = word[i].upper ()
164 self.data[row][col+i].occupied_across = True
165
166 # freeze the grid numbers etc.
167 def freeze_grid (self):
168 # numbering
169 numbering = 1
170 # run through the grid
171 for row in range (self.rows):
172 for col in range (self.cols):
173 # if grid is blank set the character to #
174 if (self.data[row][col].occupied_across is False
175 and self.data[row][col].occupied_down is False):
176 self.data[row][col].char = "#"
177 elif (self.data[row][col].across_start is True or
178 self.data[row][col].down_start is True):
179 self.data[row][col].numbered = numbering
180 numbering += 1
181
182 self.frozen_grid = True
183
184 # unfreeze the grid numbers etc.
185 def unfreeze_grid (self):
186 # run through the grid
187 for row in range (self.rows):
188 for col in range (self.cols):
189 self.data[row][col].numbered = 0
190 if (self.data[row][col].occupied_across is False and
191 self.data[row][col].occupied_down is False):
192 self.data[row][col].char = '.'
193
194 self.frozen_grid = False
195