clone

fifteen puzzle games for the terminal, with daily puzzles, weekly ladders and xp, built on bubble tea

feat/app-decomposition
puzzletea / games / hitori / grid.go
grid.go 1.7K
1package hitori23import "strings"45type cellMark int67const (8 unmarked cellMark = iota9 shaded10 circled11)1213type (14 grid [][]rune15 state string16)1718func newGrid(s state) grid {19 rows := strings.Split(string(s), "\n")20 g := make(grid, len(rows))21 for i, row := range rows {22 g[i] = []rune(row)23 }24 return g25}2627func (g grid) String() string {28 rows := make([]string, len(g))29 for i, row := range g {30 rows[i] = string(row)31 }32 return strings.Join(rows, "\n")33}3435func (g grid) clone() grid {36 c := make(grid, len(g))37 for i, row := range g {38 c[i] = make([]rune, len(row))39 copy(c[i], row)40 }41 return c42}4344func newMarks(size int) [][]cellMark {45 marks := make([][]cellMark, size)46 for y := range size {47 marks[y] = make([]cellMark, size)48 }49 return marks50}5152func cloneMarks(marks [][]cellMark) [][]cellMark {53 c := make([][]cellMark, len(marks))54 for y, row := range marks {55 c[y] = make([]cellMark, len(row))56 copy(c[y], row)57 }58 return c59}6061func serializeMarks(marks [][]cellMark) string {62 var b strings.Builder63 for y, row := range marks {64 for _, m := range row {65 switch m {66 case shaded:67 b.WriteByte('X')68 case circled:69 b.WriteByte('O')70 default:71 b.WriteByte('.')72 }73 }74 if y < len(marks)-1 {75 b.WriteByte('\n')76 }77 }78 return b.String()79}8081func deserializeMarks(s string, size int) [][]cellMark {82 rows := strings.Split(s, "\n")83 marks := make([][]cellMark, size)84 for y := range size {85 marks[y] = make([]cellMark, size)86 if y < len(rows) {87 for x, r := range rows[y] {88 if x >= size {89 break90 }91 switch r {92 case 'X':93 marks[y][x] = shaded94 case 'O':95 marks[y][x] = circled96 default:97 marks[y][x] = unmarked98 }99 }100 }101 }102 return marks103}