'animacja' gry connect 4 (C++, Qt)
-
Witam! Napisałem w C++ grę Connect 4 (http://en.wikipedia.org/wiki/Connect_Four, http://pl.wikipedia.org/wiki/Czwórki) do gry z komputerem. Do wyświetlania planszy stosuję pole Plain Text Edit. Na początku zrobiłem zwykłe przepisywanie planszy (zwykła tablica 6x7, 0 pole puste; 1,2 pionki graczy) do tego pola. Teraz próbuję zrobić funkcję, która w Plain Text Edit zrobi tekstową animację wrzucania pionka, czyli pionek pojawia się w najwyższym wierszu, po odpowiednim czasie pojawia się we wierszu niżej i niżej aż napotka inny pionek. Niestety ta funkcja nie działa, gdyż drukuje planszę dopiero po wykonaniu wszystkich kroków oraz czasem pionek komputera pojawia się wyżej niż powinien, przy czym w następnym ruchu jest on już tam gdzie powinien.
Oto te dwie funkcje:
@void MainWindow::drukujplanszegui()
{
stringstream strumien;
string tekst;for(int i=0;i<6;i++) { strumien<<pozycja[i][0]; for(int j=1;j<7;j++) { strumien<<setw(3)<<pozycja[i][j]; } strumien<<endl; } tekst = strumien.str(); QString tekst_qt = tekst.c_str(); ui->ptePlansza->clear(); ui->ptePlansza->appendPlainText(tekst_qt);
}
void MainWindow::drukujplanszeanimacja(int kolumna1, int gracz5)
{
for(int i=0;i<5;i++)
{
pozycja[i][kolumna1]=gracz5;
drukujplanszegui();
Sleep(500);
if(pozycja[i+1][kolumna1]!=0)
break;
pozycja[i][kolumna1]=0;
}
if(pozycja[5][kolumna1]==0)
pozycja[5][kolumna1]=gracz5;}@
"Plik z całym programem":http://www.speedyshare.com/vP8sy/Connect4-gui.zip
Poza tym próbuję zrobić sprawdzenie czy wykorzystywane przeze mnie listy zostały wczytane z pliku.
@MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);p2=listaZpliku("wartoscioweulozenia2.txt"); //utworzenie listy jednokierunkowej z pliku p1=listaZpliku("wartoscioweulozenia1.txt"); p1p=listaZpliku("wartoscioweulozenia1pion.txt"); p2p=listaZpliku("wartoscioweulozenia2pion.txt"); if(p2==NULL) { ui->ptePlansza->clear(); wyswietlpteplansza("Blad pliku. Sprawdz, czy pliki wartoscioweulozenia1.txt ; wartoscioweulozenia2.txt ; wartoscioweulozenia1pion.txt ; wartoscioweulozenia2pion.txt znajduja sie w folderze roboczym i uruchom program ponownie."); }
.
.
.
}
@listazpliku:
@wartosciowe* listaZpliku(string plik) //wczytuje z pliku do listy jednokierunkowej
{
wartosciowe *pocz, *akt, *og;
string a, b, c, d, e;ifstream we; we.open (plik.c_str()); if (!we.good()) return NULL; else { we>>a>>b>>c>>d>>e; //wczytanie zmiennych tymczasowych z pliku pocz= new wartosciowe; if(!we.eof()) { pocz->w1=atoi(a.c_str()); //wczytanie do listy tymczasowych zmiennych typu string skonwertowanych do liczb całkowitych pocz->w2=atoi(b.c_str()); pocz->w3=atoi(c.c_str()); pocz->w4=atoi(d.c_str()); pocz->pkt=atoi(e.c_str()); pocz->nast=NULL; og=pocz; while (!we.eof()) { we>>a>>b>>c>>d>>e; akt= new wartosciowe; akt->w1=atoi(a.c_str()); akt->w2=atoi(b.c_str()); akt->w3=atoi(c.c_str()); akt->w4=atoi(d.c_str()); akt->pkt=atoi(e.c_str()); akt->nast=NULL; og->nast=akt; og=akt; } } else pocz=NULL; we.close(); return pocz; }
}@
Niestety to sprawdzenie zawsze pokazuje, że coś poszło nie tak, mimo że gra działa normalnie. Dodam, że w wersji konsolowej, napisanej strukturalnie takie sprawdzenie działało.
-
Na wstępie program ten z Qt za wiele wspólnego nie ma, ale postaram się Ci pomóc.
Używając w kodzie Sleepa blokujesz główny wątek GUI, dlatego w czasie animacji eventy nie są obslugiwane, między innymi event odpowiadający za rysowanie PlainTextEdit, dlatego musisz wywołać go ręcznie, w metodzie MainWindow::drukujplanszegui() za ostatnią linijką dopisz
@ ui->ptePlansza->repaint();@
O wiele lepiej by to działało i wyglądało gdybyś użył do "animacji" "QTimer'a":http://qt-project.org/doc/qt-4.8/qtimer.html
Jeśli chodzi o drugi problem to zmieniłem w MainWindow::drukujplanszeanimacja() na i < 6 (bo tyle jest wierszy chyba) i zeton komputera spada do ostatniej linii, ale czy to jest przyczyną tego buga to stuprocentowej pewności nie mam z powodu bliskiej zeru czytelności kodu ;) Jeśli ktoś ma czytać Twój kod w przyszłości to polecam używać komentarzy oraz rozsądnych nazw zmiennych (CamelCase ftw, zapomnij o notacji węgierskiej).
U mnie po takich poprawkach działa i nawet jest grywalne :D
-
bq. Jeśli ktoś ma czytać Twój kod w przyszłości to polecam używać komentarzy oraz rozsądnych nazw zmiennych
Proponuję również używanie w całości języka angielskiego w kodzie źródłowym.
-
O, wielkie dzięki, animacja działa :D Za komentarze już się zabieram, po prostu jeszcze nie mam nawyku pisania ich na bieżąco.
Co do MainWindow::drukujplanszeanimacja(), w wypadku podania i < 6, gdy dojdziemy do i=5 metoda odwołuje się do nieistniejącego elementu tablicy w momencie
@if(pozycja[i+1][kolumna1]!=0)
break;@Chociaż po tej zmianie działa, wolałbym tego uniknąć.
Z założenia miało to tak działać:
@void MainWindow::drukujplanszeanimacja(int kolumna1, int gracz5)
{
for(int i=0;i<5;i++) //przeszukiwanie wierszy poza ostatnim
{
pozycja[i][kolumna1]=gracz5; //w wybranej kolumnie wstawia pionek w i-tym wierszu
drukujplanszegui(); //drukuje plansze
Sleep(300); //czeka
if(pozycja[i+1][kolumna1]!=0) //jeżeli w następnym wierszu w wybranej kolumnie jest pionek to wychodzi z pętli (pionek jest już na swoim miejscu)
break;
else
pozycja[i][kolumna1]=0; //jeśli nie to kasuje wcześniej wstawiony pionek i przechodzi do następnego wiersza
}
if(pozycja[5][kolumna1]==0) //sprawdzenie pominiętego w pętli ostatniego wiersza.
pozycja[5][kolumna1]=gracz5; //jeżeli w wybranej kolumnie w ostatnim wierszu nie ma pionka to go tam wstawia}@
Nie macie może jeszcze jakiegoś pomysłu co do tego sprawdzania czy lista się wczytała z pliku? Kod do sprawdzania jednej z list jest na początku mainwindow.cpp w domyślnym konstruktorze w komentarzu (nie chciałem żeby mnie to wkurzało, ani żebym o tym zapomniał). Jak już wcześniej pisałem sprawdzanie to zawsze wyrzuca błąd, mimo że lista się wczytała.
Jest jeszcze jedna rzecz która mnie prześladuje, aczkolwiek nie wiem czy to wina kodu, Qt, czy jeszcze czegoś innego. Otóż czasem gra jakby wykrywała dwa kliknięcia na przycisk zamiast jednego, bo po umiejscowieniu mojego pionka i pionka komputera wrzuca mój pionek tam gdzie poprzedni.
-
Przerobiłem tę metodę animacji, tak żeby działało logicznie i jest wszystko w porządku:
@void MainWindow::drukujplanszeanimacja(int kolumna1, int gracz5)
{
int n;
for(int i=5;i>=0;i--) //wyszukiwanie miejsca do wstawienia pionka
{
if(pozycja[i][kolumna1]==0)
{
n=i;
break;
}
}for(int i=0;i<n;i++) { pozycja[i][kolumna1]=gracz5; //w wybranej kolumnie wstawia pionek w i-tym wierszu drukujplanszegui(); //drukuje plansze Sleep(180); //czeka pozycja[i][kolumna1]=0; //kasuje wcześniej wstawiony pionek i przechodzi do następnego wiersza } pozycja[n][kolumna1]=gracz5; //wstawia pionek w odpowiednie miejsce drukujplanszegui(); //drukuje plansze
}@
A sprawdzanie czy pliki się wczytały wciąż nie działa z przyczyn tajemniczych :(
-
bq. Ustawiłem working directory gdzie trzeba.
Używasz shadow buildów, więc jeśli to Twoja ścieżka, to tam powinny sie znalezc:
@C:/Users/ZAP/Desktop/Connect4_gui/Connect4_gui-build-Desktop_Qt_5_0_1_MinGW_32bit-Release@
A nie w katalogu z projektem lub buildami (release/debug z małej litery).
U mnie plansze się wczytują po odkomentowaniu sprawdzania.
-
O kurcze, wielkie dzięki. Miałem straszny błąd w myśleniu. Wydawało mi się, że w ustawieniach Qt przestawiłem katalog roboczy na katalog z kodem, w którym mam pliki do wczytania, a tego nie zrobiłem. Sądziłem, że mam jakiś błąd sprawdzania, bo gra działała i wygrywała ze mną często, a większość jej 'inteligencji' opierała się na tych właśnie plikach. Jednak po prostu program sobie radził bez plików.
Sprawdzanie oczywiście przerobiłem już tak, żeby nie pozwalało na grę bez plików :)A coś mi z tyłu głowy siedziało, że mój przeciwnik stał się głupszy po przeniesieniu aplikacji na okienka.