-
Notifications
You must be signed in to change notification settings - Fork 1
/
hst_swordfish.go
164 lines (121 loc) · 4.21 KB
/
hst_swordfish.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
package sudoku
import (
"fmt"
"math/rand"
)
type swordfishTechnique struct {
*basicSolveTechnique
}
func (self *swordfishTechnique) humanLikelihood(step *SolveStep) float64 {
//TODO: reason more carefully about how hard this technique is.
return self.difficultyHelper(70.0)
}
func (self *swordfishTechnique) Description(step *SolveStep) string {
if len(step.TargetNums) != 1 {
return ""
}
groupName := "columns"
otherGroupName := "rows"
if self.groupType == _GROUP_ROW {
groupName = "rows"
otherGroupName = "columns"
}
return fmt.Sprintf("%d is only possible in two cells each in three different %s, all of which align onto three %s, which means that %d can't be in any of the other cells in those %s (%s)",
step.TargetNums[0],
groupName,
otherGroupName,
step.TargetNums[0],
otherGroupName,
step.TargetCells.Description(),
)
}
func (self *swordfishTechnique) Candidates(grid Grid, maxResults int) []*SolveStep {
return self.candidatesHelper(self, grid, maxResults)
}
func (self *swordfishTechnique) find(grid Grid, coordinator findCoordinator) {
getter := self.getter(grid)
//For this technique, the primary access is the first type of group we look at to find
//cells with only 2 spots for a given number.
//TODO: Implement the "relaxed" version of this technique, too.
for _, i := range rand.Perm(DIM + 1) {
if i == 0 {
continue
}
//The candidate we're considering
//Check if it's time to stop.
if coordinator.shouldExitEarly() {
return
}
//Consider each of the major-axis groups to see if more than three have
//only two candidates for
var majorAxisGroupsWithTwoOptionsForCandidate []CellSlice
for c := 0; c < DIM; c++ {
majorAxisGroup := getter(c)
cellsWithCandidatePossibility := majorAxisGroup.FilterByPossible(i)
if len(cellsWithCandidatePossibility) == 2 {
//TODO: shouldn't we keep track of the rows where the possibilities were, too?
majorAxisGroupsWithTwoOptionsForCandidate = append(majorAxisGroupsWithTwoOptionsForCandidate, cellsWithCandidatePossibility)
}
}
//Do we have more than three major axis groups identified?
if len(majorAxisGroupsWithTwoOptionsForCandidate) < 3 {
continue
}
//Consider every subset of size three
for _, indexes := range subsetIndexes(len(majorAxisGroupsWithTwoOptionsForCandidate), 3) {
majorAxisGroups := make([]CellSlice, 3)
for i, index := range indexes {
majorAxisGroups[i] = majorAxisGroupsWithTwoOptionsForCandidate[index]
}
//OK, now majorAxisGroups has the set of three we're operating on.
//Do their minorAxis groups line up to a set of three as well?
var minorGroupIndexSet intSet
for _, group := range majorAxisGroups {
if self.groupType == _GROUP_COL {
minorGroupIndexSet = minorGroupIndexSet.union(group.AllRows().toIntSet())
} else {
minorGroupIndexSet = minorGroupIndexSet.union(group.AllCols().toIntSet())
}
}
if len(minorGroupIndexSet) != 3 {
//Nah, didn't have three rows.
continue
}
//Woot, looks like we found a valid set.
//Generate the list of cells that will be affected
var affectedCells CellSlice
for minorGroupIndex := range minorGroupIndexSet {
if self.groupType == _GROUP_COL {
affectedCells = append(affectedCells, grid.Row(minorGroupIndex)...)
} else {
affectedCells = append(affectedCells, grid.Col(minorGroupIndex)...)
}
}
//Get rid of cells that are already filled, or where removing 'i'
//would be a no-op anyway since it's not already a possible there.
affectedCells = affectedCells.FilterByPossible(i)
//Gather the list of all cells that are "pointing" to this
var pointerCells CellSlice
for _, group := range majorAxisGroups {
pointerCells = append(pointerCells, group...)
}
//Get rid of all pointer cells from the rows of cells that will be
//affected.
affectedCells = affectedCells.RemoveCells(pointerCells)
//Okay, now the set is solid.
//Okay, we have a candidate step (unchunked). Is it useful?
step := &SolveStep{self,
affectedCells.CellReferenceSlice(),
IntSlice{i},
pointerCells.CellReferenceSlice(),
nil,
nil,
}
if step.IsUseful(grid) {
if coordinator.foundResult(step) {
return
}
}
}
}
}