A daily game
at main 177 lines 4.5 kB view raw
1import { atom } from "jotai"; 2import { atomWithRefresh, atomWithReset, RESET, selectAtom } from "jotai/utils"; 3import { arrayOfSize, groupIntoRows } from "./game"; 4 5export const defaultWidth = 6; 6export const defaultHeight = 6; 7export const defaultVariants = ["🍒", "🍍"]; 8 9export const widthAtom = atomWithReset(defaultWidth); 10export const heightAtom = atomWithReset(defaultHeight); 11export const variantsAtom = atomWithReset(defaultVariants); 12 13export const entropyAtom = atom((get) => get(variantsAtom).length); 14export const sizeAtom = atom((get) => get(widthAtom) * get(heightAtom)); 15 16export const _boardAtom = atom( 17 generateBoard(defaultWidth * defaultHeight, defaultVariants.length), 18); 19 20export const boardAtom = atom( 21 (get) => get(_boardAtom), 22 (get, set, _value?: typeof RESET) => { 23 set(sessionAtom, RESET); 24 set(_boardAtom, generateBoard(get(sizeAtom), get(entropyAtom))); 25 }, 26); 27 28export const boardMatrixAtom = atom((get) => 29 groupIntoRows(get(_boardAtom), get(widthAtom)), 30); 31 32const newCell = () => { 33 const cellAtom = atomWithReset(-1); 34 return atom( 35 (get) => get(cellAtom), 36 (get, set, reset?: typeof RESET) => { 37 if (reset === RESET) { 38 return set(cellAtom, RESET); 39 } 40 41 if (get(gameResultAtom)) { 42 return; 43 } 44 45 const options = get(variantsAtom); 46 const currentValue = get(cellAtom); 47 48 let newValue: number = currentValue + 1; 49 if (newValue === options.length) { 50 newValue = -1; 51 } 52 53 set(cellAtom, newValue); 54 }, 55 ); 56}; 57 58const _sessionAtom = atomWithRefresh((get) => 59 arrayOfSize(get(sizeAtom)).map(() => newCell()), 60); 61 62export const sessionAtom = atom( 63 (get) => get(_sessionAtom), 64 (get, set, _value?: typeof RESET) => 65 get(_sessionAtom).forEach((cell) => { 66 if (get(cell) < 0) { 67 return; 68 } 69 set(cell, RESET); 70 }), 71); 72 73export const sessionMatrixAtom = atom((get) => 74 groupIntoRows(get(_sessionAtom).map(get), get(widthAtom)), 75); 76 77export const gameResultAtom = atom( 78 (get) => 79 get(boardMatrixAtom).toString() === get(sessionMatrixAtom).toString(), 80); 81 82export const preferencesAtom = atom( 83 (get) => { 84 return { 85 width: get(widthAtom), 86 height: get(heightAtom), 87 variants: get(variantsAtom), 88 }; 89 }, 90 ( 91 _get, 92 set, 93 { 94 width, 95 height, 96 variants, 97 }: { width: number; height: number; variants: string[] }, 98 ) => { 99 set(widthAtom, width); 100 set(heightAtom, height); 101 set(variantsAtom, variants); 102 set(boardAtom); 103 }, 104); 105 106export const selectBoardSubset = (dir: "row" | "col", index: number) => 107 selectAtom( 108 boardMatrixAtom, 109 (board) => { 110 if (dir === "row") { 111 const row = board[index]; 112 if (typeof row === "undefined") { 113 throw new Error(`No board ${dir} at index ${index}`); 114 } 115 return row; 116 } else { 117 return board.map((row) => { 118 const col = row[index]; 119 if (typeof col === "undefined") { 120 throw new Error(`No board ${dir} at index ${index}`); 121 } 122 return col; 123 }); 124 } 125 }, 126 arraysAreEqual, 127 ); 128 129export const selectSessionSubset = (dir: "row" | "col", index: number) => 130 selectAtom( 131 sessionMatrixAtom, 132 (session) => { 133 if (dir === "row") { 134 const row = session[index]; 135 if (typeof row === "undefined") { 136 throw new Error(`No session ${dir} at index ${index}`); 137 } 138 return row; 139 } else { 140 return session.map((row) => { 141 const col = row[index]; 142 if (typeof col === "undefined") { 143 throw new Error(`No session ${dir} at index ${index}`); 144 } 145 return col; 146 }); 147 } 148 }, 149 arraysAreEqual, 150 ); 151 152export const selectGuessAt = (index: number) => 153 selectAtom(sessionAtom, (cells) => { 154 const cellAtom = cells[index]; 155 if (typeof cellAtom === "undefined") { 156 throw new Error(`Cell "${index}" not found.`); 157 } 158 return cellAtom; 159 }); 160 161export function selectVariantAt(index: number) { 162 return selectAtom(variantsAtom, (variants) => { 163 const variant = variants[index]; 164 if (typeof variant === "undefined") { 165 throw new Error(`Variant at ${index} does not exist.`); 166 } 167 return variant; 168 }); 169} 170 171function generateBoard(size: number, entropy: number) { 172 return arrayOfSize(size).map(() => Math.floor(Math.random() * entropy)); 173} 174 175function arraysAreEqual(a: number[], b: number[]) { 176 return a.toString() === b.toString(); 177}