-1

Im using c++ 11,

I need to save/load array to and from file. Its the battleship game its need to be done for both user and computer array but i have no idea how to start with this task.

I want to use code in class method to print it in file and get in front it but i go errors.

Any help or clue would be so much for me.

#include "Board.h"
#include <iostream>
#include <fstream>



Board::Board()
{
    for (int i=0; i<BOARD_SIZE; i++)
        for (int j=0; j<BOARD_SIZE; j++)
            board[i][j]=WATER;

    for (int i=0; i<SHIPS_NUM; i++)
        ship.push_back(Ship(SHIP_LENGTH[i], SHIP_NAME[i]));
}


Board::Board(const Board &oldBoard)
{
    for (int i=0; i<BOARD_SIZE; i++)
        for (int j=0; j<BOARD_SIZE; j++)
            board[i][j]=oldBoard.board[i][j];
    ship = oldBoard.ship;
}

// operator przypisania (kopiowanie)
Board& Board::operator=(const Board &right)
{
    if (this!=&right)
    {
        for (int i=0; i<BOARD_SIZE; i++)
            for (int j=0; j<BOARD_SIZE; j++)
                board[i][j]=right.board[i][j];
        ship = right.ship;
    }

    return *this;

}


int Board::getNumHits()
{
    int count=0;

    for (int i=0; i<BOARD_SIZE; i++)
        for (int j=0; j<BOARD_SIZE; j++)
            if (board[i][j]==HITTED)
                count++;

    return count;
}

void Board::printPrivateBoard()
{
    std::cout<<"  A B C D E F G H I J\n";
    for (int i=0; i<BOARD_SIZE; i++)
    {
        std::cout<<i<<" ";
        for (int j=0; j<BOARD_SIZE; j++)
        {
            if (board[i][j]==HITTED || board[i][j]==MISSED)
                std::cout<<board[i][j]<<" ";
            else
                std::cout<<UNKNOWN<<" ";
        }
        std::cout<<std::endl;
    }
}


void Board::printPublicBoard()
{
    std::cout<<"  A B C D E F G H I J\n";
    for (int i=0; i<BOARD_SIZE; i++)
    {
        std::cout<<i<<" ";
        for (int j=0; j<BOARD_SIZE; j++)
        {
            std::cout<<board[i][j]<<" ";
        }
        std::cout<<std::endl;
    }

}


char Board::getSpaceValue(int x, int y)
{
    return board[y][x];
}


bool Board::recordHit(int x, int y)
{
    for (int i=0; i<SHIPS_NUM; i++)
    {
        if (ship[i].recordHit(x, y))
        {
            board[y][x]=HITTED; 
            
            if (ship[i].isShipSunk())
                std::cout<<"You sunk the "<<ship[i].getName()<<"!\n";
            return true;
        }
    }
            board[y][x]=MISSED;
    return false;
}


bool Board::placeShip(int shipNum, int x, int y, bool isHorizontal)
{
 
    if (x>=BOARD_SIZE || y>=BOARD_SIZE)
        return false;

    
    if (ship[shipNum].getX()>=0 && ship[shipNum].getY()>=0)
        return false;

   
    for (int i=0; i<ship[shipNum].getSize(); i++)
    {
        
        
        if ((isHorizontal && board[y][x+i]!=WATER) ||
            (!isHorizontal && board[y+i][x]!=WATER))
            return false;
        
        if ((isHorizontal && (x+i)>=BOARD_SIZE) ||
            (!isHorizontal && (y+i)>=BOARD_SIZE))
            return false;
    }

   
    for (int i=0; i<ship[shipNum].getSize(); i++)
    {
        if (isHorizontal)
            board[y][x+i]=SHIP;
        else
            board[y+i][x]=SHIP;
    }

    
    ship[shipNum].setPosition(x, y, isHorizontal);

    return true;
}



void saveBoard(Board &b1,Board &b2) {
    std::fstream file(FILE_NAME.c_str(),std::ios::trunc|std::ios::out);
    


    file.close();

}

And the header file

#include <vector>
#include "Constants.h"
#include "Ship.h"


#ifndef STATKI_BOARD_H
#define STATKI_BOARD_H

class Board{
private:
    char board[BOARD_SIZE][BOARD_SIZE];
    std::vector<Ship> ship;
public:
    Board();
    Board(const Board &boardCopy);
    Board& operator=(const Board &boardRight);
    ~Board() {return;};
    int getNumHits();
    void printPrivateBoard();
    void printPublicBoard();
    char getSpaceValue(int x, int y);
    bool recordHit(int x, int y);
    bool placeShip(int shipNum, int x, int y, bool isHorizontal);
    void saveBoard(Board &b1,Board &b2);
};

#endif

Constants

#include <string>

#ifndef STATKI_CONSTANTS_H
#define STATKI_CONSTANTS_H
// game constants
const int SHIPS_NUM = 4;
const std::string SHIP_NAME[SHIPS_NUM] = {"Destroyer","Cruiser","Battleship","Carrier"};
const int SHIP_LENGTH[SHIPS_NUM] = {2,3,4,5};
const int TOTAL_SHIP_SPACES = 17;
const int BOARD_SIZE = 10;
const std::string FILE_NAME = "ships_board.txt";


// board constants
const char MISSED = '@';
const char HITTED = 'X';
const char UNKNOWN = '?';
const char SHIP = '_';
const char WATER = '~';


// to convert char to board position
const int LETTER_CHAR_OFFSET=65;
const int NUMBER_CHAR_OFFSET=48;


#endif //STATKI_CONSTANTS_H

Ship object

#include "Ship.h"
#include "Constants.h"

// Konstruktor inicjalizuje odrazu statek tylko wtedy
// gdy podana jest nazwa statku oraz jego wielkosc
Ship::Ship(int size, std::string name) {
    shipSize = size;
    shipName = name;
    shipFiledSymbol = new char[shipSize];
    for (int i = 0; i < shipSize ; ++i) {
        shipFiledSymbol[i] = SHIP; // ustawia wysztkie pola statku na nie trafione
    }
    xCord = -1; // Nie jest jeszcze na planszy dlatego -1
    yCord = -1; // Nie jest jeszcze na planszy dlatego -1
    isHorizontal=true;
    isSunk=false;
}

Ship::Ship(int size, std::string name, int x, int y, bool sunk, bool horizontal) {
    shipSize = size;
    shipName = name;
    shipFiledSymbol = new char[shipSize];
    for (int i = 0; i < shipSize ; ++i) {
        shipFiledSymbol[i] = SHIP; // ustawia wysztkie pola statku na nie trafione
    }
    xCord = x;
    yCord = y;
    isSunk = sunk;
    isHorizontal = horizontal;
}

// konstruktor kopiujacy
Ship::Ship(const Ship &shipCopy) {
    shipSize = shipCopy.getSize();
    shipName = shipCopy.getName();
    shipFiledSymbol = new char[shipSize];
    for (int i = 0; i < shipSize ; ++i) {
        shipFiledSymbol[i] = SHIP; // ustawia wysztkie pola statku na nie trafione
    }
    xCord = shipCopy.getX();
    yCord = shipCopy.getY();
    isSunk = shipCopy.isShipSunk();
    isHorizontal = shipCopy.isShipHorizontal();
}
// operator przypisujacy
Ship& Ship::operator=(const Ship &rightShip) {
    // Jeli lewa strona nie rowna sie prawej
    if (this!=&rightShip){
        shipSize = rightShip.getSize();
        shipName = rightShip.getName();
        shipFiledSymbol = new char[shipSize];
        // uswanie wczesniej zaalkowana pamieci przed nowa alokacja
        if (shipSize>0){
            delete [] shipFiledSymbol;
        }
        for (int i = 0; i < shipSize ; ++i) {
            shipFiledSymbol[i] = SHIP; // ustawia wysztkie pola statku na nie trafione
        }
        xCord = rightShip.getX();
        yCord = rightShip.getY();
        isSunk = rightShip.isShipSunk();
        isHorizontal = rightShip.isShipHorizontal();
    }
    return *this;
}


// destructor usuwa dynamicznie zaalokowana pamiec
Ship::~Ship()
{
    if (shipSize > 0)
        delete [] shipFiledSymbol;
}

int Ship::getSize() const {
    return shipSize;
}

std::string Ship::getName() const {
    return shipName;
}

int Ship::getX() const{
    return xCord;
}
int Ship::getY() const{
    return yCord;
}

bool Ship::isShipSunk() const{
    return isSunk;
}

bool Ship::isShipHorizontal() const{
    return isHorizontal;
}

void Ship::setPosition(int x, int y, bool horizontal) {
    xCord = x;
    yCord = y;
    isHorizontal=horizontal;
    return;
}

// funkcja zwraca informacje czy statek zostal trafiony
bool Ship::recordHit(int hitX, int hitY) {
    bool checkHorizontal = isHorizontal && (hitX<xCord || hitX>= xCord+shipSize || yCord != hitY);
    bool checkVertical = !isHorizontal && (hitY<yCord || hitY>= yCord+shipSize || xCord != hitX);
    // Jezli ktorys z tych warunkow jest prawdziy to statek nie zostal trafiony
    if(checkHorizontal || checkVertical)
        return false;
    else{
    // Jesli pierwszy warunek sie nie spelni to statek jest trafiony
        if(isHorizontal){
            shipFiledSymbol[hitX-xCord]=HITTED;
        }else{
            shipFiledSymbol[hitY-yCord]=HITTED;
        }
    }

    // Sprawdzanie czy statek zatonal
    isSunk=true;
    for (int i=0; i<shipSize; i++)
        if (shipFiledSymbol[i]==SHIP)
            isSunk=false;

    return true;
}

Ship header

#include <string>

#ifndef STATKI_SHIP_H
#define STATKI_SHIP_H

class Ship{
private:
    char* shipFiledSymbol;
    int shipSize;
    int xCord,yCord;
    bool isSunk;
    bool isHorizontal;
    std::string shipName;
public:
    Ship(int size,std::string name);
    Ship(int size,std::string name,int x,int y,bool sunk,bool horizontal);
    Ship(const Ship &shipCopy);
    Ship& operator=(const Ship &rightShip);
    ~Ship();

    int getSize() const;
    std::string getName() const;
    int getX() const;
    int getY() const;
    bool isShipSunk() const;
    bool isShipHorizontal() const;

    void setPosition(int x, int y, bool horizontal);
    bool recordHit(int hitX, int hitY);
};

#endif

And main file only for build to make (without i got winmain error)

int main(){
return 0;
}
Kuba Kluzniak
  • 422
  • 1
  • 8
  • 18

1 Answers1

1

Aggregating discussion above:

To class Board you add a << operator that knows how to write one Board.

friend std::ostream & operator<<(std::ostream & out, const Board & b);

Then in Board.cpp you implement << as per the suggestions in What are the basic rules and idioms for operator overloading? and a couple for loops to iterate over the 2D array

std::ostream & operator<<(std::ostream & out, const Board & b)
{
    for (int row = 0; row < BOARD_SIZE; row++)
    {
        for (int col = 0; col < BOARD_SIZE; col++)
        {
            out << b.board[row][col] << ' ';
        }
        out << '\n';
    }
    out << '\n';
    return out;
}

Now saveBoard is ridiculously simple:

bool saveBoard(Board &b1,Board &b2) {
    std::ofstream file(FILE_NAME);
    file << b1 << b2;
    return file.good();
}

We return bool because you always want to let the caller know if the operation failed. If the caller doesn't use the return value, that's a bug they'll have to solve on their own. You did the best you could, though I suppose you could also throw an exception. That may or may not be excessive here.

To read it back, you do pretty much the same thing, but with the >> operator:

friend std::istream & operator>>(std::istream & in, Board & b);

and then

std::istream & operator>>(std::istream & in, Board & b)
{
    for (int row = 0; row < BOARD_SIZE; row++)
    {
        for (int col = 0; col < BOARD_SIZE; col++)
        {
            in >> b.board[row][col];
        }
    }
    return in;
}

and

bool loadBoard(Board &b1,Board &b2) {
    std::ifstream file(FILE_NAME);
    file >> b1 >> b2;
    return file.good();
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Thanks for the answear and pantientc :) i dont know why but in second one i got this error message: Invalid operands to binary expression ('std::istream' (aka 'basic_istream') and 'const char') – Kuba Kluzniak May 12 '22 at 22:23
  • @KubaKluzniak You got that message because I didn't proof read enough before I posted. I left the reader trying to use an `ostream` instead of an `istream` and trying to write to a `const` variable. I've fixed it. And Now I'm going to do what I should have done in the first place; Compile it and make sure it works. – user4581301 May 12 '22 at 22:27
  • Yeah i saw this . why in out u use Board as a const and in second one not ? – Kuba Kluzniak May 12 '22 at 22:32
  • And is it a mistake using in both loop row ++ ? (outside and inside) – Kuba Kluzniak May 12 '22 at 22:37
  • In the first we are writing the data, and a function writing data should not need to change the data. This helps eliminate some bugs, like accidentally reading where you meant to write, and sometimes the promise that the function will not change the value of the variable allows the compiler to optimize better. – user4581301 May 12 '22 at 22:37
  • Yup. Also a mistake, and one the compiler didn't catch. – user4581301 May 12 '22 at 22:38
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/244704/discussion-between-kuba-kluzniak-and-user4581301). – Kuba Kluzniak May 12 '22 at 22:57