Gomoku Matched is a competitive, real-time strategy board game where two players face off to place five stones in a row on a grid. The game supports both human-vs-human matches and human-vs-AI matches, featuring machine learning-powered AI opponents with varying skill levels.
Built with Spring Boot, Redis, PostgreSQL, and a Flask-based AI service, the application delivers real-time gameplay with WebSocket support and persistent game history.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β GOMOKU HYBRID ARCHITECTURE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββ ββββββββββββββββββββββββββββββββββββββββββββ
β React + Vite β β Spring Boot Application β
β Frontend UI β β β
β β WebSocket β βββββββββββββββββββββββββββββββββββββββ β
β β’ Game Board βββββββββββββββββββΊβ β WebSocket Layer (PvP) β β
β β’ Player Input β Player vs β β β’ Real-time move broadcasting β β
β β’ Live Updates β Player moves β β β’ Player session management β β
β β’ Match Lobby β β β β’ Game state synchronization β β
β β β βββββββββββββββββββββββββββββββββββββββ β
β β HTTP/REST β β β
β βββββββββββββββββββΊβ βββββββββΌββββββββββ β
β β Player vs AI β β GAME SERVICES β β
βββββββββββββββββββ moves & state β β β β
β β β’ GameService β β
βββββββββββββββββββ β β β’ PlayerStatsSvcβ β
β PostgreSQL ββββββββββββββββββββ€ βββββββββ¬ββββββββββ β
β Database β Final Results β β β
β β β βββββββββΌββββββββββ β
β β’ Player Stats β β β KAFKA PRODUCERS β β
β β’ Game History β β β (Event Logging)β β
β β’ Match Results β β β β’ Game moves β β
βββββββββββββββββββ β β β’ Match events β β
β βββββββββββββββββββ β
βββββββββββββββββββ β β β
β Redis Cache ββββββββββββββββββββ€ β β
β β Active Games β β β
β β’ GameSessions β βββββββββββββββββββββΌββββββββββββββββββββββββ
β β’ int[][] board β βΌ
β β’ TTL: 2 hours β βββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββ β APACHE KAFKA CLUSTER β
β² β βββββββββββββββββββββββββββββββββββββββββββ β
β β β TOPIC: matchmaking-queue-events β β
β β β Event-driven matchmaking (Kafka Streams)β β
β β β Queue state in RocksDB (fault-tolerant) β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β β TOPIC: game-move-made β β
β β β Event log of ALL moves (player & AI) β β
β β β Used for: game replay, analytics β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β β TOPIC: match-created β β
β β β Published by Kafka Streams processor β β
β β β Triggers game session creation β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββ
β β
β βΌ
β βββββββββββββββββββββββββββββββββββββββββββββββ
β β KAFKA STREAMS & CONSUMER SERVICES β
β β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β β MatchmakingStreamsProcessor β β
β β β β’ Stateful queue aggregation (RocksDB) β β
β β β β’ Real-time matching β β
β β β β’ Creates matches when >=2 players β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β β MatchCreatedConsumer | β
β β β β’ Creates game session in Redis β β
β β β β’ Sends WebSocket match notifications β β
β β β β’ Players notified at /queue/match-foundβ β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β β β GameMovesConsumer β β
β β β β’ Real-time move analytics (logs only) β β
β β β β’ Pattern tracking (player & AI) β β
β β β β’ Future: Live dashboards & metrics β β
β β βββββββββββββββββββββββββββββββββββββββββββ β
β βββββββββββββββββββββββββββββββββββββββββββββββ
β
β
ββββββββββ΄βββββββββ βββββββββββββββββββββββββββββββββββββββββββββββ
β MATCHMAKING β β Python AI Microservice β
β (Kafka-based) β HTTP/REST β (Same Repository) β
β βββββββββββββββββββΊβ β
β β’ Event-driven β AI move β β’ PyTorch model inference β
β β’ Kafka Streams β requests β β’ Multiple difficulty levels β
β β’ RocksDB state β β β’ Board evaluation engine β
β β’ <100ms match β β β’ Minimax with neural net evaluation β
β β’ Fault-tolerantβ β β’ Dockerized alongside Spring Boot β
βββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMMUNICATION PATTERNS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PLAYER vs PLAYER: WebSocket bidirectional real-time communication β
β PLAYER vs AI: Spring Boot β HTTP β Python AI Service β
β MATCHMAKING: Event-driven Kafka Streams (< 100ms real-time matching) β
β ANALYTICS/LOGGING: Kafka event streams for all game/match events β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Component | Technology | Purpose |
|---|---|---|
| Backend | Java Spring Boot | REST API and WebSocket server |
| Authentication | JWT with Spring Security) | Token-based authentication |
| Real-Time Updates | Spring WebSockets (STOMP) | PvP game state broadcasting |
| Active Game Cache | Redis | In-memory sessions with 2-hour TTL |
| Event Streaming | Apache Kafka | Asynchronous event logging and processing |
| AI Opponent | Flask + Python | Microservice for move calculation |
| AI Communication | HTTP REST | Spring Boot β Flask |
| Database | PostgreSQL 15 | Player data, game history, statistics |
| Containerization | Docker Compose | Multi-service orchestration |
PvP:
- Client POST
/api/game/createwithgameType: HUMAN_VS_HUMANandplayer2Id - Backend creates GameSession with unique UUID
- Session stored in Redis with 2-hour TTL
- WebSocket rooms created for both players
PvAI:
- Client POST
/api/game/createwithgameType: HUMAN_VS_AIandaiDifficulty(e.g., "MEDIUM") - Backend creates GameSession (player2Id = null, aiDifficulty set)
- Session stored in Redis
- Player clicks board position
- WebSocket message sent to
/app/game/{gameId}/move - GameService validates move:
- Check player authorization
- Validate move legality (position empty, correct turn)
- Update int[][] board in GameSession
- Check win condition (5 in a row)
- Updated state broadcast via WebSocket to both players
- GameSession updated in Redis
- Player POST
/api/game/{gameId}/movewith row/col - GameService processes player move (same validation)
- If game still in progress, request AI move:
- HTTP POST to Flask service at
http://ai-service:8000/api/ai/move - Flask loads model based on difficulty
- Returns optimal move coordinates
- HTTP POST to Flask service at
- GameService processes AI move
- Return updated state to client
When win/draw/forfeit detected:
- GameSession status set to COMPLETED/ABANDONED
saveCompletedGameToDatabase()called:- Convert GameSession β Game entity
- Map enums and relationships
- Serialize final board state as JSONB
- Serialize move sequence as JSONB array
[[row, col, player], ...] - Save to PostgreSQL
saveMoveHistoryToDatabase()called:- Reads moves from Redis
session.moveHistory - Persists each move to
game_movetable - Includes player type, stone color, position, and AI difficulty (if applicable)
- Reads moves from Redis
- Redis session remains available for retrieval
- TTL ensures cleanup after 2 hours
Move History Format:
{
"game_id": "0e086cf4-40ae-43f6-9fce-7ac9374ac100",
"move_sequence": [
[7, 7, 1],
[8, 7, 2],
[7, 8, 1],
[8, 8, 2],
[7, 9, 1]
]
}Each move is [row, col, player] where player is 1 (Player 1) or 2 (Player 2/AI).
Separate python microservice handles AI move calculations:
- Flask: HTTP endpoint for move requests
- Isolated service: AI logic decoupled from game server
- Future extensibility: Ready for ML model integration
- Simple communication: HTTP REST requests from Spring Boot
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ACTIVE GAME LAYER (Redis) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β’ GameSession objects with int[][] board β
β β’ Key: game:session:{UUID} β
β β’ TTL: 2 hours (auto-cleanup) β
β β’ Purpose: Fast move validation and real-time updates β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ (On completion/forfeit)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PERSISTENCE LAYER (PostgreSQL) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β’ game table: Final state, winner, duration, ai_difficulty β
β β’ final_board_state: JSONB snapshot of final board β
β β’ move_sequence: JSONB array [[row,col,player],...] β
β β’ game_move table: Move-by-move history (via saveToDatabase) β
β β’ player_stats: Wins, losses, MMR tracking β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β REDIS (Single Instance) β
β Port: 6379 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STRING (Key-Value) β
β ββ game:session:uuid-1 β GameSession object β
β ββ game:session:uuid-2 β GameSession object β
β ββ game:session:uuid-3 β GameSession object β
β β
β (need to implement with cheat detec) STRING (Token Blacklist)β
β ββ blacklist:token:abc123 β "revoked" β
β ββ blacklist:token:def456 β "revoked" β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Redis instance handles active game sessions with 2-hour TTL.
- Matchmaking queue migrated Kafka Streams (RocksDB state store).
- Move received via WebSocket
- Validate against Redis GameSession
- Update int[][] board
- Broadcast to both players via WebSocket
- On completion β Save to PostgreSQL
- Player move via HTTP POST
- Validate and update Redis
- Request AI move from python service
- Apply AI move to Redis
- Return updated state
- On completion β Save to PostgreSQL
public class GameSession {
private UUID gameId;
private GameType gameType; // HUMAN_VS_HUMAN, HUMAN_VS_AI
private GameStatus status; // IN_PROGRESS, COMPLETED, ABANDONED
private UUID player1Id;
private UUID player2Id; // null for AI games
private String aiDifficulty; // "EASY", "MEDIUM", "HARD", "EXPERT" (null for PvP)
private int[][] board = new int[15][15];
private int currentPlayer; // 1 or 2
private int moveCount;
private List<int[]> moveHistory; // [[row, col, player], ...] for replay
private String winnerType; // PLAYER1, PLAYER2, AI, DRAW, NONE
private UUID winnerId;
private LocalDateTime startedAt;
private LocalDateTime endedAt;
private LocalDateTime lastActivity;
public boolean isValidMove(int row, int col);
public boolean checkIfBoardFull();
public void makeMove(int row, int col, int player);
public void switchPlayer();
} 0 1 2 3 4 ... 14
0 β
β
β
β
β
β
1 β
β
β
β
β
β
2 β
β
β β
β
β
β board[2][2] = 1
3 β
β
β
β β
β
β board[3][3] = 2
...
14 β
β
β
β
β
β
// Center position
int centerRow = 7, centerCol = 7;
board[centerRow][centerCol] = 1; // First move at center
- Check only around last placed stone
- Four directions: horizontal, vertical, two diagonals
- Count consecutive stones in each direction
- Return true if count β₯ 5
# Clone repository
git clone https://github.com/byuly/GomokuMatching
cd GomokuMatching
# Start all services
docker-compose up -d
# Wait for services to initialize (~30 seconds)
docker-compose logs -f backend
# Verify health
curl http://localhost:8080/actuator/health