-
Notifications
You must be signed in to change notification settings - Fork 1
/
grid_modification.go
238 lines (189 loc) · 5.52 KB
/
grid_modification.go
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
package sudoku
//GridModification is a series of CellModifications to apply to a Grid.
type GridModification []*CellModification
//TODO: consider changing GridModification to be value type of CellModification.
//CellModification represents a modification to be made to a given Cell in a
//grid.
type CellModification struct {
//The cell representing the cell to modify. The cell's analog (at the same
//row, col address) will be modified in the new grid.
Cell CellRef
//The number to put in the cell. Negative numbers signify no changes.
Number int
//The excludes to proactively set. Invalid numbers will be ignored.
//Indexes not listed will be left the same.
ExcludesChanges map[int]bool
//The marks to proactively set. Invalid numbers will be ignored.
//Indexes not listed will be left the same.
MarksChanges map[int]bool
}
//TODO: audit all uses of step/compoundstep.Apply()
//newCellModification returns a CellModification for the given cell that is a
//no-op.
func newCellModification(cell CellRef) *CellModification {
return &CellModification{
Cell: cell,
Number: -1,
ExcludesChanges: make(map[int]bool),
MarksChanges: make(map[int]bool),
}
}
//normalize makes sure the GridModification is legal.
func (m GridModification) normalize() {
for _, cellModification := range m {
for key := range cellModification.ExcludesChanges {
if key <= 0 || key > DIM {
delete(cellModification.ExcludesChanges, key)
}
}
for key := range cellModification.MarksChanges {
if key <= 0 || key > DIM {
delete(cellModification.MarksChanges, key)
}
}
if cellModification.Number < -1 || cellModification.Number > DIM {
cellModification.Number = -1
}
}
}
//equivalent returns true if the other grid modification is equivalent to this one.
func (m GridModification) equivalent(other GridModification) bool {
if len(m) != len(other) {
return false
}
for i, modification := range m {
otherModification := other[i]
if modification.Cell.String() != otherModification.Cell.String() {
return false
}
if modification.Number != otherModification.Number {
return false
}
if len(modification.ExcludesChanges) != len(otherModification.ExcludesChanges) {
return false
}
for key, val := range modification.ExcludesChanges {
otherVal, ok := otherModification.ExcludesChanges[key]
if !ok {
return false
}
if val != otherVal {
return false
}
}
if len(modification.MarksChanges) != len(otherModification.MarksChanges) {
return false
}
for key, val := range modification.MarksChanges {
otherVal, ok := otherModification.MarksChanges[key]
if !ok {
return false
}
if val != otherVal {
return false
}
}
}
return true
}
func (self *gridImpl) CopyWithModifications(modifications GridModification) Grid {
//TODO: test this implementation deeply! Lots of crazy stuff that could go
//wrong.
modifications.normalize()
result := new(gridImpl)
//Copy in everything
*result = *self
for i := 0; i < DIM*DIM; i++ {
cell := &result.cells[i]
cell.gridRef = result
}
result.theQueue.grid = result
cellNumberModified := false
excludesModififed := false
for _, modification := range modifications {
cell := result.cellImpl(modification.Cell.Row, modification.Cell.Col)
if modification.Number >= 0 && modification.Number <= DIM {
//cell.setNumber will handle setting all of the impossibles
if cell.setNumber(modification.Number) {
cellNumberModified = true
}
}
for key, val := range modification.ExcludesChanges {
//Key is 1-indexed
key--
if cell.excluded[key] != val {
cell.excluded[key] = val
excludesModififed = true
}
}
for key, val := range modification.MarksChanges {
//Key is 1-indexed
key--
cell.marks[key] = val
}
}
filledCellsCount := 0
if cellNumberModified {
//At least one cell's number was modified, which means we need to fix
//up the queue, numFilledCells, Invalid, Solved. (And some of those we
//also need to do if any excludes were modified but no numbers were.)
for _, cell := range result.cells {
if cell.number == 0 {
continue
}
filledCellsCount++
}
result.filledCellsCount = filledCellsCount
//Check if we're invalid.
}
if cellNumberModified || excludesModififed {
invalid := false
for _, cell := range result.cells {
//Make sure we have at least one possibility per cell
foundPossibility := false
for i := 1; i <= DIM; i++ {
if cell.Possible(i) {
foundPossibility = true
break
}
}
if !foundPossibility {
invalid = true
break
}
}
if !invalid {
//Let's do a deep check
invalid = gridGroupsInvalid(result)
}
result.invalid = invalid
if filledCellsCount == DIM*DIM && !result.invalid {
//All cells are filled and it's not invalid, so it's solved!
result.solved = true
} else {
//No way it's solved
result.solved = false
}
result.theQueue.fix()
}
return result
}
func (self *mutableGridImpl) CopyWithModifications(modifications GridModification) Grid {
result := self.MutableCopy()
modifications.normalize()
for _, modification := range modifications {
cell := modification.Cell.MutableCell(result)
if modification.Number >= 0 && modification.Number <= DIM {
cell.SetNumber(modification.Number)
}
for key, val := range modification.ExcludesChanges {
//setExcluded will skip invalid entries
cell.SetExcluded(key, val)
}
for key, val := range modification.MarksChanges {
//SetMark will skip invalid numbers
cell.SetMark(key, val)
}
}
return result
}