Skip to content

MuhammadAhmadHamim/match-kernel

Repository files navigation

MatchKernel banner

Language Frontend Type Build Status



"In matchmaking, the queue is the data structure. The wait is the cost. The match is the proof."



◈ What This Is

A rank-based multiplayer matchmaking system built as the Data Structures semester project. MatchKernel simulates the exact logic behind real game matchmaking — rank queues, sub-rank proximity, wait-time expansion, and cross-rank matching — all powered by queue-based data structures, a C++17 HTTP backend, and a live browser UI.

Not a simulation of matchmaking. An actual matchmaking engine.


◈ How the Engine Works

Phase 1  →  Same-rank players matched first
             Sub-rank proximity is the tiebreaker (I beats III)
             ↓
Phase 2  →  Unmatched players accumulate wait cycles
             ↓
Phase 3  →  Every 3 wait cycles, expansion radius grows by +1
             Players can now match one rank tier further away
             ↓
Phase 4  →  Remaining unmatched players reported
             Cross-rank matches flagged with a purple badge in UI

This mirrors how real matchmaking handles long queue times — the longer you wait, the wider the net casts.


◈ Tech Stack

Layer Technology Role
Backend C++17 + cpp-httplib HTTP server, all endpoints, matchmaking engine
Frontend HTML5, CSS3, Vanilla JS Live UI, queue visualization, match display
Build g++ MinGW, build.bat One-command compile and launch
Data In-memory + CSV input Custom queue state, match history, bulk player loading

◈ Project Structure

MatchKernel/
│
├── backend/
│   ├── main.cpp               ←  HTTP server and all endpoints
│   ├── httplib.h              ←  single-header server library
│   ├── Player.h               ←  Player struct and rank enums
│   ├── Match.h                ←  Match struct
│   ├── MatchmakingSystem.h    ←  core matchmaking engine
│   ├── JsonHelper.h           ←  JSON serialization
│   ├── queue.h                ← Custom queue implementation
│   └── Utils.h                ←  string to enum parsers
│
├── frontend/
│   ├── index.html             ←  UI layout
│   ├── style.css              ←  dark theme styling
│   └── script.js              ←  API calls and DOM rendering
│
├── sample_data.csv            ←  demo player data
├── build.bat                  ←  one command build and launch
├── TECHNICAL_DOCS.md
├── API_REFERENCE.md
└── README.md

◈ The Matchmaking Logic — In Code

// Phase 1 — Same-rank matching with sub-rank proximity priority
for(int i = 0; i < totalRanks; i++) {
    if(rankQueues[i].size() < 2) continue;

    // Convert queue to vector for flexible access
    std::vector<Player> bucket;
    while(!rankQueues[i].empty()) {
        bucket.push_back(rankQueues[i].front());
        rankQueues[i].pop();
    }

    std::vector<bool> matched(bucket.size(), false);

    for(int j = 0; j < (int)bucket.size(); j++) {
        if(matched[j]) continue;

        int bestIndex = -1;
        int bestDistance = INT_MAX;

        // Search all unmatched players after j
        for(int k = j + 1; k < (int)bucket.size(); k++) {
            if(matched[k]) continue;

            int distance = abs(static_cast<int>(bucket[j].subRank) -
                                        static_cast<int>(bucket[k].subRank));
            if(distance < bestDistance) {
                bestDistance = distance;
                bestIndex = k;
            }
        }

        // If a valid match was found
        if(bestIndex != -1) {
            matched[j] = true;
            matched[bestIndex] = true;

            Match m(++matchIdCounter, bucket[j], bucket[bestIndex]);
            formedMatches.push_back(m);
            matchesThisRound++;

            std::cout << "MATCHED (same-rank, sub-priority): ";
            m.display();
        }
    }

    // Push unmatched players back into queue
    for(int j = 0; j < (int)bucket.size(); j++) {
        if(!matched[j]) {
            rankQueues[i].push(bucket[j]);
        }
    }
}

// Phase 3 — Expansion: wait cycles unlock cross-rank matching
// For each rank bucket, check if front player can expand
for(int i = 0; i < totalRanks; i++){
    if(rankQueues[i].empty()) continue;

    Player p1 = rankQueues[i].front();
    int radius = p1.expansionRadius();
    if(radius == 0) continue;

    // Search adjacent buckets within radius
    bool matched = false;
    for(int offset = 1; offset <= radius && !matched; offset++){
        // Check bucket above
        int above = i + offset;
        if(above < totalRanks && !rankQueues[above].empty()){
            rankQueues[i].pop();
            Player p2 = rankQueues[above].front();
            rankQueues[above].pop();
            Match m(++matchIdCounter, p1, p2);
            formedMatches.push_back(m);
            matchesThisRound++;
            matched = true;
            std::cout << "Matched (EXPANDED):";
            m.display();
        }

        // Check bucket below
        int below = i - offset;
        if(!matched && below >= 0 && !rankQueues[below].empty()){
            rankQueues[i].pop();
            Player p2 = rankQueues[below].front();
            rankQueues[below].pop();
            Match m(++matchIdCounter, p1, p2);
            formedMatches.push_back(m);
            matchesThisRound++;
            matched = true;
            std::cout << "Matched (EXPANDED):";
            m.display();
        }
    }
}

◈ API Reference

Method Endpoint What It Does
GET /queue-state Returns current queue as JSON
GET /matches Returns all formed matches
GET /stats Total players and match count
POST /add-player Adds one player to the queue
POST /matchmake Triggers one full matchmaking pass
POST /load-file Bulk loads players from CSV
# Add a single player
curl -X POST http://localhost:8080/add-player \
     -d "username=Ace&rank=Gold&subrank=II"

# Bulk load from CSV
curl -X POST http://localhost:8080/load-file \
     --data-binary @sample_data.csv

◈ CSV Format

name,rank,subrank
Ace,Gold,II
Zoro,Gold,I
Levi,Platinum,III
Mikasa,Silver,I
Field Valid Values
rank Bronze · Silver · Gold · Platinum · Diamond
subrank I · II · III

◈ How to Run

Requirements: Windows 10+ · MinGW g++ with C++17 support

# Clone
git clone https://github.com/MuhammadAhmadHamim/MatchKernel.git

# Build and launch — one command does everything
.\build.bat

Server compiles, starts on http://localhost:8080, and the browser opens automatically.


◈ Demo Flow

1.  UI loads → rank queues display automatically
2.  Add players manually OR upload sample_data.csv
3.  Watch rank queues populate in real time
4.  Hit Run Matchmaking → matches appear instantly
5.  Cross-rank matches flagged with a purple badge
6.  Run multiple rounds → watch expansion logic activate

◈ Data Structures Applied

Structure Where It Appears
Queue Per-rank player Custom queues — core of the matchmaking engine
Vector Match history, unmatched player tracking
Vector of Queues Rank-to-queue mapping for O(1) queue access
Enum Rank and subrank type safety across the entire system
Struct Player and Match as clean, typed data containers

◈ Skills This Project Demonstrates


◈ A Note on This Project

Every matchmaking system in every game you've ever played runs on some version of this logic — ranked queues, wait time expansion, cross-tier fallback. MatchKernel is that logic stripped to its core, built from scratch in C++, and wired to a live UI that shows it working in real time.

Data structures aren't theory here. They're the engine.


footer

The queue decides everything. The algorithm decides the rest.

learning git

About

MatchKernel is a backend-driven matchmaking engine built in C++ that groups players by skill rank using queues and matches them through a multi-phase algorithm. Features dynamic rank expansion for long-waiting players, a REST API via cpp-httplib, and a real-time dashboard frontend in vanilla JavaScript.

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages