-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbtsolver.py
More file actions
335 lines (285 loc) · 11.7 KB
/
btsolver.py
File metadata and controls
335 lines (285 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
import filereader
import gameboard
import variable
import domain
import trail
import constraint
import constraintnetwork
import time
# dictionary mapping heuristic to number
'''
for example, to set the variable selection heuristic to MRV,
you would say,
self.setVariableSelectionHeuristic(VariableSelectionHeuristic['MinimumRemainingValue'])
this is needed when you have more than one heuristic to break ties or use one over the other in precedence.
you can also manually set the heuristics in the main.py file when reading the parameters
as the primary heuristics to use and then break ties within the functions you implement
It follows similarly to the other heuristics and chekcs
'''
VariableSelectionHeuristic = {'None': 0, 'MRV': 1, 'DH': 2}
ValueSelectionHeuristic = {'None': 0, 'LCV': 1}
ConsistencyCheck = {'None': 0, 'ForwardChecking': 1, 'ArcConsistency': 2}
HeuristicCheck = {'None': 0, 'NKP': 1, 'NKT': 2}
class BTSolver:
"Backtracking solver"
######### Constructors Method #########
def __init__(self, gb):
self.network = filereader.GameBoardToConstraintNetwork(gb)
self.trail = trail.masterTrailVariable
self.hassolution = False
self.gameboard = gb
self.numAssignments = 0
self.numBacktracks = 0
self.preprocessing_startTime = 0
self.preprocessing_endTime = 0
self.startTime = None
self.endTime = None
self.varHeuristics = 0 # refers to which variable selection heuristic in use(0 means default, 1 means MRV, 2 means DEGREE)
self.valHeuristics = 0 # refers to which value selection heuristic in use(0 means default, 1 means LCV)
self.cChecks = 0 # refers to which consistency check will be run(0 for backtracking, 1 for forward checking, 2 for arc consistency)
self.heuristicChecks = 0
# self.runCheckOnce = False
self.tokens = [] # tokens(heuristics to use)
######### Modifiers Method #########
def setTokens(self, tokens):
''' set the set of heuristics to be taken into consideration'''
self.tokens = tokens
def setVariableSelectionHeuristic(self, vsh):
'''modify the variable selection heuristic'''
self.varHeuristics = vsh
def setValueSelectionHeuristic(self, vsh):
'''modify the value selection heuristic'''
self.valHeuristics = vsh
def setConsistencyChecks(self, cc):
'''modify the consistency check'''
self.cChecks = cc
def setHeuristicChecks(self, hc):
'''modify the heurisic check (naked pairs and triples)'''
self.heuristicChecks += hc
######### Accessors Method #########
def getSolution(self):
return self.gameboard
# @return time required for the solver to attain in seconds
def getTimeTaken(self):
return self.endTime - self.startTime
######### Helper Method #########
def checkConsistency(self):
'''which consistency check to run but it is up to you when implementing the heuristics to break ties using the other heuristics passed in'''
if self.cChecks == 0:
return self.assignmentsCheck()
elif self.cChecks == 1:
return self.forwardChecking()
elif self.cChecks == 2:
return self.arcConsistency()
else:
return self.assignmentsCheck()
def checkHeuristics(self):
if self.heuristicChecks == 1:
return self.nakedPairs()
elif self.heuristicChecks == 2:
return self.nakedTriples()
elif self.heuristicChecks == 3:
return self.nakedPairs() and self.nakedTriples()
else:
return True
def assignmentsCheck(self):
"""
default consistency check. Ensures no two variables are assigned to the same value.
@return true if consistent, false otherwise.
"""
for v in self.network.variables:
if v.isAssigned():
for vOther in self.network.getNeighborsOfVariable(v):
if v.getAssignment() == vOther.getAssignment():
return False
return True
def nakedPairs(self):
"""
TODO: Implement naked pairs heuristic.
"""
pass
def nakedTriples(self):
"""
TODO: Implement naked triples heuristic.
"""
pass
def forwardChecking(self):
"""
TODO: Implement forward checking.
"""
for cell in self.network.variables:
if cell.isAssigned():
for neighbor in self.network.getNeighborsOfVariable(cell):
cVal = cell.getAssignment()
neighbor.removeValueFromDomain(cVal)
if neighbor.domain.size() == 0:
return False
return True
def arcConsistency(self):
"""
TODO: Implement Maintaining Arc Consistency.
"""
# inputs: csp: self.network with variables / self.network.variables
# local variables: queue of arcs / self.network.getNeighborsOfVariable()
def arcReduce(x, y):
change = False
for val in x.Values():
if val in y.Values():
x.removeValueFromDomain(val)
change = True
return change
arcs = []
for cell in self.network.variables:
#if cell.isAssigned():
dests = self.network.getNeighborsOfVariable(cell)
for dest in dests:
arcs.append([cell, dest])
while arcs != []:
arc = arcs[0]
arcs.remove(arc)
if arcReduce(arc[0], arc[1]):
if cell.Values() == []:
return False # failed: return false
else:
for othercell in self.network.variables:
if cell in self.network.getNeighborOfVariable(othercell) and othercell != arc[1]:
arcs.append([othercell, cell])
return True # maintainance was successful
def selectNextVariable(self):
"""
Selects the next variable to check.
@return next variable to check. null if there are no more variables to check.
"""
if self.varHeuristics == 0:
return self.getfirstUnassignedVariable()
elif self.varHeuristics == 1:
return self.getMRV()
elif self.varHeuristics == 2:
return self.getDegree()
else:
return self.getfirstUnassignedVariable()
def getfirstUnassignedVariable(self):
"""
default next variable selection heuristic. Selects the first unassigned variable.
@return first unassigned variable. null if no variables are unassigned.
"""
for v in self.network.variables:
if not v.isAssigned():
return v
return None
def getMRV(self):
"""
TODO: Implement MRV heuristic
@return variable with minimum remaining values that isn't assigned, null if all variables are assigned.
"""
pass
def getDegree(self):
"""
TODO: Implement Degree heuristic
@return variable constrained by the most unassigned variables, null if all variables are assigned.
"""
pass
def getNextValues(self, v):
"""
Value Selection Heuristics. Orders the values in the domain of the variable
passed as a parameter and returns them as a list.
@return List of values in the domain of a variable in a specified order.
"""
if self.valHeuristics == 0:
return self.getValuesInOrder(v)
elif self.valHeuristics == 1:
return self.getValuesLCVOrder(v)
else:
return self.getValuesInOrder(v)
def getValuesInOrder(self, v):
"""
Default value ordering.
@param v Variable whose values need to be ordered
@return values ordered by lowest to highest.
"""
values = v.domain.values
return sorted(values)
def getValuesLCVOrder(self, v):
"""
TODO: LCV heuristic
"""
pass
def success(self):
""" Called when solver finds a solution """
self.hassolution = True
self.gameboard = filereader.ConstraintNetworkToGameBoard(self.network,
self.gameboard.N,
self.gameboard.p,
self.gameboard.q)
######### Solver Method #########
def solve(self):
""" Method to start the solver """
self.startTime = time.time()
# try:
self.solveLevel(0)
# except:
# print("Error with variable selection heuristic.")
self.endTime = time.time()
# trail.masterTrailVariable.trailStack = []
self.trail.trailStack = []
def solveLevel(self, level):
"""
Solver Level
@param level How deep the solver is in its recursion.
@throws VariableSelectionException
contains some comments that can be uncommented for more in depth analysis
"""
# print("=.=.=.=.=.=.=.=.=.=.=.=.=.=.=.=")
# print("BEFORE ANY SOLVE LEVEL START")
# print(self.network)
# print("=.=.=.=.=.=.=.=.=.=.=.=.=.=.=.=")
if self.hassolution:
return
# Select unassigned variable
v = self.selectNextVariable()
# print("V SELECTED --> " + str(v))
# check if the assigment is complete
if (v == None):
# print("!!! GETTING IN V == NONE !!!")
for var in self.network.variables:
if not var.isAssigned():
raise ValueError("Something happened with the variable selection heuristic")
self.success()
return
# loop through the values of the variable being checked LCV
# print("getNextValues(v): " + str(self.getNextValues(v)))
for i in self.getNextValues(v):
# print("next value to test --> " + str(i))
self.trail.placeTrailMarker()
# check a value
# print("-->CALL v.updateDomain(domain.Domain(i)) to start to test next value.")
v.updateDomain(domain.Domain(i))
self.numAssignments += 1
# move to the next assignment
if self.checkConsistency() and self.checkHeuristics():
self.solveLevel(level + 1)
# if this assignment failed at any stage, backtrack
if not self.hassolution:
# print("=======================================")
# print("AFTER PROCESSED:")
# print(self.network)
# print("================ ")
# print("self.trail before revert change: ")
for i in self.trail.trailStack:
pass
# print("variable --> " + str(i[0]))
# print("domain backup --> " + str(i[1]))
# print("================= ")
self.trail.undo()
self.numBacktracks += 1
# print("REVERT CHANGES:")
# print(self.network)
# print("================ ")
# print("self.trail after revert change: ")
for i in self.trail.trailStack:
pass
# print("variable --> " + str(i[0]))
# print("domain backup --> " + str(i[1]))
# print("================= ")
else:
return