add ss.*
This commit is contained in:
parent
3ecbd34211
commit
a6a1fca8cd
61
README.txt
Normal file
61
README.txt
Normal file
@ -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.
|
BIN
bar.so
BIN
bar.so
Binary file not shown.
BIN
foo.so
BIN
foo.so
Binary file not shown.
@ -1,122 +0,0 @@
|
||||
#include "custom_player.h"
|
||||
#include <iostream>
|
||||
#include <cstdlib> // for rand
|
||||
#include <ctime> // 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];
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#ifndef __CUSTOM_PLAYER__
|
||||
#define __CUSTOM_PLAYER__
|
||||
|
||||
#include "player.h"
|
||||
#include "common.h"
|
||||
#include "board.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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
|
10
readme.txt
10
readme.txt
@ -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
|
||||
|
@ -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();
|
@ -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);
|
||||
}
|
@ -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
|
Loading…
Reference in New Issue
Block a user