Last active
August 29, 2015 14:01
-
-
Save catarak/0d6be52f2d21d8a07bad to your computer and use it in GitHub Desktop.
Battleship
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cassie Tarakajian, ctarakajian@gmail.com | |
Compiled using GCC 4.2.1, C++11 | |
This program simulates a game of Battleship. To play, | |
download the project directory, then cd into that directory. | |
$ cd <path_to_project_directory> | |
$ make | |
$ ./battle (-f)(-n)(-s) | |
The -f flag means that the computer goes first, otherwise the | |
order is chosen randomly. The -n means no submarines, which | |
changes the set of ships on the board so that there are no ships of length 1. The -s flags means the computer plays with a strategy instead of guessing randomly. | |
When a game begins, a player's ships and the computers ships | |
are placed randomly. | |
If strategy mode is on for the computer, it guesses in the most likely spot that it will hit a ship. It does this by | |
calculating a grid of likelihoods. If the computer makes a | |
hit, then it changes strategy and just guesses (intelligently) | |
near where the ship was first hit. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "game_manager.h" | |
#include <iostream> | |
std::map<char,Ship> SHIP_SET_I = { | |
{'A', Ship(5, "Aircraft Carrier")}, | |
{'B', Ship(4, "Battleship")}, | |
{'C', Ship(3, "Cruiser")}, | |
{'d', Ship(2, "Destroyer 1")}, | |
{'D', Ship(2, "Destroyer 2")}, | |
{'s', Ship(1, "Submarine 1")}, | |
{'S', Ship(1, "Submarine 2")} | |
}; | |
std::map<char,Ship> SHIP_SET_II = { | |
{'A', Ship(5, "Aircraft Carrier")}, | |
{'B', Ship(4, "Battleship")}, | |
{'c', Ship(3, "Cruiser 1")}, | |
{'C', Ship(3, "Cruiser 2")}, | |
{'d', Ship(2, "Destroyer 1")}, | |
{'D', Ship(2, "Destroyer 2")}, | |
{'E', Ship(2, "Destroyer 3")} | |
}; | |
int main(int argc, char *argv[]) | |
{ | |
srand(time(0)); | |
bool comp_first, comp_strategy; | |
comp_strategy = false; | |
std::map<char,Ship> &ship_set = SHIP_SET_I; | |
//decide which player is going first randomly | |
comp_first = rand() % 2; | |
//Probably should use an option parser library for this | |
//see which player is going first | |
std::string f = "-f", first = "--first"; | |
//no subs mode | |
std::string n = "-n", nsubs = "--nosubs"; | |
//computer uses a strategy | |
std::string s = "-s", strategy = "--strategy"; | |
//check for all of these flags | |
if (argc != 1) { | |
for (int i = 1; i < argc; i++) { | |
if (f.compare(argv[i]) == 0 || first.compare(argv[i]) == 0) // computer has the first move | |
{ | |
comp_first = true; | |
} | |
else if (n.compare(argv[i]) == 0 || nsubs.compare(argv[i]) == 0) { | |
ship_set = SHIP_SET_II; | |
} | |
else if (s.compare(argv[i]) == 0 || strategy.compare(argv[i]) == 0) { | |
comp_strategy = true; | |
} | |
else { | |
std::cout << "Unknown flag entered, recognized flags are as follows:" << std::endl; | |
std::cout << "-f or --first for the computer to have the first move." << std::endl; | |
std::cout << "-n or --nosubs for no subs mode" << std::endl; | |
std::cout << "-s or --strategy for the computer to guess strategically instead of randomly." << std::endl; | |
} | |
} | |
} | |
GameManager Battleship = GameManager(ship_set,comp_strategy,comp_first); | |
Battleship.start_game(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "board.h" | |
#include <iostream> | |
Board::Board(): _positions(new char[BOARD_SIDE][BOARD_SIDE]) | |
{ | |
initialize_board(); | |
} | |
Board::~Board() | |
{ | |
delete[] _positions; | |
} | |
//Initializez board to all o's | |
void Board::initialize_board() | |
{ | |
for (int i = 0; i < BOARD_SIDE; i++) { | |
for (int j = 0; j < BOARD_SIDE; j++) { | |
_positions[i][j] = 'o'; | |
} | |
} | |
} | |
//Returns character at position on board (in character array) | |
char Board::get_char(int row, int column) | |
{ | |
return _positions[row][column]; | |
} | |
//Returns character at position on board (in character array) | |
char Board::get_char(std::pair<int,int> &position) | |
{ | |
return _positions[position.first][position.second]; | |
} | |
//Sets character at position on the board (in character array) | |
void Board::set_char(char c, int row, int column) | |
{ | |
_positions[row][column] = c; | |
} | |
//Sets character at position on the board (in character array) | |
void Board::set_char(char c, std::pair<int,int> &position) | |
{ | |
_positions[position.first][position.second] = c; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#ifndef _BOARD_H | |
#define _BOARD_H | |
#include "ship.h" | |
#include <vector> | |
#include <map> | |
#define BOARD_SIDE 10 | |
#define HIT 'x' | |
#define MISS '/' | |
#define OPEN 'o' | |
enum Orientation {LEFT, UP, RIGHT, DOWN}; | |
//map orientation to unit vector to rotate row and columns | |
const std::map<Orientation, std::pair<int,int>> ORT_TO_VECTOR = | |
{ | |
{Orientation::LEFT, {0, -1}}, | |
{Orientation::UP, {-1, 0}}, | |
{Orientation::RIGHT, {0, 1}}, | |
{Orientation::DOWN, {1, 0}} | |
}; | |
/** | |
* A Board object is a BOARD_SIDE X BOARD_SIDE array of characters, that is initialized | |
* to all OPEN spaces. It is an abstract class. | |
*/ | |
class Board | |
{ | |
private: | |
void initialize_board(); //initalizes board to all 'o's | |
protected: | |
//BOARD_SIDE X BOARD_SIDE array of chars | |
char (*_positions)[BOARD_SIDE]; | |
//sets a character on the board, for placing ships | |
void set_char(char c, int row, int column); | |
void set_char(char c, std::pair<int,int> &position); | |
public: | |
Board(); | |
~Board(); | |
//Returns char at a position on the board | |
char get_char(int row, int column); | |
char get_char(std::pair<int,int> &position); | |
virtual void print() = 0; | |
}; | |
#endif /*_BOARD_H*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "computer_player.h" | |
#include <algorithm> | |
#include <random> | |
#include <iostream> | |
ComputerPlayer::ComputerPlayer(TargetBoard *target_board, | |
std::map<char, Ship> ship_map, | |
bool intelligent): | |
_intelligent(intelligent), | |
_target_board(target_board), | |
_ship_map(ship_map) | |
{ | |
set_max_ship_length(); | |
initialize_cardinalities(); | |
} | |
void ComputerPlayer::initialize_cardinalities() | |
{ | |
for (int i = 0; i < (BOARD_SIDE + 1)/2; i++) { //BOARD_SIDE+1 in case board | |
for (int j = 0; j < (BOARD_SIDE + 1)/2; j++) { //side is odd | |
_strategy.cardinalities[i][j] = _strategy.cardinalities[i][BOARD_SIDE-1-j] = | |
_strategy.cardinalities[BOARD_SIDE-1-i][j] = | |
_strategy.cardinalities[BOARD_SIDE-1-i][BOARD_SIDE-1-j]= i + j + (_strategy.max_ship_length-1)*2; | |
} | |
} | |
} | |
void ComputerPlayer::update_cardinalities() | |
{ | |
for (int row = 0; row < BOARD_SIDE; row++) { | |
for (int column = 0; column < BOARD_SIDE; column++) { | |
if (_strategy.cardinalities[row][column] != -1) { // make sure we are not looking at a -1 | |
int count = 0; // the cardinality counter | |
for (int distance = 1; distance < _strategy.max_ship_length; distance++) { // count the spaces to the right | |
if (column + distance >= BOARD_SIDE) { // if we are out of bounds | |
break; | |
} | |
if (_strategy.cardinalities[row][column + distance] == -1) { // we have found a space that has been shot at | |
break; | |
} | |
count++; // we are in bounds, and it is not a known space | |
} | |
for (int distance = 1; distance < _strategy.max_ship_length; distance++) { // count the spaces to the left | |
if (column - distance < 0) { // if we are out of bounds | |
break; | |
} | |
if (_strategy.cardinalities[row][column - distance] == -1) { // we have found a space that has been shot at | |
break; | |
} | |
count++; // we are in bounds, and it is not a known space | |
} | |
for (int distance = 1; distance < _strategy.max_ship_length; distance++) { // count the spaces up | |
if (row - distance < 0) { // if we are out of bounds | |
break; | |
} | |
if (_strategy.cardinalities[row - distance][column] == -1) { // we have found a space that has been shot at | |
break; | |
} | |
count++; // we are in bounds, and it is not a known space | |
} | |
for (int distance = 1; distance < _strategy.max_ship_length; distance++) { // count the spaces down | |
if (row + distance >= BOARD_SIDE) { // if we are out of bounds | |
break; | |
} | |
if (_strategy.cardinalities[row + distance][column] == -1) { // we have found a space that has been shot at | |
break; | |
} | |
count++; // we are in bounds, and it is not a known space | |
} | |
_strategy.cardinalities[row][column] = count; | |
} | |
} | |
} | |
} | |
void ComputerPlayer::print_cardinalities() | |
{ | |
std::string horizontal_line = "-----------------------------------------"; | |
std::cout << std::endl << | |
" 1 2 3 4 5 6 7 8 9 0 " << std::endl << | |
" ---------------------------- " << std::endl; | |
for (int row = 0; row < 10; row++) { // iterate through all the rows | |
std::cout << static_cast<char> (row + 65) << " | "; | |
for (int column = 0; column < 10; column++) { // iterate through all the columns | |
if (column != 0) { // we want this additional space eacg tune except the first | |
std::cout << " "; | |
} | |
std::cout << " " << _strategy.cardinalities[row][column]; // compute the index of the array | |
//to be printed and print it | |
} | |
std::cout << std::endl; | |
} | |
std::cout << std::endl; | |
} | |
void ComputerPlayer::set_max_ship_length() | |
{ | |
_strategy.max_ship_length = std::max_element(_ship_map.begin(), _ship_map.end(), ValueCompare())->second.length(); | |
} | |
std::pair<int, int> ComputerPlayer::get_move() | |
{ | |
std::pair<int,int> move = {0,0}; | |
if (!_intelligent) { | |
get_random_move(move); | |
} | |
else if(_strategy.tracking_ship) { | |
get_tracking_ship_move(move); | |
} | |
else { | |
get_strategic_move(move); | |
} | |
//computer remembers its last move | |
_last_move = move; | |
//update candinalities | |
_strategy.cardinalities[move.first][move.second] = -1; | |
return move; | |
} | |
void ComputerPlayer::get_random_move(std::pair<int,int> &move) | |
{ | |
int row, column; | |
std::random_device rd; | |
std::mt19937 gen(rd()); | |
std::uniform_int_distribution<> random_position(0, 9); | |
do { | |
row = random_position(gen); | |
column = random_position(gen); | |
} while(_target_board->get_char(row,column) != 'o'); | |
move = std::make_pair(row, column); | |
} | |
void ComputerPlayer::set_tracking_position(int &row, int &column) | |
{ | |
row = _strategy.first_hit.first | |
+ _strategy.distance * ORT_TO_VECTOR.at(static_cast<Orientation>(_strategy.ort)).first; | |
column = _strategy.first_hit.second | |
+ _strategy.distance * ORT_TO_VECTOR.at(static_cast<Orientation>(_strategy.ort)).second; | |
} | |
void ComputerPlayer::change_tracking_orientation(int &row, int &column, char &position) | |
{ | |
_strategy.ort += 1; | |
std::cout << "Orientation: " <<_strategy.ort << std::endl; | |
if (_strategy.ort > Orientation::DOWN) { | |
//change dat first hit!!!!! | |
_strategy.ship_hits.erase(_strategy.ship_hits.begin()); | |
_strategy.first_hit = _strategy.ship_hits.front(); | |
_strategy.ort = 0; | |
} | |
_strategy.distance = 0; | |
set_tracking_position(row, column); | |
position = _target_board->get_char(row, column); | |
} | |
void ComputerPlayer::get_tracking_ship_move(std::pair<int,int> &move) | |
{ | |
std::cout << "Trying to get tracking ship move!" << std::endl; | |
int trk_row, trk_column; | |
set_tracking_position(trk_row, trk_column); | |
char current_guess = _target_board->get_char(trk_row, trk_column); | |
while (current_guess == HIT || current_guess == MISS){ | |
//If ship could still possibly be in this orientation | |
if (current_guess == HIT && _strategy.distance < _strategy.max_ship_length) { | |
//then guess the next position | |
_strategy.distance += 1; | |
set_tracking_position(trk_row, trk_column); | |
//if not off of the edge of the board | |
if (trk_row < BOARD_SIDE && trk_column < BOARD_SIDE){ | |
std::cout << "Current Guess: " << current_guess << std::endl; | |
std::cout << "Row: " << trk_row << std::endl; | |
std::cout << "Column: " << trk_column << std::endl; | |
current_guess = _target_board->get_char(trk_row, trk_column); | |
} | |
else { | |
change_tracking_orientation(trk_row, trk_column, current_guess); | |
} | |
} | |
else { | |
change_tracking_orientation(trk_row, trk_column, current_guess); | |
} | |
} | |
move = std::make_pair(trk_row, trk_column); | |
} | |
void ComputerPlayer::get_strategic_move(std::pair<int,int> &move) | |
{ | |
for (int i = 0; i < BOARD_SIDE; i++) { | |
for (int j = 0; j < BOARD_SIDE; j++) { | |
if (_strategy.cardinalities[move.first][move.second] < _strategy.cardinalities[i][j]) { | |
move = std::make_pair(i, j); | |
} | |
} | |
} | |
} | |
void ComputerPlayer::update_strategy(char result) | |
{ | |
if (result == OPEN) { | |
if (_intelligent && !_strategy.tracking_ship) { | |
update_cardinalities(); | |
} | |
} | |
else { | |
if (!_strategy.tracking_ship) { | |
_strategy.num_ship_hits = 0; | |
_strategy.first_hit = _last_move; | |
_strategy.tracking_ship = true; | |
} | |
_strategy.num_ship_hits += 1; | |
_strategy.ship_hits.push_back(_last_move); | |
if(result != HIT) { | |
//if only hit one ship during tracking mode | |
if (_strategy.num_ship_hits == _ship_map.at(result).length()) { | |
update_cardinalities(); | |
reset_tracking(); | |
} | |
//if hit multiple ships | |
else { | |
_strategy.num_ship_hits = _strategy.num_ship_hits - _ship_map.at(result).length(); | |
//Don't know where we first hit the other ship, but will guess it is the next hit | |
_strategy.ship_hits.erase(_strategy.ship_hits.begin()); | |
_strategy.first_hit = _strategy.ship_hits.front(); | |
_strategy.ort = 0; | |
_strategy.distance = 0; | |
} | |
_ship_map.erase(result); | |
set_max_ship_length(); | |
} | |
} | |
} | |
void ComputerPlayer::reset_tracking() | |
{ | |
_strategy.tracking_ship = false; | |
_strategy.num_ship_hits = 0; | |
_strategy.ort = 0; | |
_strategy.distance = 0; | |
_strategy.ship_hits.erase(_strategy.ship_hits.begin(), _strategy.ship_hits.end()); | |
_strategy.first_hit = std::make_pair(-1, -1); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#ifndef _COMPUTER_PLAYER_H | |
#define _COMPUTER_PLAYER_H | |
#include "ship.h" | |
#include "board.h" | |
#include "target_board.h" | |
#include <map> | |
struct ValueCompare { | |
template <typename Lhs, typename Rhs> | |
bool operator() (const Lhs& lhs, const Rhs& rhs) const | |
{ | |
return lhs.second.length() < rhs.second.length(); | |
} | |
}; | |
/* | |
* This is the AI player class. It can either guess randomly, or can make | |
* intelligent choices in sinking ships. It has a pointer to its target board, | |
* to keep track of past moves, and the ship map to keep track of which ships | |
* the player has sunk. | |
* | |
*/ | |
class ComputerPlayer | |
{ | |
public: | |
ComputerPlayer(TargetBoard *target_board, | |
std::map<char, Ship> ship_map, | |
bool intelligent=false); | |
std::pair<int, int> get_move(); | |
//and want to know which ship hit | |
void update_strategy(char result); | |
void print_cardinalities(); | |
private: | |
bool _intelligent; | |
TargetBoard *_target_board; | |
std::map<char, Ship> _ship_map; | |
std::pair<int,int> _last_move; | |
void initialize_cardinalities(); | |
void set_max_ship_length(); | |
void update_cardinalities(); | |
void get_random_move(std::pair<int,int> &move); | |
void get_tracking_ship_move(std::pair<int,int> &move); | |
void set_tracking_position(int &row, int &column); | |
void change_tracking_orientation(int &row, int &column, char &position); | |
void reset_tracking(); | |
void get_strategic_move(std::pair<int,int> &move); | |
struct Strategy { | |
int cardinalities[BOARD_SIDE][BOARD_SIDE]; | |
bool tracking_ship; | |
int max_ship_length; | |
int num_ship_hits; | |
std::vector<std::pair<int,int>> ship_hits; | |
int ort; | |
int distance; | |
std::pair<int,int> first_hit; | |
}; | |
Strategy _strategy; | |
}; | |
#endif /*_COMPUTER_PLAYER_H*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "game_manager.h" | |
#include <iostream> | |
GameManager::GameManager(std::map<char, Ship> ships, | |
bool strategy_mode, | |
bool computer_first): | |
_user_board(ships), | |
_comp_board(ships), | |
_ship_map(ships), | |
_computer_first(computer_first), | |
_computer(&_comp_target_board, ships, strategy_mode) | |
{ | |
} | |
void GameManager::start_game() | |
{ | |
if (_computer_first) { | |
computer_turn(); | |
} | |
while(user_turn() && computer_turn()) { | |
} | |
} | |
bool GameManager::computer_turn() | |
{ | |
std::pair<int,int> comp_move = _computer.get_move(); | |
computer_attack(comp_move); | |
return continue_game(); | |
} | |
bool GameManager::user_turn() | |
{ | |
_user_target_board.print(); | |
_user_board.print(); | |
std::pair<int, int> user_move = get_user_input(); | |
user_attack(user_move); | |
return continue_game(); | |
} | |
bool GameManager::continue_game() | |
{ | |
if (_user_board.all_ships_sunk()) { | |
std::cout <<"Game is over, the evil machine has won!"<< std::endl; | |
return false; | |
} | |
else if (_comp_board.all_ships_sunk()) { | |
std::cout << "Game is over, you have won! Yay!" << std::endl; | |
return false; | |
} | |
return true; | |
} | |
std::pair<int,int> GameManager::get_user_input() | |
{ | |
std::string input1, input2; | |
do { | |
std::cout << "Please input the space you want to attack (q or quit to terminate program): "; | |
std::cin >> input1; | |
if (input1 == "q" || input1 == "quit") { | |
exit(0); | |
} | |
std::cin >> input2; | |
} while (!input_valid(input1, input2)); | |
return get_board_position(input1, input2); | |
} | |
void GameManager::user_attack(std::pair<int,int> &move) | |
{ | |
char result = _comp_board.attack_position(move); | |
std::pair<char,char> printable_move = get_printable_move(move); | |
//TODO convert move to printable | |
if (result == OPEN) { | |
std::cout << "Splash! User shot " << printable_move.first << " " | |
<< printable_move.second << " and missed!" << std::endl; | |
_user_target_board.set_miss(move); | |
} | |
else { | |
std::cout << "Boom! User shot " << printable_move.first | |
<< " " << printable_move.second << " and hit!" << std::endl; | |
_user_target_board.set_hit(move); | |
//If ship is sunk, result returns ship abbreviation | |
if (result != HIT) { | |
std::cout << "Evil Machine says, 'Drat! You sank my " << _ship_map.at(result).name() << "!'" << std::endl; | |
} | |
} | |
} | |
void GameManager::computer_attack(std::pair<int,int> &move) | |
{ | |
char result = _user_board.attack_position(move); | |
std::pair<char,char> printable_move = get_printable_move(move); | |
if (result == OPEN) { | |
std::cout << "Splash! Evil Machine shot " << printable_move.first << " " | |
<< printable_move.second << " and missed!" << std::endl; | |
_comp_target_board.set_miss(move); | |
} | |
else { | |
std::cout << "Boom! Evil Machine shot " << printable_move.first << " " << | |
printable_move.second << " and hit!" << std::endl; | |
_comp_target_board.set_hit(move); | |
if (result != HIT) { | |
std::cout << "Your " << _ship_map.at(result).name() | |
<< " has been sunk!" << std::endl; | |
} | |
} | |
_computer.update_strategy(result); | |
} | |
//Convert row char to integer | |
int GameManager::row_to_int(char r) { | |
return r - 'A'; | |
} | |
//Convert row in integer form to char form | |
char GameManager::int_to_row(int r) { | |
return r + 'A'; | |
} | |
//Convert column in char form to int form | |
int GameManager::column_to_int(char c) { | |
int column; | |
if (c != '0') { | |
column = c - '1'; | |
} | |
else { | |
column = c - '0' + 9; //'0' corresponds to column 9; | |
} | |
return column; | |
} | |
//Convert column in int form to char form | |
char GameManager::int_to_column(int c) { | |
if (c == BOARD_SIDE - 1) { // special case for maximum index | |
c = 0; | |
} | |
else { | |
c++; | |
} | |
return '0' + c; | |
} | |
std::pair<char,char> GameManager::get_printable_move(std::pair<int,int> &move) | |
{ | |
return std::make_pair(int_to_row(move.first), int_to_column(move.second)); | |
} | |
std::pair<int,int> GameManager::get_board_position(std::string &input1, std::string &input2) | |
{ | |
int row = row_to_int(input1.at(0)); | |
int column = column_to_int(input2.at(0)); | |
return std::make_pair(row, column); | |
} | |
bool GameManager::input_valid(std::string &input1, std::string &input2) | |
{ | |
if (input1 == "A" || input1 == "B" || | |
input1 == "C" || input1 == "D" || | |
input1 == "E" || input1 == "F" || | |
input1 == "G" || input1 == "H" || | |
input1 == "I" || input1 == "J") { | |
if (input2 == "1" || input2 == "2" || | |
input2 == "3" || input2 == "4" || | |
input2 == "5" || input2 == "6" || | |
input2 == "7" || input2 == "8" || | |
input2 == "9" || input2 == "0") { | |
//I don't like that I'm generating the integer position twice, but it's | |
//part of validating the user's input | |
std::pair<int,int> move = get_board_position(input1, input2); | |
char position = _user_target_board.get_char(move); | |
if (position == OPEN) { | |
return true; | |
} | |
else { | |
std::cout << "You are trying to attack a position you already hit. Try another position." << std::endl; | |
return false; | |
} | |
} | |
} | |
std::cout << "Invalid input. Please enter space to attack in the format \"A 7\"" << std::endl; | |
return false; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#ifndef _GAME_MANAGER_H | |
#define _GAME_MANAGER_H | |
#include "ship.h" | |
#include "board.h" | |
#include "ocean_board.h" | |
#include "target_board.h" | |
#include "computer_player.h" | |
#include <map> | |
/* | |
* This class manages actually playing the game, with a human player versus | |
* a computer one. This class just needs to be initalized with the set of ships, | |
* which player is going first, and whether or not the computer is intelligent. The | |
* rest is handled under the covers. | |
*/ | |
class GameManager { | |
private: | |
OceanBoard _user_board; | |
OceanBoard _comp_board; | |
TargetBoard _user_target_board; | |
TargetBoard _comp_target_board; | |
std::map<char, Ship> _ship_map; | |
bool _computer_first; | |
ComputerPlayer _computer; | |
bool user_turn(); | |
bool computer_turn(); | |
bool continue_game(); | |
std::pair<int,int> get_user_input(); | |
void user_attack(std::pair<int,int> &move); | |
void computer_attack(std::pair<int,int> &move); | |
bool input_valid(std::string &input1, std::string &input2); | |
std::pair<int,int> get_board_position(std::string &input1, std::string &input2); | |
int column_to_int(char c); | |
int row_to_int(char r); | |
char int_to_column(int c); | |
char int_to_row(int r); | |
std::pair<char,char> get_printable_move(std::pair<int,int> &move); | |
public: | |
GameManager(std::map<char, Ship> ships, bool strategy_mode, bool computer_first); | |
void start_game(); | |
}; | |
#endif /*_GAME_MANAGER_H*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Cassie Tarakajian, ctarakajian@gmail.com | |
CXXFLAGS=-pedantic -Wall -Wextra -std=c++11 -Wabi -Weffc++ -Wctor-dtor-privacy -Wold-style-cast -Woverloaded-virtual -Wsign-promo | |
all: battle | |
battle: battle.cc ship.o board.o ocean_board.o target_board.o computer_player.o game_manager.o | |
ship.o: ship.h ship.cc | |
board.o: board.h ship.h board.cc | |
ocean_board.o: ship.h board.h ocean_board.h ocean_board.cc | |
target_board.o: ship.h board.h target_board.h target_board.cc | |
computer_player.o: ship.h board.h target_board.h computer_player.cc | |
game_manager.o: ship.h board.h target_board.h ocean_board.h computer_player.h game_manager.cc | |
clean: | |
rm -rf battle *.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "ocean_board.h" | |
#include <iostream> | |
OceanBoard::OceanBoard(std::map<char, Ship> ship_map): Board(), _ship_map(ship_map) | |
{ | |
place_ships(); | |
} | |
void OceanBoard::print() | |
{ | |
//this is not the most efficient way to print, but it's clear. | |
//put ships in a printable form | |
std::vector<std::string> printable_ships; | |
for (std::map<char, Ship>::iterator ship_it = _ship_map.begin(); | |
ship_it != _ship_map.end(); | |
++ship_it) { | |
std::string abbreviation(1, ship_it->first); | |
printable_ships.push_back(abbreviation + ": " + ship_it->second.name()); | |
} | |
std::string horizontal_line = "-----------------------------------------"; | |
std::cout << std::endl << | |
" 1 2 3 4 5 6 7 8 9 0 " << std::endl << | |
" ---------------------------- " << std::endl; | |
for (size_t row = 0; row < BOARD_SIDE; row++) { // iterate through all the rows | |
//This prints out A,B,C,D,E,F | |
std::cout << static_cast<char> (row + 65) << " | "; | |
for (int column = 0; column < BOARD_SIDE; column++) { // iterate through all the columns | |
if (column != 0) { // we want this additional space eacg tune except the first | |
std::cout << " "; | |
} | |
std::cout << " " << _positions[row][column]; // compute the index of the array | |
//to be printed and print it | |
} | |
if (row < printable_ships.size()) { | |
std::cout << " " + printable_ships.at(row); | |
} | |
std::cout << std::endl; | |
} | |
std::cout << std::endl; | |
} | |
void OceanBoard::place_ships() | |
{ | |
//Randomly place ships on board | |
srand(time(0)); | |
for (std::map<char, Ship>::iterator ship_it = _ship_map.begin(); | |
ship_it != _ship_map.end(); | |
/*want to move to next ship ONLY if we can place it*/) { | |
//pick a random position | |
Orientation ort = static_cast<Orientation>(rand() % 4); //Orientation of ship, left, down, up, or right | |
unsigned int row = rand() % BOARD_SIDE; | |
unsigned int column = rand() % BOARD_SIDE; | |
//int ort_int = ort; | |
//std::cout << "Orientation: " << ort << std::endl; | |
//std::cout << "Row: " << row << std::endl; | |
//std::cout << "Column: " << column << std::endl; | |
//assume we can't place ship | |
bool overlap = true; | |
unsigned int ship_length = ship_it->second.length(); | |
char abbreviation = ship_it->first; | |
//This could maybe be refactored??? | |
if (ort == Orientation::LEFT) { | |
if ((column + 1) >= ship_length) { //if not off edge | |
overlap = false; //to see if placing ship here will overlap | |
for (unsigned int i = 0; i < ship_length; i++) { | |
if (get_char(row, column - i) != 'o') { | |
overlap = true; | |
} | |
} | |
} | |
if (!overlap) { //If ship doesn't overlap or fall off edge, put it on | |
for (unsigned int i = 0; i < ship_length; i++) { | |
set_char(abbreviation, row, column - i); | |
} | |
++ship_it; | |
} | |
} | |
else if (ort == Orientation::RIGHT) { | |
if ((BOARD_SIDE - (column + 1)) >= ship_length) { //if not off edge | |
overlap = false; //to see if placing ship here will overlap | |
for (unsigned int i = 0; i < ship_length; i++) { | |
if (get_char(row, column + i) != 'o') { | |
overlap = true; | |
} | |
} | |
} | |
if (!overlap) { //If ship doesn't overlap or fall off edge, put it on | |
for (unsigned int i = 0; i < ship_length; i++) { | |
set_char(abbreviation, row, column + i); | |
} | |
++ship_it; | |
} | |
} | |
else if (ort == Orientation::DOWN) { | |
if ((BOARD_SIDE - (row + 1)) >= ship_length) { //if not off edge | |
overlap = false; //to see if placing ship here will overlap | |
for (unsigned int i = 0; i < ship_length; i++) { | |
if (get_char(row + i, column) != 'o') { | |
overlap = true; | |
} | |
} | |
} | |
if (!overlap) { //If ship doesn't overlap or fall off edge, put it on | |
for (unsigned int i = 0; i < ship_length; i++) { | |
set_char(abbreviation, row + i, column); | |
} | |
++ship_it; | |
} | |
} | |
else if (ort == Orientation::UP) { | |
if ((row + 1) >= ship_length) { //if not off edge | |
overlap = false; //to see if placing ship here will overlap | |
for (unsigned int i = 0; i < ship_length; i++) { | |
if (get_char(row - i, column) != 'o') { | |
overlap = true; | |
} | |
} | |
} | |
if (!overlap) { //If ship doesn't overlap or fall off edge, put it on | |
for (unsigned int i = 0; i < ship_length; i++) { | |
set_char(abbreviation, row - i, column); | |
} | |
++ship_it; | |
} | |
} | |
} | |
} | |
//Sets character at position on the board (in character array) | |
char OceanBoard::attack_position(std::pair<int,int> &move) | |
{ | |
int row = move.first; | |
int column = move.second; | |
char position = _positions[row][column]; | |
if (position == OPEN) { | |
_positions[row][column] = MISS; | |
} | |
else { | |
_ship_map.at(position).inc_damage(); | |
_positions[row][column] = HIT; | |
//don't want to reveal the ship hit UNLESS ship is sunk | |
if (!_ship_map.at(position).is_sunk()) { | |
position = HIT; | |
} | |
} | |
return position; | |
} | |
bool OceanBoard::all_ships_sunk() | |
{ | |
bool all_sunk = true; | |
for (std::map<char, Ship>::iterator ship_it = _ship_map.begin(); | |
ship_it != _ship_map.end(); | |
++ship_it) { | |
if (!ship_it->second.is_sunk()) { | |
all_sunk = false; | |
return all_sunk; | |
} | |
} | |
return all_sunk; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#ifndef _OCEAN_BOARD_H | |
#define _OCEAN_BOARD_H | |
#include "board.h" | |
#include <string> | |
/* | |
* An Ocean Board is the lower board on a battleship board, which contains | |
* the ships for a player. The ships are placed on the grid, and they | |
* are storied in a map, which maps the one character code on the | |
* grid to a Ship object, which is used to figure out whether or not | |
* a Ship has been sunk. | |
* | |
*/ | |
class OceanBoard : public Board | |
{ | |
private: | |
std::map<char, Ship> _ship_map; | |
void place_ships(); | |
public: | |
OceanBoard(std::map<char, Ship> ship_map); | |
bool all_ships_sunk(); | |
virtual void print(); | |
//Returns HIT if hit, OPEN if miss, ship code if sunk a ship | |
char attack_position(std::pair<int,int> &move); | |
}; | |
#endif /*_OCEAN_BOARD_H*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "ship.h" | |
//Constructor: sets length and name according to parameters, sets damage to zero | |
Ship::Ship(int length, const std::string name): _length(length), _name(name), _damage(0) | |
{} | |
bool Ship::operator<(Ship other) const | |
{ | |
return length() > other.length(); | |
} | |
//Increments the damage by 1 | |
void Ship::inc_damage() | |
{ | |
_damage += 1; | |
} | |
//Tests if a ship is sunk, aka every spot on the ship has been hit | |
bool Ship::is_sunk() const | |
{ | |
//If the length and damage of the ship are equal | |
if (_length - _damage == 0) { | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
//Returns the name of the ship | |
const std::string Ship::name() const | |
{ | |
return _name; | |
} | |
//Returns the length of the ship | |
int Ship::length() const | |
{ | |
return _length; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#ifndef _SHIP_H | |
#define _SHIP_H | |
#include <string> | |
/** | |
* A Ship object contains the name of a ship, the length of the ship, and | |
* how much damage has been done to the ship. | |
*/ | |
class Ship | |
{ | |
private: | |
int _length; //length of ship | |
std::string _name; //name of ship | |
int _damage; //damage done to ship | |
public: | |
Ship(int length, const std::string name); //constructor | |
bool operator<(Ship other) const; | |
void inc_damage(); //increments damage by 1 | |
bool is_sunk() const; //tests if ship is sunk (all spots have been hit) | |
const std::string name() const; //returns name of ship | |
int length() const; //returns length of ship | |
}; | |
#endif /*_SHIP_H*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "target_board.h" | |
#include <iostream> | |
void TargetBoard::print() | |
{ | |
//formatting | |
std::string horizontal_line = "-----------------------------------------"; | |
std::cout << std::endl << " 1 2 3 4 5 6 7 8 9 0" << std::endl | |
<< " ----------------------------" << std::endl; | |
for (int row = 0; row < 10; row++) { // iterate through all the rows | |
//this prints out A-J | |
std::cout << static_cast<char> (row + 65) << " | "; | |
for (int column = 0; column < 10; column++) { // iterate through all columns | |
if (column != 0) { //want this additional space each time except the first | |
std::cout << " "; | |
} | |
std::cout << " " << _positions[row][column]; // compute the index of the | |
//array to be printed and print it | |
} | |
std::cout << std::endl; | |
} | |
std::cout << std::endl; | |
} | |
//Sets character at position on the board (in character array) | |
void TargetBoard::set_hit(std::pair<int,int> &position) | |
{ | |
_positions[position.first][position.second] = HIT; | |
} | |
//Sets character at position on the board (in character array) | |
void TargetBoard::set_miss(std::pair<int,int> &position) | |
{ | |
_positions[position.first][position.second] = MISS; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Cassie Tarakajian, ctarakajian@gmail.com | |
#include "board.h" | |
#ifndef _TARGET_BOARD_H | |
#define _TARGET_BOARD_H | |
/* | |
* A Target Board is the upper board on a Battleship game, which | |
* stores the various attacks that a user has made. This user can | |
* store these moves as a hit or a miss, and can therfore use it to | |
* plan their next move. | |
*/ | |
class TargetBoard: public Board | |
{ | |
private: | |
public: | |
virtual void print(); | |
void set_hit(std::pair<int,int> &position); | |
void set_miss(std::pair<int,int> &position); | |
}; | |
#endif /*_TARGET_BOARD_H*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment