This project was the final assignment for my C++ programming course at Old Dominion University, where we were asked to recreate a text-based version of Patolli, a strategic Mesoamerican board game. The program is built entirely in C++ using object-oriented design with custom ADTs for the player, board, and referee. It handles everything from input validation and turn management to movement rules and win conditions.
What I’m most proud of is how this project showcases my problem-solving skills — translating a physical board game into structured logic, managing complex game state, and designing interactions between multiple components without any visual interface. The full source code is included below.
public: // constructor Referee() { playerL = newPlayer('L'); playerN = newPlayer('N'); board = newBoard(); gameover = false; } // main gameplay function voidPlay() { int turn = 1; int lastturn = 0; int diceRoll = 0; int spot = 0; char input = ' ';
// loop until winner while (!gameover) { // decide current player Player* currentPlayer = (turn % 2 == 1) ? playerL : playerN; srand(time(NULL)); system("CLS"); board->DrawBoard(); // figure out if last move ended or if it is prompting for re-input if (lastturn != turn) { diceRoll = rand() % 6; lastturn = turn; } else { if (spot == 7 || spot == 8 || spot == 22 || spot == 23 || spot == 37 || spot == 38 || spot == 52 || spot == 53) { cout << "You landed on the edge! Move again." << endl << endl; diceRoll = rand() % 6; } else { cout << "Invalid Move! Try again." << endl << endl; } } // instant pass if (diceRoll == 0) { cout << "You rolled a 0. You must pass your turn." << endl << endl; } cout << "Player L (Letters) Score: " << playerL->GetScore() << endl; cout << "Player N (Numbers) Score: " << playerN->GetScore() << endl << endl; cout << "Player " << currentPlayer->GetName() << "'s Turn (Turn " << turn << ")" << endl; cout << "The roll is: " << diceRoll << endl; cout << "Select a piece to move (" << currentPlayer->GetPieces() << " or P to Pass): ";
cin >> input; input = toupper(input); // pass turn if (input == 'P' || diceRoll == 0) { turn++; continue; } // validate input if (!(currentPlayer->GetPieces().find(input) < currentPlayer->GetPieces().length())) { continue; } int position = -1; // find if piece is on board for (int i = 0; i < 60; i++) { if (board->GetValue(i) == input) { position = i; } } spot = (position + diceRoll) % 60; if (position >= 0) { if (spot != 0 && spot != 15 && spot != 30 && spot != 45) { if (board->GetValue(spot) != ' ') { continue; } // can't move to occupied space outside of middle } board->SetValue(spot, input); board->SetValue(position, ' '); if (spot == 7 || spot == 8 || spot == 22 || spot == 23 || spot == 37 || spot == 38 || spot == 52 || spot == 53) { continue; } // extra turn // scoring if (currentPlayer->GetName() == "L" && spot == 29) { board->SetValue(29, ' '); playerL->SetScore(playerL->GetScore() + 1); } if (currentPlayer->GetName() == "N" && spot == 59) { board->SetValue(59, ' '); playerN->SetScore(playerN->GetScore() + 1); } turn++; } else { // add new piece to board if (currentPlayer->GetName() == "L") { if (board->GetValue(31) == ' ') { board->SetValue(31, input); } else { continue; } } else { if (board->GetValue(1) == ' ') { board->SetValue(1, input); } else { continue; } } turn++; } // win conditions if (playerL->GetScore() == 6) { cout << endl << "PLAYER L WINS" << endl; gameover = true; } elseif (playerN->GetScore() == 6) { cout << endl << "PLAYER N WINS" << endl; gameover = true; } } } };
intmain() { Referee Game = Referee(); Game.Play(); return0; }
Here’s a screenshot of the game in action. It shows the initial setup phase and part of the turn cycle where players place pieces and roll the die. The visual layout of the board and player prompts were designed to be easy to follow, even in a simple text-based interface.