Code and logic for tournaments in the Mankala Engine
Structure of code and logic for tournaments in the Mankala Engine.
So, we want the tournaments to be able to:
- Create custom game rooms (with different variants, number of players, and time limit)
- Join the rooms with room codes.
- Create elimination brackets for players.
- Have empty slots filled with computerized opponents.
- A scoreboard after the game for final rankings.
The tournament is going to be part of the Multiplayer variant. For that, when a person clicks on Multiplayer game mode, they will be asked to select from the given two options of a casual 1 to 1 game or another a tournament. Then they will be asked to either create a room or join the room with the code.
Key elements to implement:
- Room codes will be created by mapping XMPP MUC JIDs (e.g.,
A3F7K2 → tournament-a3f7k2@conference.server.com). - Fill computerized opponents: When it is detected that there is an absence of required players, the game will be filled with computer opponents in all the empty spots, just like we have for the
vs AIgame mode.
Here is an example of how the tournaments.cpp can be implemented:
class TournamentManager : public QObject {
Q_OBJECT
Q_PROPERTY(QVariantList tournaments READ getTournaments NOTIFY tournamentsChanged)
Q_PROPERTY(QVariantMap activeTournament READ getActiveTournament NOTIFY activeTournamentChanged)
public:
// --- Tournament duration ---
Q_INVOKABLE QString createTournament(
const QString& variant, // "Bohnenspiel", "Oware", "Pallanguli"
int maxPlayers, // eg. 4, 8, 16
int durationDays, // tournament duration
const QString& organizerJid // creator's XMPP JID
);
Q_INVOKABLE void joinTournament(const QString& roomCode, const QString& playerJid);
Q_INVOKABLE void startTournament(const QString& tournamentId);
// --- Match management ---
Q_INVOKABLE void reportMatchResult(const QString& matchId,
const QString& winnerJid);
Q_INVOKABLE void advanceBracket(const QString& tournamentId);
// --- Computer Opponents ---
Q_INVOKABLE void fillEmptySlots(const QString& tournamentId);
// --- Room codes ---
Q_INVOKABLE QString generateRoomCode();
Q_INVOKABLE QString resolveRoomCode(const QString& code);
signals:
void tournamentsChanged();
void activeTournamentChanged();
void matchReady(const QString& matchId, const QString& player1, const QString& player2);
void tournamentComplete(const QString& tournamentId, const QVariantList& rankings);
void playerJoined(const QString& playerJid);
};
For filling empty spaces we can use the logic as:
struct Match {
QString matchId;
QString player1Jid; // empty string = unfilled slot
QString player2Jid;
QString winnerJid;
int round; // 0 = first round, 1 = quarter-finals, etc.
int position; // position within the round
bool isAIMatch; // true if one/both players are computer
};
class TournamentBracket {
public:
void initialize(int playerCount); // builds bracket tree
void seedPlayers(const QStringList& playerJids);
void fillWithAI(); // fills empty slots
Match getNextMatch() const;
void recordResult(const QString& matchId, const QString& winnerJid);
bool isComplete() const;
QVariantList getRankings() const;
private:
std::vector<std::vector<Match>> m_rounds; // rounds[0] = first round, etc.
int m_totalRounds;
};
At the end of the elimination cycle and the tournament bracket, we can get the results and show them on our created leaderboard page with the XMPP icons, usernames, and the number of games the players won.
What more can be added?
I plan to research more on whether we can store and display these games in the player profiles and create a communication channel for the players and spectators during the ongoing game, which could be both text and voice chat using XMPP and other relevant communication protocols.
Thanks for reading. 👀