Battleship

0

Witam :-)
W ostatnim czasie zaczęłam pisać w konsoli grę w statki. Jest to jak na razie jeden z moich większych projektów (uczę się programowania raczej od niedawna).
Pisząc grę miałam głównie przećwiczyć używanie wskaźników oraz posługiwanie się strukturami danych, w tym typem wyliczeniowym enum.
Kiedy doszłam do momentu, gdzie zaczęło mieć sens odpalenie gry i obejrzenie efektów swojej pracy- kompilator pokazał mi pierdyliard błędów... :-(
No nic- mimo tego, że jest ich aż tak dużo, wciąż żywię nadzieję, że będę to w stanie jakoś naprawić.
Zanim napisałam ten wątek, spędziłam kilka godzin naprawiając te błędy, które póki co naprawić potrafiłam- i komunikatów jest znacząco mniej niż na początku (musicie mi uwierzyć na słowo :-b).
Ale niestety doszłam do wniosku, że z moim dotychczasowym poziomem umiejętności ze wszystkim sobie sama nie poradzę :-( - dlatego uprzejmie proszę o pomoc.

A więc, po pierwsze:
-nigdy wcześniej w większym projekcie nie posługiwałam się enum, więc nie wiem, jak prawidłowo powinnam to rozdzielić na plik z nagłówkami (spis treści) i część zawierającą całe implementacje klas i funkcji.

-po drugie:
próbując załączyć pliki z nagłówkami pisałam na początku pliku z rozszerzeniem .h:
#ifndef __ BATTLESHIP_H__ #define __BATTLESHIP_H__
kompilator pokazuje mi błąd: unterminated ifndef- jest to dla mnie zupełnie coś nowego i nie wiem, co mam z tym zrobić :-(

  • po trzecie: cała reszta- prawie wszystko to skutki błędów w kodzie, jakie popełniłam przy tworzeniu struktur (wcześniej nie miałam wielu okazji, żeby tego używać)
    Zrobiłam zrzut ekranu i umieściłam w załączniku.

Projekt nieskończonej gry składa się z 6 plików: main.cpp, Battleship.cpp, Battleshipi.h, Instruction.cpp (ten plik to akurat jeden wielki komentarz z pseudokodem, planem tworzenia gry), Utils.cpp i Utils.h (dwa ostatnie zawierają funcje korygujące dane wprowadzane z klawiatury przez użytkownika- użyłam tego w nieco zmienionej formie przy tworzeniu gry w wisielca(poprzedni wpis)).

main.cpp:

include <iostream>
#include "Battleship.h"

using namespace std;

int main()
{
    const char * INPUT_ERROR_STRING = "input error! Please, try again.";

    Player player1;
    Player player2;

    InitializePlayer(player1, "Player1");
    InitializePlayer(player2, "Player2");

    do
    {
        PlayGame(player1, player2);
    }while(WantToPlayAgain());

    return 0;
}

Battleship.h:

enum SizeType
{
    AIRCRAFT_CARRIER_SIZE = 5,
    BATTLESHIP_SIZE = 4,
    CRUISER_SIZE = 3,
    DESTROYER_SIZE = 2,
    SUBMARINE_SIZE = 1,

    BOARD_SIZE = 10,
    NUM_SHIPS = 5,
    PLAYER_NAME_SIZE = 8, // Player1, Player2
    MAX_SHIP_SIZE = AIRCRAFT_CARRIER_SIZE
};

enum ShipType
{
    ST_NONE = 0,
    ST_AIRCRAFT_CARRIER,
    ST_BATTLESHIP,
    ST_CRUISER,
    ST_DESTROYER,
    ST_SUBMARINE
};

enum ShipOrientationType
{
    SO_HORIZONTAL = 0,
    SO_VERTICAL
};

struct ShipPositionType
{
    int row;
    int col;
};

struct Ship
{
    ShipType shipType;
    int shipSize;
    ShipOrientationType orientation;  //stworzylam tutaj typ, ale on sam nie definiuje zadnej kokrenej zmiennej =>
    ShipPositionType position;          // miszê ustawiæ wszystkie statki dla kazdego gracza
};

enum GuessType
{
    GT_NONE = 0,
    GT_MISSED,
    GT_HIT
};

struct ShipPartType
{
    ShipType shipType;
    bool isHit;
};

struct Player
{
    char playerName[PLAYER_NAME_SIZE];
    Ship ships[NUM_SHIPS];                  // musze ustawic wszystkie statki poprawnie i przekazac graczowi jeden typ statku do
    GuessType guessBoard[BOARD_SIZE][BOARD_SIZE];   // kazdego z poszczegolnych elementow tablicy
    ShipPartType shipBoard[BOARD_SIZE][BOARD_SIZE];
};

Battleship.cpp

#include <iostream>
#include <cstring>
#include <cctype>
#include "Battleship.h"
#include "Utils.h"

using namespace std;

const char * INPUT_ERROR_STRING = "input error! Please, try again.";


void InitializePlayer(Player & player, const char * playerName)
{
    if(playerName != nullptr && strlen(playerName)>0)
    {
        {
            strcpy(player, playerName);
        }

        InitializeShip(player.ships[0], AIRCRAFT_CARRIER_SIZE, ST_AIRCRAFT_CARRIER);
        InitializeShip(player.ships[1], BATTLESHIP_SIZE, ST_BATTLESHIP);
        InitializeShip(player.ships[2], CRUISER_SIZE, ST_CRUISER);
        InitializeShip(player.ships[3], DESTROYER_SIZE, ST_DESTROYER);
        InitializeShip(player.ships[4], SUBMARINE_SIZE, ST_SUBMARINE);
    }
}

void InitializeShip(Ship & ship, int shipSize, ShipType shipType)
{
        ship.shipType = ST_AIRCRAFT_CARRIER;
        ship.shipSize = AIRCRAFT_CARRIER_SIZE;
        ship.position.row = 0;
        ship.position.col = 0;
        ship.orientation = SO_HORIZONTAL;
}

void PlayGame(Player& player1, Player&player2)
{
    SetUpBoards(player1);
    SetUpBoards(player2);
}

bool WantToPlayAgain()
{
    char input;
    const char validInput[2] = {'y','n'};
    input = GetCharacter("Would you like to play again? (y/n)", INPUT_ERROR_STRING, validInput, 2, CC_LOWER_CASE);
    return input == 'y';
}

void SetUpBoards(Player& player)
{
    ClearBoards(player);
    DrawBoards(player);
}

void ClearBoards(Player&player)
{
    for(int r=0; r<BOARD_SIZE; r++)
    {
        for(int c=0; c<BOARD_SIZE; c++)
        {
            player.guessBoard[r][c] = GT_NONE;
            player.shipBoard[r][c].shipType = ST_NONE;// no ship here
            player.shipBoard[r][c].isHit = false;

        }
    }
}

void DrawBoards(const Player&player)
{
    DrawColumnsRow();
    DrawColumnsRow();
    cout<<endl;

    for(int r=0; r<BOARD_SIZE; r++)
    {
        DrawSeparatorLine();
        cout<<" ";

        DrawSeparatorLine();
        cout<<endl;

        DrawShipBoardRow(player, r);
        cout<<" ";

        DrawGuessBoardRow(player, r);
        cout<<endl;
    }
    DrawSeparatorLine();
    cout<<" ";
    DrawSeparatorLine();
    cout<<endl;

}

void DrawSeparatorLine()
{
    cout<<" ";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<"+---";
    }
    cout<<"+";
}

void DrawColumnsRow()
{
    cout<<"  ";
    for(int c=0; c<BOARD_SIZE; c++)
    {
        int columnName = c+1;
        cout<<" "<<columnName<<"  ";
    }
    cout<<"+";
}

char GetShipReprezentation(const Player& player, int row, int col)
{
    if(player.guessBoard[row][col].isHit)
    {
        return '*';// represents hit
    }

    if(player.shipBoard[row][col].shipType == ST_AIRCRAFT_CARRIER)
    {
        return 'A';
    }
    else if(player.shipBoard[row][col].shipType == ST_BATTLESHIP)
    {
        return 'B';
    }
    else if(player.shipBoard[row][col].shipType == ST_CRUISER)
    {
        return 'C';
    }
    else if(player.shipBoard[row][col].shipType == ST_DESTROYER)
    {
        return 'D';
    }
    else if(player.shipBoard[row][col].shipType == ST_SUBMARINE)
    {
        return 'S';
    }
    else return ' ';
}

char GetGuessRepresentationAt(const Player& player, int row, int col)
{
    if(player.guessBoard[row][col] == GT_HIT)
    {
        return '*';
    }
    else if(player.guessBoard[row][col == GT_MISSED])
    {
        return 'o';
    }
    else return ' ';
}

void DrawShipBoardRow(const Player& player, int row)
{
    char rowName = row + 'A';
    cout<<rowName<<"|";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<" "<<GetShipReprezentation(player, row, c)<<" ";
    }
}

void DrawGuessBoardRow(const Player& player, int row)
{
    char rowName = row + 'A';
    cout<<rowName<<"|";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<" "<<GetGuessRepresentationAt(player, row, c)<<" ";    // tu trzeba uzupelnic reprezentacja kazdego statku
    }
}

Instruction.cpp:

/*
    InitPlayer(player1, "Player1");
    InitPlayer(player2, "Player2");

    do
    {
        PlayGame(player1, player2);
    } while(WantToPlayAgain());

    PlayGame()
    {
        SetupBoard(Player1);
        SetupBoard(Player2);

        do
        {
            DrawBoards(currentPlayer)

            do
            {
                Prompt the current player for a guess
                guess = GetGuess()
            } while(!IsGuessIsValid(guess, currentPlayer));

            UpdateBoards(guess, currentPlayer, otherPlayer);
            DrawBoard(currentPlayer) // to see the result of the guess

            if(a ship was sunk on currentPlayer's turn)
            {
                output that the current player sunk that ship
            }
            WatForKeyPress

            SwitchPlayers()

        } while(!GameIsOver());

        DisplayWinner();
    }

    SetUpBoards(player)
    {
        ClearBoards(player)

        for(all the ships)
        {
            DrawBoards(player)

            currentShip = Get the current ship

            // sprawdzam, czy statek zostal umieszczony poprawnie

            do
            {
                Get Board Position for the head of the ship
                Get the ship orientation

                isValidPlacement = IsValidPlacement(currenShip, position, orientation, player)
                if (!isValidPlacement)
                {
                    output to the player that it was not a valid placement
                }
            } while(!isValidPlacement)

            PlaceShipOnBoard(currenShip, position, orientation, player)
        }
    }

    IsValidPlacement(currentShip, position, orientation, player)
    {
        if(orientatin == HO0RIZONTAL)
        {
            for(all the columns the currentShip would take up)
            {
                if(ship will overlap another ship or the ship will be off the board horizontally)
                {
                    return false
                }
            }
        }
        else
        {
            for(all the rows the currentShip would take up)
            {
                if(ship will overlap another ship or the ship will be off the board vertically)
                {
                    return false
                }
            }
        } return true
    }

    PlaceShipOnBoard(currentShip, position, orientation, player)
    {
        ship.position = position
        ship.orientation = orientation

        if(orientation == HORIZONTAL)
        {
            for(all the columns the ship would take up)
            {
                set the ship part on the board at position.row and current column
            }
        }
        else
        {
            for(all the rows that the ship would take up)
            {
                set the ship part on the board at current row and position.col
            }
        }
    }

    UpdateBoards(guess, currentPlayer, otherPlayer)
    {
        if(otherPlayer's ship board at guess is a ship)
        {
            // hit
               set hit on the currentPlayer's guess board
               apply damage to the otherPlayer's shipBoard
               return shipType
        }
        else
        {
            set miss on the currentPlayer's guess board
            return ST_NONE
        }
    }

    IsGameOver(player1, player2)
    {
        return AreAllShipsSunk(player1) || AreAllShipsSunk(player2)
    }

    AreAllShipsSunk(player)
    {
        for(all the player's ships)
        {
            if(!IsSunk(player, currentShip))
            {
                return false
            }
        } return true
    }

    IsSunk(player, ship)
    {
        if(ship.orientation == HORIZONTAL)
        {
            for(columns that ship takes up)
            {
                if(currentPosition on the shipBoard is not hit)
                {
                    return false
                }
            }
        }
        else
        {
            for(rows that the ship takes up)
            {
                if(currentPosition on the shipBoard is not hit)
                {
                    return false
                }
            }
        }
    }


*/

Utils.h:

#ifndef __ UTILS_H__
#define __UTILS_H__

enum CharacterCaseType
{
    CC_UPPER_CASE=0,
    CC_LOWER_CASE,
    CC_EITHER
};

char GetCharacter(const char* prompt, const char*error);
char GetCharacter(const char* prompt, const char* error, const char validInput[], int validInputLength, CharacterCaseType charCase);

#endif

Utils.cpp

#include <iostream>
#include <cctype>
#include "Utils.h"

using namespace std;

const int IGNORE_CHARS = 256; //to do main
const char * INPUT_ERROR_STRING = "input error! Please, try again.";

char GetCharacter(const char* prompt, const char*error)
{
    char input;
    bool failure;

    do
    {
        failure=false;
        cout<<prompt;
        cin>>input;

        if(cin.fail())
        {
            cin.clear();
            cin.ignore(IGNORE_CHARS, '\n');
            cout << error << endl;
            failure=true;
        }
        else
        {
            cin.ignore(IGNORE_CHARS, '\n');

            if(isalpha(input))
            {
                input = tolower(input);
            }
            else
            {
                cout<<error<<endl;
                failure = true;
            }
        }
    }while(failure); return input;
}

char GetCharacter(const char* prompt, const char* error, const char validInput[], int validInputLength, CharacterCaseType charCase)
{
    char input;
    bool failure;

    do
    {
        failure=false;
        cout<<prompt;
        cin>>input;

        if(cin.fail())
        {
            cin.clear();
            cin.ignore(IGNORE_CHARS, '\n');
            cout << error << endl;
            failure=true;
        }
        else
        {
            cin.ignore(IGNORE_CHARS, '\n');

            if(isalpha(input))
            {
                if(charCase == CC_UPPER_CASE)
                {
                    input = toupper(input);
                }
                else if(charCase == CC_LOWER_CASE)
                {
                    input = tolower(input);
                }

                for (int i=0;i<validInputLength;i++)
                {
                    if(input==validInput[i])
                    {
                        return input;
                    }
                }
            }
            cout<<error<<endl;
            failure=true;
        }
    }while(failure); return input;
}

Proszę o wyrozumiałość w związku z tym, że tu jest aż tak dużo błędów.
Wiem, że tego dużo, ale może jednak komuś będzie się chciało to chociaż pobieżnie przejrzeć i podpowiedzieć mi co i jak poprawić ;-)
Z góry dziękuję za odpowiedź :-)

2

Każda dyrektywa preprocesora musi być w osobnej linii, czyli:

#ifndef __ BATTLESHIP_H__ 
#define __BATTLESHIP_H__

```cpp strcpy(player, playerName); ``` `strcpy()` przyjmuje wskaźniki do ciągów znaków: https://en.cppreference.com/w/cpp/string/byte/strcpy, a Ty przekazujesz obiekt `Player`.
```cpp if(player.guessBoard[row][col].isHit) ``` `guessBoard` jest tablicą enumów `GuessType`, więc nie ma żadnego pola o nazwie `isHit`.
Błędy kopiuj i wklej jako tekst, a nie obrazek. Ten obrazek w dodatku obcina komunikat.
1

Jak się Uczysz, to Staraj się pisać porządnie, czyli modułowo, małe kawałki kodu, do każdego unit testy, jak moduły współpracują ze sobą to testy funkcjonalne. Będziesz eliminować dużo błędów na bieżąco.

0

Z uwag ogólnych Siostro @Dominika Łęczycka

  • dla nazw zmiennych/funkcji stosuj camelCase
  • w programie masz elementy takie jak Player, Ship, Board i Game. Warto stworzyć osobne pliki .h i .cpp dla każdego.
  • rzeczy wspólne, wykorzystywane przez różne elementy, na przykład enumy, wskazane jest dodać do wspólnego pliku definitions.h
  • zastanów się i zdefiniuj odpowiedzialności poszczególnych elementów. Za co ma być odpowiedzialny Ship, za co Board itd.
0

Kilka moich uwag odnośnie kodu.

  1. Generalnie każda klasa/struktura powinna być umieszczona w osobnych plikach ( definicja w nagłówku *.h i implementacja w *.cpp ) .
    To poprawi czytelność projektu i rozdzieli kod na podstawowe jednostki/moduły.
  2. Zamień wszystkie definicje wyliczeń enum na enum class, gdyż zmniejszają ryzyko wystąpienia błędów związanych z niejawną konwersją typów.
  3. Spróbuj pomyśleć o prawdziwie obiektowym kodzie ( wiem że to jest trudne dla początkujących ) w którym każda funkcja jest składową jakieś klasy.
  4. Używaj komponentów STL (np. vector,string) tam gdzie to jest możliwe.
    Na przykład strukturę Player można zaimplementować w następujący sposób:
struct Player
{
    Player( const string& _playerName, int numShips, int boardSize )
    : playerName{_playerName}
    {
       ships.resize(numShips);
       guessBoard.resize(boardSize);
       for( auto& row : guessBoard ) row.resize(boardSize);
       shipBoard.resize(boardSize);
       for( auto& row : shipBoard ) row.resize(boardSize);
    }

    string playerName;
    vector<Ship> ships;
    vector<vector<GuessType>> guessBoard;
    vector<vector<ShipPartType>> shipBoard;
};
............
Player player("Pirate",5,20);
0
MasterBLB napisał(a):

Z uwag ogólnych Siostro @Dominika Łęczycka

  • dla nazw zmiennych/funkcji stosuj camelCase
  • w programie masz elementy takie jak Player, Ship, Board i Game. Warto stworzyć osobne pliki .h i .cpp dla każdego.
  • rzeczy wspólne, wykorzystywane przez różne elementy, na przykład enumy, wskazane jest dodać do wspólnego pliku definitions.h
  • zastanów się i zdefiniuj odpowiedzialności poszczególnych elementów. Za co ma być odpowiedzialny Ship, za co Board itd.

Code::Blockds 17.12

Oczywiście dodaję zawsze w ten sposób, w który napisałeś. Jak nie działa od razu, to próbuję dopisać dyrektywy, ale tym razem pojawia mi się błąd:
unterminated ifndef

0
Dominika Łęczycka napisał(a):

Oczywiście dodaję zawsze w ten sposób, w który napisałeś. Jak nie działa od razu, to próbuję dopisać dyrektywy, ale tym razem pojawia mi się błąd:
unterminated ifndef

Jak obecnie wyglądają u Ciebie wszystkie dyrektywy (ze wszystkich plików)? (#include, #define itd.)

0
Dominika Łęczycka napisał(a):

Witam :-)
W ostatnim czasie zaczęłam pisać w konsoli grę w statki. Jest to jak na razie jeden z moich większych projektów (uczę się programowania raczej od niedawna).
Pisząc grę miałam głównie przećwiczyć używanie wskaźników oraz posługiwanie się strukturami danych, w tym typem wyliczeniowym enum.
Kiedy doszłam do momentu, gdzie zaczęło mieć sens odpalenie gry i obejrzenie efektów swojej pracy- kompilator pokazał mi pierdyliard błędów... :-(
No nic- mimo tego, że jest ich aż tak dużo, wciąż żywię nadzieję, że będę to w stanie jakoś naprawić.
Zanim napisałam ten wątek, spędziłam kilka godzin naprawiając te błędy, które póki co naprawić potrafiłam- i komunikatów jest znacząco mniej niż na początku (musicie mi uwierzyć na słowo :-b).
Ale niestety doszłam do wniosku, że z moim dotychczasowym poziomem umiejętności ze wszystkim sobie sama nie poradzę :-( - dlatego uprzejmie proszę o pomoc.

A więc, po pierwsze:
-nigdy wcześniej w większym projekcie nie posługiwałam się enum, więc nie wiem, jak prawidłowo powinnam to rozdzielić na plik z nagłówkami (spis treści) i część zawierającą całe implementacje klas i funkcji.

-po drugie:
próbując załączyć pliki z nagłówkami pisałam na początku pliku z rozszerzeniem .h:
#ifndef __ BATTLESHIP_H__ #define __BATTLESHIP_H__
kompilator pokazuje mi błąd: unterminated ifndef- jest to dla mnie zupełnie coś nowego i nie wiem, co mam z tym zrobić :-(

  • po trzecie: cała reszta- prawie wszystko to skutki błędów w kodzie, jakie popełniłam przy tworzeniu struktur (wcześniej nie miałam wielu okazji, żeby tego używać)
    Zrobiłam zrzut ekranu i umieściłam w załączniku.

Projekt nieskończonej gry składa się z 6 plików: main.cpp, Battleship.cpp, Battleshipi.h, Instruction.cpp (ten plik to akurat jeden wielki komentarz z pseudokodem, planem tworzenia gry), Utils.cpp i Utils.h (dwa ostatnie zawierają funcje korygujące dane wprowadzane z klawiatury przez użytkownika- użyłam tego w nieco zmienionej formie przy tworzeniu gry w wisielca(poprzedni wpis)).

main.cpp:

include <iostream>
#include "Battleship.h"

using namespace std;

int main()
{
    const char * INPUT_ERROR_STRING = "input error! Please, try again.";

    Player player1;
    Player player2;

    InitializePlayer(player1, "Player1");
    InitializePlayer(player2, "Player2");

    do
    {
        PlayGame(player1, player2);
    }while(WantToPlayAgain());

    return 0;
}

Battleship.h:

enum SizeType
{
    AIRCRAFT_CARRIER_SIZE = 5,
    BATTLESHIP_SIZE = 4,
    CRUISER_SIZE = 3,
    DESTROYER_SIZE = 2,
    SUBMARINE_SIZE = 1,

    BOARD_SIZE = 10,
    NUM_SHIPS = 5,
    PLAYER_NAME_SIZE = 8, // Player1, Player2
    MAX_SHIP_SIZE = AIRCRAFT_CARRIER_SIZE
};

enum ShipType
{
    ST_NONE = 0,
    ST_AIRCRAFT_CARRIER,
    ST_BATTLESHIP,
    ST_CRUISER,
    ST_DESTROYER,
    ST_SUBMARINE
};

enum ShipOrientationType
{
    SO_HORIZONTAL = 0,
    SO_VERTICAL
};

struct ShipPositionType
{
    int row;
    int col;
};

struct Ship
{
    ShipType shipType;
    int shipSize;
    ShipOrientationType orientation;  //stworzylam tutaj typ, ale on sam nie definiuje zadnej kokrenej zmiennej =>
    ShipPositionType position;          // miszê ustawiæ wszystkie statki dla kazdego gracza
};

enum GuessType
{
    GT_NONE = 0,
    GT_MISSED,
    GT_HIT
};

struct ShipPartType
{
    ShipType shipType;
    bool isHit;
};

struct Player
{
    char playerName[PLAYER_NAME_SIZE];
    Ship ships[NUM_SHIPS];                  // musze ustawic wszystkie statki poprawnie i przekazac graczowi jeden typ statku do
    GuessType guessBoard[BOARD_SIZE][BOARD_SIZE];   // kazdego z poszczegolnych elementow tablicy
    ShipPartType shipBoard[BOARD_SIZE][BOARD_SIZE];
};

Battleship.cpp

#include <iostream>
#include <cstring>
#include <cctype>
#include "Battleship.h"
#include "Utils.h"

using namespace std;

const char * INPUT_ERROR_STRING = "input error! Please, try again.";


void InitializePlayer(Player & player, const char * playerName)
{
    if(playerName != nullptr && strlen(playerName)>0)
    {
        {
            strcpy(player, playerName);
        }

        InitializeShip(player.ships[0], AIRCRAFT_CARRIER_SIZE, ST_AIRCRAFT_CARRIER);
        InitializeShip(player.ships[1], BATTLESHIP_SIZE, ST_BATTLESHIP);
        InitializeShip(player.ships[2], CRUISER_SIZE, ST_CRUISER);
        InitializeShip(player.ships[3], DESTROYER_SIZE, ST_DESTROYER);
        InitializeShip(player.ships[4], SUBMARINE_SIZE, ST_SUBMARINE);
    }
}

void InitializeShip(Ship & ship, int shipSize, ShipType shipType)
{
        ship.shipType = ST_AIRCRAFT_CARRIER;
        ship.shipSize = AIRCRAFT_CARRIER_SIZE;
        ship.position.row = 0;
        ship.position.col = 0;
        ship.orientation = SO_HORIZONTAL;
}

void PlayGame(Player& player1, Player&player2)
{
    SetUpBoards(player1);
    SetUpBoards(player2);
}

bool WantToPlayAgain()
{
    char input;
    const char validInput[2] = {'y','n'};
    input = GetCharacter("Would you like to play again? (y/n)", INPUT_ERROR_STRING, validInput, 2, CC_LOWER_CASE);
    return input == 'y';
}

void SetUpBoards(Player& player)
{
    ClearBoards(player);
    DrawBoards(player);
}

void ClearBoards(Player&player)
{
    for(int r=0; r<BOARD_SIZE; r++)
    {
        for(int c=0; c<BOARD_SIZE; c++)
        {
            player.guessBoard[r][c] = GT_NONE;
            player.shipBoard[r][c].shipType = ST_NONE;// no ship here
            player.shipBoard[r][c].isHit = false;

        }
    }
}

void DrawBoards(const Player&player)
{
    DrawColumnsRow();
    DrawColumnsRow();
    cout<<endl;

    for(int r=0; r<BOARD_SIZE; r++)
    {
        DrawSeparatorLine();
        cout<<" ";

        DrawSeparatorLine();
        cout<<endl;

        DrawShipBoardRow(player, r);
        cout<<" ";

        DrawGuessBoardRow(player, r);
        cout<<endl;
    }
    DrawSeparatorLine();
    cout<<" ";
    DrawSeparatorLine();
    cout<<endl;

}

void DrawSeparatorLine()
{
    cout<<" ";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<"+---";
    }
    cout<<"+";
}

void DrawColumnsRow()
{
    cout<<"  ";
    for(int c=0; c<BOARD_SIZE; c++)
    {
        int columnName = c+1;
        cout<<" "<<columnName<<"  ";
    }
    cout<<"+";
}

char GetShipReprezentation(const Player& player, int row, int col)
{
    if(player.guessBoard[row][col].isHit)
    {
        return '*';// represents hit
    }

    if(player.shipBoard[row][col].shipType == ST_AIRCRAFT_CARRIER)
    {
        return 'A';
    }
    else if(player.shipBoard[row][col].shipType == ST_BATTLESHIP)
    {
        return 'B';
    }
    else if(player.shipBoard[row][col].shipType == ST_CRUISER)
    {
        return 'C';
    }
    else if(player.shipBoard[row][col].shipType == ST_DESTROYER)
    {
        return 'D';
    }
    else if(player.shipBoard[row][col].shipType == ST_SUBMARINE)
    {
        return 'S';
    }
    else return ' ';
}

char GetGuessRepresentationAt(const Player& player, int row, int col)
{
    if(player.guessBoard[row][col] == GT_HIT)
    {
        return '*';
    }
    else if(player.guessBoard[row][col == GT_MISSED])
    {
        return 'o';
    }
    else return ' ';
}

void DrawShipBoardRow(const Player& player, int row)
{
    char rowName = row + 'A';
    cout<<rowName<<"|";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<" "<<GetShipReprezentation(player, row, c)<<" ";
    }
}

void DrawGuessBoardRow(const Player& player, int row)
{
    char rowName = row + 'A';
    cout<<rowName<<"|";

    for(int c=0; c<BOARD_SIZE; c++)
    {
        cout<<" "<<GetGuessRepresentationAt(player, row, c)<<" ";    // tu trzeba uzupelnic reprezentacja kazdego statku
    }
}

Instruction.cpp:

/*
    InitPlayer(player1, "Player1");
    InitPlayer(player2, "Player2");

    do
    {
        PlayGame(player1, player2);
    } while(WantToPlayAgain());

    PlayGame()
    {
        SetupBoard(Player1);
        SetupBoard(Player2);

        do
        {
            DrawBoards(currentPlayer)

            do
            {
                Prompt the current player for a guess
                guess = GetGuess()
            } while(!IsGuessIsValid(guess, currentPlayer));

            UpdateBoards(guess, currentPlayer, otherPlayer);
            DrawBoard(currentPlayer) // to see the result of the guess

            if(a ship was sunk on currentPlayer's turn)
            {
                output that the current player sunk that ship
            }
            WatForKeyPress

            SwitchPlayers()

        } while(!GameIsOver());

        DisplayWinner();
    }

    SetUpBoards(player)
    {
        ClearBoards(player)

        for(all the ships)
        {
            DrawBoards(player)

            currentShip = Get the current ship

            // sprawdzam, czy statek zostal umieszczony poprawnie

            do
            {
                Get Board Position for the head of the ship
                Get the ship orientation

                isValidPlacement = IsValidPlacement(currenShip, position, orientation, player)
                if (!isValidPlacement)
                {
                    output to the player that it was not a valid placement
                }
            } while(!isValidPlacement)

            PlaceShipOnBoard(currenShip, position, orientation, player)
        }
    }

    IsValidPlacement(currentShip, position, orientation, player)
    {
        if(orientatin == HO0RIZONTAL)
        {
            for(all the columns the currentShip would take up)
            {
                if(ship will overlap another ship or the ship will be off the board horizontally)
                {
                    return false
                }
            }
        }
        else
        {
            for(all the rows the currentShip would take up)
            {
                if(ship will overlap another ship or the ship will be off the board vertically)
                {
                    return false
                }
            }
        } return true
    }

    PlaceShipOnBoard(currentShip, position, orientation, player)
    {
        ship.position = position
        ship.orientation = orientation

        if(orientation == HORIZONTAL)
        {
            for(all the columns the ship would take up)
            {
                set the ship part on the board at position.row and current column
            }
        }
        else
        {
            for(all the rows that the ship would take up)
            {
                set the ship part on the board at current row and position.col
            }
        }
    }

    UpdateBoards(guess, currentPlayer, otherPlayer)
    {
        if(otherPlayer's ship board at guess is a ship)
        {
            // hit
               set hit on the currentPlayer's guess board
               apply damage to the otherPlayer's shipBoard
               return shipType
        }
        else
        {
            set miss on the currentPlayer's guess board
            return ST_NONE
        }
    }

    IsGameOver(player1, player2)
    {
        return AreAllShipsSunk(player1) || AreAllShipsSunk(player2)
    }

    AreAllShipsSunk(player)
    {
        for(all the player's ships)
        {
            if(!IsSunk(player, currentShip))
            {
                return false
            }
        } return true
    }

    IsSunk(player, ship)
    {
        if(ship.orientation == HORIZONTAL)
        {
            for(columns that ship takes up)
            {
                if(currentPosition on the shipBoard is not hit)
                {
                    return false
                }
            }
        }
        else
        {
            for(rows that the ship takes up)
            {
                if(currentPosition on the shipBoard is not hit)
                {
                    return false
                }
            }
        }
    }


*/

Utils.h:

#ifndef __ UTILS_H__
#define __UTILS_H__

enum CharacterCaseType
{
    CC_UPPER_CASE=0,
    CC_LOWER_CASE,
    CC_EITHER
};

char GetCharacter(const char* prompt, const char*error);
char GetCharacter(const char* prompt, const char* error, const char validInput[], int validInputLength, CharacterCaseType charCase);

#endif

Utils.cpp

#include <iostream>
#include <cctype>
#include "Utils.h"

using namespace std;

const int IGNORE_CHARS = 256; //to do main
const char * INPUT_ERROR_STRING = "input error! Please, try again.";

char GetCharacter(const char* prompt, const char*error)
{
    char input;
    bool failure;

    do
    {
        failure=false;
        cout<<prompt;
        cin>>input;

        if(cin.fail())
        {
            cin.clear();
            cin.ignore(IGNORE_CHARS, '\n');
            cout << error << endl;
            failure=true;
        }
        else
        {
            cin.ignore(IGNORE_CHARS, '\n');

            if(isalpha(input))
            {
                input = tolower(input);
            }
            else
            {
                cout<<error<<endl;
                failure = true;
            }
        }
    }while(failure); return input;
}

char GetCharacter(const char* prompt, const char* error, const char validInput[], int validInputLength, CharacterCaseType charCase)
{
    char input;
    bool failure;

    do
    {
        failure=false;
        cout<<prompt;
        cin>>input;

        if(cin.fail())
        {
            cin.clear();
            cin.ignore(IGNORE_CHARS, '\n');
            cout << error << endl;
            failure=true;
        }
        else
        {
            cin.ignore(IGNORE_CHARS, '\n');

            if(isalpha(input))
            {
                if(charCase == CC_UPPER_CASE)
                {
                    input = toupper(input);
                }
                else if(charCase == CC_LOWER_CASE)
                {
                    input = tolower(input);
                }

                for (int i=0;i<validInputLength;i++)
                {
                    if(input==validInput[i])
                    {
                        return input;
                    }
                }
            }
            cout<<error<<endl;
            failure=true;
        }
    }while(failure); return input;
}

Proszę o wyrozumiałość w związku z tym, że tu jest aż tak dużo błędów.
Wiem, że tego dużo, ale może jednak komuś będzie się chciało to chociaż pobieżnie przejrzeć i podpowiedzieć mi co i jak poprawić ;-)
Z góry dziękuję za odpowiedź :-)

Poprzednie błędy już udało mi się poprawić.
Kiedy zmieniłam warunek w funkcji GetShipReprezentation z if(player.guessBoard[row][col].isHit na if(player.guessBoard[row][col]==GT_HIT), kompilator zamiast poprzedniego błędu pokazał mi komunikat 1d returned exit status. Kiedyś miałam już taką sytuację i po prostu tamtego programu nie naprawiłam, bo nie wiedziałam, co z tym zrobić. Ale tej gry nie chciałabym tak po prostu porzucić nieskończonej, bo już dużo czasu nad tym spędziłam :-(

Czy ktoś może kiedyś rozwiązał już podobny problem?
Resetowałam już komputer.
spróbowałam też otworzyć wcześniej działający projekt, dodać odpowiednią ilość plików i po prostu przekleić treść, ale też nie działa :-(
Bardzo proszę o odpowiedź, co mogę jeszcze zrobić w tej sytuacji.

1
  1. Podaj wszystko, co Code::Blocks wypisuje na wyjściu (rozumiem, że budujesz projekt w Code::Blocks).
  2. Czy tam nie powinno być ld ("el-de") zamiast 1d?
  3. Czy w komunikacie nie ma przypadkiem nazwy collect2? (w internecie można spotkać ten błąd z taką nazwą)
  4. Zobacz to: http://users.csc.calpoly.edu/~jdalbey/101/Resources/errormessages.html Nie sądzę, by trzeba było załączyć wspomnianą na tej stronie bibliotekę math, ale być może zapomniałaś dołączyć, lub źle dołączasz, w jakimś pliku jakąś inną bibliotekę lub plik.
  5. Jak czytam w internecie, część ld być może oznacza, że to linker (a nie kompilator) wyrzuca błąd (zwrócenie statusu 1 oznacza w rozumieniu Linuksa błąd). Możesz zobaczyć dla ciekawości to: https://linux.die.net/man/1/ld

UPDATE: 6. Również na tej stronie wspominany jest podobny błąd: http://web.mst.edu/~cpp/common/common_errors.html

0

Jeżeli "statki w konsoli" nazywasz większym projektem, w dodatku tak napisanym to lepiej daj sobie z tym spokój. Piszesz niby w c++ a to wygląda jak c. Nie korzystasz z dobrodziejstw jakie daje biblioteka STL. Jedyny plus to chyba taki że korzystasz z cin cout zamiast z printf scanf tak jak większość :-D

1

Nie chce mi się wgryzać w logikę programu, ale isHit jest elementem struktury ShipPartType, więc jeśli zamienisz
if(player.guessBoard[row][col].isHit)
na
if(player.shipBoard[row][col].isHit)
to ten fragment się skompiluje (ale czy taki był zamiar i się pomyliłaś, to nie wiem).

Poza tym jest błąd w strcpy(player, playerName); ale może to już poprawiłaś.

Wygląda mi na to, że nie uruchamiasz kodu fragmentami i go nie rozbudowujesz, tylko piszesz w całości i potem próbujesz w całości to uruchomić.

Powodzenia, nie poddawaj się :-).

1

Szkoda trochę, że nie mam Windowsa, bo by było większe prawdopodobieństwo, że trafię na ten sam błąd. Spróbuję stworzyć nowy projekt z tymi samymi plikami i zawartością, co podałaś.


UPDATE: Spróbowałem. Nie uruchamiałem, ale udało mi się sprawić, że buduje się bez problemów. Nie wiem jak u Ciebie na Windowsie, ale u mnie na Linuksie wyskoczyło kilka błędów (zawsze były odpowiednie komunikaty w zakładce "Build log" "Build messages"). Przechodziłem po kolei w miejsca ich występowania i komentowałem całe linijki (skrót do zakomentowania linijki, na Linuksie przynajmniej: CTRL+SHIFT+C). Teraz spróbuję zrobić, żeby działało bez komentowania tych linijek.


UPDATE2:

Podam swoje poprawki (w kolejności raportowania błędów przez Code::Blocks):

Błąd nr 1: main.cpp|13|error: ‘InitializePlayer’ was not declared in this scope|
Poprawiłem, dodając w pliku Battleship.h linijkę: void InitializePlayer(Player & , const char * );

Błąd nr 2: main.cpp|18|error: ‘PlayGame’ was not declared in this scope|
Poprawiłem, dodając w pliku Battleship.h linijkę: void PlayGame(Player& , Player&);

Błąd nr 3: Battleship.cpp|16|error: cannot convert ‘Player’ to ‘char*’|
Poprawiłem tymczasowo, komentując całą linijkę (nie wiem, co chciałaś osiągnąć, więc nie mogę poprawić inaczej).

Błąd nr 4: Battleship.cpp|19|error: ‘InitializeShip’ was not declared in this scope; did you mean ‘InitializePlayer’?|
Poprawiłem, przenosząc funkcję InitializeShip przed funkcję InitializePlayer.

Błąd nr 5: Battleship.cpp|38|error: ‘SetUpBoards’ was not declared in this scope|
Poprawiłem, przenosząc funkcję SetUpBoards przed funkcję PlayGame.

Błąd nr 6: Battleship.cpp|38|error: ‘ClearBoards’ was not declared in this scope|
Poprawiłem, przenosząc funkcję ClearBoards przed funkcję SetUpBoards.

Błąd nr 7: Battleship.cpp|53|error: ‘DrawBoards’ was not declared in this scope|
Poprawiłem, przenosząc funkcję DrawBoards przed funkcję SetUpBoards.

Błąd nr 8: Battleship.cpp|52|error: ‘DrawColumnsRow’ was not declared in this scope|
Poprawiłem, przenosząc funkcję DrawColumnsRow przed funkcję DrawBoards.

Reszta zaraz...


UPDATE3:

Błąd nr 9: main.cpp|19|error: ‘WantToPlayAgain’ was not declared in this scope|
Poprawiłem, dodając w pliku Battleship.h linijkę bool WantToPlayAgain();.

Błąd nr 10: Battleship.cpp|69|error: ‘DrawSeparatorLine’ was not declared in this scope|
Poprawiłem, przenosząc funkcję DrawSeparatorLine przed funkcję DrawBoards.

Błąd nr 11: Battleship.cpp|86|error: ‘DrawShipBoardRow’ was not declared in this scope|
Poprawiłem, przenosząc funkcję DrawShipBoardRow przed funkcję DrawBoards.

Błąd nr 12: Battleship.cpp|79|error: ‘GetShipReprezentation’ was not declared in this scope|
Poprawiłem, przenosząc funkcję GetShipReprezentation przed funkcję DrawShipBoardRow.

Błąd nr 13: Battleship.cpp|74|error: request for member ‘isHit’ in ‘player.Player::guessBoard[row][col]’, which is of non-class type ‘const GuessType’|
Poprawiłem tymczasowo, komentując całą instrukcję if, w której znajduje się to wywołanie funkcji isHit (nie wiem, co chciałaś osiągnąć, więc nie mogę poprawić inaczej).

Błąd nr 14: Battleship.cpp|130|error: ‘DrawGuessBoardRow’ was not declared in this scope; did you mean ‘DrawShipBoardRow’?|
Poprawiłem, przenosząc funkcję DrawGuessBoardRow przed funkcję DrawBoards.

Błąd nr 15: Battleship.cpp|120|error: ‘GetGuessRepresentationAt’ was not declared in this scope; did you mean ‘GetShipReprezentation’?|
Poprawiłem, przenosząc funkcję GetGuessRepresentationAt przed funkcję DrawGuessBoardRow.

Błąd nr 16:

/usr/bin/ld: obj/Debug/Utils.o:[...]Utils.cpp|8|multiple definition of `INPUT_ERROR_STRING'; obj/Debug/Battleship.o:[...]Battleship.cpp:9: first defined here|

Nie jestem pewien, ale jak do tej pory to chyba jedyne miejsce, w którym występuje błąd związany z programem ld. Poprawiłem, usuwając definicję stałej INPUT_ERROR_STRING z plików: main.cpp oraz Utils.cpp.

Teraz, po zbudowaniu, otrzymuję komunikat:

...
Process terminated with status 0 (0 minute(s), 1 second(s))
0 error(s), 0 warning(s) (0 minute(s), 1 second(s))

Zobaczmy, czy się uruchomi...


UPDATE4: Uruchomiło się (nie grałem, bo z uwagi na tymczasowe zakomentowanie dwóch linijek mogą być jakieś błędy).


PS. Plik Intruction.cpp mam cały zakomentowany, tak jak Ty podałaś na forum, więc nie wiem – może to dlatego mi działa?

0

Właśnie- zadziałało mi na razie po prawnie- tylko po usunięciu deklaracji INPUT_ERROR_STRING z pliku Utils.cpp
(niestety rodzice zmusili mnie do odejścia od koputera, więc nie odpisałam wcześniej)

Myślisz, że powinnam pozmieniać też te wszystkie inne rzeczy?

1

Czy powinnaś pozmieniać to nie wiem. Znam jedynie podstawy C++, ale ogólnie w programowaniu jest tak, że definicje i deklaracje funkcji powinny być przed ich wywołaniem. Znam jeden język, który umożliwia dowolne układanie definicji funkcji w stosunku do wywołań: JavaScript (i jest to raczej uważane za niedobrą cechę). Natomiast w pliku nagłówkowym (z rozszerzeniem h) powinny być deklaracje wszystkich funkcji, które są używane tam, gdzie ten plik jest dołączany (deklaracje, czyli nagłówki tych funkcji, stąd nazwa pliku).


PS: Technicznie rzecz biorąc, deklaracja funkcji oraz jej nagłówek to chyba co innego. Więc nie sugeruj się moją wiedzą. ;) Możesz przeczytać np. ten artykuł: http://www-h.eng.cam.ac.uk/help/languages/C++/c++_tutorial/functions.html

0

Dobrze, a więc- to, czego nie poprawiłam juz wcześniej, a było w Twoich wskazówkach- zrobiłam teraz. Na razie nadal działa. Dziękuję bardzo za pomoc :-)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.