Commit df76b5d7 authored by Leopold Keller's avatar Leopold Keller
Browse files

commit

parent 59249af5
{
"CurrentProjectSetting": null
}
\ No newline at end of file
#include "battleship.h"
/**
* \file
* \date 08.12.2019
* \author Michael Roth
* This is the header file for the \ref Battleship class, which is the main
* class for the game.
* This class holds two \ref Player objects as well as their corresponding \ref
* GameBoard objects.
*/
#ifndef BATTLESHIP_H
#define BATTLESHIP_H
#include "gameboard.h"
#include "player.h"
#include <string>
using std::string;
#include <array>
using std::array;
/**
* @brief The Battleship class
*
* This is the main class of the game. An object is constructed by supplying two
* player names. One game round is then started by simply calling the \ref play
* function.
*
* The Players and their boards are stored in Arrays to make the implementation
* of the game logic easier.
*
* **Note:** If you find that confusing, you are welcome the change the Arrays
* into something else, or have two attributes per player.
*/
class Battleship
{
public:
/**
* @brief Battleship constructor
* @param[in] player1Name Name of the first player
* @param[in] player2Name Name of the second player
*/
Battleship(const string& player1Name, const string& player2Name);
/**
* @brief Play one game of Battleships!
*
* This functions shall do the following:
*
* - Initialize both players game boards:
* - The ships shall be placed at randomized locations
* - The 'cheat sheets' shall be empty
* - This can easily be achieved by creating new \ref GameBoard objects
* and storing them in \ref m_boards, overwriting the old boards which may be
* there already
*
* Then, do the following in a loop:
* 1. Determine the active Player either by random coin toss or first player
* always gets to start
* 2. Print both the game board and the 'cheat sheet' for the active player
* 3. Ask the active player a location she wants to shoot at
* 4. Call the \ref GameBoard::hit function for that location on the
* **inactive** Player's board
* 5. Inform the active player if she hit something and make the correct
* mark on the cheat sheet
* 6. Switch the players, e.g. active player becomes inactive and inactive
* Player becomes active
* 7. Repeat Steps 1 to 6 until one player has lost
* 8. Exit the function
*
*/
void play();
private:
/**
* @brief Array containing the two \ref Player objects
*/
array<Player, 2> m_players;
/**
* @brief Array containing the two \ref GameBoard objects, one for each player
*/
array<GameBoard, 2> m_boards;
};
#endif // BATTLESHIP_H
#include "gameboard.h"
#include <iostream>
#include <iomanip>
#include <random>
using namespace std;
using std::array;
GameBoard::GameBoard(){
for(int i=0; i<10;i++){
for(int j=0; j<10;j++)
m_enemyBoard[i][j]='.';
}
}
void GameBoard::printBoard()
{
for (int jt= 0; jt<= m_enemyBoard.size(); jt++){
}
for (int it = 0; it < 10; it++)
{
for (int kt = 0; kt < 10; kt++)
{
if (m_enemyBoard[it][kt] == *".")
cout << ".";
if (m_enemyBoard[it][kt] == *"X")
cout << "X";
if (m_enemyBoard[it][kt] == *"o")
cout << "o";
if (it == 9)
cout << "\n";
}
}
cout << "\n\n";
}
void GameBoard::randomPlaceShips(){
random_device rnd_device;
mt19937 mersenne_engine{ rnd_device() };
uniform_int_distribution<int> dist{ 0, 9 };
uniform_int_distribution<int> dist2{ 0, 3 };
auto gen = [&dist, &mersenne_engine]() {
return dist(mersenne_engine);
};
auto gen2 = [&dist2, &mersenne_engine]() {
return dist2(mersenne_engine);
};
int finalx;
int finaly;
Direction finaldir;
//Dreadnought
while(true){
int x=gen();
int y=gen();
int direction=gen2();
Direction dir;
if(direction==0) dir=Direction::north; else if(direction==1)dir=Direction::east; else if(direction==2) dir=Direction::south; else if(direction==3) dir=Direction::west;
if(dir==Direction::north && y-4 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y-1]=='.' && m_enemyBoard[x][y-2]=='.' && m_enemyBoard[x][y-3]=='.' && m_enemyBoard[x][y-4]=='.'){ m_ships[0] = Ship(x,y,5,Direction::north); break; finalx=x;finaly=y; finaldir=dir;}
if(dir==Direction::west && x-4 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x-1][y] == '.' && m_enemyBoard[x-2][y] == '.' && m_enemyBoard[x-3][y] == '.' && m_enemyBoard[x-4][y] == '.'){ m_ships[0] = Ship(x,y,5,Direction::west); break; finalx=x;finaly=y; finaldir=dir;}
if(dir==Direction::east && x+4 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x+1][y] == '.' && m_enemyBoard[x+2][y] == '.' && m_enemyBoard[x+3][y] == '.' && m_enemyBoard[x+4][y] == '.'){ m_ships[0] = Ship(x,y,5,Direction::east); break; finalx=x;finaly=y; finaldir=dir;}
if(dir==Direction::south && y+4 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y+1] == '.' && m_enemyBoard[x][y+2] == '.' && m_enemyBoard[x][y+3] == '.' && m_enemyBoard[x][y+4] == '.'){ m_ships[0] = Ship(x,y,5,Direction::south); break; finalx=x;finaly=y; finaldir=dir;}
}
for(int i=0; i<5; i++){
m_enemyBoard[finalx][finaly]= '1';
if(finaldir==Direction::north) finaly--;
if(finaldir==Direction::south) finaly++;
if(finaldir==Direction::east) finalx++;
if(finaldir==Direction::west) finalx--;
}
//Cruisers
for(int i=2; i<=3;i++){
while(true){
int x=gen();
int y=gen();
int direction=gen2();
Direction dir;
if(direction==0) dir=Direction::north; else if(direction==1)dir=Direction::east; else if(direction==2) dir=Direction::south; else if(direction==3) dir=Direction::west;
if(dir==Direction::north && y-3 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y-1]=='.' && m_enemyBoard[x][y-2]=='.' && m_enemyBoard[x][y-3]=='.'){ m_ships[i] = Ship(x,y,4,Direction::north);
for(int j=0; j<4; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;
if(dir==Direction::west && x-3 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x-1][y] == '.' && m_enemyBoard[x-2][y] == '.' && m_enemyBoard[x-3][y] == '.'){ m_ships[i] = Ship(x,y,4,Direction::west);
for(int j=0; j<4; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::east && x+3 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x+1][y] == '.' && m_enemyBoard[x+2][y] == '.' && m_enemyBoard[x+3][y] == '.'){ m_ships[i] = Ship(x,y,4,Direction::east);
for(int j=0; j<4; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::south && y+3 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y+1] == '.' && m_enemyBoard[x][y+2] == '.' && m_enemyBoard[x][y+3] == '.' ){ m_ships[i] = Ship(x,y,4,Direction::south);
for(int j=0; j<4; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
}
}
}
//Destroyers
for(int i=4; i<=6;i++){
while(true){
int x=gen();
int y=gen();
int direction=gen2();
Direction dir;
if(direction==0) dir=Direction::north; else if(direction==1)dir=Direction::east; else if(direction==2) dir=Direction::south; else if(direction==3) dir=Direction::west;
if(dir==Direction::north && y-2 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y-1]=='.' && m_enemyBoard[x][y-2]=='.'){ m_ships[i] = Ship(x,y,3,Direction::north);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;
if(dir==Direction::west && x-2 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x-1][y] == '.' && m_enemyBoard[x-2][y] == '.'){ m_ships[i] = Ship(x,y,3,Direction::west);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::east && x+2 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x+1][y] == '.' && m_enemyBoard[x+2][y] == '.'){ m_ships[i] = Ship(x,y,3,Direction::east);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::south && y+2 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y+1] == '.' && m_enemyBoard[x][y+2] == '.'){ m_ships[i] = Ship(x,y,3,Direction::south);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
}
}
}
//Submarines
for(int i=7; i<=10;i++){
while(true){
int x=gen();
int y=gen();
int direction=gen2();
Direction dir;
if(direction==0) dir=Direction::north; else if(direction==1)dir=Direction::east; else if(direction==2) dir=Direction::south; else if(direction==3) dir=Direction::west;
if(dir==Direction::north && y-1 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y-1]=='.'){ m_ships[i] = Ship(x,y,2,Direction::north);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;
if(dir==Direction::west && x-1 >=0 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x-1][y] == '.'){ m_ships[i] = Ship(x,y,2,Direction::west);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::east && x+1 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x+1][y] == '.'){ m_ships[i] = Ship(x,y,2,Direction::east);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
if(dir==Direction::south && y+1 <=9 && m_enemyBoard[x][y] == '.' && m_enemyBoard[x][y+1] == '.'){ m_ships[i] = Ship(x,y,2,Direction::south);
for(int j=0; j<3; j++){
m_enemyBoard[x][y]= (char)i;
if(dir==Direction::north) y--;
if(dir==Direction::south) y++;
if(dir==Direction::east) x++;
if(dir==Direction::west) x--;
}
break;}
}
}
}
}
\ No newline at end of file
/**
* \file
* \date 08.12.2019
* \author Michael Roth
* This is the header file for the \ref GameBoard class which represents the
* game board(s) for a single player.
*/
#ifndef GAMEBOARD_H
#define GAMEBOARD_H
#include "ship.h"
#include <array>
using std::array;
/**
* @brief The GameBoard class
*
* This class contains everything that a single player needs in order to play.
* First of all, this class contains an array of one's own ships. Furthermore it
* contains a map of locations where Player A has tried to hit ships from Player
* B.
*
* The primary functions of this board are \ref hit which represents
* a shot from the enemy. The \ref hit function handles the shot and returns
* true if any ship was actually hit by that shot. The other important function
* is \ref mark which is used to mark positions where the player tried to hit
* enemy ships.
*
* Every Player needs one instance of this in order to:
*
* - Have a registry where her own ships are located
* - Have an idea where she has already tried to hit her enemy's ships
*
* Note that while the enemy board is represented as a two dimensional array of
* `char` values, the own registry exists simply as `vector` of \ref Ship
* objects.
*
* Also, this class has the neccessary functions functions to print both of
* those boards.
*
* Note that Battleships is played on a 10 x 10 board, with valid rows and
* columns ranging from 0 to 9.
*/
class GameBoard
{
public:
/**
* @brief GameBoard constructor
*
* This shall initialize the enemy board (attribute \ref m_enemyBoard) with
* dot characters '.'. The player's ships can either be placed here also, or
* later by calling \ref randomPlaceShips.
*
*
*/
GameBoard();
/**
* @brief Prints the player's board.
*
* This function prints the player's board to the console screen. You are
* relatively free to be creative here, but make sure that all ships are
* displayed properly.
*
* Some suggestions are:
*
* - A location containing water is represented by printing '.'
* - A location containing an intact (e.g. undamaged) ship part is
* represented by its ship number (e.g. the ships index in the array)
* - A location containing a damaged ship part of an **unsunken ship** is
* represented by an 'X'
* - A location containing a part of a sunken ship is represented by an 'S'
*
* You can, as mentioned before, use other characters, but those four types of
* locations should be distinguishable from another.
*/
void printBoard();
/**
* @brief Prints the 'cheat sheet', containing markings where this player has
* hit or missed.
*
* When trying to hit the other player's ships, it does not make sense to
* shoot the same location more than once. In order to 'memorize' those
* locations, the two-dimensional array is used. In there, the characters have
* the following meanings:
*
* - '.' represents a location which has not yet beens shot
* - 'X' represents a hit ship at that locations
* - 'O' represents a shot into open water
*
* As with \ref printBoard you are free to change the characters if you fancy
* something else.
*/
void printEnemyBoard();
/**
* @brief The enemy player's shot on our board
* @param[in] row The row where we are being shot
* @param[in] col The column where we are being shot
* @return True if the shot hit any of our ships, false otherwise.
*
* This function is called when the enemy is trying to hit any of our ships.
* Generally a shot is directed at a specific location denoted by `row` and
* `col`.
*
* If there is a ship part at that location, that part shall be damaged by
* that shot.
*
* \see Part::setDamaged
* \see printBoard
*/
bool hit(int row, int col);
/**
* @brief Mark locations on the enemy board where we already shot at
* @param[in] row The row where we shot
* @param[in] col The column where we shot
* @param[in] wasHit True if the shot was a hit (e.g. we hit a ship)
*
* This function shall make a mark on the 'cheat sheet'. The mark shall be
* different depending on if we hit something there.
*
* \see hit
* \see printEnemyBoard
*/
void mark(int row, int col, bool wasHit);
/**
* @brief Randomly place ships.
*
* This function randomly places ships on the board, e.g. it populates the
* \ref m_ships vector.
*
* The following ships shall be placed:
* - 1 'Dreadnought' with 5 parts
* - 2 'Cruisers' with 4 parts
* - 3 'Destroyers' with 3 parts
* - 4 'Submarines' with 2 parts
*
* The ships shall be placed so that:
* - No ships intersect each other
* - No ship has parts outside the playing area
* - When this is called for both players, the resulting placements shall be
* different
*
*/
void randomPlaceShips();
/**
* @brief Test if all ships are sunk
* @return True if all ships on this board are sunk
*
* This function is used to determine if the player hast lost the game. As a
* reminder: The player has lost the game when she has no floating ship left.
*/
bool allShipsSunk();
private:
/**
* @brief The player's ships
*
* This is collection of the player's ships. Since there are always 10 ships
* on the board, we can use a simple `std::array`.
*/
array<Ship, 10> m_ships;
/**
* @brief The 'cheat sheet'
*
* This is a two-dimensional Array of `char` values. What this means is that
* we use an `std::array<char, 10>` to represent a row. Then, a collection of
* 10 of such rows finally is a grid, our two-dimensional structure.
*
* So the datatype for this is `array< array<char, 10>, 10 >`.
*
*/
array<array<char, 10>, 10> m_enemyBoard;
};
#endif // GAMEBOARD_H
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#include "battleship.h"
int main()
{
return 0;
}
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
battleship.cpp \
gameboard.cpp \
main.cpp \
part.cpp \
player.cpp \
ship.cpp
HEADERS += \
battleship.h \
gameboard.h \
part.h \
player.h \
ship.h
#include "part.h"
/**
* \file
* \date 08.12.2019
* \author Michael Roth
* This is the header file for the \ref Part class.
* The \ref Part class is used to represent on piece (or part)
* of a \ref Ship for the \ref Battleship game
*/
#ifndef PART_H
#define PART_H
/**
* @brief The Part class represents a part of a \ref Ship in the context of the
* \ref Battleship game.
*
* One object of Part is located at a specific * position denoted by row and
* column. Multiple Part objects make up one object of \ref Ship, as a matter of
* fact there is a composition association between those two.
*
* A ship's part has a status which indicates wether it is damaged or not.
* A part of a ship does neither 'know' to which ship it belongs neither if that
* ship is already sunk or not.
*
*/
class Part
{
public:
/**
* @brief Part constructor
* @param[in] row The grid row on which this part is to be placed
* @param[in] col The grid column on which this part is to be placed
*
* This is supposed to be the only Part constructor. The values for \ref m_row
* and \ref m_col shall be assigned from `row` and `col` respectively.
* The status \ref m_status shall be initialized with 0. indicating 'no
* damage'.
*/
Part(int row, int col);
/**
* @brief Returns wether or not this part is damaged
* @return True if damaged, False if undamaged
*
* Note that there is no extra status for a sunken \ref Ship.
* By definition, a ship is sunk when all parts were hit, e.g. are damaged.
*/
bool isDamaged() const;
/**
* @brief Sets the status of this part to a valued representing 'damaged'.
*
* The attribute \ref m_status is supposed to be 0 if there is no damage,
* and 1 if this part was damaged.
* This method shall thereby set \ref m_status to 1.
*/
void setDamaged();
/**
* @brief Returns the row with which this part was constructed
* @return The row with which this part was constructed
*/