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-08-04 15:43

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?


edytowany 8x, ostatnio: Silv, 2019-08-04 16:24
*Bląd 1: ale w pliku Battleship.h już istnieje ta linia kodu - Dominika Łęczycka 2019-08-04 16:38
Być może zmieniłaś coś w międzyczasie? U mnie jej nie było. - Silv 2019-08-04 16:41
*Tzn. w tym kodzie, co podałaś na forum. - Silv 2019-08-04 16:41

Pozostało 580 znaków

2019-08-04 16:37
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?

edytowany 1x, ostatnio: Dominika Łęczycka, 2019-08-04 16:40
Aha, jak odpowiadasz, to dobrze by było, żebyś nie cytowała całej poprzedniej odpowiedzi, a jedynie potrzebne fragmenty. :) - Silv 2019-08-04 16:38
O, dziękuję. - Silv 2019-08-04 16:40

Pozostało 580 znaków

2019-08-04 16:45
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/he[...]+/c++_tutorial/functions.html


edytowany 2x, ostatnio: Silv, 2019-08-04 16:52
Pokaż pozostałe 21 komentarzy
To dopiero bez sensu - stivens 2019-08-04 21:28
Jeśli nie znalazłoby się przeciwwskazanie do wywoływania metod publicznych, miałoby to rzeczywiście mniej sensu. - Silv 2019-08-04 21:43
Ale to przeciwskazanie wydaje sie... fanatyczne - stivens 2019-08-04 21:45
Nie wiem, o jakim piszesz? Nie podałem żadnego. - Silv 2019-08-05 00:33
Ty podałeś alternatywę w przypadku, gdyby takie przeciwwskazanie było, ale właśnie nie jestem pewien, czy jest w ogóle sens rozważać alternatywę, skoro nie ma przeciwwskazań. - Silv 2019-08-05 00:34

Pozostało 580 znaków

2019-08-04 16:58
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 :-)

W mojej ocenie bardzo dobre źródła dotyczące języka C++ to te strony: https://en.cppreference.com/w/ oraz http://www.cplusplus.com/ W obu jest np. dział Reference, czyli, hm, zwięzły opis (danego) języka. - Silv 2019-08-04 17:06
Tzn. obie strony są po angielsku, być może to będzie dla Ciebie pewien problem. Ale nie znam polskich odpowiedników (pewnie jakieś są). - Silv 2019-08-04 17:09

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