Back to Projects

Bulls & Cows

Multiplayer Game Platform

Bulls & Cows Game

Overview

Code-breaking game with Spring Boot backend and real-time WebSocket multiplayer. Players guess a secret number and get feedback (bulls for correct position, cows for wrong position). Five game modes including daily challenges and 1v1 battles.

I built this to learn WebSocket programming and real-time systems. Also wanted to practice complex state management without a frontend framework. Everything is vanilla JavaScript.

5
Game modes
Real-time
WebSocket
34
Achievements

Architecture

Bulls & Cows Architecture Diagram

The app uses both REST and WebSocket. REST handles game logic, authentication, and data persistence. WebSocket handles real-time features like multiplayer sync, friend presence, and live notifications.

Spring Boot backend manages game sessions in memory using ConcurrentHashMap. Each game mode has its own session class with different logic. PostgreSQL stores user data, game history, achievements, and friendships.

WebSocket uses STOMP messaging protocol. Users subscribe to channels for their active games and friend updates. Server broadcasts game state changes to all connected players.

Session management uses composite keys (sessionId:tabId) so users can play multiple modes in different tabs. Scheduled cleanup removes expired sessions after 5-30 minutes depending on mode.

Tech Stack

Backend

Spring Boot 3.1WebSocket (STOMP)Hibernate/JPAHikariCP

Database

Neon PostgreSQL13 entities

Frontend

Vanilla JavaScript (12 modules)STOMP.js for WebSocket

Deployment

Render (Docker container)

Technical Challenges

Thread-Safe Session Management

Problem

In multiplayer mode, two players compete on the same secret number. They make guesses concurrently from different browsers. Without proper synchronization, race conditions could corrupt game state.

Solution

Used ConcurrentHashMap for session storage (thread-safe by design). Server is the source of truth for game state. WebSocket events are sequenced to prevent out-of-order processing.

Impact

Zero race condition bugs in production. Multiplayer feels smooth and instant.

WebSocket Authentication

Problem

WebSocket connections need authentication but they don't use standard HTTP headers after the handshake. JWT tokens need validation but where do you check them?

Solution

Validate JWT during WebSocket handshake (initial HTTP upgrade). Store user info in WebSocket session attributes. Check authorization on every message. Disconnect invalid sessions immediately.

Impact

Secure real-time communication. No unauthorized access to multiplayer games or friend presence data.

Concurrent Session Management

Problem

Users can play multiple game modes simultaneously in different tabs. How do you track separate sessions without conflicts?

Solution

Composite keys: sessionId:tabId. Each mode has its own session class with different logic. ConcurrentHashMap handles concurrent access. Scheduled cleanup removes expired sessions. Mode-specific validation prevents cross-mode state bugs.

Impact

Users can play multiple modes at once without issues. Sessions stay isolated and clean up automatically.

Key Features

Practice Mode (3 difficulties with hints)
Daily Challenge (same puzzle for everyone, global leaderboard)
Time Attack (5-minute timer, multiple games)
Survival Mode (5 rounds with limited attempts)
Multiplayer 1v1 (real-time battles with friends)
Friends system with online/offline presence
34 achievements with auto-unlock
Global leaderboards

What I Learned

WebSocket Programming: Real-time sync is harder than it looks. Need to think about connection drops, reconnection logic, and message ordering. STOMP makes it easier than raw WebSocket.

Thread-Safe Concurrent Programming: ConcurrentHashMap was essential. Learned to think about race conditions and use proper synchronization. Server as source of truth prevents most sync issues.

State Management Without Frameworks: Vanilla JavaScript taught me a lot. No framework magic means you understand every piece. Modular architecture with separate files per feature kept things organized.

Complex Database Relationships: 13 entities with many-to-many relationships (users, friends, achievements). JPA makes this manageable but query optimization matters.

What I'd Do Differently: Use Redis for session storage (would allow horizontal scaling). Add proper job queue for background tasks. Implement WebSocket scaling strategy from day one. More comprehensive testing. Mobile apps would be better than mobile web.