diff --git a/index.html b/index.html index 0068160..0c589ec 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Tic Tac Toe Bolt + Vite + React
diff --git a/src/App.css b/src/App.css index 3bfd7ec..b9d355d 100644 --- a/src/App.css +++ b/src/App.css @@ -5,44 +5,38 @@ text-align: center; } -.board { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 1em; +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; } -.messages { - line-height: 1em; +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); } -.board-row { - display: flex; - flex-direction: row; - gap: 10px; +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); } -.board-cell { - width: 100px; - height: 100px; - border: 1px solid black; - display: flex; - justify-content: center; - align-items: center; - font-size: 24px; - cursor: pointer; +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } -.game-set .board-cell { - cursor: not-allowed; +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } } -.game-set .board-cell:hover { - background-color: unset; +.card { + padding: 2em; } -.board-cell:hover { - background-color: #f0f0f0; +.read-the-docs { + color: #888; } - -.board-cell.fading { - color: red; -} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index ff66e0b..f67355a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,188 +1,33 @@ -import React, { useEffect, useState } from 'react' +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' import './App.css' -type Symbol = 'X' | 'O'; - -type CellState = { - symbol?: Symbol; - isFading?: boolean; -}; - -type RowState = CellState[]; -type BoardState = RowState[]; - -type CellClickHandler = (row: number, col: number) => void; -type CellProps = CellState & { rowIndex: number, columnIndex: number, onClick?: CellClickHandler }; - -function Cell({ rowIndex, columnIndex, symbol, isFading, onClick }: CellProps) { - return
onClick && onClick(rowIndex, columnIndex)}>{symbol}
; -} - -type RowProps = { - index: number, - state: RowState - onCellClick: CellClickHandler; -}; - -function Row({ state, index, onCellClick }: RowProps) { - return ( -
- {state.map((cellState: CellState, i) => )} -
- ) -} - -type BoardProps = { - state: BoardState; - hasWinner?: boolean; - onCellClick: CellClickHandler; -} - -function Board({ state, hasWinner, onCellClick }: BoardProps) { - return ( -
- {state.map((row, i) => )} -
- ) -} - -function hasWin(board: BoardState, size: number, row: number, column: number): boolean { - const targetCell = board[row]?.[column]; - if (!targetCell) { return false; } - - let win = true; - for (let r = 0; r < size; r++) { - if (board[r]?.[column]?.symbol !== targetCell.symbol) { - win = false; - break; - } - } - if (win) { - return true; - } - - win = true; - for (let c = 0; c < size; c++) { - if (board[row]?.[c]?.symbol !== targetCell.symbol) { - win = false; - break; - } - } - if (win) { - return true; - } - if (row !== column) { - return false; - } - - - win = true; - for (let i = 0; i < size; i++) { - if (board[i]?.[i]?.symbol !== targetCell.symbol) { - win = false; - break; - } - } - - return win; -} - -type MoveRecord = { - row: number; - col: number; - player: Symbol; -}; - -type GameState = { - board: BoardState; - latestMoves:MoveRecord[]; - currentPlayer: Symbol; - winner?: Symbol; -} - -function useBoardState(size: number = 3): [GameState, { reset: ()=> void; playOn: (row: number, col: number) => boolean } ] { - const [state, updateState] = useState({ - board: Array(size).fill(Array(size).fill({})), - latestMoves: [], - currentPlayer: 'O', - }); - - return [ - state, - { - reset: () => updateState({ - board: Array(size).fill(Array(size).fill({})), - latestMoves: [], - currentPlayer: 'O', - }), - - playOn: (row: number, col: number) => { - if (state.winner) { - return false; - } - - const targetCell = state.board[row]?.[col]; - if (!targetCell || targetCell.symbol) { - return false; - } - - const cellToFading = state.latestMoves.length >= (2*size-1) ? state.latestMoves[0] : undefined; - const fadingRow = cellToFading?.row; - const fadingCol = cellToFading?.col; - - const nextState: GameState = { - board: state.board.map((oldRow, r) => oldRow.map((oldCell, c)=> { - return { - ...(oldCell.isFading ? {} : oldCell), - ...(r===row && c===col ? { symbol: state.currentPlayer } : undefined), - ...(r===fadingRow && c===fadingCol ? { isFading: true} : undefined), - }; - })), - - latestMoves: [ - ...state.latestMoves, - { - row, - col, - player: state.currentPlayer, - } - ].slice(-2*size+1), - - currentPlayer: state.currentPlayer === 'X' ? 'O' : 'X' as Symbol, - }; - - if (hasWin(nextState.board, size, row, col)) { - nextState.winner = state.currentPlayer; - } - updateState(nextState); - - return true; - }, - }, - ]; -} function App() { - const [state, { playOn, reset: resetGame }] = useBoardState(5); - - const nextPlayer = state.currentPlayer; - const latestCells = state.latestMoves; - const winner = state.winner; + const [count, setCount] = useState(0) return ( <> - -
+
+ + Vite logo + + + React logo + +
+

Vite + React

+
+

- {winner ? `Winner is ${winner}` : (<>Next player is {nextPlayer})} - {latestCells.length === 0 && ("Click any cell to start")} - {winner && } + Edit src/App.jsx and save to test HMR

+

+ Click on the Vite and React logos to learn more +

) }