1package solver23//go:generate go run ../cmd/generate_neighbors45import (6 "math/bits"7 "strings"8)910// anything is a cell value that has bits for all possible numbers set.11const anything uint16 = 1<<1 | 1<<2 | 1<<3 | 1<<4 | 1<<5 | 1<<6 | 1<<7 | 1<<8 | 1<<91213// operateBoard represents a board that is being solved.14//15// It contains cells that store all possible values for that cell. The only16// serious non-algorithmic optimization is to use uint16s as bit sets, which17// makes read/update extremely fast.18type operateBoard struct {19 cells [9 * 9]uint1620}2122// clone returns a duplicate of b.23func (b *operateBoard) clone() *operateBoard {24 newBoard := &operateBoard{25 cells: [9 * 9]uint16{},26 }2728 copy(newBoard.cells[:], b.cells[:])2930 return newBoard31}3233// String returns a string representation of the board, where each cell has its34// options listed.35func (b *operateBoard) String() string {36 var s strings.Builder3738 for i, n := range b.cells {39 if i%9 == 0 && i > 0 {40 s.WriteRune('\n')41 }42 if i%27 == 0 && i > 0 {43 s.WriteRune('\n')44 }45 if i%3 == 0 && i%9 != 0 {46 s.WriteString(" | ")47 }4849 spaces := 1050 for j := 1; j <= 9; j++ {51 if n&(1<<j) != 0 {52 s.WriteRune(rune(j) + '0')53 spaces--54 }55 }56 for ; spaces > 0; spaces-- {57 s.WriteRune(' ')58 }59 }6061 return s.String()62}6364// clear removes val from the list of options for the cell at idx and also65// updates the possibilities for all other cells.66//67// Returns whether the board is still valid.68func (b *operateBoard) clear(idx, val int) bool {69 old := b.cells[idx]7071 // Actually clear the cell72 n := b.cells[idx] & ^(1 << val)73 b.cells[idx] = n7475 // If nothing changed, short-curcuit, since this update must have already76 // fully propagated at some point in the past.77 if old == n {78 return true79 }8081 // The board is invalid, since there are no options.82 if n == 0 {83 return false84 }8586 // If we just reached a single possible number in this cell, immediately87 // set it and propagate.88 if bits.OnesCount16(n) == 1 {89 if !b.set(idx, 15-bits.LeadingZeros16(n)) {90 return false91 }92 }9394 // Check if val is only valid in a single cell in one of the units for the95 // current cell. This will quickly cascade solutions through the board96 for _, unit := range affectedUnits[idx] {97 cell := -198 for _, index := range units[unit] {99 if b.cells[index]&(1<<val) != 0 {100 if cell != -1 {101 // We found two cells in the same unit that can have the102 // same value. It's solve()'s job to handle this.103 //104 // We know the board is valid because we were passed in a105 // valid board, and checked all of our operations to ensure106 // the board stays valid.107 return true108 } else {109 cell = index110 }111 }112 }113114 // We didn't find any cells that could be set to val, which means the115 // board is not valid.116 if cell == -1 {117 return false118 }119120 // If cell is the only cell that can be set to val, and wasn't already, then set it!121 if b.cells[cell] != (1<<val) && !b.set(cell, val) {122 return false123 }124 }125126 // We're done propagating. All of the operations we've performed have127 // either kept the board valid, or returned early, so we can return true128 // here without checking again.129 return true130}131132// set puts val as the only option for idx and propagates.133//134// Returns whether the board is still valid135func (b *operateBoard) set(idx, val int) bool {136 // Clear all but val instead of just setting it, because Clear has some137 // special logic.138 //139 // Split the for loop to avoid branching inside.140 for i := 1; i < val; i++ {141 if !b.clear(idx, i) {142 return false143 }144 }145146 for i := val+1; i <=9; i++ {147 if !b.clear(idx, i) {148 return false149 }150 }151152 // Propagate setting of val on this cell to all of the neighbors.153 for _, neighbor := range neighbors[idx] {154 if !b.clear(neighbor, val) {155 return false156 }157 }158159 return true160}161162func (b *operateBoard) solve() *operateBoard {163 type option struct {164 idx int165 n int166 }167168 // Iterate over all cells and pick the one that has the smallest number of169 // choices.170 var choice *option171 for i, val := range b.cells {172 n := bits.OnesCount16(val)173 if n == 0 {174 // This board is not valid175 return nil176 } else if n == 1 {177 // We've already determined the value for this cell178 continue179 }180181 if choice == nil || n < choice.n {182 choice = &option{i, n}183 }184 }185186 // We've solved it!187 if choice == nil {188 return b189 }190191 newBoard := operateBoard{}192 // Actually try to set the value. At the end of this loop, we will either193 // have a solved puzzle, or know that it's impossible.194 for i := 1; i <= 9; i++ {195 // Skip if the cell can't take this number.196 if b.cells[choice.idx]&(1<<i) == 0 {197 continue198 }199200 // Propagate the choice of value.201 copy(newBoard.cells[:], b.cells[:])202 if !newBoard.set(choice.idx, i) {203 continue204 }205206 // Solve it!207 solved := newBoard.solve()208 if solved != nil {209 return solved210 }211 }212213 return nil214}215216// Convert a solved board to a regular board.217func (b *operateBoard) toBoard() *Board {218 out := &Board{}219 for i, v := range b.cells {220 out[i] = int8(15 - bits.LeadingZeros16(v))221 }222223 return out224}225226func newOperateBoard() *operateBoard {227 b := &operateBoard{}228 for i := range b.cells {229 b.cells[i] = anything230 }231232 return b233}