#include "Board.h" Board::Board(int n) { DEBUG("[%d-dimensional board]\n", n); if (n < 3) { CRASH("Board must be atleast 3-dimensional\n"); } numDimensions = n; dimensions.clear(); dimensions.resize(numDimensions, 1); numDirections = ((int)pow(3,numDimensions)) - 1; DEBUG("[numDirections=%d]\n", numDirections); SquareId square(numDimensions); for (int i = 0; i < numDimensions; i++) { square[i] = BLANK; } directions.resize(numDirections); pos = 0; EnumerateDirections(0,square); openFaces.clear(); possibleMoves.clear(); } void Board::SetDimensionSize(int d, int sz) { DEBUG(">>> void SetDimensionSize(int d, int sz)\n"); assert (d < numDimensions); dimensions[d] = sz; DEBUG("[Dimension %d, size = %d]\n", d, dimensions[d]); // calculate total number of squares numSquares = 1; for (int i = 0; i < numDimensions; i++) { if (dimensions[i] != BLANK) { numSquares *= dimensions[i]; } } // intialize squares squares.resize(numSquares, BLANK); squareIds.resize(numSquares); SquareId square(numDimensions); for (int i = 0; i < numDimensions; i++) { square[i] = BLANK; } EnumerateSquares(0,square); } int Board::GetStoppingIndex(Move& move) { DEBUG(">>> int GetStoppingIndex(Move& move)\n"); int f = move.GetFace(); int faceDimension; SquareId& square = move.GetColumn(); int stop = 0; if (f > 0) { DEBUG("[Falling downwards. . ."); faceDimension = f; stop = dimensions[faceDimension]-1; for (int i = 0; i < dimensions[faceDimension]; i++) { square[faceDimension] = i; if (GetValue(square) != BLANK) { stop = i-1; break; } } } else if (f < 0) { DEBUG("[Falling upwards. . ."); faceDimension = -f; stop = 0; for (int i = dimensions[faceDimension]-1; i >= 0; i++) { square[faceDimension] = i; if (GetValue(square) != BLANK) { stop = i+1; break; } } } else { assert(0); } // the move legality check is responsible for confirming that the stopping // point is within the board. DEBUG(" to %d]\n", stop); return stop; } void Board::MakeMove(Move& move) { DEBUG(">>> void MakeMove(Move& move)\n"); // NOTE: this assumes the move is legal! int f = move.GetFace(); f = ABS(f); SquareId square = move.GetColumn(); square[f] = GetStoppingIndex(move); DEBUG("[Stopping Square "); PrintSquare(square, true); // DEBUG DEBUG("]\n"); SetValue(move.GetPlayer(), square); } void Board::Print() { DEBUG(">>> void Print()\n"); // prints only 3 dimensional boards if (numDimensions != 3) { CRASH("Cannot print a %d-dimensional board\n", numDimensions); } for (int i = 0; i < numSquares; i++) { if (i != 0) { if ((i % dimensions[0]) == 0) OUTPUT("\n"); if ((i % (dimensions[0] * dimensions[1])) == 0) OUTPUT("\n"); } OUTPUT("%3c ", GetValue(i)); } OUTPUT("\n"); } int Board::GetValue(SquareId& square) { DEBUG(">>> int GetValue(SquareId& square)\n"); return GetValue(SquareToIndex(square)); } int Board::GetValue(int i) { DEBUG(">>> int GetValue(int i)\n"); return squares[i]; } void Board::SetValue(int val, SquareId& square) { DEBUG(">>> void SetValue(int val, SquareId& square)\n"); SetValue(val, SquareToIndex(square)); } void Board::SetValue(int val, int i) { DEBUG(">>> void SetValue(int val, int i)\n"); squares[i] = val; } int Board::SquareToIndex(SquareId& square) { DEBUG(">>> int SquareToIndex(SquareId& square)\n"); // DEF: nth-dimension segment size: // segsize(n) = // dimensionsize(n-1) * dimensionsize(n-2) * ... * // dimensionsize(2) * dimensionsize(2) // // overall index = (index(n) * segsize(n)) + // (index(n-1) * segsize(n-1)) + // . . . // (index(2) * segsize(2)) + // (index(1) * segsize(1)) int totalIndex = 0; int segsize = numSquares; int i; for (i = numDimensions-1; i >= 0; i--) { segsize = (int) segsize / dimensions[i]; totalIndex += segsize * square[i]; } assert ((i == -1) && (segsize == 1)); assert(totalIndex < numSquares); return totalIndex; } void Board::EnumerateDirections(int dim, SquareId& direction) { DEBUG(">>> void EnumerateDirections(int dim, SquareId& direction)\n"); if (dim == numDimensions) { // check if direction is all zeros, if so skip bool skip = true; for (int d = 0; d < numDimensions; d++) { if (direction[d] != 0) skip = false; } if (skip) return; SquareId direction2(direction); directions[pos] = direction2; DEBUG("[directions[%d] = ", pos); PrintSquare(direction2, true); // DEBUG DEBUG("]\n"); pos++; } else { for (int i = -1; i <= 1; i++) { direction[dim] = i; EnumerateDirections(dim+1,direction); } } } SquareId& Board::IndexToSquare(int index) { DEBUG(">>> SquareId& IndexToSquare(int index)\n"); return squareIds[index]; } void Board::PrintSquare(SquareId& sq, bool debug = false) { DEBUG(">>> void PrintSquare(SquareId& sq, bool debug = false)\n"); if (debug) { DEBUG("("); for (int i = 0; i < numDimensions; i++) { DEBUG("%d", sq[i]); if (i != numDimensions-1) DEBUG(","); } DEBUG(")"); } else { OUTPUT("("); for (int i = 0; i < numDimensions; i++) { OUTPUT("%d", sq[i]); if (i != numDimensions-1) OUTPUT(","); } OUTPUT(")"); } } void Board::EnumerateSquares(int dim, SquareId& square) { DEBUG(">>> void EnumerateSquares(int dim, SquareId& square)\n"); if (dim == numDimensions) { SquareId square2(square); int i = SquareToIndex(square2); squareIds[i] = square2; DEBUG("[squareIds[%d] = ", i); PrintSquare(square2, true); // DEBUG DEBUG("]\n"); } else { for (int i = 0; i < dimensions[dim]; i++) { square[dim] = i; EnumerateSquares(dim+1,square); } } } bool Board::IsDraw() { DEBUG(">>> bool IsDraw()\n"); // if there is any legal move, then the board is not considered a draw int numMoves = possibleMoves.size(); for (int i = 0; i < numMoves; i++) { if (IsLegal(possibleMoves[i])){ DEBUG(">>> bool IsDraw() return value [false]\n"); return false; } } DEBUG(">>> bool IsDraw() return value [true]\n"); return true; } bool Board::IsWin(int runLength, char player){ DEBUG(">>> bool IsWin(int %d, char %c)\n", runLength, player); // for each starting square for (int s = 0; s < numSquares; s++) { SquareId start = squareIds[s]; int val = GetValue(s); if (val == BLANK || val != player){ //not interested in other players DEBUG("Value %c not equal to actual %c\n", player, val); continue; } DEBUG("[Searching for run from "); PrintSquare(start, true); // DEBUG DEBUG("]\n"); // for each potential neighbor for (int d = 0; d < numDirections; d++) { SquareId dir = directions[d]; DEBUG("Dir = "); PrintSquare(dir, true); // DEBUG DEBUG(":\n"); // check for run SquareId curr(start); bool runFound = true; // start loop at 1 since the first element in the run // would be the starting square which is not counted // here for (int i = 1; i < runLength; i++) { SquareId next = CalculateNeighbor(curr, dir); DEBUG("Neighbor "); PrintSquare(next, true); // DEBUG if (!InBounds(next)) { DEBUG(" out of bounds\n"); runFound = false; break; } if (GetValue(curr) != player) { DEBUG(" different values\n"); runFound = false; break; } DEBUG(" (len = %d)\n", i); curr = next; } if (runFound) { if (true) { // print run // OUTPUT("Run of length %d for player %c starting at ", // runLength, val); // PrintSquare(start); // OUTPUT(" in direction "); // PrintSquare(dir); // OUTPUT("\n"); } DEBUG("... TRUE IsWin(int %d, char %c) \n", runLength, player); return true; } } } DEBUG("... FALSE IsWin(int %d, char %c) \n", runLength, player); return false; } bool Board::HasRun(int runLength, bool print = false) { DEBUG(">>> bool HasRun(int runLength, bool print = false)\n"); // for each starting square for (int s = 0; s < numSquares; s++) { SquareId start = squareIds[s]; int val = GetValue(s); if (val == BLANK) continue; DEBUG("[Searching for run from "); PrintSquare(start, true); // DEBUG DEBUG("]\n"); // for each potential neighbor for (int d = 0; d < numDirections; d++) { SquareId dir = directions[d]; DEBUG("Dir = "); PrintSquare(dir, true); // DEBUG DEBUG(":\n"); // check for run SquareId curr(start); bool runFound = true; // start loop at 1 since the first element in the run // would be the starting square which is not counted // here for (int i = 1; i < runLength; i++) { SquareId next = CalculateNeighbor(curr, dir); DEBUG("Neighbor "); PrintSquare(next, true); // DEBUG if (!InBounds(next)) { DEBUG(" out of bounds\n"); runFound = false; break; } if (GetValue(curr) != GetValue(next)) { DEBUG(" different values\n"); runFound = false; break; } DEBUG(" (len = %d)\n", i); curr = next; } if (runFound) { if (print) { // print run OUTPUT("Run of length %d for player %c starting at ", runLength, val); PrintSquare(start); OUTPUT(" in direction "); PrintSquare(dir); OUTPUT("\n"); } return true; } } } return false; } bool Board::fullBoard(){ DEBUG(">>> bool fullBoard()\n"); for (int i = 0; i < numSquares; i++) { SquareId sq = squareIds[i]; char val = GetValue(sq); DEBUG("Number of squares = %d on square = %d value of square %c \n", numSquares, i, val); if(val == BLANK) return false; } return true; } int Board::numPiecesA() { DEBUG(">>> int numPiecesA()\n"); int playerPieces = 0; // for each potential neighbor for (int i = 0; i < numSquares; i++) { SquareId sq = squareIds[i]; char val = GetValue(sq); if(val == 'A') playerPieces++; } DEBUG(">>> int numPiecesA() return value [%d]\n",playerPieces); return playerPieces; } int Board::numPiecesB() { DEBUG(">>> int numPiecesB()\n"); int playerPieces = 0; // for each potential neighbor for (int i = 0; i < numSquares; i++) { SquareId sq = squareIds[i]; char val = GetValue(sq); if(val == 'B') playerPieces++; } DEBUG(">>> int numPiecesB() return value [%d]\n",playerPieces); return playerPieces; } bool Board::InBounds(SquareId& sq) { DEBUG(">>> bool InBounds(SquareId& sq)\n"); for (int i = 0; i < numDimensions; i++) { if ((sq[i] < 0) || (sq[i] >= dimensions[i])) return false; } return true; } SquareId& Board::CalculateNeighbor(SquareId& sq, SquareId& dir) { DEBUG(">>> SquareId& CalculateNeighbor(SquareId& sq, SquareId& dir)\n"); SquareId& neighbor(sq); for (int i = 0; i < numDimensions; i++) { neighbor[i] = sq[i] + dir[i]; } return neighbor; } void Board::SetOpenFace(int f) { DEBUG(">>> void SetOpenFace(int f)\n"); int dim = ABS(f); if (!((dim < numDimensions) && (dim >= 0))) { CRASH("Illegal open face %d, not within bounds [0,%d]\n", f, numDimensions-1); } openFaces.insert(f); // add to possible moves, any move that drops a piece in this face int shouldAdd = 1; for (int i = 0; i < numDimensions; i++) { if (i != dim) shouldAdd *= dimensions[i]; } int didAdd = 0; for (int i = 0; i < numSquares; i++) { // to ensure we take only one square from each column, // add a move only when the index for |f| dimension is 0 SquareId sq = squareIds[i]; if (sq[dim] == 0) { SquareId column(sq); column[dim] = BLANK; Move m(BLANK,f,column); possibleMoves.push_back(m); didAdd++; } } assert(didAdd == shouldAdd); } bool Board::IsOpenFace(int f) { DEBUG(">>> bool IsOpenFace(int f)\n "); set::iterator it = openFaces.find(f); bool temp = (it != openFaces.end()); DEBUG("... bool IsOpenFace(int f) return bool[%d]\n", temp); return temp; } bool Board::IsLegal(Move& move) { DEBUG(">>> bool IsLegal(Move& move)\n "); int face = move.GetFace(); if (!IsOpenFace(face)) { DEBUG("Face %d is not open\n", face); DEBUG("... bool IsLegal(Move& move) 2return [false] \n"); return false; } SquareId stoppingSquare = move.GetColumn(); face = ABS(face); stoppingSquare[face] = GetStoppingIndex(move); // check that dimensions are within board for (int d = 0; d < numDimensions; d++) { if (!((stoppingSquare[d] < dimensions[d]) && (stoppingSquare[d] >= 0))) { DEBUG("Stopping square in dimension %d, %d is not within bounds [0,%d]\n", d, stoppingSquare[d], dimensions[d]-1); DEBUG("... bool IsLegal(Move& move) 3return [false] \n"); return false; } } DEBUG("... bool IsLegal(Move& move) return [true] \n"); return true; }