From 7a565686909b0223c3d7aee297cb5d5d88b32dde Mon Sep 17 00:00:00 2001 From: Mattia Di Gangi Date: Sun, 14 Oct 2018 19:30:29 +0200 Subject: [PATCH] Added board representation for ConnectFour. Called ConnectX because it should be extended to other game formats. --- ConnectX/backend/ConnectXBoard.cpp | 112 +++++++++++++++++++++++++++++ ConnectX/backend/ConnectXBoard.h | 50 +++++++++++++ ConnectX/backend/main.cpp | 11 +++ 3 files changed, 173 insertions(+) create mode 100644 ConnectX/backend/ConnectXBoard.cpp create mode 100644 ConnectX/backend/ConnectXBoard.h create mode 100644 ConnectX/backend/main.cpp diff --git a/ConnectX/backend/ConnectXBoard.cpp b/ConnectX/backend/ConnectXBoard.cpp new file mode 100644 index 0000000..3a14469 --- /dev/null +++ b/ConnectX/backend/ConnectXBoard.cpp @@ -0,0 +1,112 @@ +#include +#include "ConnectXBoard.h" + +int ConnectXBoard::numOccupied() const { + auto ctr = 0; + for (auto c : d) { + if (c != '.') + ++ctr; + } + return ctr; +} + +ConnectXBoard ConnectXBoard::makeMove(ConnectxMove move) { + if (isLegal(move)){ + auto newDescr(d); + auto changedPos = findFirstFreePositionInCol(move.col); + newDescr[changedPos] = move.symbol; + return ConnectXBoard(newDescr); + } else { + throw std::invalid_argument("Received a bad move!"); + } +} + +int ConnectXBoard::findFirstFreePositionInCol(int c) const { + auto lastPositionInCol = c + (BOARD_SIZE -1) * BOARD_SIZE; + while (lastPositionInCol >= 0){ + if (d[lastPositionInCol] == '.') + return lastPositionInCol; + else + lastPositionInCol -= BOARD_SIZE; + } + throw std::invalid_argument("Column is not free!"); +} + +bool ConnectXBoard::isLegal(ConnectxMove move) const { + if (move.col < 0 || move.col >= BOARD_SIZE) + return false; + if (d[move.col] != '.') + return false; + return true; +} + +int ConnectXBoard::checkRow(int row) const { + auto start = row * BOARD_SIZE; + return checkLine(start, 1, start + BOARD_SIZE); +} + +int ConnectXBoard::checkCol(int col) const { + return checkLine(col, BOARD_SIZE, BOARD_SIZE*BOARD_SIZE); +} + +int ConnectXBoard::checkDiagonals() const { + // Diagonal 1 + auto diag1 = checkLine(0, BOARD_SIZE + 1, BOARD_SIZE * BOARD_SIZE); + if (diag1 == 0){ + return checkLine(BOARD_SIZE - 1, BOARD_SIZE - 1, BOARD_SIZE * BOARD_SIZE); + } else { + return diag1; + } +} + +int ConnectXBoard::checkLine(int start, int increment, int topend) const { + if (d[start] == '.'){ + return 0; //Not winning line + } + auto ctr = start + increment; + while (ctr < topend){ + if (d[start] != d[ctr]) + return 0; + ctr += increment; + } + if (d[start] == symbols[0]){ + return 1; + } else { + return -1; + } +} + +std::ostream & operator<<(std::ostream & os, const ConnectXBoard & board) { + for (auto i = 0; i < BOARD_SIZE*BOARD_SIZE; ++i){ + os << board.getDescriptor()[i]; + if (i % BOARD_SIZE == BOARD_SIZE - 1) + os << std::endl; + else + os << " "; + } +} + +GameState ConnectXBoard::getGameState() const { + for (auto i = 0; i < BOARD_SIZE; ++i) { + auto score = checkRow(i); + if ( score != 0 ) + return scoreToGameState(score); + } + for (auto i = 0; i < BOARD_SIZE; ++i){ + auto score = checkCol(i); + if ( score != 0 ) + return scoreToGameState(score); + } + return scoreToGameState(checkDiagonals()); +} + +inline +GameState ConnectXBoard::scoreToGameState(int score) const { + if (score == 1) + return Win1; + if (score == -1) + return Win2; + if (numOccupied() < BOARD_SIZE*BOARD_SIZE) + return Continue; + return Draw; +} \ No newline at end of file diff --git a/ConnectX/backend/ConnectXBoard.h b/ConnectX/backend/ConnectXBoard.h new file mode 100644 index 0000000..d0a9f55 --- /dev/null +++ b/ConnectX/backend/ConnectXBoard.h @@ -0,0 +1,50 @@ +#ifndef GAMES_CONNECTXBOARD_H +#define GAMES_CONNECTXBOARD_H + +#include +#include + +/* + * Represents a board in a given position + */ +typedef enum { + Continue, Win1, Win2, Draw +} GameState; + +const int BOARD_SIZE = 4; + +typedef std::string descriptor; +typedef struct { + int col; + char symbol; +} ConnectxMove; + +class ConnectXBoard { +public: + ConnectXBoard() :d(descriptor (16, '.')) {} + ConnectXBoard(const descriptor & descr) :d(descr) {} + ConnectXBoard(const ConnectXBoard& otherBoard) = default; + ConnectXBoard(ConnectXBoard&& otherBoard) = default; + int numOccupied() const; + GameState getGameState() const; + + ConnectXBoard makeMove(ConnectxMove col); + const descriptor & getDescriptor() const { return d;} + bool isLegal(ConnectxMove col) const; + //const char& operator[] (int i) const { return d[i];} + //const char& operator[] (int i) { return d[i];} + + static constexpr char symbols[2] = {'R', 'U'}; +private: + const descriptor d; + int checkRow(int row) const ; + int checkCol(int col) const ; + int checkDiagonals() const ; + int checkLine(int start, int increment, int topend) const; + int findFirstFreePositionInCol(int c) const; + GameState scoreToGameState(int) const; +}; + +std::ostream& operator<< (std::ostream&, const ConnectXBoard&); + +#endif //GAMES_CONNECTXBOARD_H diff --git a/ConnectX/backend/main.cpp b/ConnectX/backend/main.cpp new file mode 100644 index 0000000..e5051ff --- /dev/null +++ b/ConnectX/backend/main.cpp @@ -0,0 +1,11 @@ +#include +#include "ConnectXBoard.h" + +int main () { + ConnectXBoard board; + std::cout << board.getDescriptor() << std::endl; + std::cout << board; + ConnectXBoard board1("R...RU..RU..RUU."); + std::cout << board1; + std::cout << board1.getGameState() << std::endl; +} \ No newline at end of file