Added missing classes from final year at OSU

This commit is contained in:
2019-06-17 14:04:15 -07:00
parent 8fa1ffb1b0
commit c717a0316f
166 changed files with 653934 additions and 308 deletions

View File

@@ -0,0 +1,106 @@
#include <iostream>
#include <assert.h>
#include "Board.h"
void Board::delete_grid() {
for (int c = 0; c < num_cols; c++) {
delete[] grid[c];
}
delete[] grid;
}
Board::Board(int cols, int rows) :
num_cols(cols), num_rows(rows) {
grid = new char*[num_cols];
for (int c = 0; c < num_cols; c++) {
grid[c] = new char[num_rows];
for (int r = 0; r < num_rows; r++) {
grid[c][r] = EMPTY;
}
}
}
Board::Board(const Board& other) {
num_cols = other.num_cols;
num_rows = other.num_rows;
grid = new char*[num_cols];
for (int c = 0; c < num_cols; c++) {
grid[c] = new char[num_rows];
for (int r = 0; r < num_rows; r++) {
grid[c][r] = other.grid[c][r];
}
}
}
Board& Board::operator=(const Board& rhs) {
if (this == &rhs) {
return *this;
} else {
num_cols = rhs.num_cols;
num_rows = rhs.num_rows;
if (grid != NULL) {
delete_grid();
}
grid = new char*[num_cols];
for (int c = 0; c < num_cols; c++) {
grid[c] = new char[num_rows];
for (int r = 0; r < num_rows; r++) {
grid[c][r] = rhs.grid[c][r];
}
}
return *this;
}
}
Board::~Board() {
delete_grid();
}
char Board::get_cell(int col, int row) const {
assert((col >= 0) && (col < num_cols));
assert((row >= 0) && (row < num_rows));
return grid[col][row];
}
void Board::set_cell(int col, int row, char val) {
assert((col >= 0) && (col < num_cols));
assert((row >= 0) && (row < num_rows));
grid[col][row] = val;
}
bool Board::is_cell_empty(int col, int row) const {
if (grid[col][row] == EMPTY) {
return true;
} else {
return false;
}
}
bool Board::is_in_bounds(int col, int row) const {
if ((col >= 0) && (col < num_cols) && (row >= 0) && (row < num_rows)) {
return true;
} else {
return false;
}
}
void Board::display() const {
for (int r = get_num_rows() - 1; r >= 0; r--) {
std::cout << r << ":| ";
for (int c = 0; c < get_num_cols(); c++) {
std::cout << get_cell(c, r) << " ";
}
std::cout << std::endl;
}
std::cout << " -";
for (int c = 0; c < get_num_cols(); c++) {
std::cout << "--";
}
std::cout << "\n";
std::cout << " ";
for (int c = 0; c < get_num_cols(); c++) {
std::cout << c << " ";
}
std::cout << "\n\n";
}

View File

@@ -0,0 +1,112 @@
/**
* Board class
*/
#ifndef BOARD_H
#define BOARD_H
#define EMPTY '.'
/** Directions enum for the Othello Board */
enum Direction {N,NE,E,SE,S,SW,W,NW};
/**
* This is a generic board class that serves as a wrapper for a 2D array.
* It will be used for board games like Othello, Tic-Tac-Toe and Connect 4.
*/
class Board {
public:
/**
* @param cols The number of columns in the board
* @param rows The number of rows in the board
* Constructor that creates a 2D board with the given number of columns
* and rows
*/
Board(int cols, int rows);
/**
* @param other A reference to the Board object being copied
* This is the copy constructor for the Board class
*/
Board(const Board& other);
/**
* Destructor for the Board class
*/
~Board();
/**
* @param rhs The "right-hand side" for the assignment ie. the object
* you are copying from.
* @return Returns a reference to the "left-hand side" of the assignment ie.
* the object the values are assigned to
* Overloaded assignment operator for the Board class
*/
Board& operator=(const Board& rhs);
/**
* @return Returns the number of rows in the board
* An accessor that gets the number of rows in the board
*/
int get_num_rows() const { return num_rows; }
/**
* @return Returns the number of columns in the board
* An accessor to get the number of columns in the board
*/
int get_num_cols() const { return num_cols; }
/**
* @param col The column of the cell you want to retrieve
* @param row The row of the cell you want to retrieve
* @return Returns the character at the specified cell
* Returns the character at the specified column and row
*/
char get_cell(int col, int row) const;
/**
* @param col The column of the cell you want to set
* @param row The row of the cell you want to set
* @param val The value you want to set the cell to
* Sets the cell at the given row and column to the specified value
*/
void set_cell(int col, int row, char val);
/**
* @param col The column of the cell you are checking
* @param row The row of the cell you are checking
* @return true if the cell is empty and false otherwise
*/
bool is_cell_empty(int col, int row) const;
/**
* @param col The column value for the in-bounds check
* @param row The row value for the in-bounds check
* @return true if the column is >= 0 and < num_cols and if the row is >= 0 and < num_rows. Returns false otherwise.
*/
bool is_in_bounds(int col, int row) const;
/**
* Prints the board to screen. Should probably have overloaded >> but oh well.
*/
void display() const;
protected:
/** The number of rows in the board */
int num_rows;
/** The number of columns in the board */
int num_cols;
/** A 2D array of chars representing the board */
char** grid;
/**
* Deletes the 2D array.
*/
void delete_grid();
};
#endif

View File

@@ -0,0 +1,137 @@
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include "GameDriver.h"
GameDriver::GameDriver(char* p1type, char* p2type, int num_rows, int num_cols) {
if( strcmp(p1type,"human") == 0 ) {
p1 = new HumanPlayer('X');
} else if( strcmp(p1type,"minimax") == 0 ) {
p1 = new MinimaxPlayer('X');
} else {
std::cout << "Invalid type of player for player 1" << std::endl;
}
if( strcmp(p2type,"human") == 0 ) {
p2 = new HumanPlayer('O');
} else if( strcmp(p2type,"minimax") == 0 ) {
p2 = new MinimaxPlayer('O');
} else {
std::cout << "Invalid type of player for player 2" << std::endl;
}
board = new OthelloBoard(num_rows, num_cols,p1->get_symbol(), p2->get_symbol());
board->initialize();
}
GameDriver::GameDriver(const GameDriver& other) {
board = new OthelloBoard(*(other.board));
p1 = other.p1->clone();
p2 = other.p2->clone();
}
GameDriver& GameDriver::operator=(const GameDriver& rhs) {
if (this == &rhs) {
return *this;
} else {
if( board != NULL ) {
delete board;
}
board = new OthelloBoard(*(rhs.board));
if( p1 != NULL ) {
delete p1;
p1 = rhs.p1->clone();
}
if( p2 != NULL ) {
delete p2;
p2 = rhs.p2->clone();
}
return *this;
}
}
GameDriver::~GameDriver() {
delete board;
delete p1;
delete p2;
}
void GameDriver::display() {
std::cout << std::endl << "Player 1 (" << p1->get_symbol() << ") score: "
<< board->count_score(p1->get_symbol()) << std::endl;
std::cout << "Player 2 (" << p2->get_symbol() << ") score: "
<< board->count_score(p2->get_symbol()) << std::endl << std::endl;
board->display();
}
void GameDriver::process_move(Player* curr_player, Player* opponent) {
int col = -1;
int row = -1;
bool invalid_move = true;
while (invalid_move) {
curr_player->get_move(board, col, row);
if (!board->is_legal_move(col, row, curr_player->get_symbol())) {
std::cout << "Invalid move.\n";
continue;
} else {
std::cout << "Selected move: col = " << col << ", row = " << row << std::endl;
board->play_move(col,row,curr_player->get_symbol());
invalid_move = false;
}
}
}
void GameDriver::run() {
int toggle = 0;
int cant_move_counter=0;
Player* current = p1;
Player* opponent = p2;
display();
std::cout << "Player 1 (" << p1->get_symbol() << ") move:\n";
while (1) {
if( board->has_legal_moves_remaining(current->get_symbol())) {
cant_move_counter = 0;
process_move(current, opponent);
display();
} else {
std::cout << "Can't move\n";
if( cant_move_counter == 1 ) {
// Both players can't move, game over
break;
} else {
cant_move_counter++;
}
}
toggle = (toggle + 1) % 2;
if (toggle == 0) {
current = p1;
opponent = p2;
std::cout << "Player 1 (" << p1->get_symbol() << ") move:\n";
} else {
current = p2;
opponent = p1;
std::cout << "Player 2 (" << p2->get_symbol() << ") move:\n";
}
}
if ( board->count_score(p1->get_symbol()) == board->count_score(p2->get_symbol())) {
std::cout << "Tie game" << std::endl;
} else if ( board->count_score(p1->get_symbol()) > board->count_score(p2->get_symbol())) {
std::cout << "Player 1 wins" << std::endl;
} else {
std::cout << "Player 2 wins" << std::endl;
}
}
int main(int argc, char** argv) {
if( argc != 3 ) {
std::cout << "Usage: othello <player1 type> <player2 type>" << std::endl;
exit(-1);
}
GameDriver* game = new GameDriver(argv[1],argv[2],4,4);
game->run();
return 0;
}

View File

@@ -0,0 +1,72 @@
#ifndef GAMEDRIVER_H
#define GAMEDRIVER_H
#include "OthelloBoard.h"
#include "Player.h"
#include "HumanPlayer.h"
#include "MinimaxPlayer.h"
/**
* This class represents the main driver for the game. The driver controls the turn-based behavior
* of the game.
*/
class GameDriver {
public:
/**
* @param p1type A string (human or minimax) describing the type of Player1
* @param p2type A string (human or minimax) describing the type of Player2
* @param num_rows The number of rows in Othello
* @param num_cols The number of columns in Othello
* This is the constructor for the GameDriver
*/
GameDriver(char* p1type, char* p2type, int num_rows, int num_cols);
/**
* @param other The GameDriver object you are copying from
* Copy constructor for the GameDriver class
*/
GameDriver(const GameDriver& other);
/**
* @param rhs The right-hand side of the assignment
* @return The left-hand side of the assignment
* Overloaded assignment operator for the GameDriver class.
*/
GameDriver& operator=(const GameDriver& rhs);
/**
* Destructor for the GameDriver class
*/
virtual ~GameDriver();
/**
* Runs the game and keeps track of the turns.
*/
void run();
/**
* Displays the game.
*/
void display();
private:
/** Internal Othello board object */
OthelloBoard* board;
/** Player 1 object */
Player* p1;
/** Player 2 object */
Player* p2;
/**
* @param curr_player A pointer to the player that has the current move
* @param opponent A pointer to the opponent for the player that has the current move
* Handles actually making a move in the game.
*/
void process_move(Player* curr_player, Player* opponent);
};
#endif

View File

@@ -0,0 +1,22 @@
#include <iostream>
#include "HumanPlayer.h"
HumanPlayer::HumanPlayer(char symb) : Player(symb) {
}
HumanPlayer::~HumanPlayer() {
}
void HumanPlayer::get_move(OthelloBoard* b, int& col, int& row) {
std::cout << "Enter col: ";
std::cin >> col;
std::cout << "Enter row: ";
std::cin >> row;
}
HumanPlayer* HumanPlayer::clone() {
HumanPlayer *result = new HumanPlayer(symbol);
return result;
}

View File

@@ -0,0 +1,41 @@
#ifndef HUMAN_PLAYER
#define HUMAN_PLAYER
#include "Player.h"
#include "OthelloBoard.h"
/**
* This class represents a human player
*/
class HumanPlayer : public Player {
public:
/**
* @symb The symbol used for the human player's pieces
* The constructor for the HumanPlayer class
*/
HumanPlayer(char symb);
/**
* Destructor
*/
virtual ~HumanPlayer();
/**
* @param b The current board for the game.
* @param col Holds the return value for the column of the move
* @param row Holds the return value for the row of the move
* Obtains the (col,row) coordinates for the current move
*/
void get_move(OthelloBoard* b, int& col, int& row);
/**
* @return A pointer to a copy of the HumanPlayer object
* This is a virtual copy constructor
*/
HumanPlayer *clone();
private:
};
#endif

View File

@@ -0,0 +1,14 @@
CXX = g++
CXXFLAGS = -std=c++0x
SRCS = Board.cpp OthelloBoard.cpp Player.cpp HumanPlayer.cpp GameDriver.cpp MinimaxPlayer.cpp
HEADERS = Board.h OthelloBoard.h Player.h HumanPlayer.h GameDriver.h MinimaxPlayer.h
OBJS = Board.o OthelloBoard.o Player.o HumanPlayer.o GameDriver.o MinimaxPlayer.o
all: ${SRCS} ${HEADERS}
${CXX} ${CXXFLAGS} ${SRCS} -o othello
${OBJS}: ${SRCS}
${CXX} -c $(@:.o=.cpp)
clean:
rm -f *.o othello

View File

@@ -0,0 +1,29 @@
/*
* MinimaxPlayer.cpp
*
* Created on: Apr 17, 2015
* Author: wong
*/
#include <iostream>
#include <assert.h>
#include "MinimaxPlayer.h"
using std::vector;
MinimaxPlayer::MinimaxPlayer(char symb) :
Player(symb) {
}
MinimaxPlayer::~MinimaxPlayer() {
}
void MinimaxPlayer::get_move(OthelloBoard* b, int& col, int& row) {
// To be filled in by you
}
MinimaxPlayer* MinimaxPlayer::clone() {
MinimaxPlayer* result = new MinimaxPlayer(symbol);
return result;
}

View File

@@ -0,0 +1,50 @@
/*
* MinimaxPlayer.h
*
* Created on: Apr 17, 2015
* Author: wong
*/
#ifndef MINIMAXPLAYER_H
#define MINIMAXPLAYER_H
#include "OthelloBoard.h"
#include "Player.h"
#include <vector>
/**
* This class represents an AI player that uses the Minimax algorithm to play the game
* intelligently.
*/
class MinimaxPlayer : public Player {
public:
/**
* @param symb This is the symbol for the minimax player's pieces
*/
MinimaxPlayer(char symb);
/**
* Destructor
*/
virtual ~MinimaxPlayer();
/**
* @param b The board object for the current state of the board
* @param col Holds the return value for the column of the move
* @param row Holds the return value for the row of the move
*/
void get_move(OthelloBoard* b, int& col, int& row);
/**
* @return A copy of the MinimaxPlayer object
* This is a virtual copy constructor
*/
MinimaxPlayer* clone();
private:
};
#endif

View File

@@ -0,0 +1,179 @@
/*
* OthelloBoard.cpp
*
* Created on: Apr 18, 2015
* Author: wong
*/
#include <assert.h>
#include "OthelloBoard.h"
OthelloBoard::OthelloBoard(int cols, int rows, char p1_symb, char p2_symb) :
Board(cols, rows), p1_symbol(p1_symb), p2_symbol(p2_symb) {
}
OthelloBoard::OthelloBoard(const OthelloBoard& other) :
Board(other), p1_symbol(other.p1_symbol), p2_symbol(other.p2_symbol) {
}
OthelloBoard::~OthelloBoard() {
}
void OthelloBoard::initialize() {
set_cell(num_cols / 2 - 1, num_rows / 2 - 1, p1_symbol);
set_cell(num_cols / 2, num_rows / 2, p1_symbol);
set_cell(num_cols / 2 - 1, num_rows / 2, p2_symbol);
set_cell(num_cols / 2, num_rows / 2 - 1, p2_symbol);
}
OthelloBoard& OthelloBoard::operator=(const OthelloBoard& rhs) {
Board::operator=(rhs);
p1_symbol = rhs.p1_symbol;
p2_symbol = rhs.p2_symbol;
return *this;
}
void OthelloBoard::set_coords_in_direction(int col, int row, int& next_col,
int& next_row, int dir) const {
switch (dir) {
case N:
next_col = col;
next_row = row + 1;
break;
case NE:
next_col = col + 1;
next_row = row + 1;
break;
case E:
next_col = col + 1;
next_row = row;
break;
case SE:
next_col = col + 1;
next_row = row - 1;
break;
case S:
next_col = col;
next_row = row - 1;
break;
case SW:
next_col = col - 1;
next_row = row - 1;
break;
case W:
next_col = col - 1;
next_row = row;
break;
case NW:
next_col = col - 1;
next_row = row + 1;
break;
default:
assert("Invalid direction");
}
}
bool OthelloBoard::check_endpoint(int col, int row, char symbol, int dir,
bool match_symbol) const {
int next_row = -1;
int next_col = -1;
if (!is_in_bounds(col, row) || is_cell_empty(col, row)) {
return false;
} else {
if (match_symbol) {
if (get_cell(col, row) == symbol) {
return true;
} else {
set_coords_in_direction(col, row, next_col, next_row, dir);
return check_endpoint(next_col, next_row, symbol, dir,
match_symbol);
}
} else {
if (get_cell(col, row) == symbol) {
return false;
} else {
set_coords_in_direction(col, row, next_col, next_row, dir);
return check_endpoint(next_col, next_row, symbol, dir,
!match_symbol);
}
}
}
}
bool OthelloBoard::is_legal_move(int col, int row, char symbol) const {
bool result = false;
int next_row = -1;
int next_col = -1;
if (!is_in_bounds(col, row) || !is_cell_empty(col, row)) {
return result;
}
for (int d = N; d <= NW; d++) {
set_coords_in_direction(col, row, next_col, next_row, d);
if (check_endpoint(next_col, next_row, symbol, d, false)) {
result = true;
break;
}
}
return result;
}
int OthelloBoard::flip_pieces_helper(int col, int row, char symbol, int dir) {
int next_row = -1;
int next_col = -1;
if (get_cell(col, row) == symbol) {
return 0;
} else {
set_cell(col, row, symbol);
set_coords_in_direction(col, row, next_col, next_row, dir);
return 1 + flip_pieces_helper(next_col, next_row, symbol, dir);
}
}
int OthelloBoard::flip_pieces(int col, int row, char symbol) {
int pieces_flipped = 0;
int next_row = -1;
int next_col = -1;
assert(is_in_bounds(col, row));
for (int d = N; d <= NW; d++) {
set_coords_in_direction(col, row, next_col, next_row, d);
if (check_endpoint(next_col, next_row, symbol, d, false)) {
pieces_flipped += flip_pieces_helper(next_col, next_row, symbol, d);
}
}
return pieces_flipped;
}
bool OthelloBoard::has_legal_moves_remaining(char symbol) const {
for (int c = 0; c < num_cols; c++) {
for (int r = 0; r < num_rows; r++) {
if (is_cell_empty(c, r) && is_legal_move(c, r, symbol)) {
return true;
}
}
}
return false;
}
int OthelloBoard::count_score(char symbol) const {
int score = 0;
for (int c = 0; c < num_cols; c++) {
for (int r = 0; r < num_rows; r++) {
if (grid[c][r] == symbol) {
score++;
}
}
}
return score;
}
void OthelloBoard::play_move(int col, int row, char symbol) {
set_cell(col, row, symbol);
flip_pieces(col, row, symbol);
}

View File

@@ -0,0 +1,152 @@
/*
* OthelloBoard.h
*
* Created on: Apr 18, 2015
* Author: wong
*/
#ifndef OTHELLOBOARD_H_
#define OTHELLOBOARD_H_
#include "Board.h"
/**
* This class is a specialized version of the Board class for Othello. The OthelloBoard
* class respects the rules of Othello and also keeps track of the symbols for Player
* 1 and Player 2.
*/
class OthelloBoard : public Board {
public:
/**
* @cols The number of columns in the game of Othello
* @rows The number of rows in the game of Othello
* @p1_symbol The symbol used for Player 1's pieces on the board
* @p2_symbol The symbol used for Player 2's pieces on the board
* This is a constructor for an OthelloBoard clas.
*/
OthelloBoard(int cols, int rows, char p1_symbol, char p2_symbol);
/**
* @param other The OthelloBoard object you are copying from.
* This is the copy constructor for the OthelloBoard class.
*/
OthelloBoard(const OthelloBoard& other);
/**
* The destructor for the OthelloBoard class.
*/
virtual ~OthelloBoard();
/**
* Initializes the Othello board to the starting position of the pieces
* for Players 1 and 2
*/
void initialize();
/**
* @param rhs The right-hand side object of the assignment
* @return The left-hand side object of the assignment
* This is the overloaded assignment operator for the OthelloBoard class
*/
OthelloBoard& operator=(const OthelloBoard& rhs);
/**
* @param col The column for where your piece goes
* @param row The row for where your piece goes
* @return true if the move is legal, false otherwise.
* Checks the legality of a move that places a piece at the specified col and
* row.
*/
bool is_legal_move(int col, int row, char symbol) const;
/**
* @param symbol This is the symbol for the current player.
* @param col The column for where your piece goes
* @param row The row for where your piece goes
* Flips the in-between pieces once you put down a piece the specified
* col and row position. The symbol argument specifies who the
* current move belongs to.
*/
int flip_pieces(int col, int row, char symbol);
/**
* @param symbol This symbol specifies the symbol for the current player (i.e.
* who the current move belongs to)
* @return true if there are still moves remaining, false otherwise
* Checks if the game is over.
*/
bool has_legal_moves_remaining(char symbol) const;
/**
* @param symbol The symbol representing a particular player.
* Returns the score for the player with the specified symbol.
*/
int count_score(char symbol) const;
/**
* @param col The column where the piece goes
* @param row The row where the piece goes
* Plays the move by placing a piece, with the given symbol, down at the specified
* col and row. Then, any pieces sandwiched in between the two endpoints are flipped.
*/
void play_move(int col, int row, char symbol);
/**
* @return Returns the symbol for Player 1 (the maximizing player)'s pieces
* Returns the symbol for Player 1's pieces
*/
char get_p1_symbol() { return p1_symbol; }
/**
* @return Returns the symbol for Player 2 (the minimizing player)'s pieces
* Returns the symbol for Player 2's pieces
*/
char get_p2_symbol() { return p2_symbol; }
private:
/** The symbol for Player 1's pieces */
char p1_symbol;
/** The symbol for Player 2's pieces */
char p2_symbol;
/**
* @param col The column of the starting point
* @param row The row of the starting point
* @param next_col The return value for the column
* @param next_row The return value for the row
* @param dir The direction you want to move
* Sets the coordinates of next_col and next_row to be the coordinates if you were
* moving in the direction specified by the argument dir starting at position (col,row)
*/
void set_coords_in_direction(int col, int row, int& next_col, int& next_row, int dir) const;
/**
* @param col The column of the starting point
* @param row The row of the starting point
* @param symbol The symbol of the current player. You will match (or not match) this symbol
* at the endpoint
* @param dir The direction you are moving in
* @param match_symbol If true, it will return true if the arg symbol matches the endpoint. If false,
* it will return true if the arg symbol doesn't match the endpoint.
* If you start at (col,row) and move in direction dir, this function will check the endpoint
* of a trail of pieces. If match_symbol is true, it will return true if the endpoint matches
* the argument symbol (and false otherwise). If match_symbol is false, it will return true
* if the endpoint doesn't match the argument symbol (and false otherwise).
*/
bool check_endpoint(int col, int row, char symbol, int dir,
bool match_symbol) const;
/**
* @param col The column of the starting point
* @param row The row of the starting point
* @param symbol This is the symbol at the endpoint that terminates the row of pieces flipped
* @param dir The direction you are moving
* This is a helper function for the recursive flip_pieces function.
*/
int flip_pieces_helper(int col, int row, char symbol, int dir);
};
#endif /* OTHELLOBOARD_H_ */

View File

@@ -0,0 +1,9 @@
#include "Player.h"
Player::Player(char symb) : symbol(symb) {
}
Player::~Player() {
}

View File

@@ -0,0 +1,50 @@
/**
* Player class
*/
#ifndef PLAYER_H
#define PLAYER_H
#include "OthelloBoard.h"
/**
* This is an abstract base class for a Player
*/
class Player {
public:
/**
* @param symb The symbol for the player's pieces
*/
Player(char symb);
/**
* Destructor
*/
virtual ~Player();
/**
* @return The player's symbol
* Gets the symbol for the player's pieces
*/
char get_symbol() { return symbol; }
/**
* @param b The current board
* @param col Holds the column of the player's move
* @param row Holds the row of the player's move
* Gets the next move for the player
*/
virtual void get_move(OthelloBoard* b, int& col, int& row) = 0;
/**
* @return A copy of the Player object
* Virtual copy constructor
*/
virtual Player* clone() = 0;
protected:
/** The symbol for the player's pieces*/
char symbol;
};
#endif