Skip to content

Instantly share code, notes, and snippets.

@catarak
Last active August 29, 2015 14:01
Show Gist options
  • Save catarak/0d6be52f2d21d8a07bad to your computer and use it in GitHub Desktop.
Save catarak/0d6be52f2d21d8a07bad to your computer and use it in GitHub Desktop.
Battleship
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.
//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();
}
//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;
}
//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*/
//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);
}
//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*/
//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;
}
//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*/
# 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
//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;
}
//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*/
//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;
}
//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*/
//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;
}
//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