A soccer-themed tic-tac-toe game built as an OpenAI App using Cloudflare Workers. Play against an AI by placing soccer players on a 3x3 grid based on category combinations (Country, Position, League, Team, Shirt Number).
Repository: https://github.com/ignaciojimenezr/Rabona
Live MCP Server: https://rabona.ignaciojimenezrocabado.workers.dev/mcp
Rabona is a strategic twist on classic tic-tac-toe where:
- You play as O (circles) and the AI plays as X
- Each cell represents a category combination (e.g., "Liverpool + Forward")
- You must place a player who matches both categories to claim the cell
- Win by getting 3 in a row (horizontal, vertical, or diagonal)
- Progress through difficulty levels (Easy → Medium → Hard) by winning games
- Easy: Only famous players (Priority 1)
- Medium: Mix of famous and medium-tier players (Priority 1 & 2)
- Hard: All players including less famous ones (Priority 1, 2, & 3)
Win 5 games at your current difficulty to progress to the next level!
rabona/
├── src/
│ ├── worker.ts # Cloudflare Worker entry point
│ ├── GameEngine.ts # Core game logic and AI
│ ├── GameStore.ts # Durable Object for game state persistence
│ ├── SquadStore.ts # Player data store (Node.js)
│ ├── SquadStoreWorker.ts # Player data store (Cloudflare Workers)
│ ├── types.ts # TypeScript type definitions
│ └── bundled-data.ts # Bundled CSV data (generated)
├── public/
│ ├── game-widget.html # Main game widget UI
│ └── images/ # Team logos, player positions, etc.
├── data/
│ └── squads_2025_26.csv # Player data (generated by scraper)
├── scripts/
│ ├── fetch_squads.py # Scraper for player data
│ └── bundle-data.js # Bundles CSV into TypeScript
└── wrangler.toml # Cloudflare Workers configuration
- Runtime: Cloudflare Workers (edge computing)
- State Management: Cloudflare Durable Objects (persistent game state)
- Protocol: Model Context Protocol (MCP) for OpenAI Apps
- Frontend: Vanilla HTML/CSS/JavaScript with OpenAI Widget API
- Data: CSV-based player database with priority levels
- Generates 4x4 game boards with category combinations
- Manages player selection by priority/difficulty
- Implements AI move logic (blocking, winning, random)
- Tracks difficulty progression (5 wins = level up)
- Handles game state (turns, wins, completion)
- Cloudflare Worker entry point
- MCP server implementation
- Tool handlers (create_game, make_move, ai_move, etc.)
- Durable Object integration for state persistence
- Static asset serving
- Durable Object for persistent game state
- Stores active games and user progress
- Handles difficulty state across sessions
- Interactive game UI
- OpenAI Widget State API integration
- Real-time game updates
- Progress bar visualization
- Win/lose/draw modals
Each cell has two categories that must match:
- Row categories: Country, Position, League, Team, or Shirt Number
- Column categories: Country, Position, League, Team, or Shirt Number
- Players are selected from a shuffled pool by priority
- Priority 1: Famous players (Messi, Ronaldo, etc.)
- Priority 2: Well-known players
- Priority 3: Less famous players
- Pools reshuffle when exhausted
- Win: If AI can win, it takes the winning move
- Block: If player can win, AI blocks
- Random: Otherwise, makes a random valid move
- Start at Easy difficulty
- Win 5 games → progress to Medium
- Win 5 more games → progress to Hard
- Lose → reset to Easy difficulty
- Draw → reset progress (but keep difficulty level)
The app exposes the following tools via MCP:
-
create_game: Create a new game- Optional:
resetDifficulty,progressOverride,winsOverride,difficultyOverride
- Optional:
-
guess_player: Guess a player for a cell (AI automatically moves after player's turn)- Required:
gameId,row,col,playerName
- Required:
-
skip_turn: Skip your turn- Required:
gameId
- Required: