init
This commit is contained in:
commit
f99efc4208
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.27)
|
||||
project(hw4)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(hw4 main.cpp
|
||||
board.h
|
||||
random_player.h
|
||||
strategic_player.h
|
||||
gameplay.h
|
||||
board.cpp
|
||||
random_player.cpp
|
||||
strategic_player.cpp
|
||||
gameplay.cpp
|
||||
gameplay.cpp
|
||||
player.h)
|
163
board.cpp
Normal file
163
board.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "board.h"
|
||||
#include <iostream>
|
||||
#include <cctype>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Board::Board(int rows, int cols) : rows(rows), cols(cols) {
|
||||
// Allocate memory for the 2D board
|
||||
board = new char*[2 * rows - 1];
|
||||
for (int i = 0; i < 2 * rows - 1; i++) {
|
||||
board[i] = new char[2 * cols - 1];
|
||||
for (int j = 0; j < 2 * cols - 1; j++) {
|
||||
// Initialize with dots at intersections and spaces elsewhere
|
||||
board[i][j] = (i % 2 == 0 && j % 2 == 0) ? '.' : ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Board::~Board() {
|
||||
// TODO: use free
|
||||
// Free dynamically allocated memory
|
||||
for (int i = 0; i < 2 * rows - 1; i++) {
|
||||
delete[] board[i];
|
||||
}
|
||||
delete[] board;
|
||||
}
|
||||
|
||||
char Board::get(int row, int col) const {
|
||||
return board[row][col];
|
||||
}
|
||||
|
||||
void Board::set(int row, int col, char value) {
|
||||
board[row][col] = value;
|
||||
}
|
||||
|
||||
bool Board::isLineValid(int row, int col) const {
|
||||
// A line is valid if it's in a valid position and not already occupied
|
||||
return (row > - 1 && row < 2 * rows - 1 && col > -1 && col < 2 * cols - 1) &&
|
||||
((row&1) != (col&1)) && (board[row][col] == ' ');
|
||||
}
|
||||
|
||||
void Board::placeLine(int row, int col, char player) {
|
||||
if (isLineValid(row, col)) {
|
||||
board[row][col] = tolower(player); // Place the player's initial in lowercase
|
||||
}
|
||||
}
|
||||
|
||||
int Board::checkAndMarkBox(int row, int col, char player) {
|
||||
int completedBoxes = 0;
|
||||
|
||||
// Check if the move is horizontal or vertical
|
||||
bool isHorizontal = (row % 2 == 0);
|
||||
|
||||
if (isHorizontal) {
|
||||
// Check box above the line (if row > 0)
|
||||
if (row > 0 &&
|
||||
board[row - 2][col] != ' ' && // Top line
|
||||
board[row][col] != ' ' && // Bottom line
|
||||
board[row - 1][col - 1] != ' ' && // Left line
|
||||
board[row - 1][col + 1] != ' ') { // Right line
|
||||
board[row - 1][col] = player; // Mark box with player's initial
|
||||
completedBoxes++;
|
||||
}
|
||||
|
||||
// Check box below the line (if row < 2 * rows - 2)
|
||||
if (row < 2 * rows - 2 &&
|
||||
board[row + 2][col] != ' ' && // Bottom line
|
||||
board[row][col] != ' ' && // Top line
|
||||
board[row + 1][col - 1] != ' ' && // Left line
|
||||
board[row + 1][col + 1] != ' ') { // Right line
|
||||
board[row + 1][col] = player; // Mark box with player's initial
|
||||
completedBoxes++;
|
||||
}
|
||||
} else {
|
||||
// Check box to the left (if col > 0)
|
||||
if (col > 0 &&
|
||||
board[row - 1][col - 1] != ' ' && // Top line
|
||||
board[row + 1][col - 1] != ' ' && // Bottom line
|
||||
board[row][col - 2] != ' ' && // Left line
|
||||
board[row][col] != ' ') { // Right line
|
||||
board[row][col - 1] = player; // Mark box with player's initial
|
||||
completedBoxes++;
|
||||
}
|
||||
|
||||
// Check box to the right (if col < 2 * cols - 2)
|
||||
if (col < 2 * cols - 2 &&
|
||||
board[row - 1][col + 1] != ' ' && // Top line
|
||||
board[row + 1][col + 1] != ' ' && // Bottom line
|
||||
board[row][col + 2] != ' ' && // Right line
|
||||
board[row][col] != ' ') { // Left line
|
||||
board[row][col + 1] = player; // Mark box with player's initial
|
||||
completedBoxes++;
|
||||
}
|
||||
}
|
||||
|
||||
return completedBoxes;
|
||||
}
|
||||
|
||||
|
||||
bool Board::isFull() const {
|
||||
for (int i = 0; i < 2 * rows - 1; i++) {
|
||||
for (int j = 0; j < 2 * cols - 1; j++) {
|
||||
if (board[i][j] == ' ' && (i % 2 != j % 2)) {
|
||||
return false; // There's at least one empty line
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Board::printBoard() const {
|
||||
// Function to print the board
|
||||
// Print the tens and ones places of the column numbers
|
||||
cout << " "; // Space for row numbers
|
||||
for (int col = 0; col < cols * 2 - 1; ++col) {
|
||||
if (!(col % 10)) {
|
||||
cout << col / 10; // Tens place of column numbers
|
||||
} else {
|
||||
cout << " "; // For spaces between dots
|
||||
}
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
// Print the ones place of the column numbers
|
||||
cout << " "; // Space for row numbers
|
||||
for (int col = 0; col < cols * 2 - 1; ++col) {
|
||||
cout << (col) % 10; // Print ones place
|
||||
}
|
||||
cout << endl;
|
||||
|
||||
// Print the board rows with row numbers
|
||||
for (int row = 0; row < rows * 2 - 1; ++row) {
|
||||
// Print row number on the left
|
||||
if (row / 10 > 9) {
|
||||
if (row % 10 == 0) {
|
||||
cout << row / 10;
|
||||
} else {
|
||||
cout << " ";
|
||||
}
|
||||
} else {
|
||||
if (row % 10 == 0) {
|
||||
cout << row / 10;
|
||||
} else {
|
||||
cout << " ";
|
||||
}
|
||||
}
|
||||
cout << row % 10 << " "; // Print the ones place of the row number
|
||||
|
||||
// Print the content of the row (dots, lines, spaces)
|
||||
for (int col = 0; col < cols * 2 - 1; ++col) {
|
||||
cout << board[row][col];
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int Board::getRows() const {
|
||||
return rows;
|
||||
}
|
||||
|
||||
int Board::getCols() const {
|
||||
return cols;
|
||||
}
|
28
board.h
Normal file
28
board.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef BOARD_H
|
||||
#define BOARD_H
|
||||
|
||||
class Board {
|
||||
public:
|
||||
Board(int rows, int cols);
|
||||
~Board();
|
||||
|
||||
char get(int row, int col) const;
|
||||
void set(int row, int col, char value);
|
||||
|
||||
bool isLineValid(int row, int col) const;
|
||||
void placeLine(int row, int col, char player);
|
||||
|
||||
int checkAndMarkBox(int row, int col, char player);
|
||||
bool isFull() const;
|
||||
|
||||
void printBoard() const;
|
||||
|
||||
int getRows() const;
|
||||
int getCols() const;
|
||||
|
||||
private:
|
||||
int rows, cols; // Number of boxes (not dots)
|
||||
char** board; // Dynamically allocated 2D array
|
||||
};
|
||||
|
||||
#endif // BOARD_H
|
95
gameplay.cpp
Normal file
95
gameplay.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
#include "gameplay.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
using namespace std;
|
||||
Gameplay::Gameplay(int rows, int cols, Player *player1, Player *player2): board(rows, cols), player1(player1), player2(player2) {
|
||||
|
||||
}
|
||||
|
||||
Gameplay::~Gameplay() {
|
||||
// TODO: needs revie
|
||||
free(this->player1);
|
||||
free(this->player2);
|
||||
}
|
||||
|
||||
void Gameplay::playGame() {
|
||||
std::ofstream outputFile("output.txt");
|
||||
if (!outputFile) {
|
||||
std::cerr << "Error: Could not open output file for writing." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write board dimensions to the output file
|
||||
outputFile << board.getRows() << " " << board.getCols() << std::endl;
|
||||
std::cout << board.getRows() << " " << board.getCols() << std::endl;
|
||||
char currentPlayer = player1->getName(); // Start with the RandomPlayer
|
||||
while (true) {
|
||||
int row, col;
|
||||
// Determine the move based on the current player
|
||||
if (currentPlayer == player1->getName()) {
|
||||
player1->selectLineLocation(board, row, col);
|
||||
} else {
|
||||
player2->selectLineLocation(board, row, col);
|
||||
}
|
||||
// Validate and place the move
|
||||
if (board.isLineValid(row, col)) {
|
||||
board.placeLine(row, col, currentPlayer);
|
||||
outputFile << currentPlayer << " " << row << " " << col << std::endl;
|
||||
std::cout << currentPlayer << " " << row << " " << col << std::endl;
|
||||
int boxesCompleted = board.checkAndMarkBox(row, col, currentPlayer);
|
||||
if (boxesCompleted == 0) {
|
||||
// Switch to the next player if no boxes are earned
|
||||
currentPlayer = (currentPlayer == player1->getName())
|
||||
? player2->getName()
|
||||
: player1->getName();
|
||||
}
|
||||
} else {
|
||||
std::cout << currentPlayer << " made an invalid move at (" << row << ", " << col << ")!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the board is full
|
||||
if (board.isFull()) {
|
||||
outputFile << "END" << std::endl;
|
||||
cout<<"END"<<endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Print the final board state
|
||||
board.printBoard();
|
||||
|
||||
// Determine the winner and display results
|
||||
determineWinner();
|
||||
outputFile.close();
|
||||
}
|
||||
|
||||
void Gameplay::determineWinner() {
|
||||
int randomPlayerBoxes = 0, strategicPlayerBoxes = 0;
|
||||
|
||||
// Iterate over the centers of boxes
|
||||
for (int r = 1; r < 2 * board.getRows() - 2; r += 2) { // Center rows of boxes
|
||||
for (int c = 1; c < 2 * board.getCols() - 2; c += 2) { // Center columns of boxes
|
||||
char boxOwner = board.get(r, c); // Get the owner of the box
|
||||
if (boxOwner == player1->getName()) {
|
||||
randomPlayerBoxes++;
|
||||
} else if (boxOwner == player2->getName()) {
|
||||
strategicPlayerBoxes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display results
|
||||
std::cout << "Player " << player1->getName() << " has " << randomPlayerBoxes << " boxes." << std::endl;
|
||||
std::cout << "Player " << player2->getName() << " has " << strategicPlayerBoxes << " boxes." << std::endl;
|
||||
|
||||
if (randomPlayerBoxes > strategicPlayerBoxes) {
|
||||
std::cout << "Player " << player1->getName() << " (wins)" << std::endl;
|
||||
} else if (strategicPlayerBoxes > randomPlayerBoxes) {
|
||||
std::cout << "Player " << player2->getName() << " (wins)" << std::endl;
|
||||
} else {
|
||||
std::cout << "The game is a tie!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
23
gameplay.h
Normal file
23
gameplay.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef HW4_GAMEPLAY_H
|
||||
#define HW4_GAMEPLAY_H
|
||||
|
||||
#include "board.h"
|
||||
#include "random_player.h"
|
||||
#include "strategic_player.h"
|
||||
|
||||
class Gameplay {
|
||||
private:
|
||||
Board board;
|
||||
Player* player1; // Use pointers to enable polymorphism
|
||||
Player* player2;
|
||||
|
||||
void determineWinner();
|
||||
|
||||
public:
|
||||
Gameplay(int rows, int cols, Player* player1, Player* player2);
|
||||
~Gameplay(); // Destructor to clean up allocated memory
|
||||
|
||||
void playGame();
|
||||
};
|
||||
|
||||
#endif // HW4_GAMEPLAY_H
|
42
main.cpp
Normal file
42
main.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "gameplay.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int rows, cols;
|
||||
char player1Name, player2Name;
|
||||
string player1Type, player2Type;
|
||||
|
||||
// Read board dimensions
|
||||
cin >> rows >> cols;
|
||||
|
||||
// Read player 1 details
|
||||
cin >> player1Name >> player1Type;
|
||||
|
||||
// Read player 2 details
|
||||
cin >> player2Name >> player2Type;
|
||||
|
||||
// Determine player types and initialize gameplay
|
||||
Player* randomPlayerName, *strategicPlayerName;
|
||||
if (tolower(player1Type[0]) == 'r' && tolower(player2Type[0]) == 's') {
|
||||
randomPlayerName = new RandomPlayer(player1Name);
|
||||
strategicPlayerName = new StrategicPlayer(player2Name);
|
||||
} else if (tolower(player1Type[0]) == 's' && tolower(player2Type[0]) == 'r') {
|
||||
randomPlayerName = new RandomPlayer(player2Name);
|
||||
strategicPlayerName = new StrategicPlayer(player1Name);
|
||||
}else if (tolower(player1Type[0]) == 'r' && tolower(player2Type[0]) == 'r') {
|
||||
randomPlayerName = new RandomPlayer(player2Name);
|
||||
strategicPlayerName = new RandomPlayer(player1Name);
|
||||
} else {
|
||||
randomPlayerName = new StrategicPlayer(player2Name);
|
||||
strategicPlayerName = new StrategicPlayer(player1Name);
|
||||
}
|
||||
|
||||
Gameplay game(rows, cols, randomPlayerName, strategicPlayerName);
|
||||
game.playGame();
|
||||
|
||||
return 0;
|
||||
}
|
19
player.h
Normal file
19
player.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef HW4_PLAYER_H
|
||||
#define HW4_PLAYER_H
|
||||
|
||||
#include "board.h"
|
||||
|
||||
class Player {
|
||||
private:
|
||||
char name;
|
||||
|
||||
public:
|
||||
Player(char name){
|
||||
this->name = name;
|
||||
}
|
||||
virtual void selectLineLocation(Board& board, int& row, int& col) = 0;
|
||||
virtual char getName() const = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif //HW4_PLAYER_H
|
13
random_player.cpp
Normal file
13
random_player.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "random_player.h"
|
||||
#include <cstdlib>
|
||||
|
||||
void RandomPlayer::selectLineLocation(Board& board, int& row, int& col) {
|
||||
int rows = board.getRows();
|
||||
int cols = board.getCols();
|
||||
|
||||
// Loop until a valid move is found
|
||||
do {
|
||||
row = rand() % (2 * rows - 1); // Random row within the board dimensions
|
||||
col = rand() % (2 * cols - 1); // Random column within the board dimensions
|
||||
} while (!board.isLineValid(row, col)); // Check if the move is valid
|
||||
}
|
25
random_player.h
Normal file
25
random_player.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef HW4_RANDOM_PLAYER_H
|
||||
#define HW4_RANDOM_PLAYER_H
|
||||
|
||||
#include "board.h"
|
||||
#include "player.h"
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
||||
class RandomPlayer: public Player {
|
||||
private:
|
||||
char name;
|
||||
public:
|
||||
RandomPlayer(char name): Player(name) {
|
||||
srand(static_cast<unsigned int>(time(nullptr)));
|
||||
|
||||
this->name = name;
|
||||
}
|
||||
void selectLineLocation(Board& board, int& row, int& col);
|
||||
|
||||
char getName() const override {
|
||||
return this->name;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //HW4_RANDOM_PLAYER_H
|
96
strategic_player.cpp
Normal file
96
strategic_player.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "strategic_player.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void StrategicPlayer::selectLineLocation(Board& board, int& row, int& col) {
|
||||
int rows = board.getRows();
|
||||
int cols = board.getCols();
|
||||
|
||||
// Step 1: Try to complete a box
|
||||
for (int r = 1; r < 2 * rows - 2; r += 2) { // Iterate over box centers (odd rows)
|
||||
for (int c = 1; c < 2 * cols - 2; c += 2) { // Iterate over box centers (odd cols)
|
||||
// Check adjacent lines for an opportunity to complete a box
|
||||
if (board.isLineValid(r - 1, c) && // Top line
|
||||
board.get(r + 1, c) != ' ' && // Bottom line
|
||||
board.get(r, c - 1) != ' ' && // Left line
|
||||
board.get(r, c + 1) != ' ') { // Right line
|
||||
row = r - 1;
|
||||
col = c;
|
||||
return;
|
||||
}
|
||||
if (board.isLineValid(r + 1, c) && // Bottom line
|
||||
board.get(r - 1, c) != ' ' && // Top line
|
||||
board.get(r, c - 1) != ' ' && // Left line
|
||||
board.get(r, c + 1) != ' ') { // Right line
|
||||
row = r + 1;
|
||||
col = c;
|
||||
return;
|
||||
}
|
||||
if (board.isLineValid(r, c - 1) && // Left line
|
||||
board.get(r - 1, c) != ' ' && // Top line
|
||||
board.get(r + 1, c) != ' ' && // Bottom line
|
||||
board.get(r, c + 1) != ' ') { // Right line
|
||||
row = r;
|
||||
col = c - 1;
|
||||
return;
|
||||
}
|
||||
if (board.isLineValid(r, c + 1) && // Right line
|
||||
board.get(r - 1, c) != ' ' && // Top line
|
||||
board.get(r + 1, c) != ' ' && // Bottom line
|
||||
board.get(r, c - 1) != ' ') { // Left line
|
||||
row = r;
|
||||
col = c + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Avoid moves that leave a box with one line remaining
|
||||
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 (board.isLineValid(r, c)) {
|
||||
// Simulate placing the line
|
||||
board.placeLine(r, c, name);
|
||||
|
||||
// Check if this move leaves a box with only one line remaining
|
||||
bool createsOpportunity = false;
|
||||
for (int nr = 1; nr < 2 * rows - 2; nr += 2) { // Iterate over box centers
|
||||
for (int nc = 1; nc < 2 * cols - 2; nc += 2) {
|
||||
if (board.get(nr, nc) == ' ') {
|
||||
int adjacentLines = 0;
|
||||
if (nr > 0 && board.get(nr - 1, nc) != ' ') adjacentLines++; // Top line
|
||||
if (nr < 2 * rows - 2 && board.get(nr + 1, nc) != ' ') adjacentLines++; // Bottom line
|
||||
if (nc > 0 && board.get(nr, nc - 1) != ' ') adjacentLines++; // Left line
|
||||
if (nc < 2 * cols - 2 && board.get(nr, nc + 1) != ' ') adjacentLines++; // Right line
|
||||
|
||||
if (adjacentLines == 3) { // Opponent can complete this box
|
||||
createsOpportunity = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (createsOpportunity) break;
|
||||
}
|
||||
|
||||
board.set(r, c, ' '); // Undo the simulated move
|
||||
|
||||
if (!createsOpportunity) {
|
||||
row = r;
|
||||
col = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: Fallback to the first valid move
|
||||
for (int r = 0; r < 2 * rows - 1; ++r) {
|
||||
for (int c = 0; c < 2 * cols - 1; ++c) {
|
||||
if (board.isLineValid(r, c)) {
|
||||
row = r;
|
||||
col = c;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
strategic_player.h
Normal file
26
strategic_player.h
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Created by sapan on 18-11-2024.
|
||||
//
|
||||
|
||||
#ifndef HW4_STRATEGIC_PLAYER_H
|
||||
#define HW4_STRATEGIC_PLAYER_H
|
||||
|
||||
#include "board.h"
|
||||
#include "player.h"
|
||||
|
||||
class StrategicPlayer: public Player {
|
||||
private:
|
||||
char name;
|
||||
public:
|
||||
|
||||
StrategicPlayer(char name): Player(name) {
|
||||
this->name = name;
|
||||
}
|
||||
|
||||
void selectLineLocation(Board& board, int& row, int& col);
|
||||
char getName() const override {
|
||||
return this->name;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //HW4_STRATEGIC_PLAYER_H
|
Loading…
Reference in New Issue
Block a user