A single-player Tic Tac Toe game built with React Native, TypeScript and Expo. The computer opponent uses the Minimax algorithm, to make it unbeatable. Every game will end in either a tie or a loss for the player.
- Node.js (v18 or higher)
- npm or yarn
- Expo Go app on your mobile device to run on a physical device.
- Clone the repository and navigate to the project directory:
cd tic-tac-toe- Install dependencies:
npm installStart the development server:
npm startThis will open Expo DevTools in your browser. From there, you can:
- Press
wto open in web browser - Press
ato open in Android emulator - Press
ito open in iOS simulator - Scan the QR code with Expo Go app on your physical device
npm testnpm run lint- Launch the app and choose whether you want to go first as X or let the computer go first and play second as O
- Tap on any empty cell to make your move
- The computer will respond with its move
- The game ends when someone wins or all cells are filled (tie)
- After the game ends, tap "Play Again" to start a new game
- VS Code
- Expo Go
- Claude
- GitHub
The computer opponent uses the Minimax algorithm to determine the best moves. This is a recursive algorithm that evaluates all possible game states to find the move that maximizes the computer's chances of winning while minimizing the player's chances.
The Minimax approach was chosen over a rules-based solution because it guarantees play without having to manually define and maintain a set of rules. It's also easier to verify for correctness. The 3x3 game tree is small enough that performance is not a concern.
The application uses useState and useEffect for state management, which is wrapped in a custom useGameState hook. This approach keeps the code simple and avoids unnecessary dependencies for a game of this scope.
- Separation of Concerns: Game logic is isolated in
gameLogic.tswhich makes it testable and independent of React components - Custom Hook Pattern:
useGameStateencapsulates all game state and actions, keeping components focused on the UI - TypeScript: Strict typing throughout prevents runtime errors and improves maintainability
- Component Composition: Small, single-responsibility components (Cell, Board, GameScreen) enable reuse and easier testing
screen-recording-one.mov
The bug: The display shows the correct board state, but tapping certain cells will place the mark in the wrong position. The bug is on the onPress handler in Board.tsx. It passes row + col instead of index. This caused tapping on cells, like cell (1,0), to send index 1 when it should have been index 3.
screen-recording-two.mov
The bug: When flipped in horizontal mode, the UI look good on the start screen, but when a user moves to the next screen parts of the text above the board are cut off. This is an easy fix with some styling to reduce the size of the board and text, however the outcome modal forces the game into portrait mode even though the user is still holding the device horizontally.