A competitive programming platform inspired by Clash of Code, but with enhanced features for real-time code battles
Coduel is a multiplayer online judge system that allows programmers to compete head-to-head by solving algorithmic challenges. Built with modern web technologies and Docker-based isolation, it provides a secure, scalable, and engaging platform for coding competitions.
- Real-time Multiplayer Battles: Compete against opponents in private rooms
- Multiple Rounds: Configurable best-of-N format (default: 3 rounds)
- Performance-Based Judging: Winner determined by accuracy → execution time → memory usage
- Anti-Cheat System: Real-time code spectating prevents cheating
- C (C17 standard)
- C++ (C++20 standard)
- Python 3
- Java
- JavaScript (Node.js)
- Docker Isolation: Each submission runs in isolated containers
- Resource Limits: CPU and memory constraints prevent abuse
- Timeout Protection: Configurable time limits per test case
- Multi-Run Testing: Each test runs multiple times for accurate performance metrics
- Live Code Spectating: Watch opponent's code as they type
- Socket.IO Integration: Instant updates for all room participants
- Ready Check System: Both players must be ready before match starts
- Match Results: Detailed performance comparison after each round
- CRUD Operations: Create, read, update, and delete problems
- Markdown Support: Rich problem statements with formatting
- Test Case Visibility: Public samples + hidden test cases
- Difficulty Levels: Easy, Medium, Hard, Fast
- Tagging System: Organize problems by topics
┌──────────────────────────────────────────────────────────────┐
│ User Browser │
│ (HTML/CSS/JS + Socket.IO Client + Monaco Editor) │
└────────────┬─────────────────────────────────┬──────────────┘
│ │
│ HTTP/REST │ WebSocket
▼ ▼
┌─────────────────────┐ ┌──────────────────────────┐
│ Web Server │ │ Socket.IO Server │
│ (Express.js) │◄─────────►│ (Real-time Sync) │
│ Port: 5173 │ │ - Room Management │
└──────────┬──────────┘ │ - Code Broadcasting │
│ │ - Match Coordination │
│ └──────────┬───────────────┘
│ │
│ HTTP │
▼ │
┌─────────────────────┐ │
│ API Server │ │
│ (FastAPI) │ │
│ Port: 8000 │ │
│ - Submit Code │ │
│ - Problem CRUD │ │
│ - Judge Queue │ │
└──────────┬──────────┘ │
│ │
│ │
└────────────┬────────────────────┘
│
▼
┌──────────────────┐
│ Redis │
│ Port: 6379 │
│ - Message Queue │
│ - Room State │
│ - Results Cache │
└─────────┬────────┘
│
│ Job Queue
▼
┌──────────────────┐
│ Worker │
│ (Python Daemon) │
│ - Compile Code │
│ - Run Tests │
└─────────┬────────┘
│
│ Docker API
▼
┌──────────────────┐
│ Judge Engine │
│ (Docker-in-Docker)│
│ - Isolated Exec │
│ - Resource Limit│
│ - Metrics │
└──────────────────┘
- Technology: Node.js + Express + Socket.IO
- Port: 5173
- Responsibilities:
- Serve static HTML/CSS/JS files
- WebSocket connections for real-time features
- Room state management
- Code synchronization between players
- Match result broadcasting
- Technology: FastAPI (Python)
- Port: 8000
- Endpoints:
GET /problems- List all problemsGET /problem/{id}- Problem details with test casesPOST /problem-add- Create new problemPOST /problem-edit- Update/delete problemPOST /problem/submit- Submit code for judgingGET /problem/submission/{id}- Get submission status
- Features:
- Pydantic validation
- Redis queue integration
- Problem metadata management
- Technology: Python + Docker SDK
- Responsibilities:
- Compilation: Compile source code with appropriate compiler
- Execution: Run compiled binary against test cases
- Metrics Collection: Measure time, memory, accuracy
- Multi-Run Testing: Execute each test
RUNS_PER_TESTtimes - Result Storage: Save results to Redis
- Configuration:
CPU_LIMIT: Default = half of system CPU coresMEM_LIMIT: Default = half of system RAMRUNS_PER_TEST: Default = 3 (for median calculation)PERFORMANCE_TOLERANCE: Default = 0.10 (10%)
- Base Image: Ubuntu-based with multiple compilers
- Installed Tools:
- GCC (C/C++)
- Python 3
- OpenJDK (Java)
- Node.js (JavaScript)
- GNU Time (resource monitoring)
- Security: Network disabled, filesystem read-only
- Version: 7 (Alpine)
- Usage:
- Job queue (
queue:compile) - Submission metadata (
sub:{id}) - Source code storage (
code:{id}) - Execution results (
run_result:{id}) - Compilation logs (
compile_log:{id}) - Room state (in-memory)
- Job queue (
- Docker (20.10+)
- Docker Compose (2.0+)
- Git
- Port Availability: 5173 (Web), 8000 (API), 6379 (Redis)
- Clone the repository
git clone https://github.com/Saudadeeee/Cowar.git
cd Cowar/Coduel- Configure environment (optional)
# Create .env.judge for worker configuration
cat > .env.judge << EOF
RUNS_PER_TEST=3
PERFORMANCE_TOLERANCE=0.10
CPU_LIMIT=2.0
MEM_LIMIT=1g
EOF- Build and start all services
docker-compose up --build -d- Verify services are running
docker ps
# Should show: oj_web, oj_api, oj_worker, oj_redis- Access the application
- Web UI: http://localhost:5173
- API Docs: http://localhost:8000/docs
-
Add a Problem:
- Navigate to http://localhost:5173/problem-add
- Fill in problem details:
- Title: "Sum Two Numbers"
- Difficulty: Easy
- Description: "Calculate a + b"
- Sample Input:
1 2 - Sample Output:
3 - Add test cases with visibility (public/hidden)
- Click "Create Problem"
-
View Dashboard:
- Go to http://localhost:5173/dashboard
- See problem statistics and distribution
-
Test Solo:
- Click "Training Mode" from main menu
- Select a problem
- Write code in the Monaco editor
- Click "Submit & Run"
-
Host Multiplayer Match:
- Click "Host Room" from main menu
- Configure settings:
- Enable/disable code spectator
- Select difficulty
- Choose default language
- Set number of rounds
- Share room code with opponent
-
Join a Match:
- Click "Join Room" from main menu
- Enter room code
- Wait for host to start match
- Select "Training Mode" from main menu
- Choose a problem from the list
- Read problem statement carefully
- Write solution in preferred language
- Test with sample inputs
- Submit when ready
- View detailed results (verdict, time, memory)
-
Create Room:
- Click "Host Room"
- Configure match settings
- Copy generated room code
-
Wait for Opponent:
- Share room code with friend
- Wait for them to join
- Both players mark "Ready"
-
Start Match:
- Click "Start Match" button
- Problem appears for both players
- Code editor unlocks
-
Compete:
- Write solution
- Submit when ready
- Wait for opponent to submit
- View performance comparison
-
Multi-Round:
- Winner of round gets 1 point
- Next problem loads automatically
- First to win majority wins match
-
Join Room:
- Click "Join Room"
- Enter room code from host
- Click "Ready" when prepared
-
Follow host's lead:
- Wait for match to start
- Solve problems as they appear
- Submit before opponent to gain advantage
If enabled by host:
- Click eye icon (👁️) in workspace
- Watch opponent's code in real-time
- See when they submit
- Promotes fair play and transparency
GET /problemsResponse:
{
"problems": [
{
"problem_id": "001-sum-two",
"title": "Sum Two Numbers",
"difficulty": "easy",
"number": 1,
"time_limit_ms": 2000,
"memory_limit_kb": 262144,
"tags": ["math", "implementation"],
"tests": 2
}
]
}GET /problem/{problem_id}Response: Includes statement, samples, tests, metadata
POST /problem-add
Content-Type: application/json
{
"title": "Sum Two Numbers",
"difficulty": "easy",
"description": "Calculate a + b",
"sample_input": "1 2",
"sample_output": "3",
"tests": [
{
"input": "1 2",
"output": "3",
"visibility": "public"
},
{
"input": "100 200",
"output": "300",
"visibility": "hidden"
}
],
"time_limit_ms": 2000,
"memory_limit_kb": 262144,
"tags": ["math"]
}POST /problem-edit
Content-Type: application/json
{
"problem_id": "001-sum-two",
"title": "Updated Title",
"difficulty": "medium",
// ... other fields
}POST /problem-edit
Content-Type: application/json
{
"problem_id": "001-sum-two",
"delete": true
}POST /problem/submit
Content-Type: application/json
{
"problem_id": "001-sum-two",
"language": "cpp",
"code": "#include <iostream>\nusing namespace std;\nint main() { int a,b; cin>>a>>b; cout<<a+b; }",
"std": "c++20"
}Response:
{
"submission_id": "uuid-here"
}GET /problem/submission/{submission_id}Response:
{
"meta": {
"status": "completed",
"problem_id": "001-sum-two",
"language": "cpp",
"created_at": "1730000000"
},
"compile_log": "Success",
"run_result": {
"verdict": "AC",
"tests_passed": 2,
"tests_total": 2,
"performance": {
"accuracy": 100.0,
"median_elapsed_seconds": 0.005,
"median_memory_kb": 2048
},
"test_details": [...]
}
}# Number of times to run each test (for median calculation)
RUNS_PER_TEST=3
# Performance tolerance for time comparison (10%)
PERFORMANCE_TOLERANCE=0.10
# CPU limit per container (cores)
CPU_LIMIT=2.0
# Memory limit per container
MEM_LIMIT=1g
# Timeout for compilation (seconds)
COMPILE_TIMEOUT=60
# Timeout for execution (seconds)
RUN_TIMEOUT=60# docker-compose.override.yml
version: "3.9"
services:
web:
ports:
- "3000:5173" # Change web port
api:
environment:
- DEFAULT_TIME_LIMIT_MS=5000 # 5 second default
- DEFAULT_MEMORY_LIMIT_KB=524288 # 512MB defaultThe system uses a 3-tier comparison:
-
Accuracy (Priority 1):
- Compare
tests_passed / tests_total - Higher accuracy wins immediately
- Compare
-
Execution Time (Priority 2):
- Uses median time from multiple runs
- Tolerance: ±10% (configurable)
- If within tolerance → tie, move to memory
-
Memory Usage (Priority 3):
- Uses median memory from multiple runs
- Tolerance: ±10% (configurable)
- If within tolerance → tie
Example:
Player A: 100% accuracy, 0.005s, 2048KB
Player B: 100% accuracy, 0.006s, 2048KB
Time difference: 20% → Player A wins
Coduel/
├── api/ # FastAPI backend
│ ├── app.py # Main API server (712 lines)
│ ├── Dockerfile # API container config
│ └── requirements.txt # Python dependencies
├── judge/ # Code execution engine
│ ├── compile_run.sh # Compilation/execution script
│ ├── Dockerfile # Judge container with compilers
│ └── run_with_metrics.py # Metrics collection
├── problems/ # Problem repository
│ ├── 001-sum-two/
│ │ ├── meta.json # Problem metadata
│ │ ├── statement.md # Problem description
│ │ ├── sample_input.txt # Public sample
│ │ ├── sample_output.txt
│ │ ├── input1.txt # Test case 1
│ │ ├── output1.txt
│ │ ├── input2.txt # Test case 2
│ │ └── output2.txt
│ └── 002-test/
│ └── ...
├── web/ # Frontend application
│ ├── server.js # Express + Socket.IO (468 lines)
│ ├── socket-client.js # Socket.IO client wrapper
│ ├── Dockerfile # Web container config
│ ├── package.json # Node.js dependencies
│ └── public/
│ ├── mainmenu.html # Main navigation
│ ├── dashboard.html # Statistics dashboard
│ ├── workspace.html # Code editor (2061 lines)
│ ├── roomhost.html # Room creation
│ ├── problem-add.html # Problem creation form
│ └── problem-edit.html # Problem edit form
├── worker/ # Background job processor
│ ├── worker.py # Job handler (489 lines)
│ ├── Dockerfile # Worker container config
│ └── requirements.txt # Python dependencies
├── worker_tmp/ # Temporary execution directory
├── docker-compose.yml # Service orchestration
└── .env.judge # Worker configuration
# Start all services
docker-compose up -d
# Check container status
docker ps
# Test API health
curl http://localhost:8000/problems
# Test web server
curl http://localhost:5173
# Check Redis
docker exec oj_redis redis-cli ping# Create test problem
curl -X POST http://localhost:8000/problem-add \
-H "Content-Type: application/json" \
-d '{
"title": "Test",
"difficulty": "easy",
"sample_input": "1 2",
"sample_output": "3",
"tests": [{"input":"1 2","output":"3","visibility":"public"}]
}'
# Submit solution
curl -X POST http://localhost:8000/problem/submit \
-H "Content-Type: application/json" \
-d '{
"problem_id": "001-test",
"language": "cpp",
"code": "#include <iostream>\nusing namespace std;\nint main() { int a,b; cin>>a>>b; cout<<a+b; }"
}'
# Check result (use submission_id from above)
curl http://localhost:8000/problem/submission/{submission_id}- Terminal 1: Start services
docker-compose up-
Browser 1: Host room
- Open http://localhost:5173
- Click "Host Room"
- Enable spectator mode
- Copy room code
-
Browser 2: Join room
- Open http://localhost:5173 (incognito)
- Click "Join Room"
- Paste room code
- Both click "Ready"
-
Start Match:
- Host clicks "Start Match"
- Both solve problem
- Submit code
- View winner modal
- Enable spectator in room settings
- Join room with 2 browsers
- Type code in Browser 1
- Click eye icon in Browser 2
- Verify code appears in real-time
# Clean up containers
docker-compose down -v
# Rebuild from scratch
docker-compose up --build --force-recreate# Find process using port 5173
lsof -i :5173
kill -9 <PID>
# Or change port in docker-compose.yml
# ports: ["3000:5173"]# Check Redis logs
docker logs oj_redis
# Restart Redis
docker-compose restart redis
# Test connection
docker exec oj_redis redis-cli ping# Check worker logs
docker logs oj_worker -f
# Verify Docker socket
docker exec oj_worker ls -la /var/run/docker.sock
# Check queue
docker exec oj_redis redis-cli LLEN queue:compile# Check Socket.IO connection
# Browser DevTools → Network → WS → Should see connection
# Check web server logs
docker logs oj_web -f
# Verify Redis pub/sub
docker exec oj_redis redis-cli MONITOR# Increase timeout in .env.judge
RUN_TIMEOUT=120
# Restart worker
docker-compose restart worker# Check performance tolerance
# In .env.judge:
PERFORMANCE_TOLERANCE=0.10 # 10%
# View detailed comparison logs
docker logs oj_web | grep "comparison"Enable verbose logging:
# In docker-compose.yml
services:
worker:
environment:
- LOG_LEVEL=DEBUGCheck all logs:
docker-compose logs -f- User authentication & profiles
- Persistent leaderboard
- Problem difficulty rating
- Submission history
- Code templates per language
- Syntax highlighting in spectator
- Match replay system
- Tournament bracket system
- Team battles (2v2)
- Spectator-only role
- Live streaming integration
- Code diff viewer
- Anti-plagiarism detection
- Rating system (ELO)
- Achievement badges
- Kubernetes deployment
- CDN for static assets
- Database persistence (PostgreSQL)
- Microservices architecture
- Mobile app (React Native)
- i18n (multiple languages)
- Dark/light theme toggle
- Accessibility improvements
We welcome contributions! Here's how:
- Fork the repository
git clone https://github.com/Saudadeeee/Coduel.git
cd Cowar
git remote add upstream https://github.com/Saudadeeee/Coduel.git- Create a feature branch
git checkout -b feature/amazing-feature-
Make your changes
- Follow existing code style
- Add comments for complex logic
- Test thoroughly
-
Commit your changes
git add .
git commit -m "Add amazing feature"- Push to your fork
git push origin feature/amazing-feature- Open a Pull Request
- Describe your changes
- Link related issues
- Add screenshots if UI changes
- Code Style: Follow ESLint/Prettier for JS, Black for Python
- Commits: Use conventional commits (feat/fix/docs/style/refactor)
- Testing: Test manually before submitting PR
- Documentation: Update README if adding features
This project is licensed under the MIT License - see below for details:
MIT License
Copyright (c) 2025 Saudadeeee
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Inspiration: CodinGame's Clash of Code
- Technologies:
- Docker - Containerization
- FastAPI - Python web framework
- Socket.IO - Real-time communication
- Express.js - Node.js web framework
- Redis - In-memory data store
- Monaco Editor - Code editor
- Community: Thanks to all beta testers and contributors
Need help? Here are your options:
- Documentation: Check the
/Codueldirectory for detailed guides - Issues: Open a GitHub issue
- Discussions: Join GitHub Discussions
- Logs: Run
docker-compose logs -ffor debugging
Built with ❤️ by competitive programmers, for competitive programmers
Star this repo | Report Bug | Request Feature
If you find this project helpful, consider supporting its development!
Your support helps keep this project maintained and growing. Thank you! 🙏