A modern implementation of Tic-Tac-Toe with multiple game modes. Play against a friend or challenge an AI opponent in either classic or "wild" mode.
- Player Modes: Play against a friend (2-player) or against the computer
- Game Modes:
- Standard: Classic Tic-Tac-Toe rules
- Wild: Each player can place either X or O on any turn
- AI Opponent: Strategic computer player with randomized behavior for unpredictability
- Responsive UI: Built with React and styled with Tailwind CSS
- Comprehensive Test Suite: Full test coverage with Vitest
- React 18 with TypeScript
- Vite for build tooling
- Tailwind CSS for styling
- Vitest + React Testing Library for testing
- Headless UI for accessible components
This project uses pnpm as its package manager.
- Node.js (v16 or higher recommended)
- pnpm (v9.6.0 or higher)
- Install dependencies:
pnpm install- Start the development server:
pnpm run dev- Open your browser to http://localhost:5173/
pnpm run dev- Start development serverpnpm run build- Build for productionpnpm run preview- Preview production buildpnpm run test- Run tests in watch modepnpm run test:ui- Run tests with UI interfacepnpm run coverage- Generate test coverage reportpnpm run lint- Run ESLint
Use the control panel on the right side of the screen to configure your game:
- Player Mode: Toggle between playing against a friend (2-player) or against the computer
- Game Mode: Switch between Standard (classic rules) or Wild mode
Standard Mode
- Players alternate turns placing their assigned mark (X or O)
- First player to get three in a row (horizontal, vertical, or diagonal) wins
- In human vs. computer games, the human player is always O
Wild Mode
- Players can choose to place either X or O on each turn
- Creates a more strategic game where blocking becomes complex
- Winning typically requires the opponent to make a mistake
src/
├── components/ # React components
│ ├── cell.tsx
│ ├── game-control.tsx
│ ├── game-grid.tsx
│ ├── mark-choice-dialog.tsx
│ ├── notifications.tsx
│ └── styled-button.tsx
├── computer-player/ # AI logic
│ ├── computer-player.ts
│ └── functions.ts
├── state/ # State management
│ ├── reducer.ts
│ └── functions.ts
├── tests/ # Test suites
├── app.tsx # Main application component
├── models.ts # TypeScript interfaces
└── constants.ts # Game constants
The AI opponent uses a strategic approach with intentional unpredictability:
Standard Mode
- Evaluates winning moves
- Blocks opponent's winning moves
- Uses strategic positioning (center, corners)
- Includes random behavior to avoid being too predictable
Wild Mode
- Attempts winning moves with either X or O
- Evaluates "safe" moves that won't create immediate losses
- Falls back to random moves when no clear strategy exists
- Wild mode inherently limits winning strategies due to the flexible mark placement
Note: Wild mode's rules create unique gameplay dynamics where:
- Traditional tactics like forking and blocking are ineffective or counterproductive
- Wins typically only occur when a player makes a mistake
- Games often trend toward draws when both players play carefully
- Human player is always assigned O in Standard mode
- UI is optimized for medium and large screens; small screen experience could be improved
- Computer player behavior can become predictable with extended play
- Wild mode dialog has keyboard navigation accessibility issues
Potential improvements for future versions:
- Responsive Design: Fully responsive grid that adapts to all screen sizes
- Mark Selection: Allow human players to choose X or O in Standard mode
- Difficulty Levels: Multiple AI difficulty settings (easy, medium, hard)
- Enhanced AI: More sophisticated algorithms for varied difficulty levels
- Visual Design: Comprehensive design system with unique visual identity
- Accessibility: Full WCAG compliance, including keyboard navigation for all dialogs
- Game Modes: Additional variants (4x4 grid, connect-4-style, etc.)
- Multiplayer: Online multiplayer with WebSocket support
This project was built with:
- Type safety via TypeScript
- Component testing with React Testing Library
- State management using React's useReducer hook
- Accessible UI components from Headless UI
- Utility-first styling with Tailwind CSS
This project is private and for demonstration purposes.