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  }