comparison react_games/src/Robot/main.tsx @ 37:fb9bcd3145cb

[ReactGames] Few games I made using react just to practice few things.
author MrJuneJune <me@mrjunejune.com>
date Mon, 01 Dec 2025 20:22:47 -0800
parents
children
comparison
equal deleted inserted replaced
36:84672efec192 37:fb9bcd3145cb
1 /**
2 * Scenario
3 * Design a miniature browser game in which a single “robot” sprite navigates a
4 * rectangular grid to reach a goal while avoiding obstacles. Everything—from bootstrapping the project
5 * to shipping a playable, restartable game—must be accomplished within one hour.
6 *
7 *
8 * Grid 10 x 10
9 * Robot top left corner
10 * Obstical is going to be 10
11 *
12 * one of the grid is going to be the goal
13 *
14 * Movement logic keyboard or WASD okay
15 */
16
17 import React, { CSSProperties, memo, useEffect, useReducer } from "react";
18
19 enum CellValue {
20 PLAYER,
21 GOAL,
22 OBSTICAL,
23 EMPTY
24 }
25
26 interface Cell {
27 value: CellValue;
28 }
29
30 interface RowProp {
31 rowValue: Cell[];
32 }
33
34 interface CellComponentProp {
35 cell: Cell;
36 }
37
38 enum GameStatus {
39 PLAYABLE,
40 WON,
41 }
42
43 interface RobotState {
44 x: number;
45 y: number,
46 }
47
48 interface GameState {
49 board: Cell[][];
50 status: GameStatus;
51 robotState: RobotState;
52 }
53
54 enum GameActionType {
55 MOVE,
56 RESET,
57 }
58
59 type GameAction = {type: GameActionType.MOVE, command: string } | { type: GameActionType.RESET }
60
61 const MAX_WIDTH: number = 10;
62 const MAX_COLUMN: number = 10;
63 const BOARD: Cell[][] = Array.from(
64 { length: MAX_WIDTH },
65 () => Array(MAX_COLUMN).fill({value: CellValue.EMPTY})
66 );
67
68 BOARD[0][0] = {
69 value: CellValue.PLAYER
70 }
71 BOARD[0][5] = {
72 value: CellValue.OBSTICAL
73 }
74 BOARD[6][5] = {
75 value: CellValue.GOAL
76 }
77
78
79 const getCellValue = (cell: Cell) => {
80 switch(cell.value) {
81 case CellValue.EMPTY:
82 return (
83 <></>
84 )
85 case CellValue.PLAYER:
86 return (
87 <>🤖</>
88 )
89 case CellValue.OBSTICAL:
90 return (
91 <>🧱</>
92 )
93 case CellValue.GOAL:
94 return (
95 <>🪙</>
96 )
97 }
98 }
99
100 const CellComponent = memo(({cell}: CellComponentProp) => {
101 const cellStyle: CSSProperties = {
102 padding: "10px",
103 width: "50px",
104 height: "50px",
105 border: "1px solid #ccc",
106 }
107 return (
108 <div style={cellStyle}>
109 {getCellValue(cell)}
110 </div>
111 )
112 })
113
114 const Row = memo(({rowValue}: RowProp) => {
115 const rowStyle: CSSProperties = {
116 display: "flex",
117 }
118 return (
119 <div style={rowStyle}>
120 {rowValue.map((cell, idx) => {return <CellComponent key={idx} cell={cell} />})}
121 </div>
122 )
123 })
124
125 function boardReducer(state: GameState, action: GameAction): GameState {
126 if (action.type === GameActionType.RESET) return INITIAL_GAME_STATE;
127
128 const { x, y } = state.robotState;
129 let newX = x;
130 let newY = y;
131
132 switch (action.command) {
133 case "W": newX = Math.max(0, x - 1); break;
134 case "A": newY = Math.max(0, y - 1); break;
135 case "S": newX = Math.min(MAX_WIDTH - 1, x + 1); break;
136 case "D": newY = Math.min(MAX_COLUMN - 1, y + 1); break;
137 default: return state;
138 }
139
140 const newBoard = state.board.map(row => row.slice());
141
142 newBoard[x][y] = { value: CellValue.EMPTY }; // old spot
143 newBoard[newX][newY] = { value: CellValue.PLAYER }; // new spot
144
145 return {
146 board: newBoard,
147 status: GameStatus.PLAYABLE,
148 robotState: { x: newX, y: newY },
149 };
150 }
151
152 const INITIAL_GAME_STATE: GameState = {
153 board: BOARD,
154 status: GameStatus.PLAYABLE,
155 robotState: { x: 0, y: 0 },
156 }
157
158 const Current = () => {
159 const [state, dispatch] = useReducer(boardReducer, INITIAL_GAME_STATE);
160
161 useEffect(() => {
162 const handleKeyDown = (event: KeyboardEvent) => {
163 dispatch({type: GameActionType.MOVE, command: event.key.toUpperCase()})
164 };
165
166 document.addEventListener("keydown", handleKeyDown);
167 }, [])
168
169
170
171 return (
172 <>
173 {state.board.map((row, idx) => { return <Row key={idx} rowValue={row} />})}
174 </>
175 );
176 }
177
178 export {
179 Current
180 }