Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] OX z kursy "od 0 do gier kodera"

Ostatnio zmodyfikowano 2013-07-17 23:20
Autor Wiadomość
ciekawski
Temat założony przez niniejszego użytkownika
[C++] OX z kursy "od 0 do gier kodera"
» 2013-07-17 22:36:41
Witam,

zmagałem się dzisiaj z OXem z kursu "od 0..." i gdy napisałem już ostatnia linijkę kodu oraz próbie kompilacji otrzymałem masę błędów. Większość okazała się drobnymi literówkami więc szybko zacząłem naprawiać swe błędy. Niestety nie wszystko szło tak gładko. Próbując znaleźć rozwiązanie problemu w internecie uznałem, że najlepszą opcją będzie sprawdzenie przykładowego kodu Xiona. Ku mojemu zdziwieniu okazało się, że dostaje ten sam błąd.

Jest to:
error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
i dotyczy tablicy z rozwiązaniami w pliku game.cpp:
C/C++
const LINIE[][ 3 ][ 2 ] = { { { 0, 0 }, { 0, 1 }, { 0, 2 } },
    { { 1, 0 }, { 1, 1 }, { 1, 2 } },
    { { 2, 0 }, { 2, 1 }, { 2, 2 } },
    { { 0, 0 }, { 1, 0 }, { 2, 0 } },
    { { 0, 1 }, { 1, 1 }, { 2, 1 } },
    { { 0, 2 }, { 1, 2 }, { 2, 2 } },
    { { 0, 0 }, { 1, 1 }, { 2, 2 } },
    { { 2, 0 }, { 1, 1 }, { 0, 2 } } };

Program skłąda się z 3 plików:
main.cpp
C/C++
// TicTacToe - wielki projekt gry typu "kółko i krzyżyk"

#include <iostream>
#include <conio.h>
#include "game.h"

void main()
{
    // rozpoczynamy grę
    StartGry();
   
    // pętla nieskończona - częsty element w grach
    // wyjdziemy z niej przy pomocy break w odpowiednim momencie
    for(;; )
    {
        // rysujemy planszę
        RysujPlansze();
       
        // sprawdzamy stan gry i czynimy odpowiednie działania
        if( g_StanGry == GS_MOVE )
        {
            // ruch któregoś gracza
            // pobieramy go więc i wywołujemy funkcję Ruch()
            unsigned uNumerPola;
            std::cin >> uNumerPola;
            Ruch( uNumerPola );
        }
        else if( g_StanGry == GS_WON || g_StanGry == GS_DRAW )
        // koniec gry (wygraną lub remisem)
        // wtedy przerywamy pętlę
             break;
       
    }
   
    // czekamy na dowolny klawisz
    getch();
}


game.cpp
C/C++
// Właściwy kod gry

#include <iostream>
#include <ctime>
#include "game.h"


// zmienne
//--------

// plansza w postaci tablicy 3x3
FIELD g_aPlansza[ 3 ][ 3 ] = { { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY },
    { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY },
    { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY } };

// stan gry (nie rozpoczęta, ruch gracza, wygrana lub remis)
GAMESTATE g_StanGry = GS_NOTSTARTED;

// znak aktualnego gracza (kółko lub krzyżyk)
SIGN g_AktualnyGracz;


//----------------------------------------------------------------------------------------


// funkcje
//--------

// funkcja wywoływana na starcie gry
bool StartGry()
{
    // najpierw sprawdzamy, czy gra już nie trwa;
    // jeżeli tak, to funkcja kończy się porażką
    if( g_StanGry != GS_NOTSTARTED ) return false;
   
    // losujemy gracza, który będzie zaczynał
    srand( static_cast < unsigned >( time( NULL ) ) );
    g_AktualnyGracz =( rand() % 2 == 0 ? SGN_CIRCLE
        : SGN_CROSS );
   
    // ustawiamy stan gry na ruch graczy
    g_StanGry = GS_MOVE;
   
    // wszystko się udało, zatem zwracamy true
    return true;
}

// rysowanie (albo raczej wypisywanie) pola gry
bool RysujPlansze()
{
    // także sprawdzamy, czy aby na pewno gra rozpoczęłas ię
    if( g_StanGry == GS_NOTSTARTED ) return false;
   
    // czyścimy okienko konsoli przy pomocy polecenia systemowego CLS
    system( "cls" );
   
    // pokazujemy szyld tytułowy
    std::cout << "   KOLKO I KRZYZYK   " << std::endl;
    std::cout << "---------------------" << std::endl;
    std::cout << std::endl;
   
   
    // następnie wypisujemy właściwą planszę
    // oczywiście, czynimy to przy pomocy dwóch pętli for
    std::cout << "        -----" << std::endl;
    for( int i = 0; i < 3; ++i )
    {
        // lewa część ramki
        std::cout << "        |";
       
        // wiersz
        for( int j = 0; j < 3; ++j )
        {
            // sprawdzamy, czy dane pole jest puste
            if( g_aPlansza[ i ][ j ] == FLD_EMPTY )
            // wtedy wyświetlamy jego numerek
            // tak, żeby użytkownik wiedział, jaką liczbę wpisać, gdy będzie
            // chciał postawić na nim kółko lub krzyżyk :)
                 std::cout << i * 3 + j + 1;
            else
            // jeżeli natomiast pole jest zajęte,
            // wyświetlamy odpowiadający mu znak;
            // korzystamy tu ze sztuczki z odpowiednimi wartościami stałych typu
            // FIELD; dzięki niej unikamy dodatkowego if'a (tudzież operatora ?:)
                 std::cout << static_cast < char >( g_aPlansza[ i ][ j ] );
           
        }
       
        // prawa część ramki
        std::cout << "|" << std::endl;
    }
    std::cout << "        -----" << std::endl;
    std::cout << std::endl;
   
    // wreszcie, pokazujemy dolny komunikat
    // jest on albo prośbą o podanie ruchu, albo informacją o stanie gry
    // wszystko zależy o tegoż stanu, a zatem stosujemy instrukcję switch
    switch( g_StanGry )
    {
    case GS_MOVE:
        // ruch gracza, a więc gra trwa
       
        // musimy więc poprosić o ruch
        std::cout << "Podaj numer pola, w ktorym" << std::endl;
        std::cout << "chcesz postawic ";
    std::cout <<( g_AktualnyGracz == SGN_CIRCLE ? "kolko": "krzyzyk" ) << ": ";
       
        break;
    case GS_WON:
        // gra zakończona, ktoś wygrał :)
       
        // wyświetlamy odpowiednią informację
        std::cout << "Wygral gracz stawiajacy ";
    std::cout <<( g_AktualnyGracz == SGN_CIRCLE ? "kolka": "krzyzyki" ) << "!";
       
        break;
    case GS_DRAW:
        // może też się zdarzyć remis
       
        // także pokazujemy wiadomość
        std::cout << "Remis!";
       
        break;
    }
   
    // wszystko poszło OK, zatem powiadamiamy o tym
    return true;
}

// wykonanie ruchu i sprawdzenie jego rezultatu
bool Ruch( unsigned uNumerPola )
{
    // znowuż musimy sprawdzić stan gry
    if( g_StanGry != GS_MOVE ) return false;
   
    // następnie sprawdzamy, czy numer pola mieści się w przedziale <1; 9>
    if( !( uNumerPola >= 1 && uNumerPola <= 9 ) ) return false;
   
    // jeśli doszliśmy dotąd, to wszystko jest w porządku i możemy wykonać ruch;
    // przeliczamy więc numer pola na indeksy tablicy i przypisujemy znak gracza;
    // (tylko wtedy, gdy pole jest puste)
    // po raz któryś-tam z kolei używamy przy tym sztuczki z wartościami enumów
    unsigned uY =( uNumerPola - 1 ) / 3;
    unsigned uX =( uNumerPola - 1 ) % 3;
    if( g_aPlansza[ uY ][ uX ] == FLD_EMPTY )
         g_aPlansza[ uY ][ uX ] = static_cast < FIELD >( g_AktualnyGracz );
    else
    // jeżeli próbowano dwukrotnie postawić znak w tym samym miejscu,
    // to niestety nie możemy na to pozwolić :)
         return false;
   
    // no i teraz następuje najbardziej zakręcona część programu :))
    // (taaak, poprzednia linijka wcale nią nie była :DD)
    // musimy mianowicie określić stan gry na podstawie planszy;
    // mogło się przecież zdarzyć, że któryś gracz ułożył linię poziomą, pionową
    // lub ukosną i wygrał, prawda? :) tutaj musimy to określić
    // można to zrobić na kilka sposobów: najprostszy do wymyślenia, ale najbardziej
    // pogmatwany jest zwykły ciąg instrukcji warunkowych, kontrolujących każde pole...
    // sprytniejszą metodą jest jednak użycie tablicy przeglądowej i pętli for
    const LINIE[][ 3 ][ 2 ] = { { { 0, 0 }, { 0, 1 }, { 0, 2 } }, // górna pozioma
        { { 1, 0 }, { 1, 1 }, { 1, 2 } }, // środkowa pozioma
        { { 2, 0 }, { 2, 1 }, { 2, 2 } }, // dolna pozioma
        { { 0, 0 }, { 1, 0 }, { 2, 0 } }, // lewa pionowa
        { { 0, 1 }, { 1, 1 }, { 2, 1 } }, // środkowa pionowa
        { { 0, 2 }, { 1, 2 }, { 2, 2 } }, // prawa pionowa
        { { 0, 0 }, { 1, 1 }, { 2, 2 } }, // przekątna backslashowa
        { { 2, 0 }, { 1, 1 }, { 0, 2 } } }; // przekątna slashowa
    FIELD Pole, ZgodnePole;
    unsigned uLiczbaZgodnychPol;
    for( int i = 0; i < 8; ++i )
    {
        // i przebiega po kolejnych możliwych liniach (jest ich osiem)
       
        // zerujemy zmienne pomocnicze
        Pole = ZgodnePole = FLD_EMPTY; // obie zmienne zostaną ustawione na FLD_EMPTY
        uLiczbaZgodnychPol = 0;
       
        for( int j = 0; j < 3; ++j )
        {
            // j przebiega po trzech polach w każdej linii
           
            // pobieramy rzeczone pole
            // to zdecydowanie najbardziej pogmatwane wyrażenie :)
            Pole = g_aPlansza[ LINIE[ i ][ j ][ 0 ] ][ LINIE[ i ][ j ][ 1 ] ];
           
            // jeżeli sprawdzane pole różni się od tego, które ma się zgadzać...
            if( Pole != ZgodnePole )
            {
                // to zmieniamy zgadzane pole na to aktualne
                ZgodnePole = Pole;
                uLiczbaZgodnychPol = 1;
            }
            else
            // jeśli natomiast oba pola się zgadzają, no to inkrementujemy
            // licznik takich zgodnych pól
                 ++uLiczbaZgodnychPol;
           
        }
       
        // teraz sprawdzamy, czy udało nam się zgodzić linię
        if( uLiczbaZgodnychPol == 3 && ZgodnePole != FLD_EMPTY )
        {
            // jeżeli tak, no to ustawiamy stan gry na wygraną
            g_StanGry = GS_WON;
           
            // przerywamy pętlę i funkcję
            return true;
        }
    }
   
    // istnieje jeszcze możliwość, że nastąpił remis
    // wtedy funkcja dojdzie tutaj i cała plansza będzie pokryta krzyżykami i kółkami
    // sprawdzamy to przy pomocy odpowiedniej pętli, zliczającej zapełnione pola
    // jeżeli jest ich tyle, ile wszystkich, no to kończymy remisem
    unsigned uLiczbaZapelnionychPol = 0;
    for( int i = 0; i < 3; ++i )
    for( int j = 0; j < 3; ++j )
    if( g_aPlansza[ i ][ j ] != FLD_EMPTY )
         ++uLiczbaZapelnionychPol;
   
    if( uLiczbaZapelnionychPol == 3 * 3 )
    {
        // ustawiamy stan gry na remis
        g_StanGry = GS_DRAW;
       
        // kończymy funkcję
        return true;
    }
   
    // oczywiście, może się zdarzyć, iż gra po prostu toczy się dalej
    // w takim wypadku zmieniamy jedynie aktualnego gracza
    g_AktualnyGracz =( g_AktualnyGracz == SGN_CIRCLE ? SGN_CROSS: SGN_CIRCLE );
   
    // zwracamy true
    return true;
}


game.h
C/C++
// Plik nagłówkowy właściwego kodu gry

// zabezpieczenie przez wielokrotnym dołączeniem
#pragma once


// typy
//-----

// znak - czyli kółko lub krzyżyk :)
enum SIGN { SGN_CIRCLE = 'O', SGN_CROSS = 'X' };

// pojedyncze pole (może być puste, mieć kółko lub krzyżyk)
enum FIELD { FLD_EMPTY = 0, // puste
    FLD_CIRCLE = SGN_CIRCLE, // kółko
    FLD_CROSS = SGN_CROSS }; // krzyżyk

// stan gry
enum GAMESTATE { GS_NOTSTARTED, // nie rozpoczęta
    GS_MOVE, // ruch gracza
    GS_WON, // wygrana gracza
    GS_DRAW }; // remis


// deklaracje zmiennych
//---------------------

// zmienna określająca stan gry musi być widoczna także na zewnątrz
// modułu game.cpp; poniższa deklaracja czyni zadość tej potrzebie

// stan gry
extern GAMESTATE g_StanGry;


// prototypy funkcji
//------------------

// rozpoczęcie gry
bool StartGry();

// narysowanie planszy
bool RysujPlansze();

// wykonanie ruchu
bool Ruch( unsigned );
Wyczytałem, że jest to częsty błąd jeśli nazwa deklaracji w pliku nagłówkowym i pliku źródłowym są takie same, ale u mnie deklaracja ta występuje tylko raz w pliku game.cpp
P-88140
ciekawski
Temat założony przez niniejszego użytkownika
» 2013-07-17 22:50:17
zmieniłem 
const
 na
int
  i wszystko działa jak należy.

Pytanie jednak dlaczego program nie chce się skompilować przy użyciu
const
?
P-88142
DejaVu
» 2013-07-17 22:50:55
const to nie typ danych.
P-88143
ciekawski
Temat założony przez niniejszego użytkownika
» 2013-07-17 23:00:46
czyli, że na przyszłość chcąc deklarować jakąś stałą trzeba określać ją za pomocą const i typu? (np
const float
)


i swoją drogą cóż mogło sprawić, że twórca tutoriala popełnił taki błąd?
P-88145
Berux
» 2013-07-17 23:20:40
const to modyfikator. Używamy go, żeby poinformować kompilator, że definiujemy stałą. Jednak samo słówko const, bez określenia jakiego typu jest stała, spowoduje błąd.
P-88147
« 1 »
  Strona 1 z 1