Battleship

Odpowiedz Nowy wątek
2019-07-31 20:05
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ź :-)

edytowany 2x, ostatnio: Dominika Łęczycka, 2019-07-31 20:36

Pozostało 580 znaków

2019-07-31 22:02
2

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

#ifndef __ BATTLESHIP_H__ 
#define __BATTLESHIP_H__


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.

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.

A jak mogę poprawić to ostatnie? Bo pomyślałam, żeby może zmienić warunek na: if(player.guessBoard[row][col]==GT_HIT), ale wtedy kompilator zamiast poprzedniego błędu pokazuje mi: 1d returned 1 exit status (*wszystko inne już poprawiłam) - Dominika Łęczycka 2019-08-03 16:58
Tzn. ogólnie poczekajmy na odpowiedź @twonek, ale ten ostatni błąd może być niepowiązany z tym problemem z if-em. - Silv 2019-08-03 17:08

Pozostało 580 znaków

2019-07-31 22:12
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.


Pozostało 580 znaków

2019-08-01 09:27
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.

"Sugeruję wyobrazić sobie Słońce widziane z orbity Merkurego, a następnie dupę tej wielkości. W takiej właśnie dupie specjalista ma teksty o wspaniałej atmosferze, pracy pełnej wyzwań i tworzeniu innowacyjnych rozwiązań. Pracuje się po to, żeby zarabiać, a z resztą specjalista sobie poradzi we własnym zakresie, nawet jeśli firma mieści się w okopie na granicy obu Korei."
-somekind,
konkretny człowiek-konkretny przekaz :]
Tyle, że ja mam problem z podłączeniem poszczególnych plików do projektu 🙁 Bo czasem- tak jak teraz wyskakują mi dość dziwne błędy (Te dyrektywy w kodzie były w osobnych liniach, tak mi się jakoś skopiowało do wpisu) - Dominika Łęczycka 2019-08-02 11:15
W każdym IDE w jakim pisałem programy była opcja "Add header/source file to existing project" która działała bez zarzutu. Czego ty używasz? - MasterBLB 2019-08-02 16:49

Pozostało 580 znaków

2019-08-01 10:05
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);
Tyle, że ja mam problem z podłączeniem poszczególnych plików do projektu 🙁 Bo czasem- tak jak teraz wyskakują mi dość dziwne błędy (Te dyrektywy na początku w kodzie były w osobnych liniach, tak mi się jakoś skopiowało do wpisu) - - Dominika Łęczycka 2019-08-02 11:16

Pozostało 580 znaków

2019-08-03 14:49
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

Pozostało 580 znaków

2019-08-03 15:25
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.)


edytowany 1x, ostatnio: Silv, 2019-08-03 15:26
właśnie zauważyłam, że nie dodałam #endif na końcu pliku Battleship.h -na razie pozbyłam się tego błędu ;-) PS. Jesteś pewny, że powinnam tworzyć oddzielny plik dla każdej klasy? Bo to gra na konsolę i jest w gruncie rzeczy dość prosta (możnaby nawet powiedzieć prymitywna). Jak się uprzeć, to można by wszystko napisać w jednym pliku. Naprawdę byłoby lepiej, gdyby to wszystko porozdzielać? Wtedy do takiej mini-gry ów plików zrobi się naprawdę dużo... - Dominika Łęczycka 2019-08-03 15:35
Nie jestem pewien. To zależy. Nie znam się na C/C++, ale ogólnie rzecz biorąc: jeśli myślisz, że nie wyjdziesz ponad np. 10 funkcji/3 klasy, to być może warto zostawić wszystko w jednym pliku. Jeśli jednak zakładasz lub zauważysz w pewnym momencie, że tych funkcji i klas będzie/powinno być znacznie więcej, by to miało sens, to myślę, że lepiej należy rozdzielić. Ogólnie podział programu na pliki jest jednym z fundamentów architektury programów. Tak czy siak kiedyś będziesz musiała zacząć to dzielić, jeśli planujesz programować więcej niż takie gry jak teraz. :) - Silv 2019-08-03 15:41
PS. Nie mówię oczywiście, że ta gra jest nic niewarta. Chodzi mi o inne rozróżnienie: czasem nawet nic niewarte, ale złożone aplikacje potrafią posiadać bardzo dużo plików. - Silv 2019-08-03 15:43
Dziękuję za podpowiedź ;-) Myślę, że tutaj zostawię już liczbę plików tak, jak jest, Ale przy kolejnych odrobinę większych projektach na pewno wezmę to pod uwagę 🙂 - Dominika Łęczycka 2019-08-03 15:47
Jeśli chodzi o jakieś źródło – nie mogę podać Ci jednego źródła, bo każde ma wady i zalety, ale wpisz sobie w Google: C++ reasons splitting code multiple files i przejrzyj wyniki. Zwróciłbym szczególną uwagę na wątki ze stron "*.stackexchange.com" oraz "stackoverflow.com". Ewentualnie możesz nawet wpisać tak w Google – site:stackoverflow.com C++ reasons splitting code multiple files. Znajdzie Ci wyłącznie strony w domenie "stackoverflow.com". - Silv 2019-08-03 16:06

Pozostało 580 znaków

2019-08-03 18:27
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.

Pozostało 580 znaków

2019-08-03 23:00
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/[...]/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


edytowany 8x, ostatnio: Silv, 2019-08-03 23:05
Pokaż pozostałe 5 komentarzy
-W projekcie znajduje się jedna funkcja int main zwracająca 0, w ustawieniach mam zaznaczone Have g++ follow the C++11 ISO language standard [-std=c++11] -próbowałam też zaznaczyćTreat as errors the warnings demanded by strict IScO C and ISO C++ [-pedantic-errors], ale nie pomogło -Posiadałam wcześniej działający projekt o nazwie test (w który wklejałam sobie różne fragmenty kody, żeby testować)- dodałam do projektu odpowiednią ilość plików i przekleiłam kod z poprzedniego projektu- dalej ten sam błąd. -Spróbowała też stworzyć nowy projekt testowy, w którym - Dominika Łęczycka 2019-08-04 13:57
w którym umieściłam wszystko w jednym pliku (bo pomyślałam, że może z łączeniem plików jest problem), ale dalej pokazuje mi się ten sam błąd -oryginalny projekt nazywa się Battleship, w nazwach poszczególnych plików nie ma żadnych 'dziwnych znaków', tylko duże i małe litery -projekt znajduje się w folderze na pulpicie o nazwie C++, ale z tym raczej nie powinno być problemu, bo inne projekty znajdujące się tam działają. Próbowałam też przenieść projekt na pulpit, ale nie pomogło. - Dominika Łęczycka 2019-08-04 13:57
@Dominika Łęczycka: jeśli odpowiadasz w temacie, pisz odpowiedzi, nie komentarze. Trudniej się dyskutuje, jeśli każda odpowiedź dotyczy czego innego, a tak byłby jeden ciąg odpowiedzi z siebie wynikających. - Silv 2019-08-04 15:11
a, jasne- postaram się na przyszłość. Przepraszam, jeśli zaciemniam przekaz - Dominika Łęczycka 2019-08-04 15:16
Jeśli chodzi o komentarze, to zaraz je przeczytam. Odpowiem w odpowiedzi, jeśli to coś w temacie. - Silv 2019-08-04 15:16

Pozostało 580 znaków

2019-08-04 03:55
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

edytowany 2x, ostatnio: au7h, 2019-08-04 04:01
Motywujące to raczej nie jest. - Silv 2019-08-04 06:14
A ja Ci zrobię na złość i sobie nie odpuszczę 😝😝😝 - Dominika Łęczycka 2019-08-04 10:54
A uczę się tego języka dopiero jakieś 2 miesiące i projekt jest większy w porównaniu do innych, które robiłam do tej pory. Nie w porównaniu do tego, co można stworzyć w c++ - Dominika Łęczycka 2019-08-04 10:56
Czego ty oczekujesz??? Że ucząc się wszystkiego sama(bo nauczycielka informatyki w liceum powiedziała mi, że wskaźniki są trudne i zaczniemy się ich uczyć dopiero za 2 lata) od razu napiszę prawdziwą dużą grę? Projekty nie na konsolę też pisałam, ale np. Z dodatkiem buildera -więc teraz czysto w c++ mi jest trudniej - Dominika Łęczycka 2019-08-04 11:01
I naprawdę lubię to, co robię, a napewno sobie nie odpuszczę, bo ty tak twierdzisz 🖕 - Dominika Łęczycka 2019-08-04 11:02

Pozostało 580 znaków

2019-08-04 12:08
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ę :-).


Pokaż pozostałe 15 komentarzy
Jak użyjesz extern, to nie możesz przypisywać już wartości. - Stefan_3N 2019-08-04 15:56
o, na razie (po usunięciu) udało się odpalić :-) - Dominika Łęczycka 2019-08-04 15:58
Proszę :-). Staraj się pisać kod małymi fragmentami i nie idź dalej, póki to co masz się nie kompiluje, bo potem trudno znaleźć przyczynę. - Stefan_3N 2019-08-04 16:02
O, nawet nie zauważyłem, że taka dyskusja tu jest. - Silv 2019-08-04 16:32

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0