Created
June 8, 2018 17:01
-
-
Save jrepp/207276971d98f410f1c8a5a0bcff2d7d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Checkers.cpp : Defines the entry point for the console application. | |
| // | |
| #include "CheckersGame.h" | |
| #include <iostream> | |
| int main() | |
| { | |
| std::cout << "Welcome to checkers" << endl; | |
| uint numPlayers = 0; | |
| do | |
| { | |
| std::cout << "How many people are playing?" << endl << ">"; | |
| std::cin >> numPlayers; | |
| } while (numPlayers > 2); | |
| CheckersGame::createInstance(numPlayers); | |
| CheckersGame* checkers = CheckersGame::instance(); | |
| // Ignore first new line for input | |
| cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | |
| while (!checkers->isGameComplete()) | |
| { | |
| checkers->update(); | |
| } | |
| std::cout << "Winning player is " << checkers->getWinningPlayer() << endl; | |
| CheckersGame::destroyInstance(); | |
| return 0; | |
| }// Checkers.cpp : Defines the entry point for the console application. | |
| // | |
| #include "CheckersGame.h" | |
| #include <iostream> | |
| int main() | |
| { | |
| std::cout << "Welcome to checkers" << endl; | |
| uint numPlayers = 0; | |
| do | |
| { | |
| std::cout << "How many people are playing?" << endl << ">"; | |
| std::cin >> numPlayers; | |
| } while (numPlayers > 2); | |
| CheckersGame::createInstance(numPlayers); | |
| CheckersGame* checkers = CheckersGame::instance(); | |
| // Ignore first new line for input | |
| cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | |
| while (!checkers->isGameComplete()) | |
| { | |
| checkers->update(); | |
| } | |
| std::cout << "Winning player is " << checkers->getWinningPlayer() << endl; | |
| CheckersGame::destroyInstance(); | |
| return 0; | |
| } | |
| #pragma once | |
| #include <iostream> | |
| #include <vector> | |
| #include "stdafx.h" | |
| // Square board size | |
| #define BOARD_SIZE 8 | |
| using namespace std; | |
| class Player; | |
| struct BoardPosition | |
| { | |
| uint row; | |
| uint column; | |
| friend ostream& operator<<(ostream& os, const BoardPosition& pos); | |
| }; | |
| // TODO: Use a better data structure to avoid allocations | |
| struct PlayerMove | |
| { | |
| bool isJump = false; | |
| vector<BoardPosition> moves; | |
| }; | |
| class CheckersBoard | |
| { | |
| public: | |
| CheckersBoard(); | |
| void initialize(Player** players); | |
| void drawBoard(); | |
| enum ValidateMove_Reason | |
| { | |
| Validate_Success, | |
| Validate_Invalid, | |
| Validate_InvalidPosition, | |
| Validate_InvalidMove, | |
| Validate_InvalidJump, | |
| }; | |
| ValidateMove_Reason validateMove(Player* player, PlayerMove& move); | |
| bool isPositionFree(BoardPosition& position); | |
| bool isPositionOccupied(BoardPosition& position, uint& playerId); | |
| void applyMove(Player* player, PlayerMove& move); | |
| bool isValidPosition(BoardPosition& position); | |
| bool canMovePiece(Player* player, BoardPosition& pos1, BoardPosition& pos2, bool& isJump); | |
| uint getBoardPieces(Player* player, vector<BoardPosition>& pieces); | |
| private: | |
| bool getIntermediatePosition(BoardPosition& pos1, BoardPosition& pos2, BoardPosition& intermediatePos); | |
| void setIdAtPos(int id, BoardPosition& position); | |
| char getDisplayChar(uint playerId); | |
| // Could store half the number of columns since only some squares are being used | |
| int m_board[BOARD_SIZE][BOARD_SIZE]; | |
| // Default board definitions | |
| // TODO: Make data-driven instead of hard-coded | |
| const uint PlayerStartingRows[MAX_NUM_PLAYERS] = { 0, 5 }; | |
| const uint NumStartingRows = 3; | |
| const int EmptyBoardPosId = -1; | |
| }; | |
| #include <iostream> | |
| #include "CheckersBoard.h" | |
| #include "CheckersGame.h" | |
| #include "Player/Player.h" | |
| ostream& operator<<(ostream& os, const BoardPosition& pos) | |
| { | |
| char rowChar = 'A' + pos.row; | |
| os << rowChar << pos.column; | |
| return os; | |
| } | |
| CheckersBoard::CheckersBoard() | |
| { | |
| } | |
| void CheckersBoard::initialize(Player** players) | |
| { | |
| memset(m_board, EmptyBoardPosId, BOARD_SIZE * BOARD_SIZE * sizeof(int)); | |
| bool oddColumns = true; | |
| for (uint i = 0; i < MAX_NUM_PLAYERS; ++i) | |
| { | |
| int playerId = EmptyBoardPosId; | |
| Player* player = players[i]; | |
| if (player != nullptr) | |
| { | |
| playerId = player->getPlayerId(); | |
| } | |
| uint startingRow = PlayerStartingRows[i]; | |
| uint lastRow = startingRow + NumStartingRows; | |
| // if (lastRow >= BOARD_SIZE) - ERROR | |
| uint numPieces = 0; | |
| for (; startingRow < lastRow; ++startingRow) | |
| { | |
| uint column = oddColumns ? 1 : 0; | |
| for (; column < BOARD_SIZE; column += 2) | |
| { | |
| m_board[startingRow][column] = playerId; | |
| ++numPieces; | |
| } | |
| oddColumns = !oddColumns; | |
| } | |
| if (player) | |
| { | |
| player->setNumPieces(numPieces); | |
| } | |
| } | |
| } | |
| void CheckersBoard::drawBoard() | |
| { | |
| // Top legend | |
| cout << " "; | |
| for (int column = 1; column < BOARD_SIZE + 1; ++column) | |
| { | |
| cout << " " << column; | |
| } | |
| cout << endl; | |
| char row = 'A'; | |
| for (int i = 0; i < BOARD_SIZE; ++i) | |
| { | |
| // Row legend | |
| cout << row; | |
| row++; | |
| // Board | |
| for (int j = 0; j < BOARD_SIZE; ++j) | |
| { | |
| cout << '|' << getDisplayChar(m_board[i][j]); | |
| } | |
| cout << "|" << endl; | |
| } | |
| } | |
| char CheckersBoard::getDisplayChar(uint playerId) | |
| { | |
| // Could cache | |
| Player* player = CheckersGame::instance()->getPlayer(playerId); | |
| if (player) | |
| { | |
| return player->getPlayerSymbol(); | |
| } | |
| return '.'; | |
| } | |
| void CheckersBoard::applyMove(Player* player, PlayerMove& move) | |
| { | |
| uint numMoves = move.moves.size(); | |
| BoardPosition initialPos = move.moves[0]; | |
| BoardPosition finalPos = move.moves[numMoves - 1]; | |
| for (uint i = 0; i < numMoves - 1; ++i) | |
| { | |
| BoardPosition intermediatePos; | |
| if (getIntermediatePosition(move.moves[i], move.moves[i + 1], intermediatePos)) | |
| { | |
| if (Player* player = CheckersGame::instance()->getPlayer(m_board[intermediatePos.row][intermediatePos.column])) | |
| { | |
| player->setNumPieces(player->getNumRemainingPieces() - 1); | |
| } | |
| setIdAtPos(EmptyBoardPosId, intermediatePos); | |
| } | |
| } | |
| setIdAtPos(EmptyBoardPosId, initialPos); | |
| setIdAtPos(player->getPlayerId(), finalPos); | |
| } | |
| CheckersBoard::ValidateMove_Reason CheckersBoard::validateMove(Player* player, PlayerMove& move) | |
| { | |
| if (!player) | |
| { | |
| //ERROR | |
| return CheckersBoard::Validate_Invalid; | |
| } | |
| uint numPositions = move.moves.size(); | |
| if (numPositions < 2) | |
| { | |
| return CheckersBoard::Validate_Invalid; | |
| } | |
| uint playerId = player->getPlayerId(); | |
| BoardPosition pos1 = move.moves[0]; | |
| if (!isValidPosition(pos1)) | |
| { | |
| return CheckersBoard::Validate_InvalidPosition; | |
| } | |
| if (m_board[pos1.row][pos1.column] != playerId) | |
| { | |
| return CheckersBoard::Validate_InvalidMove; | |
| } | |
| bool isJumpMove = false; | |
| for (uint i = 1; i < numPositions; ++i) | |
| { | |
| BoardPosition pos2 = move.moves[i]; | |
| if (!canMovePiece(player, pos1, pos2, isJumpMove)) | |
| { | |
| return CheckersBoard::Validate_InvalidMove; | |
| } | |
| pos1 = pos2; | |
| // Not allowed more than one move between positions if not jumping another piece | |
| if (!isJumpMove && i != numPositions - 1) | |
| { | |
| return CheckersBoard::Validate_InvalidJump; | |
| } | |
| } | |
| return CheckersBoard::Validate_Success; | |
| } | |
| bool CheckersBoard::canMovePiece(Player* player, BoardPosition& pos1, BoardPosition& pos2, bool& isJump) | |
| { | |
| if (!isValidPosition(pos1) || !isValidPosition(pos2)) | |
| { | |
| return false; | |
| } | |
| uint playerId = -1; | |
| if (isPositionOccupied(pos1, playerId) && player->getPlayerId() != playerId) | |
| { | |
| return false; | |
| } | |
| if (!isPositionFree(pos2)) | |
| { | |
| return false; | |
| } | |
| int rowMove = pos2.row - pos1.row; | |
| int columnMove = pos2.column - pos1.column; | |
| uint rowMoveDist = abs(rowMove); | |
| // If expecting a jump, ensure that that the move is 2 spaces | |
| if (isJump && rowMoveDist != 2) | |
| { | |
| return false; | |
| } | |
| if (rowMoveDist != abs(columnMove)) | |
| { | |
| return false; | |
| } | |
| // Validate direction for the player | |
| if ((rowMove) * player->getMovementDirection() < 0) | |
| { | |
| return false; | |
| } | |
| // Validate that a different player is in between for jumps | |
| if (rowMoveDist == 2) | |
| { | |
| BoardPosition intermediatePos; | |
| getIntermediatePosition(pos1, pos2, intermediatePos); | |
| if (!isPositionOccupied(intermediatePos, playerId) || playerId == player->getPlayerId()) | |
| { | |
| return false; | |
| } | |
| isJump = true; | |
| } | |
| return true; | |
| } | |
| bool CheckersBoard::getIntermediatePosition(BoardPosition& pos1, BoardPosition& pos2, BoardPosition& intermediatePos) | |
| { | |
| int rowMove = pos2.row - pos1.row; | |
| int columnMove = pos2.column - pos1.column; | |
| if (abs(rowMove) == 2 && abs(columnMove) == 2) | |
| { | |
| intermediatePos = { pos1.row + (rowMove / 2), pos1.column + (columnMove / 2) }; | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool CheckersBoard::isValidPosition(BoardPosition& position) | |
| { | |
| return position.row >= 0 && position.row < BOARD_SIZE && position.column >= 0 && position.column < BOARD_SIZE; | |
| } | |
| void CheckersBoard::setIdAtPos(int id, BoardPosition& position) | |
| { | |
| m_board[position.row][position.column] = id; | |
| } | |
| bool CheckersBoard::isPositionFree(BoardPosition& position) | |
| { | |
| return m_board[position.row][position.column] == EmptyBoardPosId; | |
| } | |
| bool CheckersBoard::isPositionOccupied(BoardPosition& position, uint& playerId) | |
| { | |
| int player = m_board[position.row][position.column]; | |
| if (player != EmptyBoardPosId) | |
| { | |
| playerId = player; | |
| return true; | |
| } | |
| return false; | |
| } | |
| uint CheckersBoard::getBoardPieces(Player* player, vector<BoardPosition>& pieces) | |
| { | |
| uint playerId = player->getPlayerId(); | |
| uint numPieces = 0; | |
| for (uint i = 0; i < BOARD_SIZE; ++i) | |
| { | |
| for (uint j = 0; j < BOARD_SIZE; ++j) | |
| { | |
| if (m_board[i][j] == playerId) | |
| { | |
| BoardPosition boardPiece = { i, j }; | |
| pieces.push_back(boardPiece); | |
| numPieces++; | |
| } | |
| } | |
| } | |
| return numPieces; | |
| } | |
| #pragma once | |
| #include "stdafx.h" | |
| #include "CheckersBoard.h" | |
| #include "Player\Player.h" | |
| class CheckersGame | |
| { | |
| public: | |
| ~CheckersGame(); | |
| static CheckersGame* instance() { return s_instance; } | |
| static void createInstance(unsigned int numHumanPlayers); | |
| static void destroyInstance(); | |
| void update(); | |
| bool isGameComplete(); | |
| int getWinningPlayer(); | |
| Player* getPlayer(uint playerId); | |
| protected: | |
| CheckersGame(uint numHumanPlayers); | |
| private: | |
| CheckersBoard * m_board; | |
| Player* m_players[MAX_NUM_PLAYERS]; | |
| uint m_turn = 0; | |
| uint m_playerTurn = 0; | |
| int m_winningPlayer = -1; | |
| static CheckersGame* s_instance; | |
| }; | |
| #include <stdlib.h> | |
| #include <time.h> | |
| #include "CheckersGame.h" | |
| #include "Player\AIPlayer.h" | |
| #include "Player\HumanPlayer.h" | |
| CheckersGame* CheckersGame::s_instance = nullptr; | |
| void CheckersGame::createInstance(uint numHumanPlayers) | |
| { | |
| if (s_instance == nullptr) | |
| { | |
| s_instance = new CheckersGame(numHumanPlayers); | |
| } | |
| else | |
| { | |
| //ERROR | |
| } | |
| } | |
| void CheckersGame::destroyInstance() | |
| { | |
| delete s_instance; | |
| s_instance = nullptr; | |
| } | |
| CheckersGame::CheckersGame(uint numHumanPlayers) | |
| { | |
| uint playerId = 0; | |
| for (; playerId < numHumanPlayers; ++playerId) | |
| { | |
| m_players[playerId] = new HumanPlayer(playerId); | |
| } | |
| for (; playerId < MAX_NUM_PLAYERS; ++playerId) | |
| { | |
| m_players[playerId] = new AIPlayer(playerId); | |
| } | |
| m_board = new CheckersBoard(); | |
| m_board->initialize(m_players); | |
| srand((unsigned)time(NULL)); | |
| } | |
| CheckersGame::~CheckersGame() | |
| { | |
| delete m_board; | |
| } | |
| void CheckersGame::update() | |
| { | |
| std::cout << "Turn: " << m_turn + 1 << ", Player " << m_players[m_playerTurn]->getPlayerSymbol() << " with " << m_players[m_playerTurn]->getNumRemainingPieces() << " pieces left " << endl; | |
| m_board->drawBoard(); | |
| bool hasRemainingMoves = m_players[m_playerTurn]->playTurn(m_board); | |
| ++m_turn; | |
| m_playerTurn = m_turn % MAX_NUM_PLAYERS; | |
| if (!hasRemainingMoves) | |
| { | |
| m_winningPlayer = m_playerTurn; | |
| } | |
| // If other player has no pieces remaining, game is complete - Could wait for next update loop | |
| else if (m_players[m_playerTurn]->getNumRemainingPieces() == 0) | |
| { | |
| m_winningPlayer = (m_playerTurn + 1) % MAX_NUM_PLAYERS; | |
| } | |
| } | |
| Player* CheckersGame::getPlayer(uint playerId) | |
| { | |
| if (playerId >= 0 && playerId <= MAX_NUM_PLAYERS) | |
| { | |
| return m_players[playerId]; | |
| } | |
| // Invalid ID | |
| return nullptr; | |
| } | |
| bool CheckersGame::isGameComplete() | |
| { | |
| // No remaining moves | |
| return m_winningPlayer != -1; | |
| } | |
| int CheckersGame::getWinningPlayer() | |
| { | |
| return m_winningPlayer; | |
| } | |
| #pragma once | |
| #include <vector> | |
| #include "../stdafx.h" | |
| #include "../CheckersBoard.h" | |
| using namespace std; | |
| class Player | |
| { | |
| public: | |
| Player(uint playerId); | |
| virtual ~Player() {} | |
| uint getPlayerId() { return m_playerId; } | |
| virtual char getPlayerSymbol(); | |
| bool playTurn(CheckersBoard* board); | |
| virtual bool getNextMove(CheckersBoard* board, PlayerMove& nextMove) = 0; | |
| void setNumPieces(uint numPieces) { m_numPieces = numPieces; } | |
| uint getNumRemainingPieces() { return m_numPieces; } | |
| enum BoardMovementDirection | |
| { | |
| BoardMoveDir_Down = 1, | |
| BoardMoveDir_Up = -1, | |
| }; | |
| BoardMovementDirection getMovementDirection() { return m_boardDirection; } | |
| protected: | |
| void calculateValidMoves(CheckersBoard* board, vector<PlayerMove>& validMoves); | |
| void buildJumpMoveSet(CheckersBoard* board, BoardPosition& position, vector<PlayerMove>& validMoves, PlayerMove& move); | |
| uint m_playerId; | |
| uint m_numPieces = 0; | |
| BoardMovementDirection m_boardDirection; | |
| }; | |
| #include "../stdafx.h" | |
| #include "Player.h" | |
| Player::Player(uint playerId) | |
| : m_playerId(playerId) | |
| { | |
| if ((m_playerId % 2) == 0) | |
| { | |
| m_boardDirection = BoardMoveDir_Down; | |
| } | |
| else | |
| { | |
| m_boardDirection = BoardMoveDir_Up; | |
| } | |
| } | |
| char Player::getPlayerSymbol() | |
| { | |
| if ((m_playerId % 2) == 0) | |
| { | |
| return 'X'; | |
| } | |
| else | |
| { | |
| return 'O'; | |
| } | |
| } | |
| bool Player::playTurn(CheckersBoard* board) | |
| { | |
| PlayerMove nextMove; | |
| if (getNextMove(board, nextMove)) | |
| { | |
| board->applyMove(this, nextMove); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void Player::calculateValidMoves(CheckersBoard* board, vector<PlayerMove>& validMoves) | |
| { | |
| vector<BoardPosition> pieces; | |
| board->getBoardPieces(this, pieces); | |
| int rowDir = getMovementDirection(); | |
| for (auto piece : pieces) | |
| { | |
| // Possible moves | |
| bool isJump = false; | |
| // Try single move | |
| BoardPosition possibleMove = { piece.row + rowDir, piece.column + 1 }; | |
| if (board->canMovePiece(this, piece, possibleMove, isJump)) | |
| { | |
| PlayerMove move; | |
| move.moves.push_back(piece); | |
| move.moves.push_back(possibleMove); | |
| validMoves.push_back(move); | |
| } | |
| possibleMove = { piece.row + rowDir, piece.column - 1 }; | |
| if (board->canMovePiece(this, piece, possibleMove, isJump)) | |
| { | |
| PlayerMove move; | |
| move.moves.push_back(piece); | |
| move.moves.push_back(possibleMove); | |
| validMoves.push_back(move); | |
| } | |
| // Test Jump | |
| PlayerMove jumpMove; | |
| jumpMove.isJump = true; | |
| jumpMove.moves.push_back(piece); | |
| buildJumpMoveSet(board, piece, validMoves, jumpMove); | |
| } | |
| } | |
| void Player::buildJumpMoveSet(CheckersBoard* board, BoardPosition& position, vector<PlayerMove>& validMoves, PlayerMove& move) | |
| { | |
| uint jumpRowPos = position.row + getMovementDirection() * 2; | |
| bool isJump = true; | |
| BoardPosition jumpPos = { jumpRowPos, position.column - 2 }; | |
| if (board->canMovePiece(this, position, jumpPos, isJump)) | |
| { | |
| // Copy move | |
| PlayerMove newMove = move; | |
| newMove.moves.push_back(jumpPos); | |
| validMoves.push_back(newMove); | |
| buildJumpMoveSet(board, jumpPos, validMoves, newMove); | |
| } | |
| jumpPos = { jumpRowPos, position.column + 2 }; | |
| if (board->canMovePiece(this, position, jumpPos, isJump)) | |
| { | |
| // Copy move | |
| PlayerMove newMove = move; | |
| newMove.moves.push_back(jumpPos); | |
| validMoves.push_back(newMove); | |
| buildJumpMoveSet(board, jumpPos, validMoves, newMove); | |
| } | |
| } | |
| #pragma once | |
| #include "../stdafx.h" | |
| #include "Player.h" | |
| #include <string> | |
| class HumanPlayer : public Player | |
| { | |
| public: | |
| HumanPlayer(uint playerId); | |
| protected: | |
| virtual bool getNextMove(CheckersBoard* board, PlayerMove& nextMove) override; | |
| private: | |
| void readMove(CheckersBoard* board, PlayerMove& move); | |
| void convertString(std::string stringPos, BoardPosition& pos); | |
| bool m_showHint = true; | |
| }; | |
| #include "../stdafx.h" | |
| #include "HumanPlayer.h" | |
| #include <iostream> | |
| #include <sstream> | |
| #include <algorithm> | |
| #include <iterator> | |
| #include <locale> | |
| #include <cctype> | |
| HumanPlayer::HumanPlayer(uint playerId) | |
| : Player(playerId) | |
| { | |
| } | |
| bool HumanPlayer::getNextMove(CheckersBoard* board, PlayerMove& nextMove) | |
| { | |
| if (m_showHint) | |
| { | |
| std::cout << "Checker moves can done by entering in a series of positions separated by commas. (e.g. A1,B2) Additional positions can be entered for series of jumps (e.g. A1,C3,E5)" << endl; | |
| std::cout << "To see this again, type help" << endl; | |
| m_showHint = false; | |
| } | |
| bool hasJumpMovesAvailable = false; | |
| vector<PlayerMove> validMoves; | |
| calculateValidMoves(board, validMoves); | |
| for (auto validMove : validMoves) | |
| { | |
| if (validMove.isJump) | |
| { | |
| hasJumpMovesAvailable = true; | |
| break; | |
| } | |
| } | |
| do | |
| { | |
| readMove(board, nextMove); | |
| CheckersBoard::ValidateMove_Reason validateResult = board->validateMove(this, nextMove); | |
| bool jumpUsed = nextMove.isJump == hasJumpMovesAvailable; | |
| if (validateResult == CheckersBoard::Validate_Success && jumpUsed) | |
| { | |
| break; | |
| } | |
| else | |
| { | |
| switch (validateResult) | |
| { | |
| case CheckersBoard::Validate_Invalid: | |
| std::cout << "Invalid data passed in" << endl; | |
| break; | |
| case CheckersBoard::Validate_InvalidPosition: | |
| std::cout << "Invalid board position" << endl; | |
| break; | |
| case CheckersBoard::Validate_InvalidMove: | |
| std::cout << "Move is invalid or blocked" << endl; | |
| break; | |
| case CheckersBoard::Validate_InvalidJump: | |
| std::cout << "Invalid jump specified" << endl; | |
| break; | |
| default: | |
| if (!jumpUsed) | |
| { | |
| std::cout << "Invalid move. Jump move available but not taken" << endl; | |
| } | |
| else | |
| { | |
| std::cout << "Invalid move" << endl; | |
| } | |
| break; | |
| } | |
| nextMove.moves.clear(); | |
| } | |
| } while (true); | |
| return true; | |
| } | |
| void HumanPlayer::readMove(CheckersBoard* board, PlayerMove& move) | |
| { | |
| // TODO: Handle non-ascii | |
| std::cout << "> Move: "; | |
| std::string playerInput; | |
| std::getline(std::cin, playerInput); | |
| std::size_t pos = playerInput.find("help"); | |
| if (pos != -1) | |
| { | |
| std::cout << "Checker moves can done by entering in a series of positions separated by commas. (e.g. A1,B2) Additional positions can be entered for series of jumps (e.g. A1,C3,E5)" << endl; | |
| } | |
| else | |
| { | |
| std::stringstream ss(playerInput); | |
| std::string token; | |
| while (std::getline(ss, token, ',')) | |
| { | |
| BoardPosition pos; | |
| convertString(token, pos); | |
| if (board->isValidPosition(pos)) | |
| { | |
| move.moves.push_back(pos); | |
| if (move.moves.size() > 1) | |
| { | |
| int rowMove = move.moves[1].row - move.moves[0].row; | |
| move.isJump = (abs(rowMove) == 2); | |
| } | |
| } | |
| else | |
| { | |
| std::cout << "Invalid position : " << token << endl; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| string trim(const string& str) | |
| { | |
| size_t first = str.find_first_not_of(' '); | |
| if (string::npos == first) | |
| { | |
| return str; | |
| } | |
| size_t last = str.find_last_not_of(' '); | |
| return str.substr(first, (last - first + 1)); | |
| } | |
| void HumanPlayer::convertString(std::string stringPos, BoardPosition& pos) | |
| { | |
| string trimmedString = trim(stringPos); | |
| // Convert string to upper case | |
| std::transform(trimmedString.begin(), trimmedString.end(), trimmedString.begin(), | |
| [](unsigned char c) { return std::toupper(c); }); | |
| // Only handling a-z | |
| char rowChar = trimmedString.c_str()[0]; | |
| pos.row = rowChar - 'A'; | |
| string columnString = trimmedString.substr(1); | |
| pos.column = uint(stoi(columnString)) - 1; | |
| } | |
| #pragma once | |
| #include "../stdafx.h" | |
| #include "Player.h" | |
| class AIPlayer : public Player | |
| { | |
| public: | |
| AIPlayer(uint playerId); | |
| virtual bool getNextMove(CheckersBoard* board, PlayerMove& nextMove) override; | |
| private: | |
| void chooseMove(CheckersBoard* board, vector<PlayerMove>& validMoves, PlayerMove& move); | |
| }; | |
| #include "AIPlayer.h" | |
| #include <algorithm> | |
| AIPlayer::AIPlayer(uint playerId) | |
| : Player(playerId) | |
| { | |
| } | |
| bool AIPlayer::getNextMove(CheckersBoard* board, PlayerMove& nextMove) | |
| { | |
| vector<PlayerMove> validMoves; | |
| calculateValidMoves(board, validMoves); | |
| if (validMoves.size() > 0) | |
| { | |
| chooseMove(board, validMoves, nextMove); | |
| // Print out move | |
| std::cout << "AI Move: "; | |
| for (auto& position : nextMove.moves) | |
| { | |
| std::cout << position << ","; | |
| } | |
| std::cout << endl; | |
| return true; | |
| } | |
| return false; | |
| } | |
| void AIPlayer::chooseMove(CheckersBoard* board, vector<PlayerMove>& validMoves, PlayerMove& move) | |
| { | |
| // TODO: AI for evaluating state of the board to pick a good choice | |
| vector<PlayerMove> jumpMoves; | |
| for (auto validMove : validMoves) | |
| { | |
| if (validMove.isJump) | |
| jumpMoves.push_back(validMove); | |
| } | |
| if (jumpMoves.size() > 0) | |
| { | |
| auto moveSizeSort = [](const PlayerMove& move1, const PlayerMove& move2) -> bool | |
| { | |
| return move1.moves.size() > move2.moves.size(); | |
| }; | |
| sort(jumpMoves.begin(), jumpMoves.end(), moveSizeSort); | |
| move = jumpMoves[0]; | |
| } | |
| else | |
| { | |
| move = validMoves[rand() % validMoves.size()]; | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment