Mercurial
view react_games/src/Robot/main.tsx @ 78:e7bf9e002850
amend
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Wed, 31 Dec 2025 15:07:43 -0800 |
| parents | fb9bcd3145cb |
| children |
line wrap: on
line source
/** * Scenario * Design a miniature browser game in which a single “robot” sprite navigates a * rectangular grid to reach a goal while avoiding obstacles. Everything—from bootstrapping the project * to shipping a playable, restartable game—must be accomplished within one hour. * * * Grid 10 x 10 * Robot top left corner * Obstical is going to be 10 * * one of the grid is going to be the goal * * Movement logic keyboard or WASD okay */ import React, { CSSProperties, memo, useEffect, useReducer } from "react"; enum CellValue { PLAYER, GOAL, OBSTICAL, EMPTY } interface Cell { value: CellValue; } interface RowProp { rowValue: Cell[]; } interface CellComponentProp { cell: Cell; } enum GameStatus { PLAYABLE, WON, } interface RobotState { x: number; y: number, } interface GameState { board: Cell[][]; status: GameStatus; robotState: RobotState; } enum GameActionType { MOVE, RESET, } type GameAction = {type: GameActionType.MOVE, command: string } | { type: GameActionType.RESET } const MAX_WIDTH: number = 10; const MAX_COLUMN: number = 10; const BOARD: Cell[][] = Array.from( { length: MAX_WIDTH }, () => Array(MAX_COLUMN).fill({value: CellValue.EMPTY}) ); BOARD[0][0] = { value: CellValue.PLAYER } BOARD[0][5] = { value: CellValue.OBSTICAL } BOARD[6][5] = { value: CellValue.GOAL } const getCellValue = (cell: Cell) => { switch(cell.value) { case CellValue.EMPTY: return ( <></> ) case CellValue.PLAYER: return ( <>🤖</> ) case CellValue.OBSTICAL: return ( <>🧱</> ) case CellValue.GOAL: return ( <>🪙</> ) } } const CellComponent = memo(({cell}: CellComponentProp) => { const cellStyle: CSSProperties = { padding: "10px", width: "50px", height: "50px", border: "1px solid #ccc", } return ( <div style={cellStyle}> {getCellValue(cell)} </div> ) }) const Row = memo(({rowValue}: RowProp) => { const rowStyle: CSSProperties = { display: "flex", } return ( <div style={rowStyle}> {rowValue.map((cell, idx) => {return <CellComponent key={idx} cell={cell} />})} </div> ) }) function boardReducer(state: GameState, action: GameAction): GameState { if (action.type === GameActionType.RESET) return INITIAL_GAME_STATE; const { x, y } = state.robotState; let newX = x; let newY = y; switch (action.command) { case "W": newX = Math.max(0, x - 1); break; case "A": newY = Math.max(0, y - 1); break; case "S": newX = Math.min(MAX_WIDTH - 1, x + 1); break; case "D": newY = Math.min(MAX_COLUMN - 1, y + 1); break; default: return state; } const newBoard = state.board.map(row => row.slice()); newBoard[x][y] = { value: CellValue.EMPTY }; // old spot newBoard[newX][newY] = { value: CellValue.PLAYER }; // new spot return { board: newBoard, status: GameStatus.PLAYABLE, robotState: { x: newX, y: newY }, }; } const INITIAL_GAME_STATE: GameState = { board: BOARD, status: GameStatus.PLAYABLE, robotState: { x: 0, y: 0 }, } const Current = () => { const [state, dispatch] = useReducer(boardReducer, INITIAL_GAME_STATE); useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { dispatch({type: GameActionType.MOVE, command: event.key.toUpperCase()}) }; document.addEventListener("keydown", handleKeyDown); }, []) return ( <> {state.board.map((row, idx) => { return <Row key={idx} rowValue={row} />})} </> ); } export { Current }