Optymalizacja Snake'a oraz ulepszenie metody kończącej grę.
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!

Optymalizacja Snake'a oraz ulepszenie metody kończącej grę.

AutorWiadomość
Temat założony przez niniejszego użytkownika
Optymalizacja Snake'a oraz ulepszenie metody kończącej grę.
» 2017-06-16 20:33:35
No heja wszystkim!

Właśnie jestem na finiszu pisania konsolowego snejka, którego chciałbym później przenieść do SFMLa. Jednak mam malutki problem - otóż nie wiem jak opisać metodę, która kończyłaby grę, gdy głowa węża będzie chciała zjeść swój tułów. Kolizję z krawędziami mam oprogramowaną. No i drugi problem - otóż wąż porusza się oraz wydłuża poprawnie, jednak na kliknięcie jakiejkolwiek strzałki reaguje z małym, lecz odczuwalnym opóźnieniem.

Tutaj zamieszczam swój kod:

C/C++
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <conio.h>
#include <windows.h>

using namespace std;

const int UP = 72, DOWN = 80, LEFT = 75, RIGHT = 77;

class Single_Area
{
    bool food; // zmienna logiczna przechowujaca male jedzenie
    bool band; // zmienna logiczna okreslajaca czy jest banda
    bool head; // zmienna logiczna przypisujaca wartosc logiczna czy na danym polu jest glowa weza
    friend class Area_and_Snake;
    friend void play();
   
public:
   
    Single_Area() // konstruktor pojedynczego pola
    {
        food = false;
        band = false;
        head = false;
    }
   
    void place_food() // metoda umieszczajaca jedzenie
    {
        food = true;
    }
   
    void place_band() // metoda umieszczajaca krawedz
    {
        band = true;
    }
   
    void place_head() // metoda umieszczajaca glowe weza
    {
        head = true;
    }
   
    void remove_head() // metoda usuwajaca glowe weza
    {
        head = false;
    }
   
    void remove_food() // metoda usuwajaca jedzenie z pola
    {
        food = false;
    }
   
    void draw_snake() // metoda rysujaca weza
    {
        cout << "@";
    }
};

class Area_and_Snake
{
    Single_Area ** T; // utworzenie wskaznika na wskaznik tablicy
    int * tail_x; // sa to utworzone wskazniki, ktore beda dynamicznymi tablicami
    int * tail_y; // przechowujacymi polozenie kazdego elementu ogona weza
    int N;
    int M; // zmienne N, M przechowujace wymiary dynamicznej tablicy dwuwymiarowej
    int x_coordinate; // koordynata X glowy weza
    int y_coordinate; // koordynata Y glowy weza
    int step; // zmienna przechowujaca ile ruchow waz wykonal
    int length; // zmienna przechowujaca dlugosc weza
    char key; // zmienna znakowa przechowujaca rodzaj wcisnietego klawisza
    int direction; // zmienna przechowujaca kierunek ruchu weza
    friend void play( Area_and_Snake board );
   
public:
   
    Area_and_Snake() // konstruktor planszy
    {
        length = 0; // poczatkowa dlugosc weza
        step = 0; // poczatkowa ilosc krokow
       
        cout << "Jakich wymiarow chcesz, by byla plansza? Podaj ilosc wierszy: ";
        cin >> N;
        cout << "Podaj ilosc kolumn: ";
        cin >> M;
       
        T = new Single_Area *[ N ]; // dynamiczne utworzenie wierszy tablicy
       
        for( int i = 0; i < N; i++ ) // dynamiczne utworzenie kolumn tablicy
        {
            T[ i ] = new Single_Area[ M ];
        }
       
        tail_x = new int[ 10000 ]; // alokacja dwoch tablic dla przechowywania polozenia ogona weza
        tail_y = new int[ 10000 ]; // musza miec duzy rozmiar, bo nie wiemy jak duzo ruchow wykonamy
       
        x_coordinate = 5; // poczatkowe rozmieszczenie
        y_coordinate = 5; // glowy weza
       
        T[ x_coordinate ][ y_coordinate ].place_head(); // umiesc na tym polu glowe
    }
   
    ~Area_and_Snake() // destruktor planszy
    {
        for( int i = 0; i < N; i++ )
        {
            delete[] T[ i ]; // dealokuje wiersze
        }
       
        delete[] T; // teraz dealokacja tablicy wskaznikow
        delete[] tail_x; // usuwanie tablic
        delete[] tail_y; // przechowujacych polozenie ogona
        T = NULL;
        tail_x = NULL;
        tail_y = NULL; // od teraz tablice za zdealokowane i wskazuja nigdzie
    }
   
    void set_food( int n, int m ) // metoda rozmieszczajaca losowo jedzenie na planszy
    {
        srand( time( NULL ) );
        n = 1 + rand() %( N - 2 - 1 + 1 ); // losowanie liczby z przedzialu [1, N - 2] dla wiersza
        m = 1 + rand() %( M - 2 - 1 + 1 ); // losowanie liczby z przedzialu [1, M - 2] dla kolumny
       
        T[ n ][ m ].place_food();
    }
   
    void create_borders() // metoda tworzaca
    {
        for( int i = 0; i < N; i++ )
        {
            T[ i ][ 0 ].place_band();
            T[ i ][ M - 1 ].place_band();
        }
        for( int i = 1; i < M; i++ )
        {
            T[ 0 ][ i ].place_band();
            T[ N - 1 ][ i ].place_band();
        }
    }
   
    void increase_length() // metoda inkrementujaca dlugosc weza
    {
        length++;
    }
   
    void display() // metoda odpowiedzialna za rysowanie calej planszy
    {
        for( int i = 0; i < N; i++ )
        {
            cout << endl;
            for( int j = 0; j < M; j++ )
            {
                if( T[ i ][ j ].band ) cout << "#";
                else if( T[ i ][ j ].food ) cout << "o";
                else if( T[ i ][ j ].head ) cout << "@";
                else cout << " ";
               
            }
        }
    }
   
    void move_snake() // metoda sterujaca calym wezem, opracowanie mechaniki ruchu to byla porazka
    {
        if( kbhit() )
        {
            key = getch();
            if( key == UP ) direction = 1;
           
            if( key == DOWN ) direction = 2;
           
            if( key == LEFT ) direction = 3;
           
            if( key == RIGHT ) direction = 4;
           
        }
       
        step++;
        tail_x[ step ] = x_coordinate;
        tail_y[ step ] = y_coordinate;
       
        if( direction == 1 )
        {
            y_coordinate--;
            T[ tail_y[ step - length ] ][ tail_x[ step - length ] ].remove_head();
            T[ y_coordinate ][ x_coordinate ].place_head();
        }
        else if( direction == 2 )
        {
            y_coordinate++;
            T[ tail_y[ step - length ] ][ tail_x[ step - length ] ].remove_head();
            T[ y_coordinate ][ x_coordinate ].place_head();
        }
        else if( direction == 3 )
        {
            x_coordinate--;
            T[ tail_y[ step - length ] ][ tail_x[ step - length ] ].remove_head();
            T[ y_coordinate ][ x_coordinate ].place_head();
        }
        else
        {
            x_coordinate++;
            T[ tail_y[ step - length ] ][ tail_x[ step - length ] ].remove_head();
            T[ y_coordinate ][ x_coordinate ].place_head();
        }
    }
   
    void if_over() // jesli uderzono w bande koniec gry, nie wiem jeszcze jak zakonczyc gre gdy glowa chce pozrec tulow
    {
        if( T[ y_coordinate ][ x_coordinate ].head == T[ y_coordinate ][ x_coordinate ].band )
        {
            cout << "\n\nPrzegrales! :(\n\n";
            exit( 0 );
        }
        else return;
       
    }
   
    void if_food() // jesli waz zjadl jablko, to wydluza sie i zmiana lokalizacji jedzenia
    {
        if( T[ y_coordinate ][ x_coordinate ].head == T[ y_coordinate ][ x_coordinate ].food )
        {
            T[ y_coordinate ][ x_coordinate ].remove_food();
            increase_length();
            set_food( 0, 0 );
        }
    }
};

void play( Area_and_Snake board ) // funkcja sterujaca cala gra
{
    board.create_borders();
    board.set_food( 0, 0 );
    board.display();
   
    while( true )
    {
        Sleep( 70 );
        board.move_snake();
        board.if_food();
        board.if_over();
        system( "CLS" );
        board.display();
    }
}

main()
{
    Area_and_Snake board;
   
    play( board );
    return 0;
}


Metoda odpowiedzialna za kończenie gry (gdy pozycja głowy węża jest równa pozycji bandy to koniec gry, jak go mogę zmodyfikować, by się też kończyła gra gdy wąż chce zjeść siebie samego?:

C/C++
void if_over() // jesli uderzono w bande koniec gry, nie wiem jeszcze jak zakonczyc gre gdy glowa chce pozrec tulow
{
    if( T[ y_coordinate ][ x_coordinate ].head == T[ y_coordinate ][ x_coordinate ].band )
    {
        cout << "\n\nPrzegrales! :(\n\n";
        exit( 0 );
    }
    else return;
   
}

Mogłbym Was prosić o pomoc w sprawie kończenia gry i poprawienia czasu reakcji na wciśnięcie klawisza?

Dzięki za pomoc! :)
P-162599
» 2017-06-16 21:12:48
Tak się składa, że właśnie pisze snake (tylko na innych zasadach, dla dwóch graczy) w SFML i ja to robie tak:

Każdy segment ciała ma wskaźnik na kolejny, czyli:
C/C++
class Segment
{
    Segment * next;
    int PosX;
    int PosY;
}

Oczywiście okrojona wersja.

I teraz patrzysz, czy pozycja głowy (1 element) == pozycja innych elementów. Najlepiej mieć tablice jako mapę:

C/C++
Segment * ptr; = next; // pierwszy element to głowa, nastepne to kolejne segmenty ciala

while( ptr != nullptr ) // jesli next == nullptr to znaczy, ze to ostatni segment ciala
{
   
    if( ptr->PosX == PosX && ptr->PosY == PosY ) // jesli glowa jest na pozycji ciala
         return false;
   
    ptr = ptr->next;
}

return true;

Generalnie to tylko ogólny zamysł, jeszcze tego nie implementowałem ale taki mam plan.
P-162604
» 2017-06-16 22:38:35
Każdy segment ciała ma wskaźnik na kolejny
Po co implementować listę za każdym razem, gdy potrzebujesz listy? Nie wystarczy ci std::list<> do tego?

C/C++
if( T[ y_coordinate ][ x_coordinate ].head == T[ y_coordinate ][ x_coordinate ].band )
Czy przypadkiem x/y_coordinate to nie jest zawsze pozycja głowy? Wystarczy samo T[y_coordinate][x_coordinate].band jako warunek.
P-162615
Temat założony przez niniejszego użytkownika
» 2017-06-17 15:33:54
Faktycznie, wystarczy tylko sprawdzać położenie bandy ;) Trywialne rozwiązanie, nie wiem czemu chciałem być "bardziej papieski niż papież" i tak napisałem :P

Ale dalej mam problem z kolizją węża, nie mam zupełnie pomysłu jak rozbudować warunek, by się kończył też, gdy głowa się kończyła z kolizją tułowia. Prawdę powiedziawszy to tułów jest głową, której ostatni element jest sukcesywnie usuwany.
P-162624
» 2017-06-17 15:55:37
Może sprawdzaj przed wykonaniem ruchu, czy na polu docelowym głowy już jest 'głowa'.
P-162625
« 1 »
 Strona 1 z 1