Back to projects

C++ collaboration

European Roulette

A European roulette game with a graphical SDL3 table, multiple bet types, provably fair results, and wallet-backed rounds.

  • C++20
  • SDL3
  • Game logic
  • Integration
  • Testing
European roulette interface showing the projected table, placed chips, balance, and round information.
RoleContributing Programmer
Team2-person collaboration
LanguageC++20
FocusGame and platform integration

In motion

Gameplay and systems

Recorded from the working SDL3 client. The video shows the complete round loop, while the selected frame highlights the table interaction clearly.

Complete roulette demonstrationThe full recording follows repeated rounds through chip selection, table placement, result resolution, payouts, and balance updates.
European roulette table with several chips placed and the current stake displayed.
Table interactionA round in progress with multiple chips placed on the projected European table and the selected stake shown above it.

Overview

Project concept

The roulette game is another part of the Monte Carlo Platform. It models a European wheel and supports inside and outside bets, including straight, split, street, corner, six-line, basket, colour, parity, low and high, dozens, columns, and neighbour-style selections.

The SDL3 client connects table interaction to round logic, result generation, payout calculation, and wallet operations so a bet can move through a complete reserve, play, and settlement cycle.

Role

What I worked on

I contributed to the roulette game and its integration with the wider platform while working closely with my teammate. Some implementation work was completed together and appears under the teammate's source-control history, so I describe this page as a collaborative contribution rather than claiming sole ownership.

My work involved understanding and working with the game flow, wallet connection, testing, and debugging across the client and supporting systems. This gave me practical experience entering a larger C++ codebase and tracing behaviour across module boundaries.

Design focus

Core pillars

Complete bet vocabulary

The model represents the common European roulette bets and maps them to the numbers, odds, and payout rules they cover.

Spatial table interaction

The client translates pointer positions into table selections so chips, highlighted cells, and underlying bet objects remain synchronized.

Round integrity

Random results, recorded round data, wallet reservations, settlement, and cancellation form one traceable lifecycle.

Implementation

Technical contributions

Collaborative game implementation

Participated in building and reviewing the roulette flow from placing a bet through displaying and resolving a result.

Platform integration

Worked with the connection between player bets, round state, shared wallet behaviour, and the surrounding C++ platform.

Testing and debugging

Helped test gameplay paths, investigate integration problems, and verify that user actions produced consistent results.

Codebase navigation

Learned to follow data across UI, game rules, randomness, persistence, and service boundaries in a larger shared project.

Process

Implementation notes

Bet representation

Bet types are represented with number masks and payout rules, allowing the game to evaluate many table selections through a shared calculation path.

Table projection

The SDL3 interface uses a projected table layout and hit testing to translate screen positions into valid roulette selections.

Fair result generation

The system uses cryptographic randomness and stores proof-related data for reproducible verification and high-volume result testing.

Code samples

Selected C++ systems

Selected excerpts from the shared two-person codebase. I worked with these systems during game implementation, integration, testing, and debugging; they are shown to explain the architecture, not to claim sole authorship.

BetManager.cpp

One representation for many bet types

Maps each roulette bet to a compact number mask, allowing inside and outside bets to use the same winner-checking path.

std::array<uint32_t, 2> BetManager::getMaskForBet(
    const PackedBet& bet)
{
    const uint8_t type = bet.getType();
    const uint8_t anchor = bet.getAnchor();
    const uint8_t neighbor = bet.getNeighbor();
    std::array<uint32_t, 2> mask = { 0, 0 };

    switch (static_cast<BetType>(type)) {
    case BetType::Red:   return M_RED;
    case BetType::Black: return M_BLACK;
    case BetType::Split:
        setBit(anchor, mask);
        setBit(neighbor, mask);
        return mask;
    case BetType::Street:
        for (int i = 0; i < 3; ++i)
            setBit(anchor + i, mask);
        return mask;
    default:
        return mask;
    }
}
BetManager.cpp

Shared payout calculation

Checks every active bet against the winning-number mask and returns the original stake together with the profit.

Money BetManager::calculatePayout(uint8_t winningNumber)
{
    int64_t totalCents = 0;
    const auto winMask = makeWinningMask(winningNumber);

    for (const auto& bet : activeBets) {
        const auto betMask = getMaskForBet(bet);
        if (!maskOverlaps(betMask, winMask))
            continue;

        const int64_t amount = bet.getAmount();
        const int64_t multiplier =
            getMultiplier(bet.getType());

        totalCents += amount + amount * multiplier;
    }

    return Money(totalCents);
}
GameInput.cpp

Reserve the wager before spinning

Totals the placed chips, reserves the stake through the wallet, and only advances the round after a successful response.

const int64_t totalStake = std::accumulate(
    betManager->getActiveBets().begin(),
    betManager->getActiveBets().end(),
    int64_t{ 0 },
    [](int64_t total, const PackedBet& bet) {
        return total + bet.amount;
    });

const auto reserveResult = WalletClient::ReserveBet(
    roundId,
    roundId + "-reserve",
    totalStake);

if (!reserveResult.success) {
    setRoundPhase(RoundPhase::Failed);
    refreshBalanceFromWallet();
    return;
}

playerBalance = Money(reserveResult.balance_after);
setRoundPhase(RoundPhase::Reserved);

Reflection

Challenges

The main challenge was the number of systems involved in a single round. Input, chip placement, bet validation, random results, payouts, visual feedback, wallet state, and persistence all have to agree.

Collaborative ownership also made communication important: a change in a shared type or transaction flow could affect both games and several services.

Learning

What I learned

I gained a better understanding of how to integrate game code with services and how to debug a result that crosses several layers instead of living in one class.

I also learned the value of precise ownership language. This page focuses on the parts I participated in while still explaining the full system needed to understand the work.

Next steps

Future iterations

I would add more automated end-to-end round tests, improve accessibility and feedback in the table interface, and document module boundaries more thoroughly.