422 lines
11 KiB
C++
422 lines
11 KiB
C++
#include "Config.h"
|
|
#include "Board.h"
|
|
#include "Move.h"
|
|
#include "../Database/GameLogManager.h"
|
|
#include "../Utilities/SymbolToStr.h"
|
|
#include <iostream>
|
|
|
|
|
|
//------------------------------- Constructor & deconstructor ----------------------------------------
|
|
|
|
Board::Board(int boardSize)
|
|
// grid holds the matrix used for game logic
|
|
// validMovesGrid is only used to show valid moves
|
|
// display grid is used to potentially combine grid and validMovesGrid for displaying the board
|
|
{
|
|
BoardSize = boardSize;
|
|
|
|
grid = new char*[boardSize];
|
|
for (int i = 0; i < boardSize; ++i) {
|
|
grid[i] = new char[boardSize];
|
|
}
|
|
|
|
validMovesGrid = new char*[boardSize];
|
|
for (int i = 0; i < boardSize; ++i) {
|
|
validMovesGrid[i] = new char[boardSize];
|
|
}
|
|
|
|
displayGrid = new char*[boardSize];
|
|
for (int i = 0; i < boardSize; ++i) {
|
|
displayGrid[i] = new char[boardSize];
|
|
}
|
|
|
|
for (int y = 0; y < boardSize; y++)
|
|
{
|
|
for (int x = 0; x < boardSize; x++)
|
|
{
|
|
grid[y][x] = '.';
|
|
validMovesGrid[y][x] = '.';
|
|
displayGrid[y][x] = '.';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Board::deleteBoardMemory()
|
|
// when the game is over and maybe GameReviewer was executed
|
|
// this method has to be called to free up the allocated memory
|
|
// a destructor could do the job too
|
|
{
|
|
for (int i = 0; i < BoardSize; ++i)
|
|
{
|
|
delete[] grid[i];
|
|
}
|
|
delete[] grid;
|
|
|
|
for (int i = 0; i < BoardSize; ++i)
|
|
{
|
|
delete[] validMovesGrid[i];
|
|
}
|
|
delete[] validMovesGrid;
|
|
|
|
for (int i = 0; i < BoardSize; ++i)
|
|
{
|
|
delete[] displayGrid[i];
|
|
}
|
|
delete[] displayGrid;
|
|
}
|
|
|
|
|
|
//-------------------------------- Database & pre-game operations ------------------------------------
|
|
|
|
std::string Board::retrieveBoard()
|
|
// turns grid into string format to be saved inside database
|
|
// this string can be used to create a grid using loadBoard method
|
|
{
|
|
int size = BoardSize * BoardSize + BoardSize;
|
|
std::string result = "";
|
|
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
result += symbolToStr(grid[y][x]);
|
|
|
|
if (!(y == BoardSize-1 && x == BoardSize-1))
|
|
{
|
|
result += "|";
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void Board::newGameSetup()
|
|
// grid represents the starting state of the board when playing a new game
|
|
// called for board before passing its pointer for creating a SinglePlayerGame/MultiPlayerGame instance
|
|
{
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
grid[y][x] = '.';
|
|
}
|
|
}
|
|
|
|
grid[BoardSize/2 - 1][BoardSize/2 - 1] = 'W';
|
|
grid[BoardSize/2][BoardSize/2] = 'W';
|
|
grid[BoardSize/2][BoardSize/2 - 1] = 'B';
|
|
grid[BoardSize/2 - 1][BoardSize/2] = 'B';
|
|
|
|
updateDisplayGrid();
|
|
}
|
|
|
|
|
|
void Board::loadBoard(std::string Board)
|
|
// recreates grid based on a string inside database
|
|
// this string is created by retrieveBoard method
|
|
{
|
|
int index = 0;
|
|
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
grid[y][x] = static_cast<char>(Board[index]);
|
|
index += 2;
|
|
}
|
|
}
|
|
|
|
updateDisplayGrid();
|
|
}
|
|
|
|
|
|
// ------------------------------------- grid and game operations -----------------------------------------
|
|
|
|
void Board::prepareBoardForMove(char thisTurnColor)
|
|
// this method has to be called when a valid move is made
|
|
// updated displayGrid with the latest changes in grid
|
|
// validMovesGrid will find valid moves based on the new grid
|
|
// if user has chosen to see valid moves inside the board (SHOW_AVAILABLE_PLACES_FOR_PIECES is set to true)
|
|
// then they are putted inside the displayGrid (combining grid and validMovesGrid)
|
|
{
|
|
updateDisplayGrid();
|
|
prepareValidMovesGrid(thisTurnColor);
|
|
if (Config::getInstance() -> SHOW_AVAILABLE_PLACES_FOR_PIECES)
|
|
putValidMoves();
|
|
}
|
|
|
|
|
|
void Board::flip(int x, int y)
|
|
// changes the color of the piece inside the given coordinates
|
|
{
|
|
if (grid[y][x] == 'B')
|
|
grid[y][x] = 'W';
|
|
else if (grid[y][x] == 'W')
|
|
grid[y][x] = 'B';
|
|
}
|
|
|
|
|
|
void Board::putPiece(int x, int y, char color)
|
|
{
|
|
grid[y][x] = color;
|
|
|
|
Move move(x, y, color);
|
|
addLog(move);
|
|
|
|
// each comnination of coresponding elements represent a diraction
|
|
// left-up & left & left-down & up & down & right-uo & right & right-down
|
|
const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
|
|
const int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
|
|
|
for (int d = 0; d < 8; ++d)
|
|
{
|
|
int flips = countFlipsInDirection(x, y, dx[d], dy[d], color);
|
|
|
|
if (flips > 0)
|
|
{
|
|
int xc = x + dx[d];
|
|
int yc = y + dy[d];
|
|
|
|
for (int step = 0; step < flips; ++step)
|
|
{
|
|
flip(xc, yc);
|
|
xc += dx[d];
|
|
yc += dy[d];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int Board::countBlack()
|
|
// counts the black pieces of the Board
|
|
{
|
|
int count = 0;
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (grid[y][x] == 'B') count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
int Board::countWhite()
|
|
// counts the white pieces of the Board
|
|
{
|
|
int count = 0;
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (grid[y][x] == 'W') count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
//------------------------------------------ validGrid operations --------------------------------------------
|
|
|
|
int Board::countValidMoves()
|
|
// returns the number of valid moves that can be made for the current turn
|
|
// should be called when validMovesGrid is created for the turn
|
|
// in a nutshell, after prepareBoardForMove method is called
|
|
// used to decide of a game has to end
|
|
{
|
|
int count = 0;
|
|
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (validMovesGrid[y][x] == 'O') count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
|
|
int Board::isValid(int x, int y, char color)
|
|
// returns the number of gains (opponent pieces fliped) if a piece with the given color is put in the given coords
|
|
// if it returns 0, then the move is inValid
|
|
// used as the primary parameter for the medium bot
|
|
{
|
|
if (grid[y][x] != '.') return 0;
|
|
|
|
int totalFlips = 0;
|
|
|
|
// each comnination of coresponding elements represent a diraction
|
|
// left-up & left & left-down & up & down & right-uo & right & right-down
|
|
const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
|
|
const int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};
|
|
|
|
for (int d = 0; d < 8; d++)
|
|
{
|
|
totalFlips += countFlipsInDirection(x, y, dx[d], dy[d], color);
|
|
}
|
|
|
|
return totalFlips;
|
|
}
|
|
|
|
|
|
int Board::countFlipsInDirection(int x, int y, int dx, int dy, char color)
|
|
// counts the number of flips happened by putting a piece with the given color inside the given coordinates
|
|
// dx and dy represent the direction and more specificly, the steps of x and y
|
|
{
|
|
char oppColor = (color == 'B' ? 'W' : 'B');
|
|
|
|
int flips = 0;
|
|
int xc = x + dx;
|
|
int yc = y + dy;
|
|
|
|
|
|
while ((xc >= 0 && xc < BoardSize) && (yc >= 0 && yc < BoardSize) && grid[yc][xc] == oppColor)
|
|
{
|
|
flips++;
|
|
xc += dx;
|
|
yc += dy;
|
|
}
|
|
|
|
// if loop doesn't meet a same colored piece, one of x coords or y coords will get out of bond
|
|
// or the element loop breaked for is whether a '.' (empty place)
|
|
if ((xc < 0 || xc == BoardSize) || (yc < 0 || yc == BoardSize) || grid[yc][xc] != color)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return flips;
|
|
}
|
|
|
|
|
|
void Board::resetValidMovesGrid()
|
|
// clears validGrid elements to empty places
|
|
{
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
validMovesGrid[y][x] = '.';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Board::prepareValidMovesGrid(char color)
|
|
// first ValidMovesGrid resets itself so previous data won't interupt the process
|
|
// then check each place in grid, if it's valid, it'll put O inside the coresponding coordinates inside itself
|
|
{
|
|
resetValidMovesGrid();
|
|
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (isValid(x, y, color) != 0)
|
|
validMovesGrid[y][x] = 'O';
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------- display and displayGrid operations -----------------------------------------
|
|
void Board::updateDisplayGrid()
|
|
// displayGrid be copied based on the elements (pieces) inside grid
|
|
{
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
displayGrid[y][x] = grid[y][x];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Board::putValidMoves()
|
|
// validMovesGrid has the valid moves coordinates inside itself
|
|
// by calling this method, displayGrid will mark valid moves which
|
|
// validMovesGrid marks
|
|
{
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (validMovesGrid[y][x] == 'O')
|
|
{
|
|
displayGrid[y][x] = 'O';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Board::display()
|
|
// outputs displayGrid with viasual decorations
|
|
{
|
|
std::cout << " ";
|
|
for (int i = 1; i <= BoardSize; i++)
|
|
{
|
|
std::cout << i << " ";
|
|
}
|
|
std::cout << std::endl;
|
|
|
|
std::cout << " ";
|
|
std::cout << (char)201;
|
|
for (int i = 0; i < BoardSize; i++)
|
|
{
|
|
std::cout << (char)205 << (char)205 << (char)205;
|
|
}
|
|
std::cout << (char)187 << std::endl;
|
|
|
|
for (int y = 0; y < BoardSize; y++)
|
|
{
|
|
std::cout << y + 1 << " " << (char)186;
|
|
for (int x = 0; x < BoardSize; x++)
|
|
{
|
|
if (y == CursorY && x == CursorX) std::cout << "[";
|
|
else std::cout << " ";
|
|
|
|
char element = displayGrid[y][x];
|
|
if (element == 'W') // white piece
|
|
{
|
|
std::cout << 'X';
|
|
}
|
|
else if (element == 'B') // black piece
|
|
{
|
|
std::cout << 'O';
|
|
}
|
|
else if (element == '.') // empty space
|
|
{
|
|
std::cout << ' ';
|
|
}
|
|
else if (element == 'O') // valid move
|
|
{
|
|
std::cout << (char)249; // ∙
|
|
}
|
|
|
|
if (y == CursorY && x == CursorX) std::cout << "]";
|
|
else std::cout << " ";
|
|
}
|
|
std::cout << (char)186 << std::endl;
|
|
}
|
|
|
|
std::cout << " ";
|
|
std::cout << (char)200;
|
|
for (int i = 0; i < BoardSize; i++)
|
|
{
|
|
std::cout << (char)205 << (char)205 << (char)205;
|
|
}
|
|
std::cout << (char)188 << std::endl;
|
|
}
|
|
// (char)186 = ║ (char)200 = ╚ (char)188 = ╝ (char)205 = ═ (char)206 = ╦ (char)202 = ╩ (char)187 = ╗ (char)201 = ╔
|
|
// (char)79 = O
|
|
// (char)254 = ■
|