clone

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

feat/elo-difficulty
puzzletea / games / sudokurgb / dynamic_board_test.go
dynamic_board_test.go 6.7K
1package sudokurgb23import (4 "strings"5 "testing"67 "charm.land/lipgloss/v2"8 "github.com/FelineStateMachine/puzzletea/game"9 "github.com/FelineStateMachine/puzzletea/theme"10 "github.com/charmbracelet/x/ansi"11)1213func TestCellViewOmitsCrosshairAndBoxTint(t *testing.T) {14 g := newGrid(nil)15 g[2][2].v = 216 g[1][4].v = 317 m := Model{18 cursor: game.Cursor{X: 1, Y: 1},19 grid: g,20 analysis: analyzeGrid(g),21 keys: DefaultKeyMap,22 }2324 gotSameBox := cellView(m, 2, 2, false)25 wantSameBox := userCellStyle(2).26 Width(cellWidth).27 AlignHorizontal(lipgloss.Center).28 Render("■")29 if gotSameBox != wantSameBox {30 t.Fatalf("same-box cellView() = %q, want %q", gotSameBox, wantSameBox)31 }3233 gotSameRow := cellView(m, 4, 1, false)34 wantSameRow := userCellStyle(3).35 Width(cellWidth).36 AlignHorizontal(lipgloss.Center).37 Render("●")38 if gotSameRow != wantSameRow {39 t.Fatalf("same-row cellView() = %q, want %q", gotSameRow, wantSameRow)40 }41}4243func TestCellViewUsesCursorBackground(t *testing.T) {44 g := newGrid(nil)45 g[1][1].v = 246 m := Model{47 cursor: game.Cursor{X: 1, Y: 1},48 grid: g,49 analysis: analyzeGrid(g),50 keys: DefaultKeyMap,51 }5253 gotFilled := cellView(m, 1, 1, false)54 wantFilled := valueCursorStyle(2).55 Width(cellWidth).56 AlignHorizontal(lipgloss.Center).57 Render(game.CursorLeft + "■" + game.CursorRight)58 if gotFilled != wantFilled {59 t.Fatalf("filled cursor cellView() = %q, want %q", gotFilled, wantFilled)60 }6162 m.grid[1][1].v = 063 gotEmpty := cellView(m, 1, 1, false)64 wantEmpty := game.CursorStyle().65 Width(cellWidth).66 AlignHorizontal(lipgloss.Center).67 Render(game.CursorLeft + "·" + game.CursorRight)68 if gotEmpty != wantEmpty {69 t.Fatalf("empty cursor cellView() = %q, want %q", gotEmpty, wantEmpty)70 }71}7273func TestProvidedCellViewUsesGivenTint(t *testing.T) {74 g := newGrid([]cell{{x: 0, y: 0, v: 2}})75 m := Model{76 grid: g,77 keys: DefaultKeyMap,78 cursor: game.Cursor{X: 8, Y: 8},79 analysis: analyzeGrid(g),80 providedGrid: buildProvidedGrid([]cell{{x: 0, y: 0, v: 2}}),81 }8283 got := cellView(m, 0, 0, false)84 want := lipgloss.NewStyle().85 Bold(true).86 Foreground(symbolColor(2)).87 Background(theme.GivenTint(game.DefaultBorderColors().BackgroundBG)).88 Width(cellWidth).89 AlignHorizontal(lipgloss.Center).90 Render("■")91 if got != want {92 t.Fatalf("provided cellView() = %q, want %q", got, want)93 }94}9596func TestBuildBoardBlockRendersHintBands(t *testing.T) {97 g := newGrid(nil)98 g[0][0].v = 199 g[0][1].v = 2100 g[0][2].v = 3101 m := Model{102 grid: g,103 analysis: analyzeGrid(g),104 keys: DefaultKeyMap,105 }106107 layout := buildBoardBlock(m, false)108 lines := strings.Split(ansi.Strip(layout.Block), "\n")109 if got, want := layout.HintHeight, colHintHeight; got != want {110 t.Fatalf("HintHeight = %d, want %d", got, want)111 }112 if got, want := layout.HintWidth, rowHintWidth; got != want {113 t.Fatalf("HintWidth = %d, want %d", got, want)114 }115 if got := []rune(lines[layout.HintHeight])[layout.HintWidth]; got != '┌' {116 t.Fatalf("grid top border should start after hints, got %q", got)117 }118 if strings.TrimSpace(lines[layout.HintHeight][:layout.HintWidth]) != "" {119 t.Fatalf("row hints should not occupy the border row, got %q", lines[layout.HintHeight][:layout.HintWidth])120 }121 if strings.TrimSpace(lines[layout.HintHeight+1][:layout.HintWidth]) == "" {122 t.Fatalf("row hints missing on first content row: %q", lines[layout.HintHeight+1][:layout.HintWidth])123 }124 if strings.TrimSpace(lines[0][layout.HintWidth:]) == "" {125 t.Fatalf("column hints missing from top band: %q", lines[0])126 }127}128129func TestHintRenderingUsesSolvedAndErrorStyles(t *testing.T) {130 analysis := boardAnalysis{}131 analysis.rowCounts[0][1] = houseQuota132 analysis.rowCounts[0][2] = 2133 analysis.rowCounts[0][3] = 4134 analysis.rowOverQuota[0][3] = true135 analysis.colCounts[0][2] = houseQuota136137 rowHints := rowHintView(analysis, false)138 wantSolved := hintSolvedStyle(1).Render("3")139 if !strings.Contains(rowHints, wantSolved) {140 t.Fatalf("rowHintView() = %q, want solved chip %q", rowHints, wantSolved)141 }142 wantError := hintErrorStyle().Render("4")143 if !strings.Contains(rowHints, wantError) {144 t.Fatalf("rowHintView() = %q, want error chip %q", rowHints, wantError)145 }146147 colHints := colHintView(analysis, false)148 wantColSolved := hintSolvedStyle(2).Render("3")149 if !strings.Contains(colHints, wantColSolved) {150 t.Fatalf("colHintView() = %q, want solved chip %q", colHints, wantColSolved)151 }152}153154func TestSolvedBoardUsesSuccessBackground(t *testing.T) {155 p := theme.Current()156 g := validCompleteGrid()157 m := Model{158 grid: g,159 analysis: analyzeGrid(g),160 keys: DefaultKeyMap,161 cursor: game.Cursor{X: 8, Y: 8},162 }163164 gotCell := cellView(m, 0, 0, true)165 wantCell := userCellStyle(1).166 Bold(true).167 Foreground(p.SolvedFG).168 Background(p.SuccessBG).169 Width(cellWidth).170 AlignHorizontal(lipgloss.Center).171 Render("▲")172 if gotCell != wantCell {173 t.Fatalf("solved cellView() = %q, want %q", gotCell, wantCell)174 }175176 gotRowHints := rowHintView(m.analysis, true)177 wantRowHint := solvedBoardStyle().Render("3")178 if !strings.Contains(gotRowHints, wantRowHint) {179 t.Fatalf("solved rowHintView() = %q, want success background %q", gotRowHints, wantRowHint)180 }181182 gotColHints := colHintView(m.analysis, true)183 if !strings.Contains(gotColHints, wantRowHint) {184 t.Fatalf("solved colHintView() = %q, want success background %q", gotColHints, wantRowHint)185 }186187 wantSpacer := solvedBoardStyle().Render(" ")188 if !strings.Contains(gotRowHints, wantSpacer) {189 t.Fatalf("solved rowHintView() = %q, want solved spacer %q", gotRowHints, wantSpacer)190 }191}192193func TestCellViewUsesErrorBackgroundForBoxConflictOnly(t *testing.T) {194 g := newGrid(nil)195 g[0][0].v = 3196 g[0][1].v = 3197 g[1][2].v = 3198 g[2][0].v = 3199200 m := Model{201 grid: g,202 analysis: analyzeGrid(g),203 keys: DefaultKeyMap,204 cursor: game.Cursor{X: 8, Y: 8},205 }206207 got := cellView(m, 0, 0, false)208 want := boxConflictCellStyle().209 Width(cellWidth).210 AlignHorizontal(lipgloss.Center).211 Render("●")212 if got != want {213 t.Fatalf("box conflict cellView() = %q, want %q", got, want)214 }215216 rowOnly := newGrid(nil)217 for x := 0; x < 4; x++ {218 rowOnly[0][x].v = 1219 }220 m.grid = rowOnly221 m.analysis = analyzeGrid(rowOnly)222 got = cellView(m, 0, 0, false)223 want = userCellStyle(1).224 Width(cellWidth).225 AlignHorizontal(lipgloss.Center).226 Render("▲")227 if got != want {228 t.Fatalf("row-only conflict cellView() = %q, want %q", got, want)229 }230}231232func TestUserCellViewUsesDefaultBoardBackground(t *testing.T) {233 g := newGrid(nil)234 g[0][0].v = 2235 m := Model{236 grid: g,237 analysis: analyzeGrid(g),238 keys: DefaultKeyMap,239 cursor: game.Cursor{X: 8, Y: 8},240 }241242 got := cellView(m, 0, 0, false)243 want := userCellStyle(2).244 Width(cellWidth).245 AlignHorizontal(lipgloss.Center).246 Render("■")247 if got != want {248 t.Fatalf("user cellView() = %q, want %q", got, want)249 }250}