Mercurial
view react_games/src/Connectfour/main.tsx @ 70:4bc56e88e1f3
Remove unnecessary files.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 25 Dec 2025 20:10:46 -0800 |
| parents | fb9bcd3145cb |
| children |
line wrap: on
line source
import { useReducer, createContext, useContext, memo, Dispatch } from 'react' enum P { R = '🔴', Y = '🟡', _ = '' } type Board = P[][] // 6 rows × 7 cols type Status = 'PLAYING' | 'TIE' | 'R_WON' | 'Y_WON' type Action = | { type: 'DROP'; col: number } | { type: 'RESET' } interface State { board: Board turn: P.R | P.Y status: Status } const ROWS = 6, COLS = 7 const emptyBoard = (): Board => Array.from({ length: ROWS }, () => Array(COLS).fill(P._)) const init: State = { board: emptyBoard(), turn: P.R, status: 'PLAYING' } function firstEmptyRow(board: Board, col: number): number | null { for (let r = ROWS - 1; r >= 0; r--) if (board[r][col] === P._) return r return null } function scanWinner(b: Board): Status { const lines = [ [ 1, 0], [0, 1], // vertical, horizontal [ 1, 1], [1, -1], // two diagonals ] for (let r = 0; r < ROWS; r++) for (let c = 0; c < COLS; c++) if (b[r][c] !== P._) for (const [dr, dc] of lines) if ( b[r + dr]?.[c + dc] === b[r][c] && b[r + 2*dr]?.[c + 2*dc] === b[r][c] && b[r + 3*dr]?.[c + 3*dc] === b[r][c] ) return b[r][c] === P.R ? 'R_WON' : 'Y_WON' return b.flat().every(p => p !== P._) ? 'TIE' : 'PLAYING' } function reducer(s: State, a: Action): State { if (a.type === 'RESET') return init if (s.status !== 'PLAYING') return s // game over const row = firstEmptyRow(s.board, a.col) if (row == null) return s // full column // clone touched row only const newRow = [...s.board[row]] newRow[a.col] = s.turn const newBoard = s.board.map((r, i) => (i === row ? newRow : r)) const nextTurn = s.turn === P.R ? P.Y : P.R const status = scanWinner(newBoard) return { board: newBoard, turn: nextTurn, status } } const DispatchCtx = createContext<Dispatch<Action> | null>(null) const useGameDispatch = () => { const d = useContext(DispatchCtx) if (!d) throw new Error('outside provider') return d } /* ------- Leaf ------- */ interface CellProps { v: P } const Cell = memo<CellProps>(({ v }) => ( <div style={{ width: 52, height: 52, margin: 2, borderRadius: '50%', background: '#0e2a5a', display: 'grid', placeItems: 'center', fontSize: 30 }}> {v} </div> )) /* ------- Column button ------- */ const ColBtn = ({ col }: { col: number }) => { const dispatch = useGameDispatch() return ( <button style={{ flex: 1, height: 20, cursor: 'pointer' }} onClick={() => dispatch({ type: 'DROP', col })} /> ) } /* ------- Board ------- */ const BoardView = memo(({ board }: { board: Board }) => ( <div> {/* clickable top buttons */} <div style={{ display: 'flex' }}> {Array.from({ length: COLS }, (_, c) => <ColBtn key={c} col={c} />)} </div> {/* grid */} {board.map((row, r) => ( <div key={r} style={{ display: 'flex' }}> {row.map((v, c) => <Cell key={c} v={v} />)} </div> ))} </div> )) function ConnectFour() { const [state, dispatch] = useReducer(reducer, init) return ( <DispatchCtx.Provider value={dispatch}> <h2 style={{ textAlign: 'center' }}> {state.status === 'PLAYING' && `Turn: ${state.turn}`} {state.status === 'TIE' && 'Tie game'} {state.status === 'R_WON' && 'Red wins!'} {state.status === 'Y_WON' && 'Yellow wins!'} </h2> <BoardView board={state.board} /> {state.status !== 'PLAYING' && ( <button style={{ marginTop: 16 }} onClick={() => dispatch({ type: 'RESET' })}> Play again </button> )} </DispatchCtx.Provider> ) } export { ConnectFour, }