Autor Wątek: [C++] Snake w konsoli  (Przeczytany 2051 razy)

Offline hudypatyk

  • Użytkownik

# Maj 25, 2010, 20:08:09
Witam! Mam problem z moja gierką, próbowałem na początku jakoś rozwiązać problem sam lecz niestety, nie udało mi się. Problem wygląda następująco: gdy wąż połyka swoją ofiarę jego ciało powinno wydłużać się o dodatkowy segment a następnie podążać torem głową.
Wyświetlanie Głowy:
Cytuj
void Waz(int x, int y)
{
    if(Zwrot == 68){gotoxy(x,y);cout<<char(16);}
    else if(Zwrot == 87){gotoxy(x,y);cout<<char(30);}
    else if(Zwrot == 83){gotoxy(x,y);cout<<char(31);}
    else if(Zwrot == 65){gotoxy(x,y);cout<<char(17);}
}
Główna pętla programu:
Cytuj
while(Koniec == true)
    {
        WyswietlPlansze(IloscPunktow, Poziom);
        Waz(x,y);
        Jedzenie();
        Sleep(100);
        while(kbhit()) Zwrot = getch();

      // sprawdzanie kolizji

         switch(Zwrot) // switch sprawdza ktory klawisz zostal wcisniety
              {
                    case 68: //d
                    x++;
                    break;
                    case 87: //w
                    y--;
                    break;
                    case 83: //s
                    y++;
                    break;
                    case 65: //a
                    x--;
                    break;
                    case 27: // esc
                    Koniec = false;
                    break;
              }
         if(x==j_x && y==j_y){Jedzenie(); IloscPunktow+=10;}
    }
Ogólny widok:

Wiem ze współrzędne głowy powinny być zapisywane do zmiennych tymczasowych dzięki którym ciało poruszało by się torem głowy ale nie wiem jak to wykonać w praktyce. Proszę o pomoc.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xirdus

  • Moderator

# Maj 25, 2010, 20:44:14
Możesz zrobić tak jak ja:
1. Robisz strukturę z dwoma intami: x i y (nazwijmy ją coord). Głowa węża, segmenty ciała i jabłuszko są typu coord.
2. Coordy tworzące ciało węża przechowujesz w std::vector.
3. Przy każdym obiegu pętli najpierw do wektora dodajesz kopie głowy, przesuwasz głowę i jeśli głowa nie trafiła na jabłko to kasujesz najbardziej tylnią część ciała.
4. Enjoy!

Nie wiem czy nie lepiej byłoby zrobić listę zamiast vectora; jeśli ktoś wie to niech powie.

Ps
while(Koniec == true) brzmi jak "dopóki gra się skończyła" :) Ale to już jest czepianie się.

Offline Lerhes

  • Użytkownik

# Maj 25, 2010, 20:53:33
Kolega powyżej bardzo dobrze mówi, ja bym użył zamiast wektora, kolejkę: queue z biblioteki standardowej.

Offline Xirdus

  • Moderator

# Maj 25, 2010, 21:55:42
@lerhes: Nieeee, po queue nie można iterować, więc odpada sprawdzanie kolizji. A jak tu zrobić snake'a bez sprawdzania kolizji? Bardziej chodziło mi o vector vs list, obie chyba tu by pasowały, nie wiem która lepsza. Gdy robiłem swojego snake'a, to wiele o tym googlowałem, jednak wciąż nie wiem, czym się te kontenery różnią.

Edit: Właściwie, to ja tak jakby zrobiłem snake'a bez kolizji
« Ostatnia zmiana: Maj 25, 2010, 22:28:50 wysłana przez Xirdus »

Offline hudypatyk

  • Użytkownik

# Maj 25, 2010, 23:58:17
Możesz zrobić tak jak ja:
1. Robisz strukturę z dwoma intami: x i y (nazwijmy ją coord). Głowa węża, segmenty ciała i jabłuszko są typu coord.
2. Coordy tworzące ciało węża przechowujesz w std::vector.
3. Przy każdym obiegu pętli najpierw do wektora dodajesz kopie głowy, przesuwasz głowę i jeśli głowa nie trafiła na jabłko to kasujesz najbardziej tylnią część ciała.
4. Enjoy!
Można bardziej łopatologicznie z tymi coordami?

#edit
Muszę głowę węza i jedzenie trzymać w strukturze ? czy wystarczy mi same 'segmenty' ciała weza ? bo w przeciwnym wypadku musiał bym od nowa organizować kod snake :d
#edit2
ok zrobilem tak:
Cytuj
struct Segment
{
    int Px;
    int Py;
};
vector <Segment> Cialo;
Teraz mam Cialo[].Px i Cialo[].Py
Dalej nie wiem, co z tym zrobić. Jak zapisać pozycje głowy do vektora a później wykorzystać ją jako drogę dla ciała?.
« Ostatnia zmiana: Maj 26, 2010, 00:56:13 wysłana przez hudypatyk »

Offline Lerhes

  • Użytkownik

# Maj 26, 2010, 01:21:39
@Xirdus: Racja, nie pomyślałem nic a nic. Znacznie lepiej zastosować deque.

@ hudypatyk: Ja bym kombinował tak, myślę że kod jest czytelny:
#include <iostream>
#include <deque>

using namespace std;

int main()
{
deque< pair<int,int> > pozycjeWeza;
int x=1,y=2;
//Waz ma tam jakis zwrot, nastepuje krok petli.
//Dodajemy nowa pozycje weza jezeli cos zjadl, jezeli nie to usuwamy ostatnia.
pozycjeWeza.push_front( make_pair(x,y) );
if( ! ZjadlCosISiePowiekszyl() )
pozycjeWeza.pop_front();
//Teraz mozemy przeiterowac dla danej klatki po pozycjeWeza i narysowac go calego.
deque< pair<int,int> >::iterator it = pozycjeWeza.begin();
while (it != pozycjeWeza.end())
{
cout << "Ustawiamy komorke weza na pozycji: " << (*it).first<<" "<<(*it).second;
it++;
}
    return 0;
}
« Ostatnia zmiana: Maj 26, 2010, 01:25:22 wysłana przez Lerhes »

Offline hudypatyk

  • Użytkownik

# Maj 26, 2010, 11:47:57
Szczerze mówiąc nigdy nie miałem do czynienia z biblioteką 'deque' i nie za bardzo wiem o co chodzi w kodzie po wyżej, może ktoś rzucić linka do jakiegoś tutoriala odnośnie tej biblioteki ? a może ktoś wie co dalej zrobić z tymi vektorami ?.

Offline Lerhes

  • Użytkownik

# Maj 26, 2010, 12:45:47
deque to kolejka tylko że można dodawać/zdejmować elementy z jej przodu i z tyłu, a do tego można po niej iterować...
działa bardzo prosto, podobnie jak inne kontenery z biblioteki standardowej.

To może inaczej, miałeś do czynienia z vector? Bo jeżeli tak, to deque jest bardzo podobne w działaniu.

Może jednak trochę opiszę co tam jest.
pair<int,int>
Żeby nie tworzyć struktury, wykorzystałem szablon: pair. Bardzo prosto działa: "trzyma" w środku dwie liczby typu int (to będą nasze współrzędne). Aby utworzyć parę posługujemy się funkcją: make_pair(x,y) -> gdzie x i y to nasze liczby które umieszczamy w środku. Odwołujemy się do takich licz w ten sposób:
pair<int,int> para = make_pair(5,7);
cout<<para.first;   //Wypisze 5
cout<<para.second; //Wypisze 7
Fajnie mamy prosty sposób na korzystanie z pary liczb. Teraz wykorzystujemy to żeby stworzyć kolejkę takich par (czyli zapamiętać, gdzie są "elementy" które tworzą węża).
//Tworzymy kolejke z takimi parami.
deque< pair<int,int> > pozycjeWeza;
//Dodawanie nowych par jest niezwykle proste:
pozycjeWeza.push_front( make_pair(11,66) );
//Zwroc uwage ze dodajemy "push_front" -> czyli do przodu.
pozycjeWeza.push_front( make_pair(12,66) );
pozycjeWeza.push_front( make_pair(12,67) );

//Co teraz mamy w obiekcie pozycjeWeza? Takie pary: (lewa strona to "prod" prawa strona to "tyl")
//(12,67) (12,66) (11,66)   
//Sa to pozycje na ktorych jest "cialo" weza. Zalozmy ze sie teraz poruszyl, nic nie zjadajac, na pozycje 13, 67, czyli dodajemy nowy punkt:
pozycjeWeza.push_front( make_pair(13,67) );
//Ale przy okazji niszczymy ostatni element (ogon sie zwinal)
pozycjeWeza.pop_back();
//Mamy teraz w obiekcie pozycjeWeza taka sytuacje:
//(13,67) (12,67) (12,66)
//Czyli z przodu dolozylismy, z tylu wywalilismy. Proste? Proste! O to nam chodzilo? A jakze!
Ostatnia część kodu, pokazywała jak "przeglądnąć" obiekt pozycjeWeza po to aby narysować kolejne bloki na planszy (albo żeby sprawdzić czy wąż nie wszedł w samego siebie, albo żeby policzyć jaką ma długość etc).
Wykorzystuję tutaj iteratory. Powinieneś wiedzieć co to jest, zresztą krótko: Są to obiekty które zachowują się trochę jak wskaźniki i można przy
ich pomocy łatwo przeglądać co znajduje się w obiekcie pozycjeWeza, co ilustruje kod:
deque< pair<int,int> >::iterator it = pozycjeWeza.begin();
while (it != pozycjeWeza.end())
{
cout << "Ustawiamy komorke weza na pozycji: " << (*it).first<<" "<<(*it).second;
it++;
}

Edit: Nie musisz wszystkiego rozumieć o "deque" albo "pair", wystarczy że potraktujesz je jako narzędzia, które chociaż nie do końca rozumiesz jak funkcjonują, to wiesz jak ich używać, co robią i gdzie je wykorzystać. (to tak jak z wiertarką: Nie wiesz co sprawia że ona się kręci, ale umiesz ją użyć, wiesz co z nią robić i umiesz ją wykorzystać w praktyce).
« Ostatnia zmiana: Maj 26, 2010, 13:58:25 wysłana przez Lerhes »

Offline hudypatyk

  • Użytkownik

# Maj 26, 2010, 13:42:41
Wielkie dzięki! w sumie to dobra opcja, wszystko pięknie ładnie... ale... jak mam zrobić aby ciało poruszało się za głową?
Mam coś takiego:
Cytuj

         if(x==j_x && y==j_y) // sprawdza czy waz zjadl ofiare
             {
                    Jedzenie();
                    IloscPunktow+=10;
                    pozycjeWeza.push_front( make_pair(x,y) );  // dodaje nowa pare pozycji glowy
             }
         else // jezeli nie zjadl ofiary to dodaje nowa pozycje a usuwa stara
             {
                   pozycjeWeza.push_front( make_pair(x,y) );
                   pozycjeWeza.pop_front();
             }
Teraz mam problem wyświetlaniem ciała, kod poniżej wypisuje mi "O" na współrzędnych nx,ny a nie podąża za głową, co robię źle ?
Cytuj
       
deque< pair<int,int> >::iterator it = pozycjeWeza.begin();
       while (it != pozycjeWeza.end())
            {

                int nx = (*it).first;
                int ny = (*it).second;
                    gotoxy(nx,ny);
          cout << "O";
               it++;
             }
« Ostatnia zmiana: Maj 26, 2010, 13:44:45 wysłana przez hudypatyk »

Offline Lerhes

  • Użytkownik

# Maj 26, 2010, 14:00:32
Bo się pomyliłem :) = przepraszam.
pozycjeWeza.pop_back();   -> usuwa koniec.

pozycjeWeza.pop_front(); -> usuwa z początku (co dodałeś to usnuął).

Offline hudypatyk

  • Użytkownik

# Maj 26, 2010, 14:09:17
Suuuuper!!!!! wszystko działa! ale jest jeden mały błąd, gdy wąż zje ofiarę to zamiast dodać segment za nim to dodaje przed niego, następnie na głowie a dopiero później za głową. Jakieś sugestie :D ?

#EDIT
Ok problem rozwiązałem! Wielkie dzięki Lerhes za pomoc!
#edit
Jeszcze jedno pytanie, czy jest jakaś funkcja która sprawdza ilość elementów tak jak przy vektrorach: x.size();?
Potrzebna mi jest do wyzerowania wszystkich elementów gdy gracz wpadnie w jedna ze ścian.
Prosił bym jeszcze o wytłumaczeniu na jakiej zasadzie zrobić kolizje głowy z segmentami?
« Ostatnia zmiana: Maj 26, 2010, 15:10:28 wysłana przez hudypatyk »

Offline Lerhes

  • Użytkownik

# Maj 26, 2010, 15:55:45
Kto piszę tę grę? My czy ty? Jest tutaj taki dział dla leniwych, którzy chcą żeby wszystko robić za nich. Podpowiedzieć coś, co dla kogoś może być trudne to jedna sprawa, ale tłumaczyć wszystko, to coś innego.

Gdy wpiszesz w google: deque to 2 link jest stroną: http://www.cplusplus.com/reference/stl/deque/
I jest! Funkcja size() do rozmiaru, funkcja clear() do czyszczenia wszystkiego, można też używać operatorów [] czyli:  pozycjeWeza[5] -> do odczytania pary liczb.

Kolizję możesz zrobić, na przykład pisząc sobie funkcję pomocniczą (tym razem użyję operatora [] zamiast iteratorów):
bool sprawdzCzyJestZderzenieZWezem( deque< pair<int,int> > &kolejka, int x, int y)
{
bool czyOK = false;
for(unsigned int i =0; i < kolejka.size(); i++)
{
if( kolejka[i].first == x  &&  kolejka[i].second == y  )
czyOK = true;
}
return czyOK;
}
Uruchamiamy ją:
sprawdzCzyJestZderzenieZWezem(pozycjeWeza,x,y);
Jeżeli zwróci true, to znaczy że wąż zderzył się sam ze sobą. Jako x, y -> wysyłamy pozycję do sprawdzenia (tam gdzie zaraz wejdzie głowa).
« Ostatnia zmiana: Maj 26, 2010, 15:58:35 wysłana przez Lerhes »