import { atom } from "jotai"; import { atomWithRefresh, atomWithReset, RESET, selectAtom } from "jotai/utils"; import { arrayOfSize, groupIntoRows } from "./game"; export const defaultWidth = 6; export const defaultHeight = 6; export const defaultVariants = ["🍒", "🍍"]; export const widthAtom = atomWithReset(defaultWidth); export const heightAtom = atomWithReset(defaultHeight); export const variantsAtom = atomWithReset(defaultVariants); export const entropyAtom = atom((get) => get(variantsAtom).length); export const sizeAtom = atom((get) => get(widthAtom) * get(heightAtom)); export const _boardAtom = atom( generateBoard(defaultWidth * defaultHeight, defaultVariants.length), ); export const boardAtom = atom( (get) => get(_boardAtom), (get, set, _value?: typeof RESET) => { set(sessionAtom, RESET); set(_boardAtom, generateBoard(get(sizeAtom), get(entropyAtom))); }, ); export const boardMatrixAtom = atom((get) => groupIntoRows(get(_boardAtom), get(widthAtom)), ); const newCell = () => { const cellAtom = atomWithReset(-1); return atom( (get) => get(cellAtom), (get, set, reset?: typeof RESET) => { if (reset === RESET) { return set(cellAtom, RESET); } if (get(gameResultAtom)) { return; } const options = get(variantsAtom); const currentValue = get(cellAtom); let newValue: number = currentValue + 1; if (newValue === options.length) { newValue = -1; } set(cellAtom, newValue); }, ); }; const _sessionAtom = atomWithRefresh((get) => arrayOfSize(get(sizeAtom)).map(() => newCell()), ); export const sessionAtom = atom( (get) => get(_sessionAtom), (get, set, _value?: typeof RESET) => get(_sessionAtom).forEach((cell) => { if (get(cell) < 0) { return; } set(cell, RESET); }), ); export const sessionMatrixAtom = atom((get) => groupIntoRows(get(_sessionAtom).map(get), get(widthAtom)), ); export const gameResultAtom = atom( (get) => get(boardMatrixAtom).toString() === get(sessionMatrixAtom).toString(), ); export const preferencesAtom = atom( (get) => { return { width: get(widthAtom), height: get(heightAtom), variants: get(variantsAtom), }; }, ( _get, set, { width, height, variants, }: { width: number; height: number; variants: string[] }, ) => { set(widthAtom, width); set(heightAtom, height); set(variantsAtom, variants); set(boardAtom); }, ); export const selectBoardSubset = (dir: "row" | "col", index: number) => selectAtom( boardMatrixAtom, (board) => { if (dir === "row") { const row = board[index]; if (typeof row === "undefined") { throw new Error(`No board ${dir} at index ${index}`); } return row; } else { return board.map((row) => { const col = row[index]; if (typeof col === "undefined") { throw new Error(`No board ${dir} at index ${index}`); } return col; }); } }, arraysAreEqual, ); export const selectSessionSubset = (dir: "row" | "col", index: number) => selectAtom( sessionMatrixAtom, (session) => { if (dir === "row") { const row = session[index]; if (typeof row === "undefined") { throw new Error(`No session ${dir} at index ${index}`); } return row; } else { return session.map((row) => { const col = row[index]; if (typeof col === "undefined") { throw new Error(`No session ${dir} at index ${index}`); } return col; }); } }, arraysAreEqual, ); export const selectGuessAt = (index: number) => selectAtom(sessionAtom, (cells) => { const cellAtom = cells[index]; if (typeof cellAtom === "undefined") { throw new Error(`Cell "${index}" not found.`); } return cellAtom; }); export function selectVariantAt(index: number) { return selectAtom(variantsAtom, (variants) => { const variant = variants[index]; if (typeof variant === "undefined") { throw new Error(`Variant at ${index} does not exist.`); } return variant; }); } function generateBoard(size: number, entropy: number) { return arrayOfSize(size).map(() => Math.floor(Math.random() * entropy)); } function arraysAreEqual(a: number[], b: number[]) { return a.toString() === b.toString(); }