clone

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

refactor/root-tui-model
puzzletea / games / lightsout / generator.go
generator.go 1.5K
1package lightsout23import (4 "math/rand/v2"5)67// Generate creates a new w x h grid with a solvable puzzle.8// It starts with all lights off and applies random toggles.9func Generate(w, h int) [][]bool {10 rng := rand.New(rand.NewPCG(rand.Uint64(), rand.Uint64()))11 return GenerateSeeded(w, h, rng)12}1314func GenerateSeeded(w, h int, rng *rand.Rand) [][]bool {15 grid := make([][]bool, h)16 for y := range h {17 grid[y] = make([]bool, w)18 }1920 // Iterate through every cell and randomly decide whether to "click" it.21 // Since clicking a cell twice cancels out, and the order of clicks doesn't matter,22 // iterating once through all cells covers all reachable states from "all off".23 for y := range h {24 for x := range w {25 if rng.IntN(2) == 1 {26 Toggle(grid, x, y)27 }28 }29 }3031 // Ensure the puzzle isn't already solved (all off).32 if IsSolved(grid) {33 // Toggle one random cell to ensure at least some lights are on.34 Toggle(grid, rng.IntN(w), rng.IntN(h))35 }3637 return grid38}3940// Toggle switches the state of the cell at (x,y) and its immediate neighbors.41// It assumes grid is rectangular.42func Toggle(grid [][]bool, x, y int) {43 h := len(grid)44 if h == 0 {45 return46 }47 w := len(grid[0])4849 safeToggle := func(c, r int) {50 if c >= 0 && c < w && r >= 0 && r < h {51 grid[r][c] = !grid[r][c]52 }53 }5455 safeToggle(x, y)56 safeToggle(x, y-1)57 safeToggle(x, y+1)58 safeToggle(x-1, y)59 safeToggle(x+1, y)60}6162func IsSolved(grid [][]bool) bool {63 for _, row := range grid {64 for _, cell := range row {65 if cell {66 return false67 }68 }69 }70 return true71}