Project Info
Inspiration
We are international students who bonded over board games on the first day of the hackathon. It was a great way to break the ice, but we quickly realized a problem: once we leave, we can’t keep playing together because most quality online board game platforms are behind paywalls or require complex setups. We wanted to democratize this experience. We built Boardify to let friends anywhere play any board game—whether it’s a classic they miss from home or a brand-new game they just invented—instantly and for free.
What it does
Boardify is an AI-powered game engine that turns text descriptions into fully playable, multiplayer online card games in seconds. Input: You type a game name (e.g., "Uno", "Exploding Kittens") or a completely new idea (e.g., "A card game about hacking where you steal GPUs"). Generation: Our AI agent researches the official rules (or improvises new ones), designs the deck, and generates the game logic. Visualization: It generates custom card assets and artwork on the fly. Play: It deploys a live multiplayer room where you and your friends can join and play immediately in the browser.
How we built it
We built a sophisticated "Universal Game Engine" that decouples game rules from the core logic, allowing AI to script the game behavior dynamically. Frontend: Built with React and deployed on Vercel for a responsive, real-time interface. AI Pipeline (hosted on Modal): Research: We use Perplexity Sonar to search the web for comprehensive, up-to-date rules for existing games. Logic Generation: We use Anthropic Claude 3.5 Sonnet to convert those rules into a strict, proprietary JSON Game Schema (defining deck structure, turn phases, and win conditions) and a Python Plugin (handling complex mechanics like "stacking draw cards" or "blocking actions"). Visuals: We use Flux to generate thematic artwork for the card faces and backgrounds based on the game's theme. Research: We use Perplexity Sonar to search the web for comprehensive, up-to-date rules for existing games. Logic Generation: We use Anthropic Claude 3.5 Sonnet to convert those rules into a strict, proprietary JSON Game Schema (defining deck structure, turn phases, and win conditions) and a Python Plugin (handling complex mechanics like "stacking draw cards" or "blocking actions"). Visuals: We use Flux to generate thematic artwork for the card faces and backgrounds based on the game's theme. Backend & Infrastructure: The entire backend runs on Modal. We use Modal's sandboxing capabilities to safely execute and validate the AI-generated Python code before the game starts, ensuring the game is bug-free and playable.
Challenges we ran into
The "Universal" Problem: Designing a single JSON schema that could describe every possible card game was incredibly difficult. We had to abstract mechanics like "reverse turn order," "skip next player," and "reaction cards" (like Nope in Exploding Kittens) into a generalized event system. AI Hallucinations: Early on, the AI would generate rules that didn't make sense (e.g., a deck with 0 cards). We solved this by implementing a validation step in a Modal sandbox that "test-runs" the generated game configuration to catch errors before the user sees them. Real-time State Sync: Managing the game state for multiplayer sessions where rules change dynamically required a robust WebSocket architecture.
Accomplishments we're proud of
The Hybrid Engine: We're proud that we didn't just make a "text adventure." We built a real game engine where the AI writes actual Python code (plugins) to handle edge cases the JSON schema can't cover. This allows for complex logic like "challenging a Wild Draw 4" in Uno. Instant Deployment: Going from a text prompt to a playable multiplayer link in under a minute is a huge technical feat involving three different AI models working in concert. Sandboxed Safety: Successfully using Modal to sandbox the AI-generated code means our platform is secure, even though it executes arbitrary code generated by an LLM.
What we learned
Structured Output is Key: We learned that asking an LLM to "write code" is messy, but asking it to "fill out this specific JSON schema" yields incredibly reliable results. Infrastructure Matters: Using serverless GPUs via Modal allowed us to scale our heavy AI workloads (like Flux image generation) without managing complex infrastructure. Community: We learned the value of finding mentors early! Their feedback helped us narrow our scope from "all board games" to "card-based games," which made the project achievable in 36 hours.
What's next
Board Support: Expanding our "Universal Engine" to support spatial boards (like Chess or Catan), not just card stacks. Voice Mode: Adding a voice interface so you can argue about the rules with the AI dealer, just like in real life. Persistent Campaigns: Allowing the AI to remember past games and evolve the rules over a series of sessions (Legacy-style games).
🃏 Card Game Engine
A fully generic multiplayer card game engine built with FastAPI + WebSockets on the backend and Next.js 14 on the frontend. Any card game can be defined in a single JSON file — the engine reads it and runs a working multiplayer version.
Currently ships with a complete, playable Exploding Kittens implementation.
🗂 Project Structure
game-engine/
├── backend/
│ ├── app/
│ │ ├── main.py ← FastAPI entry point (game-agnostic)
│ │ ├── models/
│ │ │ └── game.py ← Domain models (Card, Player, GameState…)
│ │ ├── schemas/
│ │ │ └── requests.py ← API request/response schemas
│ │ ├── routers/
│ │ │ ├── rooms.py ← HTTP REST endpoints
│ │ │ └── websocket.py ← WebSocket real-time endpoint
│ │ ├── services/
│ │ │ ├── game_loader.py ← Reads JSON → builds game state
│ │ │ ├── room_manager.py ← In-memory rooms + broadcast
│ │ │ └── engines/
│ │ │ ├── exploding_kittens.py ← EK-specific rules
│ │ │ └── generic.py ← Fallback engine
│ │ └── games/
│ │ └── exploding_kittens.json ← 🎮 Game definition
│ ├── requirements.txt
│ └── Dockerfile
│
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx ← Home: create / join room
│ │ │ ├── join/page.tsx ← Join via code or link
│ │ │ └── room/[roomCode]/page.tsx ← Active game room
│ │ ├── components/
│ │ │ ├── game/
│ │ │ │ ├── GameRoom.tsx ← Main orchestrator
│ │ │ │ ├── GameCard.tsx ← Generic card component
│ │ │ │ ├── GameTable.tsx ← Table, deck, discard, opponents
│ │ │ │ ├── PlayerHand.tsx ← Local player's hand
│ │ │ │ ├── GameLog.tsx ← Scrolling event log
│ │ │ │ ├── PendingActionPanel.tsx ← Modals for Favor, Nope, etc.
│ │ │ │ └── SeeTheFutureModal.tsx
│ │ │ └── lobby/
│ │ │ └── Lobby.tsx ← Waiting room UI
│ │ ├── hooks/
│ │ │ ├── useGameSocket.ts ← Auto-reconnecting WebSocket
│ │ │ └── useGameActions.ts ← Action dispatch helpers
│ │ ├── lib/
│ │ │ └── api.ts ← Typed API client
│ │ └── types/
│ │ └── game.ts ← TypeScript interfaces
│ ├── package.json
│ └── Dockerfile
│
└── docker-compose.yml
🚀 Quick Start
Without Docker
Backend:
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
Frontend:
cd frontend
cp .env.local.example .env.local # edit if your backend isn't on :8000
npm install
npm run dev
Open http://localhost:3000.
With Docker Compose
docker compose up --build
🎮 How to Play (Exploding Kittens)
- Create a room — enter your name, choose Exploding Kittens, click Create.
- Share the link — click "Copy Invite Link" in the lobby and send it to friends (or share the 6-digit room code directly).
- Friends join — they visit
/join?room=XXXXXXor paste the link, enter their name. - Host starts — once 2–5 players are in the lobby, the host clicks "Start Game".
- Play!
Card interactions
| Card | How to use |
|---|---|
| Action cards (Skip, Attack, Shuffle, See Future) | Click the card in your hand |
| Favor | Click Favor → choose a target player |
| Cat combos (Taco, Rainbow, Beard, etc.) | Click first cat, then click a matching cat, then choose a target |
| Nope | When a Nope button appears, click it to cancel an action |
| Defuse | Automatically used when you draw an Exploding Kitten |
| Bomb placement | After defusing, drag the slider to choose where to reinsert the bomb |
➕ Adding a New Game (e.g. Uno)
- Create
backend/app/games/uno.jsonfollowing the schema below. - (Optional) Create
backend/app/services/engines/uno.pyfor game-specific rules. If absent, the generic engine is used. - That's it — the game appears in the frontend dropdown automatically.
JSON Game Definition Schema
{
"id": "my_game",
"name": "My Card Game",
"description": "...",
"rules": {
"minPlayers": 2,
"maxPlayers": 8,
"handSize": 7,
"turnStructure": {
"phases": [
{ "id": "play", "name": "Play a Card", "description": "...", "isOptional": false },
{ "id": "draw", "name": "Draw", "description": "...", "isOptional": true }
],
"canPassTurn": true,
"mustPlayCard": false,
"drawCount": 1
},
"winCondition": {
"type": "empty_hand",
"description": "First player to empty their hand wins!"
},
"specialRules": []
},
"cards": [
{
"id": "card_key", // unique identifier
"name": "Card Name",
"type": "action", // action | defense | reaction | special | combo
"subtype": "card_key", // used by the engine for effect logic
"emoji": "🃏",
"description": "What this card does",
"effects": [
{
"type": "skip", // effect type for engine logic
"target": "self", // self | others | choose | all
"description": "...",
"metadata": {}
}
],
"isPlayable": true,
"isReaction": false, // true = can be played out of turn
"count": 4, // copies in the deck
"metadata": {}
}
],
"ui": {
"tableBackground": "#1a472a",
"turnPrompt": "It's your turn!",
"winMessage": "🎉 {playerName} wins!",
"actionLabels": {
"draw_card": "Draw",
"play_card": "Play"
}
}
}
Custom Engine Module (optional)
If you need game-specific rules (e.g. Uno's draw-2, reverse, wild), create:
backend/app/services/engines/my_game.py
It must export two functions:
def setup_game(state: GameState) -> None:
"""Deal cards, set up zones, set state.phase = 'playing'."""
...
def apply_action(state: GameState, action: ActionRequest) -> tuple[bool, str, list[str]]:
"""Returns (success, error_message, triggered_effects)."""
...
🔌 API Reference
| Method | Path | Description |
|---|---|---|
| GET | /api/games | List available game types |
| POST | /api/rooms/create | Create a new room |
| POST | /api/rooms/{code}/join | Join a room |
| POST | /api/rooms/{code}/start?player_id=X | Start the game (host only) |
| POST | /api/rooms/{code}/action | Send a game action |
| GET | /api/rooms/{code}/state | Get current game state |
| WS | /ws/{code}/{playerId} | Real-time state updates |
Action types (Exploding Kittens)
| type | Extra fields | Description |
|---|---|---|
draw_card | — | Draw the top card of the deck |
play_card | cardId, optional targetPlayerId, optional metadata.comboPairId | Play a card from hand |
nope | cardId | Play a Nope to cancel a pending action |
select_target | targetPlayerId or metadata.cardId | Resolve Favor target |
insert_exploding | metadata.position | Place bomb back in deck after defusing |
🏗 Architecture Notes
- State is fully server-side. The client never modifies state locally — it only sends actions and receives broadcasts.
- Per-player state masking. Each WebSocket broadcast sends a personalised view with other players' hands hidden.
- Game engines are pluggable.
game_loader._get_engine(game_type)dynamically imports the engine by name, falling back togeneric.py. - JSON-driven card data. Card definitions, rules, and UI strings all live in the JSON file. The engine modules handle only the imperative logic.
- Pydantic v2 models throughout — all state is validated and serialised consistently.
🧩 Extending the Generic Engine
The generic.py engine handles:
- Drawing cards
- Playing cards (discard + log)
empty_handwin condition
For more complex games, override setup_game and apply_action in a dedicated engine module.
Analysis
View
Metric
- 17
- 16
- 7
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- CSSIn code
- FastAPIIn code
- JavaScriptIn code
- Next.jsIn code
- PythonIn code
- ReactIn code
- Tailwind CSSIn code
- TypeScriptIn code
9 of 9 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
425 KB
Source files
59
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
AlexiaHyn/Boardify
260 files · 4.0 MB · @ 53d94c9
Structure
Interface
45 files · 17%Screens, components and styles rendered to the user.
Application logic
7 files · 3%Domain rules, services and shared utilities.
Data & schema
4 files · 2%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python56%
- TypeScript27%
- Markdown14%
- CSS2%
- JavaScript0%
- YAML0%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
game-engine/frontend/package.json
npm · 10- next
- react
- react-dom
- +7 more
game-engine/backend/requirements.txt
pypi · 8- anthropic
- fastapi
- httpx
- modal
- pydantic
- python-multipart
- uvicorn[standard]
- websockets
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.