initial working version

This commit is contained in:
eay 2023-05-29 14:16:49 -07:00
commit b71d81d8d6
9 changed files with 895 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin/*

2
Makefile Normal file
View File

@ -0,0 +1,2 @@
azool:
g++ src/*.cc -I./include -o bin/azool -Werror -Weffc++ -std=c++11

140
doc/design.txt Normal file
View File

@ -0,0 +1,140 @@
Classes:
class Factory {
public:
Factory() : tileCounts(), isEmpty(false) {}
private:
int tileCounts[NUMCOLORS]; // initialize to 0s
bool isEmpty; // initialize to false
}
class Player {
public:
Player();
void checkValidMove(TileColor color, int rowIdx) {
// check if valid move (color exists on selected factory, grid doesn't already have this color on that row,
// row is either empty or already has the same color)
}
void takeTilesFromFactory(int factoryIdx, TileColor color, int rowIdx) {
// call game board -> takeTiles(factoryIdx, color)
placeTiles(color, rowIdx);
}
void takeTilesFromPool(TileColor color, int rowIdx) {
// call game board -> takeTilesFromPool
int numTiles = 0;
bool poolPenalty = myGameBoardPtr->takeTilesFromPool(color, numTiles);
placeTiles(rowIdx, numTiles);
} // takeTilesFromPool
void placeTiles(int rowIdx, int numTiles) {
// increment row with # of new tiles
rows[rowIdx] += numTiles;
int maxNumInRow = rowIdx + 1;
// if tiles overflow the row, take penalty(ies)
if (rows[rowIdx] > maxNumInRow) {
myNumPenaltiesForTurn += (rows[rowIdx] - maxNumInRow);
rows[rowIdx] = maxNumInRow;
}
} // placeTiles
void endRound() {
// determine which rows are full of tiles
// update the grid
// send extra tiles back to the game board
// find the score for each tile placed
for (int ii = 0; ii < NUMCOLORS; ++ii) {
if (myRows[ii].first == (ii+1)) {
// filled a row. now place a tile on the grid
// determine which column it belongs to
int col = (ii + 5 - myRows[ii].second) % 5;
grid[ii][col] = true;
// search horizontally and vertically for points
myScore += scoreTile(ii, col);
// return extra tiles
myBoardPtr->returnTilesToBag(ii, myRows[ii].second);
// reset rows for next turn
myRows[ii].first = 0;
myRows[ii].second = NONE;
}
}
// Account for penalties
} // endRound
int scoreTile(int row, int col) {
int tileScore = 1;
// Get column score
for (int ii = row - 1; ii > -1; --ii) {
if (!grid[ii][col]) {
break;
}
tileScore++;
} // iterate above current row
for (int ii = row; ii < NUMCOLORS; ++ii) {
if (!grid[ii][col]) {
break;
}
tileScore++;
} // iterate from current row down
// Get row score
for (int ii = col - 1; ii > -1; --ii) {
if (!grid[row][ii]) {
break;
}
tileScore++;
} // iterate left of current column
for (int ii = col; ii < NUMCOLORS; ++ii) {
if (!grid[row][ii]) {
break;
}
tileScore++;
} // iterate from current column to right
} // scoreTile
void finalScore() {
// compute bonuses for rows, columns, 5 of a kind
}
private:
// color = r + c % 5
bool myGrid[5][5];
std::pair<int, TileColor> myRows[NUMCOLORS]; // first - # of tiles on that row, second - color of tiles on row
GameBoard* myBoardPtr;
int myScore;
int myNumPenaltiesForTurn;
};
// who manages turns and rounds? probably the main function
class GameBoard {
public:
enum TileColor {
NONE = -1,
RED = 0,
BLUE,
GREEN,
YELLOW,
BLACK,
NUMCOLORS
};
GameBoard();
bool takeTilesFromFactory(int factoryIdx, TileColor color, uint6_t& numTiles) {
// clear factory
// add other tiles to pool
// return # of tiles given to player
// if failed, return false
}
bool takeTilesFromPool(TileColor color, uint6_t& numTiles) {
// zero color count in pool
// return # of tiles given to player (-1 if failed)
// if failed, return false
}
bool returnTilesToBag(uint6_t numTiles, TileColor color);
void endGame(); // called by a player if they get a row
private:
void dealTiles();
void resetTiles();
members:
- vector of factories?
int pool[NUMCOLORS]; // stores the count of each color currently in the pool; initialize to 0s
bool whiteTileInPool; // initialize to true
std::vector<TileColor> tileBag; // initialize to 20 of each color
bool lastRound; // initialize to false
};

43
include/GameBoard.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef GAMEBOARD_H_
#define GAMEBOARD_H_
#include <vector>
#include <ostream>
#include <cstring>
#include "tile_utils.h"
class GameBoard {
public:
struct Factory {
Factory() : tileCounts() {
memset(tileCounts, 0, azool::NUMCOLORS*sizeof(int));
}
int tileCounts[azool::NUMCOLORS];
}; // struct Factory
GameBoard();
GameBoard(int factories);
friend std::ostream& operator<<(std::ostream& out, const GameBoard& board);
bool validFactoryRequest(int factoryIdx, azool::TileColor color);
bool takeTilesFromFactory(int factoryIdx, azool::TileColor color, int& numTiles);
bool takeTilesFromPool(azool::TileColor color, int& numTiles, bool& poolPenalty);
void returnTilesToBag(int numTiles, azool::TileColor color);
void dealTiles();
int numFactories() { return tileFactories.size(); }
bool endOfRound() {
// round ends when the pool and tile factories are empty
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
if (pool[ii] > 0) return false;
}
return tileFactories.empty();
}
private:
void resetBoard();
// - vector of factories?
std::vector<Factory> tileFactories;
int maxNumFactories;
int pool[azool::NUMCOLORS]; // stores the count of each color currently in the pool; initialize to 0s
bool whiteTileInPool; // initialize to true
std::vector<azool::TileColor> tileBag; // initialize to 20 of each color
bool lastRound; // initialize to false
};
#endif // GAMEBOARD_H_

41
include/Player.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef PLAYER_H_
#define PLAYER_H_
#include "GameBoard.h"
#include "tile_utils.h"
#include <string>
class Player {
public:
Player(GameBoard* const board, std::string name = "1");
void takeTurn();
bool takeTilesFromFactory(int factoryIdx, azool::TileColor color, int rowIdx);
bool takeTilesFromPool(azool::TileColor color, int rowIdx);
bool discardFromFactory(int factoryIdx, azool::TileColor color);
bool discardFromPool(azool::TileColor color);
void placeTiles(int rowIdx, azool::TileColor color, int numTiles);
void endRound(bool& fullRow);
void finalizeScore();
int getScore() const { return myScore; }
void printMyBoard() const;
bool tookPenalty() const { return myTookPoolPenaltyThisTurn; }
const std::string getPlayerName() const { return myName; }
private:
Player(const Player&) = delete;
Player operator=(const Player&) = delete;
bool checkValidMove(azool::TileColor color, int rowIdx) const;
int scoreTile(int row, int col);
// first - # of tiles on that row, second - color of tiles on row
bool myGrid[azool::NUMCOLORS][azool::NUMCOLORS];
typedef std::pair<int, azool::TileColor> TileRow;
TileRow myRows[azool::NUMCOLORS];
GameBoard* const myBoardPtr;
int myScore;
int myNumPenaltiesForTurn;
bool myTookPoolPenaltyThisTurn;
const std::vector<int> PenaltyPoints = {0, 1, 2, 3, 5, 7, 10, 13, 15};
std::string myName;
}; // class Player
#endif // PLAYER_H_

23
include/tile_utils.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef TILE_UTILS_H_
#define TILE_UTILS_H_
namespace azool {
enum TileColor {
NONE = -1,
RED = 0,
BLUE,
GREEN,
YELLOW,
BLACK,
NUMCOLORS
};
const std::string TileColorStrings[NUMCOLORS] = {
"red",
"blue",
"green",
"yellow",
"black"
};
const char TileColorSyms[NUMCOLORS] = { 'r', 'b', 'g', 'y', 'k' };
}
#endif // TILE_UTILS_H_

132
src/GameBoard.cc Normal file
View File

@ -0,0 +1,132 @@
#include "GameBoard.h"
#include <algorithm>
#include <random>
GameBoard::GameBoard() :
tileFactories(),
maxNumFactories(5), // TODO(feature) base on # of players? 2n+1
pool(),
whiteTileInPool(true),
tileBag(),
lastRound(false) {
resetBoard();
} // GameBoard::GameBoard
GameBoard::GameBoard(int factories) :
tileFactories(),
maxNumFactories(factories),
pool(),
whiteTileInPool(true),
tileBag(),
lastRound(false) {
tileBag.reserve(azool::NUMCOLORS * 20);
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
pool[ii] = 0; // initialize pool to 0s
// initialize tile bag to 20 of each color
for (int jj = 0; jj < 20; jj++) {
tileBag[ii * 20 + jj] = static_cast<azool::TileColor>(ii);
}
}
} // GameBoard::GameBoard
std::ostream& operator<<(std::ostream& out, const GameBoard& board) {
// user will input 1-indexed value, even though we 0-index internally
int factCt = 1;
out << "Factories:\n";
for (auto factory : board.tileFactories) {
out << factCt++ << " ";
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
for (int jj = 0; jj < factory.tileCounts[ii]; ++jj) {
out << azool::TileColorStrings[ii] << ",";
}
}
out << "\n";
}
out << "\nPOOL:\n";
if (board.whiteTileInPool) {
out << "[-1]\n";
}
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
out << azool::TileColorStrings[ii] << " x " << board.pool[ii] << "\n";
}
return out;
}
bool GameBoard::validFactoryRequest(int factoryIdx, azool::TileColor color) {
// check if color exists on specified factory
bool retVal = factoryIdx < tileFactories.size() and
tileFactories[factoryIdx].tileCounts[color] > 0;
return retVal;
}
bool GameBoard::takeTilesFromFactory(int factoryIdx, azool::TileColor color, int& numTiles) {
// clear factory
// add other tiles to pool
// return # of tiles given to player
// if invalid return false
if (!validFactoryRequest(factoryIdx, color)) {
return false;
}
numTiles = tileFactories[factoryIdx].tileCounts[color];
// zero out the tiles of this color before adding the rest to the pool
tileFactories[factoryIdx].tileCounts[color] = 0;
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
pool[ii] += tileFactories[factoryIdx].tileCounts[ii];
}
tileFactories.erase(tileFactories.begin() + factoryIdx);
return true;
}
bool GameBoard::takeTilesFromPool(azool::TileColor color, int& numTiles, bool& poolPenalty) {
numTiles = pool[color]; // # of tiles given to player
if (numTiles == 0) {
// invalid - no tiles of the given color are in the pool
return false;
}
// zero color count in pool
pool[color] = 0;
poolPenalty = whiteTileInPool;
whiteTileInPool = false;
return true;
}
void GameBoard::returnTilesToBag(int numTiles, azool::TileColor color) {
for (int ii = 0; ii < numTiles; ++ii) {
tileBag.emplace_back(color);
}
}
// random shuffle then read from the beginning of the vector?
void GameBoard::dealTiles() {
tileFactories.clear();
whiteTileInPool = true;
int numFactories = std::min(static_cast<int>(tileBag.size()) / 4, maxNumFactories);
if (tileBag.size() < 4*numFactories) {
numFactories++;
}
// TODO(implementation) - set random seed (probably somewhere else)
std::shuffle(tileBag.begin(), tileBag.end(), std::default_random_engine(590));
auto itr = tileBag.begin();
for (int ii = 0; ii < numFactories and itr != tileBag.end(); ++ii) {
Factory fact;
for (int jj = 0; jj < 4; jj++) {
fact.tileCounts[*itr]++;
itr++;
if (itr == tileBag.end()) {
break;
} // last factory may have less than 4 tiles
}
tileFactories.push_back(fact);
}
} // GameBoard::dealTiles
void GameBoard::resetBoard() {
memset(pool, 0, azool::NUMCOLORS*sizeof(int));
tileBag.clear();
tileBag.reserve(azool::NUMCOLORS * 20);
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
// initialize tile bag to 20 of each color
for (int jj = 0; jj < 20; jj++) {
tileBag.emplace_back(static_cast<azool::TileColor>(ii));
}
}
whiteTileInPool = true;
} // GameBoard::resetBoard

449
src/Player.cc Normal file
View File

@ -0,0 +1,449 @@
#include "Player.h"
#include <iostream>
#include <cstring>
Player::Player(GameBoard* const board, std::string name) :
myGrid(),
myRows(),
myBoardPtr(board),
myName(name),
myScore(0),
myNumPenaltiesForTurn(0),
myTookPoolPenaltyThisTurn(false) {
int gridSize = azool::NUMCOLORS * azool::NUMCOLORS;
memset(myGrid, 0, gridSize*sizeof(bool));
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
myRows[ii].first = 0;
myRows[ii].second = azool::NONE;
} // initialize rows
} // Player::Player
bool Player::checkValidMove(azool::TileColor color, int rowIdx) const {
// check if valid move
// grid doesn't already have this color on that row,
// row is either empty or already has the same color
if (color == azool::NONE) {
std::cerr << "HEY THIS IS WEIRD -- "
<< "ASKING FOR COLOR = NONE?" << std::endl;
return false; // invalid and also probably shouldn't happen
}
int colIdx = (5 + static_cast<int>(color) - rowIdx) % 5;
if (myGrid[rowIdx][colIdx]) {
return false; // already have that color on this row
}
if (!(myRows[rowIdx].second == color or
myRows[rowIdx].second == azool::NONE)) {
// TODO(implementation) to check for empty, should we check the color or # of tiles?
return false;
}
return true;
} // Player::checkValidMove
bool Player::takeTilesFromFactory(int factoryIdx, azool::TileColor color, int rowIdx) {
// call game board -> takeTiles(factoryIdx, color)
int numTiles = 0;
if (checkValidMove(color, rowIdx) and
myBoardPtr->takeTilesFromFactory(factoryIdx, color, numTiles)) {
placeTiles(rowIdx, color, numTiles);
return true;
}
return false;
} // Player::takeTilesFromFactory
bool Player::takeTilesFromPool(azool::TileColor color, int rowIdx) {
// call game board -> takeTilesFromPool
int numTiles = 0;
bool poolPenalty = false;
if (!checkValidMove(color, rowIdx)) {
return false;
}
if (!myBoardPtr->takeTilesFromPool(color, numTiles, poolPenalty)) {
return false; // couldn't get that tile from the pool
}
if (poolPenalty) {
myTookPoolPenaltyThisTurn = poolPenalty;
myNumPenaltiesForTurn++;
}
placeTiles(rowIdx, color, numTiles);
return true;
} // Player::takeTilesFromPool
void Player::placeTiles(int rowIdx, azool::TileColor color, int numTiles) {
// increment row with # of new tiles
myRows[rowIdx].first += numTiles;
// TODO(debug) I can imagine a bug here where the row changes colors...make sure that's not possible
myRows[rowIdx].second = color;
int maxNumInRow = rowIdx + 1;
// if tiles overflow the row, take penalty(ies)
if (myRows[rowIdx].first > maxNumInRow) {
myNumPenaltiesForTurn += (myRows[rowIdx].first - maxNumInRow);
myRows[rowIdx].first = maxNumInRow;
}
} // Player::placeTiles
void Player::endRound(bool& fullRow) {
// determine which rows are full of tiles
// update the grid
// send extra tiles back to the game board
// find the score for each tile placed
// color = row + column % 5
// column = (row + 5 - color) % 5
for (int rowIdx = 0; rowIdx < azool::NUMCOLORS; ++rowIdx) {
if (myRows[rowIdx].first == (rowIdx+1)) {
// filled a row. now place a tile on the grid
// determine which column it belongs to
// TODO(debug) -- possible bug -- what if color == -1?
int col = (5 + myRows[rowIdx].second - rowIdx) % 5;
myGrid[rowIdx][col] = true;
myScore += scoreTile(rowIdx, col);
// return extra tiles -- rowIdx = the number of leftover tiles
myBoardPtr->returnTilesToBag(rowIdx, myRows[rowIdx].second);
// reset rows for next turn
myRows[rowIdx].first = 0;
myRows[rowIdx].second = azool::NONE;
}
}
#ifdef DEBUG
std::cout << "Had " << myNumPenaltiesForTurn << " penalties this turn" << std::endl;
#endif
if (myNumPenaltiesForTurn >= PenaltyPoints.size()) {
myScore -= PenaltyPoints[PenaltyPoints.size() - 1];
}
else {
myScore -= PenaltyPoints[myNumPenaltiesForTurn];
}
// reset for next turn
myTookPoolPenaltyThisTurn = false;
myNumPenaltiesForTurn = 0;
// Check if there's a full row on the grid
for (int rowIdx = 0; rowIdx < azool::NUMCOLORS; ++rowIdx) {
fullRow = true;
for (int colIdx = 0; colIdx < azool::NUMCOLORS; ++colIdx) {
if (!myGrid[rowIdx][colIdx]) {
fullRow = false;
break;
}
} // iter over elements in row
if (fullRow) {
break;
// found a full row; will signal end of game
// break out of loop
}
} // iter over rows in grid
} // Player::endRound
int Player::scoreTile(int tileRow, int tileCol) {
// search horizontally and vertically for points
int tileScore = 1;
// Get column score
#ifdef DEBUG
std::cout << "Placing tile at " << tileRow << "," << tileCol << std::endl;
#endif
for (int rowIdx = tileRow - 1; rowIdx > -1; --rowIdx) {
#ifdef DEBUG
std::cout << " Checking grid at " << rowIdx << "," << tileCol << ": " << myGrid[rowIdx][tileCol] << std::endl;
#endif
if (!myGrid[rowIdx][tileCol]) {
break;
}
tileScore++;
} // iterate above current tileRow
for (int rowIdx = tileRow + 1; rowIdx < azool::NUMCOLORS; ++rowIdx) {
#ifdef DEBBUG
std::cout << " Checking grid at " << rowIdx << "," << tileCol << ": " << myGrid[rowIdx][tileCol] << std::endl;
#endif
if (!myGrid[rowIdx][tileCol]) {
break;
}
tileScore++;
} // iterate from current tileRow down
// Get tileRow score
for (int colIdx = tileCol - 1; colIdx > -1; --colIdx) {
#ifdef DEBUG
std::cout << " Checking grid at " << tileRow << "," << colIdx << ": " << myGrid[tileRow][colIdx] << std::endl;
#endif
if (!myGrid[tileRow][colIdx]) {
break;
}
tileScore++;
} // iterate left of current column
for (int colIdx = tileCol + 1; colIdx < azool::NUMCOLORS; ++colIdx) {
#ifdef DEBUG
std::cout << " Checking grid at " << tileRow << "," << colIdx << ": " << myGrid[tileRow][colIdx] << std::endl;
#endif
if (!myGrid[tileRow][colIdx]) {
break;
}
tileScore++;
} // iterate from current column to right
std::cout << " Final Tile Score: " << tileScore << std::endl;
return tileScore;
} // Player::scoreTile
void Player::finalizeScore() {
int numRows = 0;
int numCols = 0;
// compute bonus for rows
for (int rowIdx = 0; rowIdx < azool::NUMCOLORS; ++rowIdx) {
bool fullRow = true;
for (int colIdx = 0; colIdx < azool::NUMCOLORS; ++colIdx) {
if (!myGrid[rowIdx][colIdx]) {
fullRow = false;
break;
}
} // iterate over elements in row
if (fullRow) {
numRows++;
}
} // iterate over rows
myScore += (numRows*2);
// compute bonuses for columns
for (int colIdx = 0; colIdx < azool::NUMCOLORS; ++colIdx) {
bool fullCol = true;
for (int rowIdx = 0; rowIdx < azool::NUMCOLORS; ++rowIdx) {
if (!myGrid[rowIdx][colIdx]) {
fullCol = false;
}
} // iterate over elements in column
if (fullCol) {
numCols++;
}
} // iterate over columns
myScore += (numCols*7);
// compute bonuses for 5 of a kind
int numFives = 0;
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
bool allFive = true;
// column = (row + 5 - color) % 5
for (int rowIdx = 0; rowIdx < azool::NUMCOLORS; ++rowIdx) {
int colIdx = (ii + 5 - rowIdx) % 5;
if (!myGrid[rowIdx][colIdx]) {
allFive = false;
break;
}
} // check each row for color ii
if (allFive) {
numFives++;
}
} // iterate over all colors
myScore += (numFives*10);
} // Player::finalizeScore
void Player::printMyBoard() const {
// TODO(implementation) - replace cout with ostream
std::cout << "*******************************\n";
std::cout << "PLAYER: " << myName << "\n";
std::cout << *myBoardPtr << "\n\n";
for (int ii = 0; ii < azool::NUMCOLORS; ++ii) {
std::cout << ii+1 << ") ";
for (int jj = azool::NUMCOLORS; jj > (ii+1); --jj) {
std::cout << " ";
}
for (int jj = ii; jj > -1; --jj) {
if (myRows[ii].second == azool::NONE or
jj >= myRows[ii].first) {
std::cout << "_";
}
else if (jj < myRows[ii].first) {
std::cout << azool::TileColorSyms[myRows[ii].second];
}
}
// print grid row
std::cout << " |";
for (int jj = 0; jj < azool::NUMCOLORS; ++jj) {
// color = row + column % 5
char color = (ii + jj) % 5;
if (myGrid[ii][jj]) {
std::cout << static_cast<char>(azool::TileColorSyms[color] - 32) << "|";
}
else {
std::cout << azool::TileColorSyms[color] << "|";
}
}
std::cout << "\n";
} // iterate over rows
std::cout << "Penalties: " << myNumPenaltiesForTurn << std::endl;
std::cout << "Score: " << myScore << std::endl;
// TODO(feature) - print penalty tiles (?)
} // Player::printMyBoard
bool Player::discardFromFactory(int factoryIdx, azool::TileColor color) {
int numTiles = -1;
if (myBoardPtr->takeTilesFromFactory(factoryIdx, color, numTiles)) {
myNumPenaltiesForTurn += numTiles;
return true;
}
return false;
} // Player::discardFromFactory
bool Player::discardFromPool(azool::TileColor color) {
bool poolPenalty = false;
int numTiles = -1;
if (myBoardPtr->takeTilesFromPool(color, numTiles, poolPenalty)) {
if (poolPenalty) {
myNumPenaltiesForTurn++;
}
myNumPenaltiesForTurn += numTiles;
return true;
}
return false;
} // Player::discardFromPool
namespace {
int promptForFactoryIdx(int maxNumFactories) {
static const char* promptFactoryIdxDraw = "Which factory? enter index\n";
char factInput[256]; // wayy more than we need
std::cout << promptFactoryIdxDraw << std::flush;
std::cin >> factInput;
int factIdx = std::atoi(factInput);
if (factIdx < 1 or factIdx > maxNumFactories) {
return -1;
}
return factIdx;
}
azool::TileColor promptForColor() {
static const char* promptColorDraw = "Which color? [r|b|g|y|k]\n";
char colorInput = '\0';
std::cout << promptColorDraw << std::flush;
std::cin >> colorInput;
switch(colorInput) {
case 'r':
return azool::RED;
break;
case 'b':
return azool::BLUE;
break;
case 'g':
return azool::GREEN;
break;
case 'y':
return azool::YELLOW;
break;
case 'k':
return azool::BLACK;
break;
default:
return azool::NONE;
} // end switch
return azool::NONE;
}
int promptForRow() {
static const char* promptRowPlacement = "Place on which row? enter number [1-5]\n";
char rowInput[256] = {0};
std::cout << promptRowPlacement;
std::cin >> rowInput;
int rowIdx = std::atoi(rowInput);
if (rowIdx < 1 or rowIdx > azool::NUMCOLORS) {
return -1;
}
return rowIdx;
}
} // anonymous namespace
void Player::takeTurn() {
// print game board, handle user input
if (myBoardPtr->endOfRound()) return;
printMyBoard();
static const char* promptDrawInput = "What would you like to do?\n"
"[f] take from factory [p] take from pool "
"[d] discard tile(s) "
"[P] print game board again\n";
static const char* promptDiscardInput = "From factory or pool? [f|p]\n";
// TODO(feature) -- remove options when they're not valid?
// (ie don't print [f] factory when there are no factories left)
static const char* invalidEntryMessage = "Invalid entry, try again.\n";
static const char* invalidMoveMessage = "That move was invalid, try again.\n";
bool fullInput = false;
while (!fullInput) {
std::cout << promptDrawInput << std::flush;
char drawType;
std::cin >> drawType;
if (drawType == 'f') {
int factIdx = promptForFactoryIdx(myBoardPtr->numFactories());
// draw from factory
if (factIdx == -1) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
azool::TileColor colorSelection = promptForColor();
if (colorSelection == azool::NONE) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
int rowSelection = promptForRow();
if (rowSelection == -1) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
// user enters 1-5; we use 0 indexing internally
if (!takeTilesFromFactory(factIdx - 1, colorSelection, rowSelection - 1)) {
std::cout << invalidMoveMessage << std::flush;
continue;
}
fullInput = true;
}
else if (drawType == 'p') {
// draw from pool
azool::TileColor colorSelection = promptForColor();
if (colorSelection == azool::NONE) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
int rowSelection = promptForRow();
if (rowSelection == -1) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
// user enters 1-5; we use 0 indexing internally
if (!takeTilesFromPool(colorSelection, rowSelection - 1)) {
std::cout << invalidMoveMessage << std::flush;
continue;
}
fullInput = true;
}
else if (drawType == 'd') {
// TODO(implementation) (make these strings more betterer)
std::cout << promptDiscardInput << std::flush;
char discardFrom = '\0';
std::cin >> discardFrom;
if (discardFrom == 'f') {
int factIdx = promptForFactoryIdx(myBoardPtr->numFactories());
// draw from factory
if (factIdx == -1) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
azool::TileColor colorSelection = promptForColor();
if (colorSelection == azool::NONE) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
int numTiles = -1;
if (!discardFromFactory(factIdx - 1, colorSelection)) {
std::cout << invalidMoveMessage << std::flush;
continue;
}
fullInput = true;
} // from factory
else if (discardFrom == 'p') {
azool::TileColor colorSelection = promptForColor();
if (colorSelection == azool::NONE) {
std::cout << invalidEntryMessage << std::flush;
continue;
}
if (!discardFromPool(colorSelection)) {
std::cout << invalidMoveMessage << std::flush;
continue;
}
fullInput = true;
} // discard from pool
}
else if (drawType == 'P') {
printMyBoard();
}
else {
std::cout << invalidEntryMessage << std::flush;
}
} // while !fullinput
// options: take tile from pool or take tile from factory
// TODO(feature) - temporary...reprint board and hold briefly?
} // Player::takeTurn

64
src/main.cc Normal file
View File

@ -0,0 +1,64 @@
#include "GameBoard.h"
#include "Player.h"
#include <iostream>
// who manages turns and rounds? probably the main function
void testPrint(GameBoard* game) {
game->dealTiles();
Player p1(game);
p1.printMyBoard();
return;
}
void playGame(GameBoard* game) {
game->dealTiles();
std::vector<Player*> players = {new Player(game, "P1"), new Player(game, "P2")};
bool endOfGame = false;
Player* firstPlayer = players[0]; // pointers to keep track of first and second player
Player* secondPlayer = players[1];
while (!endOfGame) {
while (!game->endOfRound()) {
// TODO figure out how order will work for > 2 players
firstPlayer->takeTurn();
secondPlayer->takeTurn();
}
std::cout << "End of round!" << std::endl;
bool p0EndsGame = false;
bool p1EndsGame = false;
players[0]->endRound(p0EndsGame);
players[1]->endRound(p1EndsGame);
if (players[0]->tookPenalty()) {
firstPlayer = players[0];
secondPlayer = players[1];
}
else if (players[1]->tookPenalty()) {
firstPlayer = players[1];
secondPlayer = players[0];
}
else {
std::cerr << "SOMETHING WEIRD - SoMeone has to go first...\n" << std::flush;
firstPlayer = players[0];
secondPlayer = players[1];
}
if (!(p0EndsGame or p1EndsGame)) {
game->dealTiles();
}
}
players[0]->finalizeScore();
players[1]->finalizeScore();
std::cout << " Final scores:\n"
<< players[0]->getScore() << "\n" << std::flush;
players[0]->printMyBoard();
std::cout << players[0]->getScore() << "\n" << std::flush;
players[1]->printMyBoard();
if (players[0]) delete players[0];
if (players[1]) delete players[1];
}
int main() {
GameBoard* game = new GameBoard();
playGame(game);
if (game) delete game;
return 0;
}