diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6a3bda8 --- /dev/null +++ b/README.txt @@ -0,0 +1,61 @@ +Team member 1: +Name: Sandipsinh Rathod +Email: sdr5549@psu.edu +Class: CMPSC 330 + +Team member 2: +Name: Sapan Shah +Email: scs6041@psu.edu +Class: CMPSC 330 +Homework 5 +Due Date: December 6, 2024 + +Command used to create object file for strategic player +g++ -O3 -shared -fPIC -ansi -pedantic -std=c++14 board.cxx ss.cxx -o SandipsinhRathodsdr5549_SapanShahscs6041.so + +Restructuring HW2 and HW4 in HW5 +1. Class Abstraction and Polymorphism + + HW2: The game logic was directly implemented without clear abstractions for players, relying on procedural logic. + HW4: Introduced polymorphism via Player, RandomPlayer, and StrategicPlayer classes. Players had distinct behavior, and polymorphism allowed dynamic handling of different player types. + HW5: Built on this by creating the StrategicPlayer1 class derived from IPlayer, implementing a robust and modular strategy for player decisions: + Interface Implementation: StrategicPlayer1 implements IPlayer, ensuring that all player-related behaviors are well-defined and standardized across implementations. + Enhanced Strategies: Added sophisticated methods like FindBoxCompletingMove and ForceOpponentMistake, making the strategy logic adaptable and focused on optimizing game outcomes. + +2. Dynamic Memory Management + + HW2: Basic memory management using raw pointers with manual deallocation. + HW4: Introduced the RAII (Resource Acquisition Is Initialization) pattern for managing board memory allocation and deallocation efficiently. + HW5: Continued RAII principles with a more structured approach to object lifecycles, ensuring robust cleanup: + Explicit initialization and cleanup methods (Init, Close) for the StrategicPlayer1 class. + Use of dynamic memory in a controlled and predictable way, minimizing risks of memory leaks. + +3. Board Representation + + HW2: The board was represented as a dynamically allocated 2D array, with logic scattered across functions. + HW4: Encapsulated board management into the Board class, which included methods for placing lines, checking box completion, and printing the board. + HW5: Enhanced the Board class functionality: + Added methods like ListEmptyLines to provide the player class with detailed insights into game state. + Improved the interface between the board and player, enabling complex strategic evaluations such as chain creation and cost evaluation. + +4. Player Strategies + + HW2: All moves were processed in a straightforward, procedural manner without differentiation between players. + HW4: Differentiated players using classes for random and strategic play. However, the strategic player logic was limited to basic box completion. + HW5: Refined the strategic player logic significantly: + Multi-step Decision Making: Implemented a three-step approach for move selection: completing boxes, avoiding chains, and fallback to optimal moves. + Move Evaluation: Added sophisticated cost evaluation (EvaluateMoveCost) and chain detection (DoesMoveCreateChain). + +5. Event-Driven Design + + HW4: The player and board interactions were tightly coupled. + HW5: Introduced event-driven methods (EventAddLine, EventAddBox) to separate game state updates from decision-making logic. This enhances modularity and makes the code easier to extend and debug. + +Learnings + + Modularity and Reusability: Encapsulation of functionality (e.g., board management, player behaviors) allows for better reuse and testing. The separation of concerns between classes improved maintainability. + Polymorphism and Interfaces: Leveraging polymorphism (IPlayer) provided a flexible framework to implement diverse player strategies while ensuring consistency. + Strategic Thinking: Developing a multi-step strategy for players helped refine algorithmic thinking and fostered a deeper understanding of game theory. + RAII and Memory Management: The shift to controlled memory allocation and deallocation in RAII patterns reduced errors and improved reliability. + Event-Driven Programming: Designing the game logic around events encouraged a cleaner architecture and reduced tight coupling. + Iterative Refinement: By analyzing and building upon previous homework, we demonstrated the importance of iterative development and incremental improvement in programming. \ No newline at end of file diff --git a/bar.so b/bar.so deleted file mode 100644 index 2e83e41..0000000 Binary files a/bar.so and /dev/null differ diff --git a/foo.so b/foo.so deleted file mode 100644 index 9661272..0000000 Binary files a/foo.so and /dev/null differ diff --git a/harshit/custom_player.cxx b/harshit/custom_player.cxx deleted file mode 100644 index 9d8eb61..0000000 --- a/harshit/custom_player.cxx +++ /dev/null @@ -1,122 +0,0 @@ -#include "custom_player.h" -#include -#include // for rand -#include // for time -using namespace std; - -// Factory function for dynamic library loading -extern "C" IPlayer* PlayerFactory() { - return new CustomPlayer(); -} - -// Constructor -CustomPlayer::CustomPlayer() { - srand(time(0)); -} - -// Destructor -CustomPlayer::~CustomPlayer() {} - -// Initializes the player with the board size and player symbols -void CustomPlayer::Init(int dots_in_rows, int dots_in_cols, char _player_box, char _player_line) { - board.AllocateBoard(dots_in_rows, dots_in_cols); - player_box = _player_box; - player_line = _player_line; - empty_lines = new Loc[board.GetRows() * board.GetCols()]; -} - -// Cleans up dynamically allocated memory -void CustomPlayer::Close() { - board.FreeBoard(); - delete[] empty_lines; -} - -// Returns the player info -std::string CustomPlayer::PlayerInfo() { - return "Custom Strategic Player"; -} - -// Updates the board when a line is added -void CustomPlayer::EventAddLine(char bar, const Loc& loc) { - board(loc) = bar; -} - -// Updates the board when a box is completed -void CustomPlayer::EventAddBox(char box, const Loc& loc) { - board(loc) = box; -} - -// Updates the list of available line locations -void CustomPlayer::ListEmptyLines() { - empty_lines_count = 0; - for (int r = 0; r < board.GetRows(); r++) { - for (int c = 0; c < board.GetCols(); c++) { - if (board(r, c) == ' ' && Loc(r, c).IsLineLocation()) { - empty_lines[empty_lines_count++] = Loc(r, c); - } - } - } -} - -// Helper function to check if a line completes a box -bool CustomPlayer::DoesLineCompleteBox(const Loc& loc) { - if (loc.IsLineHorizontalLocation()) { - if (loc.row > 0 && board(loc.row - 1, loc.col) != ' ' && - board(loc.row - 1, loc.col - 1) != ' ' && board(loc.row - 1, loc.col + 1) != ' ') - return true; - if (loc.row < board.GetRows() - 1 && board(loc.row + 1, loc.col) != ' ' && - board(loc.row + 1, loc.col - 1) != ' ' && board(loc.row + 1, loc.col + 1) != ' ') - return true; - } else if (loc.IsLineVerticalLocation()) { - if (loc.col > 0 && board(loc.row, loc.col - 1) != ' ' && - board(loc.row - 1, loc.col - 1) != ' ' && board(loc.row + 1, loc.col - 1) != ' ') - return true; - if (loc.col < board.GetCols() - 1 && board(loc.row, loc.col + 1) != ' ' && - board(loc.row - 1, loc.col + 1) != ' ' && board(loc.row + 1, loc.col + 1) != ' ') - return true; - } - return false; -} - -// Helper function to avoid giving the opponent a chance to complete a box -bool CustomPlayer::WouldGiveOpponentBox(const Loc& loc) { - // Temporarily add the line to the board - board(loc) = player_line; - - // Check if the opponent can complete a box in the next move - for (int r = 0; r < board.GetRows(); r++) { - for (int c = 0; c < board.GetCols(); c++) { - if (board(r, c) == ' ' && Loc(r, c).IsLineLocation() && - DoesLineCompleteBox(Loc(r, c))) { - board(loc) = ' '; // Revert the move - return true; - } - } - } - - // Revert the move - board(loc) = ' '; - return false; -} - -// Determines the next move -Loc CustomPlayer::SelectLineLocation() { - ListEmptyLines(); - - // Strategy: Prioritize moves that complete a box - for (int i = 0; i < empty_lines_count; i++) { - if (DoesLineCompleteBox(empty_lines[i])) { - return empty_lines[i]; - } - } - - // Avoid moves that give the opponent a chance to complete a box - for (int i = 0; i < empty_lines_count; i++) { - if (!WouldGiveOpponentBox(empty_lines[i])) { - return empty_lines[i]; - } - } - - // Otherwise, pick a random move - return empty_lines[rand() % empty_lines_count]; -} \ No newline at end of file diff --git a/harshit/custom_player.h b/harshit/custom_player.h deleted file mode 100644 index f617333..0000000 --- a/harshit/custom_player.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __CUSTOM_PLAYER__ -#define __CUSTOM_PLAYER__ - -#include "player.h" -#include "common.h" -#include "board.h" -#include -#include - -class CustomPlayer : public IPlayer { -private: - Board board; // Game board to track state - char player_box; // Character representing the player's boxes - char player_line; // Character representing the player's lines - Loc* empty_lines; // Array to store available line locations - int empty_lines_count; // Number of available line locations - -public: - CustomPlayer(); - ~CustomPlayer(); - - // Initializes the player with board details and player symbols - void Init(int dots_in_rows, int dots_in_cols, char player_box, char player_line) override; - - // Cleans up dynamically allocated memory - void Close() override; - - // Returns the player information - std::string PlayerInfo() override; - - // Called when a line is added to the board - void EventAddLine(char bar, const Loc& loc) override; - - // Called when a box is completed on the board - void EventAddBox(char box, const Loc& loc) override; - - // Determines the next move based on the current state of the board - Loc SelectLineLocation() override; - -private: - // Updates the list of available line locations - void ListEmptyLines(); - - // Helper function to check if a line completes a box - bool DoesLineCompleteBox(const Loc& loc); - - // Helper function to avoid giving the opponent a chance to complete a box - bool WouldGiveOpponentBox(const Loc& loc); - - // Evaluate the best line location strategically - Loc GetBestStrategicMove(); -}; - -#endif \ No newline at end of file diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 215f01e..0000000 --- a/readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -Build mazerunner program and player_randommove.so library - -g++ -ansi -pedantic -std=c++14 board.cxx dotsboxesgm.cxx main.cxx -o dotsboxes -g++ -shared -fPIC -ansi -pedantic -std=c++14 random_player.cxx board.cxx -o random_player.so - - -playing the dots and boxes games with multiple players - -./dotsboxes ./strategic_player.so ./random_player.so ./strategic_player.so ./random_player.so - diff --git a/strategic_player1.cxx b/ss.cxx similarity index 94% rename from strategic_player1.cxx rename to ss.cxx index 79bc139..379970a 100644 --- a/strategic_player1.cxx +++ b/ss.cxx @@ -1,4 +1,17 @@ -#include "strategic_player1.h" +// Team member 1 +// Name: Sandipsinh Rathod +// Email: sdr5549@psu.edu +// Team member 2 +// Name: Sapan Shah +// Email: scs6041@psu.edu +// +// Program homework 5 +// Class: CMPSC 330 +// Current Date: 6/12/24 9:15 PM +// Due Date: 6/12/24 11:59 PM + + +#include "ss.h" extern "C" IPlayer *PlayerFactory() { return new StrategicPlayer1(); diff --git a/strategic_player1.h b/ss.h similarity index 100% rename from strategic_player1.h rename to ss.h diff --git a/strategic_player.cxx b/strategic_player.cxx deleted file mode 100644 index e734ba3..0000000 --- a/strategic_player.cxx +++ /dev/null @@ -1,171 +0,0 @@ -#include "strategic_player.h" -#include "common.h" - -extern "C" IPlayer* PlayerFactory() -{ - return new StrategicPlayer(); -} - -// Helper functions -inline int normalize(int x) { - return (x + 1) >> 1; -} - -inline char getPoint(Board &board, const int row, const int col) { - return board(row, col); -} - -inline bool isLineValid(Board &board, const int row, const int col) { - return (row > -1 && row < board.GetRows() && col > -1 && col < board.GetCols()) && - ((row & 1) != (col & 1)) && (getPoint(board, row, col) == ' '); -} - -inline void set(Board &board, int r, int c, char ch) { - board(r, c) = ch; -} -// - - -string StrategicPlayer::PlayerInfo() { - return "Sandipsinh Rathod (sdr5549@psu.edu), Sapan Shah (scs6041@psu.edu)"; -} - -void StrategicPlayer::Init(int board_rows, int board_cols, char box_type, char line_type) { - this->name = box_type; - this->box_name = line_type; - this->board.AllocateBoard(board_rows, board_cols); -} - - -StrategicPlayer::~StrategicPlayer() { - board.FreeBoard(); -} - -void StrategicPlayer::Close() { - board.FreeBoard(); -} - -/// TODO: check if we needs checks :) -void StrategicPlayer::EventAddLine(char bar, const Loc &loc) { - set(board, loc.row, loc.col, bar); -} - -void StrategicPlayer::EventAddBox(char box, const Loc &loc) { - set(board, loc.row, loc.col, box); -} - -inline void selectLine(Board &board, int &row, int &col, int rows, int cols, char name) { - int max_row = 2 * rows - 2; - int max_col = 2 * cols - 2; - - // Step 1: Try to complete a box -#pragma omp parallel for collapse(2) - for (int r = 1; r < max_row; r += 2) { - // Iterate over box centers (odd rows) - for (int c = 1; c < max_col; c += 2) { - // Iterate over box centers (odd cols) - // Check adjacent lines for an opportunity to complete a box - if (isLineValid(board, r - 1, c) && // Top line - getPoint(board, r + 1, c) != ' ' && // Bottom line - getPoint(board, r, c - 1) != ' ' && // Left line - getPoint(board, r, c + 1) != ' ') { - // Right line - row = r - 1; - col = c; - return; - } - if (isLineValid(board, r + 1, c) && // Bottom line - getPoint(board, r - 1, c) != ' ' && // Top line - getPoint(board, r, c - 1) != ' ' && // Left line - getPoint(board, r, c + 1) != ' ') { - // Right line - row = r + 1; - col = c; - return; - } - if (isLineValid(board, r, c - 1) && // Left line - getPoint(board, r - 1, c) != ' ' && // Top line - getPoint(board, r + 1, c) != ' ' && // Bottom line - getPoint(board, r, c + 1) != ' ') { - // Right line - row = r; - col = c - 1; - return; - } - if (isLineValid(board, r, c + 1) && // Right line - getPoint(board, r - 1, c) != ' ' && // Top line - getPoint(board, r + 1, c) != ' ' && // Bottom line - getPoint(board, r, c - 1) != ' ') { - // Left line - row = r; - col = c + 1; - return; - } - } - } - - // Step 2: Avoid moves that leave a box with one line remaining -#pragma omp parallel for collapse(2) - for (int r = 0; r < 2 * rows - 1; ++r) { - // Iterate over all valid rows - for (int c = 0; c < 2 * cols - 1; ++c) { - // Iterate over all valid cols - if (isLineValid(board, r, c)) { - // Simulate placing the line - set(board, r, c, name); - - // Check if this move leaves a box with only one line remaining - bool createsOpportunity = false; - for (int nr = 1; nr < max_row; nr += 2) { - // Iterate over box centers - for (int nc = 1; nc < max_col; nc += 2) { - if (getPoint(board, nr, nc) == ' ') { - int adjacentLines = 0; - if (nr > 0 && getPoint(board, nr - 1, nc) != ' ') adjacentLines++; // Top line - if (nr < max_row && getPoint(board, nr + 1, nc) != ' ') adjacentLines++; // Bottom line - if (nc > 0 && getPoint(board, nr, nc - 1) != ' ') adjacentLines++; // Left line - if (nc < max_col && getPoint(board, nr, nc + 1) != ' ') adjacentLines++; // Right line - - if (adjacentLines == 3) { - // Opponent can complete this box - createsOpportunity = true; - break; - } - } - } - if (createsOpportunity) break; - } - - set(board, r, c, ' '); // Undo the simulated move - - if (!createsOpportunity) { - row = r; - col = c; - return; - } - } - } - } - - // Step 3: Fallback to the first valid move -#pragma omp parallel for collapse(2) - for (int r = 0; r < 2 * rows - 1; ++r) { - for (int c = 0; c < 2 * cols - 1; ++c) { - if (isLineValid(board, r, c)) { - row = r; - col = c; - return; - } - } - } -} - -Loc StrategicPlayer::SelectLineLocation() { - int rows = normalize(board.GetRows()); - int cols = normalize(board.GetCols()); - - int row, col; - selectLine(board, row, col, rows, cols, name); - - return Loc(row, col); -} diff --git a/strategic_player.h b/strategic_player.h deleted file mode 100644 index 5ece82e..0000000 --- a/strategic_player.h +++ /dev/null @@ -1,38 +0,0 @@ - -#ifndef HW4_STRATEGIC_PLAYER_H -#define HW4_STRATEGIC_PLAYER_H - -#include "common.h" -#include "board.h" -#include "player.h" - -class StrategicPlayer: public IPlayer { - char name; - char box_name; - - Board board; -public: - string PlayerInfo(); - // Init(const int,const int) will be called before playing the game - // You can create your own data-structure - void Init - ( int board_rows // the size of board (including dots, lines, and boxes) - , int board_cols // the size of board (including dots, lines, and boxes) - , char box_type // the character for the player's boxes - , char line_type // the character for the player's lines - ); - // Close() will be called after finishing playing the game - // You can remove all dynamically allocated memories - void Close(); - // EventAddLine() and EventAddBox() will be called - // when a player adds a line or when a system assign a box's owner - void EventAddLine(char bar, const Loc& loc); - void EventAddBox (char box, const Loc& loc); - // Loc SelectLineLocation() will be called - // when the game system ask where your player want to add a line - Loc SelectLineLocation(); - - ~StrategicPlayer(); -}; - -#endif //HW4_STRATEGIC_PLAYER_H