Mercurial
view react_games/src/LightsOut/main.tsx @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | fb9bcd3145cb |
| children |
line wrap: on
line source
import { ActionDispatch, CSSProperties, memo, useCallback, useReducer } from 'react'; /** * create lights out game * * - 5 x 5 grid * - when click it will turn on lights on top left right bottom. * - count number of moves * - timers * - when every thing is on then tthey win */ interface Cell { value: boolean; // true on and false off. } type Board = Cell[][]; enum GameStatus { PLAYABLE, WON, } interface GameState { board: Board; status: GameStatus; moves: number; } const MAX_COL = 5; const MAX_ROW = 5; const DIRECTION = [ [0, 0], [0, 1], [1, 0], [0, -1], [-1, 0], ] const constructGameState = (): GameState => { const board = Array.from( { length: MAX_ROW }, () => Array.from({ length: MAX_COL}, () => ({ value: false }) ) ); for (let i = 0; i < 30; i++) { const r = Math.floor(Math.random() * MAX_ROW); const c = Math.floor(Math.random() * MAX_COL); for (const [dr, dc] of DIRECTION) { if (board[r+dr]?.[c+dc]) { board[r+dr][c+dc].value = !board[r+dr]?.[c+dc].value } } } return { board, status: GameStatus.PLAYABLE, moves: 0, } } interface Styles { board: CSSProperties; cell: (isLightOn: boolean) => CSSProperties; } const styles: Styles = { board: { display: "grid", gridTemplateColumns: `repeat(${MAX_COL}, 1fr)`, width: MAX_COL * 30, }, cell: (isLightOn: boolean) => ({ width: "100%", aspectRatio: "1 / 1", border: `1px solid #222`, backgroundColor: isLightOn ? "white" : "black", transition: "background-color 120ms ease", cursor: "pointer", }) } interface CellComponentProp { cellValue: Cell; rowPos: number; colPos: number; dispatch: ActionDispatch<[action: Action]>; } const CellComponent = memo(({ cellValue, rowPos, colPos, dispatch }: CellComponentProp) => { const onClickHandler = useCallback(()=>{ dispatch({type: "place", newRowPos: rowPos, newColPos: colPos}); }, [rowPos, colPos, dispatch]); return ( <div onClick={onClickHandler} style={styles.cell(cellValue.value)}></div> ) }) type Action = | { type: "place", newRowPos: number, newColPos: number } | { type: "reset" } const gameStateReducer = (state: GameState, action: Action): GameState => { if (state.status === GameStatus.WON) return state; switch(action.type) { case "place": const allDirection: number[][] = []; for (const [dr, dc] of DIRECTION) { allDirection.push([action.newRowPos + dr, action.newColPos + dc]); } const newBoard: Board = state.board.map( (row, rowIdx) => row.map((col, colIdx) => (allDirection.some( ([nr, nc]) => rowIdx === nr && colIdx === nc )) ? { ...col, value: !col.value } : col) ) const newStatus = (newBoard.flat().filter((cell) => cell.value).length === MAX_COL * MAX_ROW) ? GameStatus.WON : GameStatus.PLAYABLE; return { ...state, status: newStatus, board: newBoard, moves: state.moves + 1, }; case "reset": return constructGameState(); } } const LightsOut = () => { const [gameState, dispatch] = useReducer(gameStateReducer, null, constructGameState); return ( <> <h1> Game Status {gameState.status}</h1> <h2> Number of moves so far {gameState.moves}</h2> <div style={styles.board}> { gameState.board.map( (row, rowIdx) => row.map( (cellValue, colIdx) => (<CellComponent key={`${rowIdx}-${colIdx}`} cellValue={cellValue} rowPos={rowIdx} colPos={colIdx} dispatch={dispatch}/>) ) ) } </div> </> ); } export { LightsOut }