Wymagane jest zapoznanie się z następującymi podrozdziałami:
OL031 - instalacja biblioteki SDL w środowisku Dev-C++
OL032 - inicjalizacja biblioteki SDL
OL033 - powierzchnie graficzne w SDL
OL034 - przygotowanie plików własnej biblioteki graficznej
OL035 - rysowanie po powierzchni graficznej
OL036 - algorytm Bresenhama rysowania odcinka
OL037 - obcinanie grafiki do prostokąta
OL038 - podstawowe algorytmy wypełniania obszarów
OL039 - algorytm SmithaOL040 - praca w środowisku sterowanym zdarzeniami
OL041 - czcionka bitmapowa
OL042 - czcionka wektorowa
OL043 - przyciski poleceń
OL050 - macierze - podstawowe operacje na macierzach
OL051 - przekształcenia na płaszczyźnie
OL052 - algorytm wypełniania wielokątów
OL053 - rysowanie okręgów i kół
OL054 - rysowanie elips i owali
UWAGA!!!
Bieżące opracowanie NIE JEST KURSEM programowania biblioteki SDL. Są to materiały przeznaczone do zajęć na kole informatycznym w I LO w Tarnowie.
Przed pracą z tym rozdziałem utwórz w środowisku Dev-C++ nowy projekt SDL i dołącz do niego pliki SDL_gfx.cpp oraz SDL_gfx.h. Jeśli nie przeczytałeś zalecanych rozdziałów, koniecznie zrób to, a wszystko stanie się jasne. W przeciwnym razie odpuść sobie również ten rozdział. Opis funkcji bibliotecznych znajdziesz w podsumowaniu SDL013.
Artykuł nie jest już rozwijany
Go Moku w języku japońskim oznacza "Pięć Kamieni". Jest to starożytna gra logiczna wywodząca się z Azji - bardzo popularna w Japonii i w Chinach. Jeśli zapytamy o nią początkującego w tej grze, to odpowie nam, że jest bardzo prosta, z kolei mistrz odpowie, że jest bardzo trudna. Wszystko zależy od poziomu gracza. Gra jest bardzo "uzależniająca", lecz jednocześnie uczy logicznego myślenia oraz przewidywania i konsekwencji różnych posunięć. Biegłość osiąga się po kilkunastu miesiącach nauki, natomiast poziom mistrzowski jest dostępny tylko dla uzdolnionych graczy po kilku latach intensywnego treningu.
Zasady są następujące:
Plansza gry nosi nazwę goban. Istnieje kilka odmian plansz - my zajmiemy się planszami zbudowanymi z 15 x 15 pól lub 19 x 19 pól.
Gracze wykonują ruchy na przemian posługując się okrągłymi kamyczkami w kolorze czarnym i białym. Piony stawia się na przecięciu linii siatki gobanu. Rozpoczyna gracz czarny. Grę wygrywa ten z graczy, który jako pierwszy ustawi w kolumnie, rzędzie lub po przekątnej dokładnie 5 pionów w swoim kolorze jeden obok drugiego (więcej niż 5 pionów nie oznacza zwycięstwa). Poniżej mamy przykład wygranej przez piony czarne.
Przebieg rozgrywki można notować zapisując kolejne posunięcia gracza czarnego i białego. Każda pozycja na planszy posiada współrzędne - literkę określającą kolumnę oraz liczbę określającą wiersz. Na przykład czarny zwykle rozpoczyna na polu h8 (środek planszy) - biały zwykle stawia po przekątnej, np. na polu i7:
Rozgrywka gomoku | ||
Lp. | Czarny | Biały |
1. | h8 | i7 |
2. | ... | ... |
Celem naszego projektu będzie napisanie programu wykorzystującego utworzoną przez nas bibliotekę graficzną SDL_gfx oraz bibliotekę SDL_gui, który obsłuży planszę do gry Go Moku. Program powinien umożliwiać:
- Rozpoczęcie nowej gry.
- Wyświetlanie przebiegu rozgrywki.
- Cofanie i przywracanie cofniętych ruchów.
- Rozpoznawanie wygranej.
- Zapis stanu rozgrywki do pliku
- Odczyt stanu rozgrywki z pliku
Na początek opracujemy procedurę rysowania planszy gry. Plansza będzie budowana z "klocków" (przyjęcie takiego rozwiązania wyjaśni się później), które odpowiadają polu zajętemu przez pion. Ponieważ piony stawiamy na przecięciu linii siatki, to granice pól nie będą przebiegały przez linie siatki, lecz w środku pomiędzy nimi. Ideę tę wyjaśnia poniższy rysunek:
Przy tym podziale planszy gry zauważamy, iż składa się ona z dziewięciu rodzajów pól w zależności od ich położenia na planszy. Pola te oznaczmy kolejnymi literkami od a a do j. Spójrz na poniższy rysunek:
Pola a, c, g i j występują tylko w narożnikach planszy. Pola b w górnym wierszu (z wyjątkiem narożników), a pola d, f i h odpowiednio w lewej kolumnie, w prawej kolumnie oraz w dolnym wierszu. Reszta planszy wypełniona jest polami typu e. Narzucającym się rozwiązaniem będzie zaprojektowanie funkcji, która jako parametry przyjmie współrzędne kolumna wiersz pola planszy i narysuje odpowiednie pole we właściwym miejscu ekranu.
Określmy następujące parametry:
pfsize - rozmiar planszy gry. Możliwe wartości to 15 (plansza 15 x 15) lub 19 (plansza (19 x 19). pcol - numer kolumny, w której znajduje się pole planszy, wartości od 1 do rozmiar. prow - numer kolumny, w której znajduje się pole gry, wartości od 1 do rozmiar Każde pole planszy GoMoku może zawierać cztery linie fragmentu siatki w następującym układzie:
Wszystkie linie biegną od środka pola do środka boku pola:
lg - linia górna
ld - linia dolna
ll - linia lewa
lp - linia prawa
Warunki rysowania poszczególnych linii są następujące
linia górna | lg | prow < pfsize |
linia dolna | ld | prow > 1 |
linia lewa | ll | pcol > 1 |
linia prawa | lp | pcol < pfsize |
Otrzymaliśmy niesamowicie proste warunki! Pozwolą nam one narysować dowolny element planszy gry w zależności od jego położenia.
Teraz określimy wymiary pól planszy oraz sposób obliczania ich współrzędnych na ekranie. Oprzemy się na wymiarach ekranu, które można odczytać bezpośrednio ze struktury SDL_Surface. Ustalmy co następuje:
pfsize - rozmiar planszy gry. Możliwe wartości to 15 (plansza 15 x 15) lub 19 (plansza (19 x 19). screen - struktura SDL_Surface screen.w - szerokość ekranu w pikselach - przynajmniej 800! screen.h - wysokość ekranu w pikselach - przynajmniej 600! pfx - współrzędna x lewego górnego narożnika planszy pfy - współrzędna y lewego górnego narożnika planszy pwh - szerokość lub wysokość pola gry - jest ono kwadratowe pfwh - szerokość lub wysokość planszy Całą planszę gry umieścimy w lewej części okna. Wokół pól musimy zarezerwować obszar na opis współrzędnych. Jeśli do opisu zastosujemy czcionkę vecprop9x12.fnt, to paski te powinny posiadać szerokość przynajmniej 16 pikseli. Poniższy rysunek przedstawia wstępne rozplanowanie ekranu gry.
Wykonujemy następujący algorytm obliczeniowy:
K01: pfwh ← screen.h - 32 ; wyznaczamy wstępną szerokość/wysokość planszy K02: pwh ← pfwh div pfsize ; wyznaczamy wstępną szerokość/wysokość pola gry K03: Jeśli pwh mod 2 = 0, pwh ← pwh - 1 ; pole gry powinno powinno być szerokie/wysokie na nieparzystą ilość pikseli. Wtedy środek będzie równo odległy od jego krawędzi K04: pfwh ← pwh • pfsize ; obliczamy rzeczywistą szerokość/wysokość planszy K05: pfx ← (screen.h - pfwh) div 2 ; modyfikujemy współrzędne lewego górnego narożnika planszy K06: pfy ← pfx K07: Koniec Algorytm ten wyznacza położenie planszy na ekranie, jej rozmiary oraz wielkość pola gry. Będzie on wykonywany przy każdej zmianie typu planszy. Mając te wymiary możemy teraz określić współrzędne ekranowe każdego pola gry w zależności od jego numeru wiersza i kolumny:
px ← (pcol - 1) • pwh + pfx
py ← (pfsize - prow) • pwh + pfy
Wiemy już jak wyznaczyć wielkość pola gry, jego położenie na ekranie oraz zawarte w nim linie siatki. Kolejnym problemem będzie rysowanie pól z różną zawartością. Na potrzeby naszej gry określmy następujące typy pól:
wskazane kursorem |
nie wskazane kursorem |
|
pole puste | ||
czarny pion | ||
ostatnio postawiony czarny pion |
||
czarny pion w zwycięskiej piątce |
||
biały pion | ||
ostatnio postawiony biały pion |
||
biały pion w zwycięskiej piątce |
Zatem do funkcji rysującej pole gry będziemy przekazywali dodatkowy parametr - pcode, który określa rodzaj pola. Zastosujemy kodowanie binarne:
Bity b1b0 sterują rysowaniem pionów czarnych i białych.
Bity b3 b2 sterują typem pionu.
Bit b4 steruje kolorem tła pola gry.Mamy już wszystko, co potrzebne do utworzenia procedury rysującej planszę gry. Poniżej znajduje się program testujący rysowanie planszy gry. Nie jest to jeszcze właściwa gra. Pamiętaj, aby do katalogu projektowego przekopiować czcionkę vecprop9x12.fnt.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P055A - Test rysowania planszy do gry Go Moku //---------------------------------------------- #include <SDL/SDL_gfx.h> #include <SDL/SDL_gui.h> // Stałe globalne //--------------- const int SCRX = 640; // stałe określające szerokość i wysokość const int SCRY = 480; // ekranu w pikselach const int C_LOW = 0xffcc99; // kolor normalny tła const int C_HIGH = 0xffffcc; // kolor podświetlonego tła const int C_CODE = 0x10; // maska koloru tła const int T_CODE = 0xB; // maska typu pola const int P_CODE = 0x3; // maska koloru pionów const int P_BLACK = 1; // kod czarnego gracza const int P_WHITE = 2; // kod białego gracza const int P_LAST = 4; // ostatni pion const int P_WIN = 8; // pion zwycięski // Zmienne globalne, widoczne we wszystkich funkcjach //--------------------------------------------------- SDL_Surface * screen; gfxFont * font = gfxOpenFont("vecprop9x12.fnt"); Uint32 pfsize; // rozmiar planszy w wierszach/kolumnach = 15 lub 19 Uint32 pfx,pfy; // współrzędne lewego górnego narożnika planszy Uint32 pfwh; // szerokość/wysokość planszy w pikselach Uint32 pwh; // szerokość/wysokość pola gry // Funkcje usługowe //----------------- // Funkcja oblicza wszystkie wymiary nowej planszy. // ps - liczba kolumn/wierszy na planszy = 15 lub 19 //-------------------------------------------------- void calcNewPlayfield(Uint32 ps) { pfsize = ps; pfx = 16; pfwh = screen->h - 32; pwh = pfwh / ps; if(!(pwh % 2)) pwh--; pfwh = pwh * ps; pfx = pfy = (screen->h - pfwh) >> 1; } // Funkcja rysuje tło planszy wraz // z opisem kolumn i wierszy //----------------------------------- void DrawPlayfield() { if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); SDL_Rect r; r.x = r.y = 0; r.w = r.h = screen->h; SDL_FillRect(screen, &r, C_LOW); // rysujemy opisy kolumn na dole planszy char c[3]; // przechowuje tekst kolumn lub wierszy Uint32 x, y, i; x = pfx + (pwh >> 1); // pozycja x pierwszego znaku y = pfwh + pfx + ((pfx - font->h) >> 1); c[0] = 'a'; c[1] = 0; for(i = 0; i < pfsize; i++) { gfxDrawText(screen, font, c, x - (gfxTextLength(font, c) >> 1), y, 0, -1); c[0]++; x += pwh; } // rysujemy opisy wierszy x = pfx >> 1; y = pfwh + pfx - (pwh >> 1) - (font->h >> 1); for(i = 1; i <= pfsize; i++) { c[0] = 48 + (i % 10); if(i > 9) { c[1] = c[0]; c[0] = '1'; c[2] = 0; } else c[1] = 0; gfxDrawText(screen, font, c, x - (gfxTextLength(font, c) >> 1), y, 0, -1); y -= pwh; } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, screen->h, screen->h); } // Funkcja rysuje pole planszy // pcol - numer kolumny // prow - numer wiersza // pcode - kod pola //---------------------------- void DrawCell(Uint32 pcol, Uint32 prow, Uint32 pcode) { if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); SDL_Rect r; // wypełniamy tło r.x = (pcol - 1) * pwh + pfx; r.y = (pfsize - prow) * pwh + pfy; r.w = r.h = pwh; SDL_FillRect(screen, &r, (pcode & C_CODE) ? C_HIGH : C_LOW); // rysujemy linie siatki Uint32 lcx = r.x + pwh / 2; Uint32 lcy = r.y + pwh / 2; Uint32 lrx = r.x + pwh - 1; Uint32 lly = r.y + pwh - 1; Uint32 llen = 1 + (pwh >> 1); if(prow < pfsize) gfxVLine(screen, lcx, r.y, 0, llen); if(prow > 1) gfxVLine(screen, lcx, lcy, 0, llen); if(pcol < pfsize) gfxHLine(screen, lcx, lcy, 0, llen); if(pcol > 1) gfxHLine(screen, r.x, lcy, 0, llen); // w 9 polach środkowych planszy rysujemy małe koła na przecięciu linii Uint32 fs = (pfsize + 1) >> 1; if(((prow == fs - 2) || (prow == fs) || (prow == fs + 2)) && ((pcol == fs - 2) || (pcol == fs) || (pcol == fs + 2))) gfxFillCircle(screen, lcx, lcy, 2, 0); // rysujemy piona Uint32 pr = (llen * 3) / 4; if(pcode & P_CODE) { gfxFillCircle(screen, lcx, lcy, pr, (pcode & P_BLACK) ? 0x1f1f1f : 0xffffff); gfxCircle(screen, lcx, lcy, pr, 0); if(pcode & P_LAST) { Uint32 clen = 1 + (pwh >> 1); gfxHLine(screen, (r.x + lcx) >> 1, lcy, 0xff0000, clen); gfxVLine(screen, lcx, (r.y + lcy) >> 1, 0xff0000, clen); } else if(pcode & P_WIN) { pr >>= 2; if(pr < 3) pr = 3; gfxFillCircle(screen, lcx, lcy, pr, 0xff0000); } } if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, r.x, r.y, r.w, r.h); } //*********************** // *** Program główny *** //*********************** int main(int argc, char * argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE))) exit(-1); SDL_WM_SetCaption("Test Go Moku",""); // ustalamy tryb 15 x 15 calcNewPlayfield(15); DrawPlayfield(); // czekamy na klawisz ESC lub zamknięcie okna SDL_Event event; // Unia zawierająca struktury zdarzeń bool running = true; // zmienna decyzyjna while(running) { // rysujemy planszę gry z przypadkowymi typami pól for(int i = 1; i <= pfsize; i++) for(int j = 1; j <= pfsize; j ++) if(!(rand() % 5)) DrawCell(i, j, rand()); else DrawCell(i, j, 0); SDL_WaitEvent(&event); // czekamy na zdarzenie switch(event.type) // sprawdzamy, co się wydarzyło { case SDL_KEYDOWN: if(event.key.keysym.sym != SDLK_ESCAPE) break; case SDL_QUIT: running = false; break; } } // zamykamy czcionkę gfxCloseFont(font); return 0; }
Ciąg dalszy w kolejnym rozdziale.
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe