From bef9cd248e64a5f879373e40bd836e21a21a17ce Mon Sep 17 00:00:00 2001 From: Madison Rye Progress Date: Thu, 28 Aug 2025 22:16:29 -0700 Subject: [PATCH] State tests 100% --- state/cell.go | 17 +++++++++++----- state/cell_test.go | 2 +- state/field.go | 4 ++-- state/field_test.go | 2 +- state/state.go | 35 ++++++++++++++++++-------------- state/state_test.go | 49 ++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 82 insertions(+), 27 deletions(-) diff --git a/state/cell.go b/state/cell.go index e59c269..556e9fb 100644 --- a/state/cell.go +++ b/state/cell.go @@ -51,10 +51,10 @@ func (c cell) vivify() cell { // kill sets the state of the cell to dead. func (c cell) kill() cell { c = c &^ statebit - if c.flagged() { - return c.setCorrect(true) - } else { + if c.marked() { return c.setCorrect(false) + } else { + return c.setCorrect(true) } } @@ -99,6 +99,13 @@ func (c cell) flag() cell { } // clear clears all bits except for the status. -func (c cell) clear() cell { - return c &^ (markbit | flagbit | correctbit | completebit) +func (c cell) clear(deadCorrect bool) cell { + // Unset all bits except status + c = c &^ (markbit | flagbit | correctbit | completebit) + + // If a dead cell is to be marked correct so long as it is *not* marked, set the correct bit + if deadCorrect && !c.state() { + c |= correctbit + } + return c } diff --git a/state/cell_test.go b/state/cell_test.go index ae3fb0c..3b4727c 100644 --- a/state/cell_test.go +++ b/state/cell_test.go @@ -89,7 +89,7 @@ func TestCell(t *testing.T) { So(c.flagged(), ShouldBeFalse) So(c.correct(), ShouldBeTrue) So(c.complete(), ShouldBeTrue) - c = c.clear() + c = c.clear(false) So(c.state(), ShouldBeTrue) So(c.marked(), ShouldBeFalse) So(c.flagged(), ShouldBeFalse) diff --git a/state/field.go b/state/field.go index 0e2ec76..3e93f35 100644 --- a/state/field.go +++ b/state/field.go @@ -94,6 +94,6 @@ func (f *field) flag(p Point) { } // clear clears all but the cell's state. -func (f *field) clear(p Point) { - f.cells[f.i(p)] = f.cells[f.i(p)].clear() +func (f *field) clear(p Point, deadCorrect bool) { + f.cells[f.i(p)] = f.cells[f.i(p)].clear(deadCorrect) } diff --git a/state/field_test.go b/state/field_test.go index 53e9cfc..9c9ade9 100644 --- a/state/field_test.go +++ b/state/field_test.go @@ -44,7 +44,7 @@ func TestField(t *testing.T) { So(f.marked(p), ShouldBeTrue) f.flag(p) So(f.flagged(p), ShouldBeTrue) - f.clear(p) + f.clear(p, false) So(f.correct(p), ShouldBeFalse) So(f.complete(p), ShouldBeFalse) So(f.marked(p), ShouldBeFalse) diff --git a/state/state.go b/state/state.go index 78a3985..0dd57af 100644 --- a/state/state.go +++ b/state/state.go @@ -42,7 +42,7 @@ func (s *State) String() string { for x := 0; x < s.sectionSize*s.cellsPerSection; x++ { for y := 0; y < s.sectionSize*s.cellsPerSection; y++ { p := Point{x, y} - if s.cells.correct(p) { + if s.cells.correct(p) && (s.cells.marked(p) || s.cells.flagged(p)) { if s.cells.state(p) { buf.WriteString("O") } else { @@ -54,7 +54,11 @@ func (s *State) String() string { } else if s.cells.flagged(p) { buf.WriteString("x") } else { - buf.WriteString(" ") + if s.cells.state(p) { + buf.WriteString(".") + } else { + buf.WriteString(" ") + } } } } @@ -72,19 +76,19 @@ func (s *State) Flag() []bool { } func (s *State) Clear() { - s.clear(*s.cursor) + s.clear(*s.cursor, true) } func (s *State) view(cursor []int) { } func (s *State) initSection(p Point) { - s.sections.clear(p) + s.sections.clear(p, false) startX := p.X * s.cellsPerSection startY := p.Y * s.cellsPerSection for x := 0; x < s.cellsPerSection; x++ { for y := 0; y < s.cellsPerSection; y++ { - s.cells.clear(Point{x + startX, y + startY}) + s.cells.clear(Point{x + startX, y + startY}, true) if rand.Int()%2 == 0 { s.cells.kill(Point{x + startX, y + startY}) } else { @@ -92,6 +96,7 @@ func (s *State) initSection(p Point) { } } } + s.update(p) } func (s *State) mark(p Point) []bool { @@ -104,8 +109,8 @@ func (s *State) flag(p Point) []bool { return s.update(p) } -func (s *State) clear(p Point) { - s.cells.clear(p) +func (s *State) clear(p Point, deadCorrect bool) { + s.cells.clear(p, deadCorrect) s.update(p) } @@ -113,7 +118,7 @@ func (s *State) sectionCorrect(p Point) bool { sectionCorrect := true for x := 0; x < s.cellsPerSection; x++ { for y := 0; y < s.cellsPerSection; y++ { - sectionCorrect = sectionCorrect && s.cells.correct(Point{p.X + x, p.Y + y}) + sectionCorrect = sectionCorrect && s.cells.correct(Point{p.X*s.cellsPerSection + x, p.Y*s.cellsPerSection + y}) } } return sectionCorrect @@ -137,15 +142,15 @@ func (s *State) clearValidCompletedSections() []bool { cleared := make([]bool, s.sections.size*s.sections.size) for x := 0; x < s.sectionSize; x++ { for y := 0; y < s.sectionSize; y++ { - clearable := s.sections.complete(Point{x, y}) - s.sections.complete(Point{x + 1%s.sectionSize, y}) - s.sections.complete(Point{x, y + 1%s.sectionSize}) - s.sections.complete(Point{x + 1%s.sectionSize, y + 1%s.sectionSize}) + clearable := s.sections.complete(Point{x, y}) && + s.sections.complete(Point{(x + 1) % s.sectionSize, y}) && + s.sections.complete(Point{x, (y + 1) % s.sectionSize}) && + s.sections.complete(Point{(x + 1) % s.sectionSize, (y + 1) % s.sectionSize}) if clearable { cleared[y*s.sectionSize+x] = true - cleared[y*s.sectionSize+(x+1%s.sectionSize)] = true - cleared[(y*s.sectionSize+s.sectionSize%len(cleared))+x] = true - cleared[(y*s.sectionSize+s.sectionSize%len(cleared))+(x+1%s.sectionSize)] = true + cleared[(y*s.sectionSize + (x+1)%s.sectionSize)] = true + cleared[((y*s.sectionSize+s.sectionSize)%(len(cleared)) + x)] = true + cleared[((y*s.sectionSize+s.sectionSize)+(x+1%s.sectionSize))%len(cleared)] = true } } } diff --git a/state/state_test.go b/state/state_test.go index 44780a3..9279823 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -9,6 +9,14 @@ import ( func TestState(t *testing.T) { Convey("Given a game state", t, func() { s := New(2, 2) + for x := 0; x < 4; x++ { + for y := 0; y < 4; y++ { + s.cells.kill(Point{x, y}) + if x < 2 && y < 2 { + s.sections.clear(Point{x, y}, false) + } + } + } Convey("You can get the size", func() { So(s.size(), ShouldEqual, 4) @@ -23,20 +31,55 @@ func TestState(t *testing.T) { Convey("It shows correct guesses", func() { s.Mark() s.flag(Point{0, 1}) - So(s.String(), ShouldEqual, "OX \n \n \n \n") + So(s.String(), ShouldEqual, "OX \n. \n \n \n") }) Convey("It shows incorrect guesses", func() { s.Flag() s.mark(Point{0, 1}) - So(s.String(), ShouldEqual, "xo \n \n \n \n") + So(s.String(), ShouldEqual, "xo \n. \n \n \n") }) Convey("It shows cleared guesses as empty", func() { s.Flag() s.mark(Point{0, 1}) s.Clear() - So(s.String(), ShouldEqual, " o \n \n \n \n") + So(s.String(), ShouldEqual, ".o \n. \n \n \n") + }) + }) + + Convey("You can complete sections and clear portions of the board", func() { + s.cells.vivify(Point{0, 0}) + s.cells.vivify(Point{2, 0}) + s.cells.vivify(Point{0, 2}) + s.cells.vivify(Point{2, 2}) + + Convey("It marks sections as correct when they are correctly guessed", func() { + res := s.mark(Point{0, 0}) + So(s.cells.correct(Point{0, 0}), ShouldBeTrue) + So(s.sections.correct(Point{0, 0}), ShouldBeTrue) + So(s.String(), ShouldEqual, "O . \n \n. . \n \n") + So(res, ShouldResemble, make([]bool, 4)) + }) + + Convey("It marks sections as complete if they meet the criteria", func() { + s.mark(Point{0, 0}) + s.mark(Point{2, 0}) + res := s.mark(Point{0, 2}) + So(res, ShouldResemble, make([]bool, 4)) + So(s.String(), ShouldEqual, "O O \n \nO . \n \n") + So(s.sections.correct(Point{0, 0}), ShouldBeTrue) + So(s.sections.correct(Point{1, 0}), ShouldBeTrue) + So(s.sections.correct(Point{0, 1}), ShouldBeTrue) + So(s.sections.complete(Point{0, 0}), ShouldBeTrue) + }) + + Convey("It clears sections when they meet the criteria", func() { + s.mark(Point{0, 0}) + s.mark(Point{2, 0}) + s.mark(Point{0, 2}) + res := s.mark(Point{2, 2}) + So(res, ShouldResemble, []bool{true, true, true, true}) }) }) })