Starting on UI. Coverage no longer 100% :c

This commit is contained in:
Madison Rye Progress
2026-03-18 22:05:07 -07:00
parent bbc87afee1
commit bbbcebc79c
10 changed files with 313 additions and 73 deletions

View File

@ -1,20 +1,27 @@
package state
import "strconv"
import (
"fmt"
"strconv"
)
// History returns the gameplay history for replays
func (s *State) History() string {
return s.history
}
func UnmarshalAll(history string) *State {
func UnmarshalAll(history string) (*State, error) {
s := Unmarshal(history)
for {
if !s.Step() {
res, err := s.Step()
if err != nil {
return nil, err
}
if !res {
break
}
}
return s
return s, nil
}
func Unmarshal(history string) *State {
@ -25,16 +32,33 @@ func Unmarshal(history string) *State {
}
}
func (s *State) Step() bool {
func (s *State) Step() (bool, error) {
if s.historyIndex >= len(s.history) {
return false
return false, nil
}
switch s.history[s.historyIndex] {
case 'i', 'm', 'f', 'c', 'r', 'l', 'u', 'd', 'R', 'L', 'U', 'D':
err := s.beforeAct()
if err != nil {
return false, err
}
}
switch s.history[s.historyIndex] {
case 'g':
s.historyStart()
if s.initialized {
return false, fmt.Errorf("initialization step in invalid location (index %d)", s.historyIndex)
}
err := s.historyStart()
if err != nil {
return false, err
}
case 'i':
s.historyInitSection()
err := s.historyInitSection()
if err != nil {
return false, err
}
case 'm':
s.mark(*s.cursor)
case 'f':
@ -57,15 +81,43 @@ func (s *State) Step() bool {
s.cursorSectionUp()
case 'D':
s.cursorSectionDown()
case 't': // TODO for now, this is just sugar. Will be a timestamp for events completed.
for {
s.historyIndex++
if s.historyIndex >= len(s.history) || s.history[s.historyIndex] == ')' {
break
}
}
case ' ', '\n', '\t':
break
case '#':
for {
s.historyIndex++
if s.historyIndex >= len(s.history) || s.history[s.historyIndex] == '\n' {
break
}
}
default:
return false, fmt.Errorf("invalid step in history: %s (index %d)", string(s.history[s.historyIndex]), s.historyIndex)
}
s.historyIndex++
return true
return true, nil
}
func (s *State) historyStart() {
func (s *State) beforeAct() error {
if !s.initialized {
return fmt.Errorf("tried to act on an uninitialized state (index %d)", s.historyIndex)
}
return nil
}
func (s *State) historyStart() error {
peek := s.historyIndex + 1
p, peek := s.historyPoint(peek)
p, peek, err := s.historyPoint(peek)
if err != nil {
return err
}
s.sectionSize = p.X
s.cellsPerSection = p.Y
s.cells = newField(s.size())
@ -75,11 +127,17 @@ func (s *State) historyStart() {
s.colHeaders = make([]header, s.size())
s.historyIndex = peek
s.score.Blackout = make([]bool, s.size())
s.initialized = true
return nil
}
func (s *State) historyInitSection() {
func (s *State) historyInitSection() error {
peek := s.historyIndex + 1
p, peek := s.historyPoint(peek)
p, peek, err := s.historyPoint(peek)
if err != nil {
return err
}
segment := []byte(s.history[peek : peek+s.cellsPerSection*s.cellsPerSection])
for i, c := range segment {
curr := Point{
@ -98,36 +156,49 @@ func (s *State) historyInitSection() {
peek++
}
s.historyIndex = peek
return nil
}
func (s *State) historyPoint(index int) (Point, int) {
func (s *State) historyPoint(index int) (Point, int, error) {
var x, y string
// Advance past paren
// Advance past opening paren
index++
for {
if index >= len(s.history) {
return Point{}, 0, fmt.Errorf("point.X never ended? (index %d)", index)
}
switch s.history[index] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
x += string(s.history[index])
index++
continue
case ',':
index++
default:
return Point{}, 0, fmt.Errorf("invalid character in point.X: %s (index %d)", string(s.history[index]), index)
}
break
}
// Advance past comma
index++
for {
if index >= len(s.history) {
return Point{}, 0, fmt.Errorf("point.Y never ended? (index %d)", index)
}
switch s.history[index] {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
y += string(s.history[index])
index++
continue
case ')':
break
default:
return Point{}, 0, fmt.Errorf("invalid character in point.Y: %s (index %d)", string(s.history[index]), index)
}
break
}
pX, _ := strconv.Atoi(x)
pY, _ := strconv.Atoi(y)
return Point{pX, pY}, index
return Point{pX, pY}, index, nil
}