github.com/joshprzybyszewski/masyu@v0.0.0-20230508015604-f31a025f6e7e/solve/settle_state.go (about) 1 package solve 2 3 import ( 4 "github.com/joshprzybyszewski/masyu/model" 5 ) 6 7 type settledState uint8 8 9 const ( 10 invalid settledState = 1 11 solved settledState = 2 12 validUnsolved settledState = 3 13 unexpected settledState = 4 14 ) 15 16 // returns true if the state is still valid 17 func settle( 18 s *state, 19 ) settledState { 20 if s.hasInvalid { 21 return invalid 22 } 23 24 if s.paths.hasCycle { 25 return settleCycle(s) 26 } 27 28 if s.rules.runAllChecks(s) == invalid { 29 return invalid 30 } 31 32 if s.hasInvalid { 33 return invalid 34 } 35 36 if s.paths.hasCycle { 37 return settleCycle(s) 38 } 39 40 return validUnsolved 41 } 42 43 func settleCycle( 44 s *state, 45 ) settledState { 46 47 // Please only call this if the state has a cycle 48 if !s.paths.hasCycle { 49 return unexpected 50 } 51 52 if s.paths.cycleSeenNodes != len(s.nodes) { 53 // there's a cycle, but it doesn't include all of the nodes. 54 return invalid 55 } 56 57 // we found a state that includes a cycle with all of the nodes. 58 // avoid all of the remaining spots in the state, and see if it's 59 // still valid: this eliminates the bad state of having tertiary paths set. 60 61 avoidAllUnknowns(s) 62 63 if !hasValidCrossings(s) { 64 return invalid 65 } 66 67 if checkEntireRuleset(s) != validUnsolved { 68 return invalid 69 } 70 71 if !s.paths.hasCycle { 72 return unexpected 73 } 74 75 // re-validate our assumptions after checking all the rules 76 if s.hasInvalid || s.paths.cycleSeenNodes != len(s.nodes) { 77 return invalid 78 } 79 80 return solved 81 } 82 83 func hasValidCrossings(s *state) bool { 84 bit := model.DimensionBit(1 << 1) 85 for i := 1; i <= int(s.size); i++ { 86 if !hasValidCrossingsForVerticalBit(s, bit) || 87 !hasValidCrossingsForHorizontalBit(s, bit) { 88 return false 89 } 90 bit <<= 1 91 } 92 93 return true 94 } 95 96 func hasValidCrossingsForVerticalBit( 97 s *state, 98 bit model.DimensionBit, 99 ) bool { 100 var l uint8 101 for i := 1; i <= int(s.size); i++ { 102 if s.horizontalLines[i]&bit == bit { 103 l++ 104 } else if s.horizontalAvoids[i]&bit != bit { 105 return true 106 } 107 } 108 return l%2 == 0 109 } 110 111 func hasValidCrossingsForHorizontalBit( 112 s *state, 113 bit model.DimensionBit, 114 ) bool { 115 var l uint8 116 for i := 1; i <= int(s.size); i++ { 117 if s.verticalLines[i]&bit == bit { 118 l++ 119 } else if s.verticalAvoids[i]&bit != bit { 120 return true 121 } 122 } 123 return l%2 == 0 124 } 125 126 func avoidAllUnknowns( 127 s *state, 128 ) { 129 var col model.Dimension 130 for row := model.Dimension(1); row <= model.Dimension(s.size); row++ { 131 for col = model.Dimension(1); col <= model.Dimension(s.size); col++ { 132 if !s.horLineAt(row, col) { 133 s.avoidHor(row, col) 134 } 135 if !s.verLineAt(row, col) { 136 s.avoidVer(row, col) 137 } 138 } 139 } 140 } 141 142 func checkEntireRuleset(s *state) settledState { 143 max := model.Dimension(s.size + 1) 144 maxBit := max.Bit() 145 var dim2 model.DimensionBit 146 for dim1 := model.Dimension(0); dim1 <= max; dim1++ { 147 s.rules.checkHorizontal(dim1, 0) 148 s.rules.checkVertical(0, dim1) 149 for dim2 = 1; dim2 <= maxBit; dim2 <<= 1 { 150 s.rules.checkHorizontal(dim1, dim2) 151 s.rules.checkVertical(dim2, dim1) 152 } 153 } 154 155 return s.rules.runAllChecks(s) 156 }