cozy-games/packages/mnswpr/core/minesweeper/board.js
2026-07-04 07:49:46 +02:00

63 lines
2.1 KiB
JavaScript

// @ts-check
import { mulberry32, randInt } from '../session/rng.js'
import { eightWay } from '../grid/neighbors.js'
/**
* @typedef {{ rows: number, cols: number, mines: number, id?: string }} Config
* @typedef {{ mine: boolean, adjacent: number, status: 'hidden' | 'flagged' | 'revealed' }} Cell
*/
/**
* The set of cells kept mine-free for first-click safety: the clicked cell, plus
* its 8 neighbors when the board has room for all mines outside that 3x3. Falls
* back to just the clicked cell on boards too dense to spare the neighborhood.
*
* @param {Config} config
* @returns {Set<number>} coordinate keys (r * cols + c)
*/
export function excludeAround(config, r, c) {
const { rows, cols, mines } = config
const set = new Set([r * cols + c])
const roomFor3x3 = rows * cols - 9 >= mines
if (roomFor3x3) {
for (let dr = -1; dr <= 1; dr++) {
for (let dc = -1; dc <= 1; dc++) {
const nr = r + dr
const nc = c + dc
if (nr >= 0 && nc >= 0 && nr < rows && nc < cols) set.add(nr * cols + nc)
}
}
}
return set
}
/**
* Deterministically place mines and compute adjacency counts, mutating the grid
* in place. Pure function of (seed, config, exclude) — same inputs, same board.
*
* @param {number} seed
* @param {Config} config
* @param {Set<number>} exclude - coordinate keys never to mine (first-click safety)
* @param {import('../grid/grid.js').Grid<Cell>} grid
* @returns {Set<number>} the mined coordinate keys
*/
export function placeMines(seed, config, exclude, grid) {
const rng = mulberry32(seed)
const { rows, cols, mines } = config
const placed = new Set()
while (placed.size < mines) {
const key = randInt(rng, rows) * cols + randInt(rng, cols)
if (placed.has(key) || exclude.has(key)) continue
placed.add(key)
}
grid.forEach((cell, r, c) => { cell.mine = placed.has(r * cols + c) })
grid.forEach((cell, r, c) => {
if (cell.mine) { cell.adjacent = 0; return }
let n = 0
for (const [nr, nc] of eightWay(grid, r, c)) {
if (grid.at(nr, nc).mine) n++
}
cell.adjacent = n
})
return placed
}