start on scoring in state

This commit is contained in:
Madison Rye Progress
2025-09-22 22:14:57 -07:00
parent 6b5674308e
commit 1bf7e0c828
3 changed files with 100 additions and 24 deletions

View File

@ -11,8 +11,6 @@ type model struct {
columnStates, rowStates [][]int columnStates, rowStates [][]int
columnsCorrect, rowsCorrect []bool columnsCorrect, rowsCorrect []bool
history string
} }
func newModel(sectionSize, cellsPerSection int) model { func newModel(sectionSize, cellsPerSection int) model {

View File

@ -14,17 +14,28 @@ func (p Point) String() string {
return fmt.Sprintf("(%d,%d)", p.X, p.Y) return fmt.Sprintf("(%d,%d)", p.X, p.Y)
} }
type score struct {
Clears, Score, Factor int
Blackout []bool
}
type header struct {
alive []int
complete bool
}
type State struct { type State struct {
cursor *Point cursor *Point
clears, score, factor int score score
blackout []bool
history string history string
historyIndex int historyIndex int
sectionSize, cellsPerSection int sectionSize, cellsPerSection int
cells, sections *field cells, sections *field
rowHeaders, colHeaders []header
} }
func New(sectionSize, cellsPerSection int) *State { func New(sectionSize, cellsPerSection int) *State {
@ -50,6 +61,7 @@ func (s *State) size() int {
return s.sectionSize * s.cellsPerSection return s.sectionSize * s.cellsPerSection
} }
// String is a utility method for providing a simpler string representation in tests.
func (s *State) String() string { func (s *State) String() string {
var buf bytes.Buffer var buf bytes.Buffer
for x := 0; x < s.sectionSize*s.cellsPerSection; x++ { for x := 0; x < s.sectionSize*s.cellsPerSection; x++ {
@ -80,14 +92,18 @@ func (s *State) String() string {
return buf.String() return buf.String()
} }
func (s *State) Mark() []bool { func (s *State) Score() score {
s.history += "m" return s.score
return s.mark(*s.cursor)
} }
func (s *State) Flag() []bool { func (s *State) Mark() {
s.history += "m"
s.mark(*s.cursor)
}
func (s *State) Flag() {
s.history += "f" s.history += "f"
return s.flag(*s.cursor) s.flag(*s.cursor)
} }
func (s *State) Clear() { func (s *State) Clear() {
@ -119,14 +135,14 @@ func (s *State) initSection(p Point) {
s.update(p) s.update(p)
} }
func (s *State) mark(p Point) []bool { func (s *State) mark(p Point) {
s.cells.mark(p) s.cells.mark(p)
return s.update(p) s.update(p)
} }
func (s *State) flag(p Point) []bool { func (s *State) flag(p Point) {
s.cells.flag(p) s.cells.flag(p)
return s.update(p) s.update(p)
} }
func (s *State) clear(p Point, deadCorrect bool) { func (s *State) clear(p Point, deadCorrect bool) {
@ -158,7 +174,7 @@ func (s *State) updateCompletedSections() {
} }
} }
func (s *State) clearValidCompletedSections() []bool { func (s *State) scoreValidCompletedSections() {
cleared := make([]bool, s.sections.size*s.sections.size) cleared := make([]bool, s.sections.size*s.sections.size)
for x := 0; x < s.sectionSize; x++ { for x := 0; x < s.sectionSize; x++ {
for y := 0; y < s.sectionSize; y++ { for y := 0; y < s.sectionSize; y++ {
@ -174,26 +190,90 @@ func (s *State) clearValidCompletedSections() []bool {
} }
} }
} }
// Increase scores
blackout := true
for i, v := range cleared { for i, v := range cleared {
if v { if v {
s.initSection(Point{i % s.sectionSize, i / s.sectionSize}) s.initSection(Point{i % s.sectionSize, i / s.sectionSize})
s.score.Clears++
s.score.Score += s.score.Factor + 1
s.score.Blackout[i] = true
} }
} blackout = blackout && s.score.Blackout[i]
return cleared
} }
func (s *State) update(p Point) []bool { // If all sections have been cleared at least once, increase the factor, clear the tracker, and start over with tracking.
if blackout {
s.score.Blackout = make([]bool, s.sections.size*s.sections.size)
s.score.Factor++
}
}
// updateAllHeaders runs updateHeaders for every row/column.
func (s *State) updateAllHeaders() {
for i := 0; i < s.size(); i++ {
s.updateHeaders(Point{i, i})
}
}
// updateHeaders makes sure that the counts of alive cells in rows/columns are accurate, and whether or not the row/column is complete.
func (s *State) updateHeaders(p Point) {
var rowHeader, colHeader header
var rowCounter, colCounter int
rowHeader.complete = true
colHeader.complete = true
// Loop through each row/column that contains this point
for i := 0; i < s.size(); i++ {
rowHeader.complete = rowHeader.complete && s.cells.complete(Point{i, p.Y})
if s.cells.state(Point{i, p.Y}) {
rowCounter++
} else {
if rowCounter > 0 {
rowHeader.alive = append(rowHeader.alive, rowCounter)
}
rowCounter = 0
}
colHeader.complete = colHeader.complete && s.cells.complete(Point{p.X, i})
if s.cells.state(Point{p.X, i}) {
colCounter++
} else {
if colCounter > 0 {
colHeader.alive = append(colHeader.alive, colCounter)
}
colCounter = 0
}
}
// In case there are any living cells at the end of the row/column
if rowCounter > 0 {
rowHeader.alive = append(rowHeader.alive, rowCounter)
}
if colCounter > 0 {
colHeader.alive = append(colHeader.alive, colCounter)
}
s.rowHeaders[p.Y] = rowHeader
s.colHeaders[p.X] = colHeader
}
func (s *State) update(p Point) {
sectionPoint := Point{p.X / s.cellsPerSection, p.Y / s.cellsPerSection} sectionPoint := Point{p.X / s.cellsPerSection, p.Y / s.cellsPerSection}
if s.cells.correct(p) && s.sectionCorrect(sectionPoint) { if s.cells.correct(p) && s.sectionCorrect(sectionPoint) {
s.sections.setCorrect(sectionPoint, true) s.sections.setCorrect(sectionPoint, true)
s.updateCompletedSections() s.updateCompletedSections()
return s.clearValidCompletedSections() s.scoreValidCompletedSections()
return
} }
s.sections.setCorrect(sectionPoint, false) s.sections.setCorrect(sectionPoint, false)
for i := 0; i < s.sectionSize; i++ { for i := 0; i < s.sectionSize; i++ {
s.sections.setComplete(Point{sectionPoint.X, i}, false) s.sections.setComplete(Point{sectionPoint.X, i}, false)
s.sections.setComplete(Point{i, sectionPoint.Y}, false) s.sections.setComplete(Point{i, sectionPoint.Y}, false)
} }
return make([]bool, s.sections.size*s.sections.size)
s.updateHeaders(p)
} }

View File

@ -55,18 +55,16 @@ func TestState(t *testing.T) {
s.cells.vivify(Point{2, 2}) s.cells.vivify(Point{2, 2})
Convey("It marks sections as correct when they are correctly guessed", func() { Convey("It marks sections as correct when they are correctly guessed", func() {
res := s.mark(Point{0, 0}) s.mark(Point{0, 0})
So(s.cells.correct(Point{0, 0}), ShouldBeTrue) So(s.cells.correct(Point{0, 0}), ShouldBeTrue)
So(s.sections.correct(Point{0, 0}), ShouldBeTrue) So(s.sections.correct(Point{0, 0}), ShouldBeTrue)
So(s.String(), ShouldEqual, "O . \n \n. . \n \n") 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() { Convey("It marks sections as complete if they meet the criteria", func() {
s.mark(Point{0, 0}) s.mark(Point{0, 0})
s.mark(Point{2, 0}) s.mark(Point{2, 0})
res := s.mark(Point{0, 2}) s.mark(Point{0, 2})
So(res, ShouldResemble, make([]bool, 4))
So(s.String(), ShouldEqual, "O O \n \nO . \n \n") So(s.String(), ShouldEqual, "O O \n \nO . \n \n")
So(s.sections.correct(Point{0, 0}), ShouldBeTrue) So(s.sections.correct(Point{0, 0}), ShouldBeTrue)
So(s.sections.correct(Point{1, 0}), ShouldBeTrue) So(s.sections.correct(Point{1, 0}), ShouldBeTrue)
@ -78,8 +76,8 @@ func TestState(t *testing.T) {
s.mark(Point{0, 0}) s.mark(Point{0, 0})
s.mark(Point{2, 0}) s.mark(Point{2, 0})
s.mark(Point{0, 2}) s.mark(Point{0, 2})
res := s.mark(Point{2, 2}) s.mark(Point{2, 2})
So(res, ShouldResemble, []bool{true, true, true, true}) //So(res, ShouldResemble, []bool{true, true, true, true})
}) })
}) })
}) })