Skip to content

byuly/GomokuMatching

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

82 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Gomoku Matched !

Classic Strategy Board Game with AI Opponents

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.

architecture!

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         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               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

the tech stack

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

gameplay!

1. Game Creation

PvP:

  1. Client POST /api/game/create with gameType: HUMAN_VS_HUMAN and player2Id
  2. Backend creates GameSession with unique UUID
  3. Session stored in Redis with 2-hour TTL
  4. WebSocket rooms created for both players

PvAI:

  1. Client POST /api/game/create with gameType: HUMAN_VS_AI and aiDifficulty (e.g., "MEDIUM")
  2. Backend creates GameSession (player2Id = null, aiDifficulty set)
  3. Session stored in Redis

2. Move Processing (PvP)

  1. Player clicks board position
  2. WebSocket message sent to /app/game/{gameId}/move
  3. 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)
  4. Updated state broadcast via WebSocket to both players
  5. GameSession updated in Redis

3. Move Processing (PvAI)

  1. Player POST /api/game/{gameId}/move with row/col
  2. GameService processes player move (same validation)
  3. 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
  4. GameService processes AI move
  5. Return updated state to client

4. Game Completion

When win/draw/forfeit detected:

  1. GameSession status set to COMPLETED/ABANDONED
  2. 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
  3. saveMoveHistoryToDatabase() called:
    • Reads moves from Redis session.moveHistory
    • Persists each move to game_move table
    • Includes player type, stone color, position, and AI difficulty (if applicable)
  4. Redis session remains available for retrieval
  5. 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).


ai service!

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

storage architecture

2-tier storage

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    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 data structure example

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           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).

data flow overview~

Player vs Player

  1. Move received via WebSocket
  2. Validate against Redis GameSession
  3. Update int[][] board
  4. Broadcast to both players via WebSocket
  5. On completion β†’ Save to PostgreSQL

Player vs AI

  1. Player move via HTTP POST
  2. Validate and update Redis
  3. Request AI move from python service
  4. Apply AI move to Redis
  5. Return updated state
  6. On completion β†’ Save to PostgreSQL

game session object

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();
}

board coordinate system:

    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

detecting the winner

  • Check only around last placed stone
  • Four directions: horizontal, vertical, two diagonals
  • Count consecutive stones in each direction
  • Return true if count β‰₯ 5

getting started!!!

quick start

# 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

About

play gomoku, super fast!! ai!! fun fun

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors