Go Moku - część 1

Powrót do spisu treści

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 Smitha
OL040 - 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
OL055 - Go Moku - część 1

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ł w PRZEBUDOWIE


SDLProgram planszy gry w Go Moku jest prawie gotowy. Pozostał jedynie do rozwiązania problem zapisu i odczytu stanu gry z dysku.

Ponieważ w trakcie tworzenia programu wyszły na jaw pewne niedoróbki w bibliotece GUI (to normalne, patrz MicroSoft!!!), przed uruchomieniem programu ściągnij na swój komputer uaktualnione pliki biblioteczne. Przypominam, iż zapisujemy je w katalogu C:\Dev-Cpp\include\Sdl. Są to: SDL_gui.h oraz SDL_gui.cpp.

 

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P055B - Test odczytu myszki
//----------------------------

#include <SDL/SDL_gfx.h>
#include <SDL/SDL_gui.h>

using namespace std;

// Stałe globalne
//---------------

const int SCRX = 800;      // stałe określające szerokość i wysokość
const int SCRY = 600;      // 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 glacza
const int P_WHITE  = 2;        // kod białego gracza

const int P_LAST   = 4;        // ostatni pion
const int P_WIN    = 8;        // pion zwycięski

const int MAXBUTTON = 6;       // liczba przycisków

// Zmienne globalne, widoczne we wszystkich funkcjach
//---------------------------------------------------

SDL_Surface * screen;
gfxFont     * font = gfxOpenFont("vecprop9x12.fnt");

Uint32 pfsize = 0;  // 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

Uint32 pf[20][20];             // plansza gry
Uint32 pl[200][2];             // kolejne ruchy graczy
Uint32 plnr;                   // numer ruchu
Uint32 plh;                    // liczba wierszy tekstu na liście

bool playing = true;           // określa, czy pole gry reaguje na ruchy graczy
bool player;                   // określa bieżącego gracza

gfxButton    * btn[MAXBUTTON]; // przyciski sterujące grą
gfxOption    * opt;            // opcje gry
gfxScrollBar * sbr;            // pasek przewijania listy ruchów
SDL_Rect       plr;            // prostokąt listy ruchów

// Funkcje usługowe
//-----------------

// Funkcja zamienia liczbę na tekst
//---------------------------------

char * utoa(Uint32 x)
{
  static char c[4];
  
  c[0] = 48 + x / 100; x %= 100;
  c[1] = 48 + x / 10;  x %= 10;
  c[2] = 48 + x;
  c[3] = 0;
  
  while((c[0] == '0') && c[1])
    for(int i = 1; i < 4; i++) c[i-1] = c[i];
  return c;
}

// 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], * p; // 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++)
  {
    p = utoa(i);
    gfxDrawText(screen, font, p, x - (gfxTextLength(font, p) >> 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);
}
 
// Funkcja odczytuje z listy numer wiersza i kolumny
//--------------------------------------------------

void ReadColRow(Uint32 pos, Uint32 &col, Uint32 &row)
{
  col = pl[pos >> 1][pos % 2] >> 8;
  row = pl[pos >> 1][pos % 2] & 0xff;
}

// Funkcja wstawia do planszy piona
// wg listy pl oraz pozycji plnr
//---------------------------------

void InsertMove()
{
  Uint32 col, row;
  
  if(playing)
  {
    if(plnr)
    {
       ReadColRow(plnr - 1, col, row);
       pf[row][col] &= P_LAST ^ 0xff; // zerujemy bit ostatniego piona
       DrawCell(col, row, pf[row][col]);
    }
    ReadColRow(plnr, col, row);
    pf[row][col] |= (plnr % 2) ? P_WHITE : P_BLACK;
    pf[row][col] |= P_LAST;
    DrawCell(col, row, pf[row][col]);
    plnr++;
    if((plnr >> 1) > sbr->value + sbr->vprop - 1)
      sbr->value = (plnr >> 1) - sbr->vprop + 1;
    sbr->Refresh();
    (* sbr->call)(sbr);

// sprawdzamy, czy po wykonaniu ruchu zawodnik jest zwycięzcą

    Sint32 dx[] = {-1, -1,  0,  1};
    Sint32 dy[] = { 0,  1,  1,  1};
    Uint32 x,y,c,n,xp,yp;
  
    c = pf[row][col] & P_CODE;  // kolor bieżącego gracza
    for(int i = 0; i < 4; i++)
    {
      x = col;
      y = row;
      while((x > 0) && (x <= pfsize) && (y > 0) && (y <= pfsize) &&
            ((pf[y][x] & P_CODE) == c))
      {
        xp = x; yp = y; x += dx[i]; y += dy[i];     
      }
      n = 0;
      x = xp; y = yp;
      while((x > 0) && (x <= pfsize) && (y > 0) && (y <= pfsize) &&
            ((pf[y][x] & P_CODE) == c))
      {
        n++;
        x -= dx[i]; y -= dy[i];     
      }
      if(n == 5)
      {
        playing = false;
        for(int j = 1; j <= 5; j++)
        {
          pf[yp][xp] &= P_CODE;
          pf[yp][xp] |= P_WIN;
          DrawCell(xp, yp, pf[yp][xp]);
          xp -= dx[i]; yp -= dy[i];                  
        }
        break;
      }
    }
  }
}

// Funkcja inicjuje nową grę
//--------------------------

void InitGame()
{
  Uint32 size;
  gfxOptElement * p = opt->item;
  
  if(!p->option) p = p->next;
  
  size = p->tag;

  playing = true;
  if(pfsize != size)
  {
    CalcNewPlayfield(size);
    DrawPlayfield();
  }
  for(int i = 1; i <= size; i++)
    for(int j = 1; j <= size; j++)
    {
      pf[i][j] = 0;
      DrawCell(i,j,0);
    }
  for(int i = 0; i < 181; i++) pl[i][0] = pl[i][1] = 0;

  plnr = 0;
  sbr->value = 0;
  sbr->Refresh(); (* sbr->call)(sbr);
}

// *************************************************
// *** Funkcje obsługujące kontrolki ***
// *************************************************

// Funkcja cofa wykonany ruch
//---------------------------

void TakeBack()
{
  Uint32 col, row;
  

  if(!playing)
  {
    playing = true;
    for(int i = 1; i <= pfsize; i++)
      for(int j = 1; j <= pfsize; j++)
        if(pf[i][j] & P_WIN)
        {
          pf[i][j] &= P_WIN ^ 0xff;
          DrawCell(j, i, pf[i][j]);
        }            
  }
  if(plnr)
  {
    plnr--;
    ReadColRow(plnr, col, row);
    pf[row][col] = 0;
    DrawCell(col, row, 0);
    if(plnr)
    {
      ReadColRow(plnr - 1, col, row);
      pf[row][col] |= P_LAST;
      DrawCell(col, row, pf[row][col]);      
    }
    if((plnr >> 1) < sbr->value)
    {
      sbr->value = plnr >> 1;
      if(sbr->value >= (sbr->vprop >> 1)) sbr->value -= (sbr->vprop >> 1);
      else sbr->value = 0;
    }
    sbr->Refresh();
    (* sbr->call)(sbr);
  }     
}

// O ile jest to możliwe, funkcja przywraca cofnięty ruch
//-------------------------------------------------------

void Restore()
{
  Uint32 col, row;
  
  if(pl[plnr >> 1][plnr % 2]) InsertMove();
}

// Obsługa przycisków
//-------------------

void BtnClick(gfxGUIObject * sender)
{
  switch(sender -> tag)
  {
    case 0 : InitGame(); break;
    case 1 : TakeBack(); break;
    case 2 : Restore(); break;
    case 3 : break;
    case 4 : break;
    case 5 : exit(0);
  }  
}

// Funkcja wyświetla zawartość listy ruchów na podstawie paska przewijania
//------------------------------------------------------------------------

void Update_pl(gfxGUIObject * sender)
{
  
  if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);
  SDL_FillRect(screen, &plr, C_LOWER);
  Uint32 y = plr.y;
  Uint32 d = font->h + (font->h >> 1);
  char * txp;
  for(int i = 0; i < sbr->vprop; i++)
  {
    Uint32 n = i + sbr->value + 1;
    txp = utoa(n);
    Uint32 x = plr.x + font->h + gfxTextLength(font,"000") - gfxTextLength(font, txp);
    x = gfxDrawText(screen, font, txp, x, y, 0xff0000, -1);
    x = gfxDrawText(screen, font, " :  ", x, y, 0x0000ff, -1);
    for(int j = 0; j < 2; j++)
    {
      Uint32 plmove = pl[n-1][j];
      if(plmove && ((n << 1) - 2 + j < plnr))
      {
        x += gfxDrawChar(screen, font, (plmove >> 8) + 96, x, y, j ? 0xffffff : 0, -1);
        gfxDrawText(screen, font, utoa(plmove & 0xff), x, y, j ? 0xffffff : 0, -1);
      }
      else
        x = gfxDrawText(screen, font, "...", x, y, j ? 0xffffff : 0, -1);
      if(!j) x = plr.x + (plr.w >> 1) + (font->h << 1);
    }
    y += d;  
  }
  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
  SDL_UpdateRect(screen, plr.x, plr.y, plr.w, plr.h);     
}

// Funkcja ustawia panel sterujący gry
//------------------------------------

void InitControls()
{
  if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

// Najpierw wypełniamy tło pulpitu szarym kolorem
  
  SDL_Rect r;
  r.x = screen->h; r.y = 0;
  r.w = screen->w - screen->h;
  r.h = screen->h;
  SDL_FillRect(screen, &r, C_FACE);
    
// W na środku pierwszego wiersza umieszczamy napis z tytułem gry

  char * t = "Plansza do gry w GO MOKU";
  Uint32 x, y, d;
  d = font->h + (font->h >> 1);  // 1,5 wiersza tekstu
  x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1);
  y = font->h >> 1;
  gfxDrawText(screen, font, t, x - 1, y - 1, C_UPPER,-1);
  gfxDrawText(screen, font, t, x + 1, y + 1, C_LOWER,-1);
  gfxDrawText(screen, font, t, x, y, C_TEXT,-1);
  
// Teraz wypisujemy wiersz copyright

  t = "(C)2008 Koło Informatyczne";
  x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1);
  y += d;
  gfxDrawText(screen, font, t, x, y, C_TEXTBG,-1);
  t = "I LO w Tarnowie";
  x = r.x + (r.w >> 1) - (gfxTextLength(font, t) >> 1);
  y += d;
  gfxDrawText(screen, font, t, x, y, C_TEXTBG,-1);
  
// Rysujemy poziomą linię pod napisem copyright

  y += d;
  gfxHLine(screen, r.x, y, C_UPPER, r.w);
  gfxHLine(screen, r.x, y + 1, C_LOWER, r.w);
  
// Rysujemy trójwymiarową ramkę

  d = 8;
  r.x += d; r.y = y + d; r.w -= d << 1; r.h -= r.y + d;

  r.x--; r.y--;
  gfxRectangle(screen, &r, C_UPPER);
  r.x += 2; r.y += 2;
  gfxRectangle(screen, &r, C_LOWER);
  r.x--; r.y--;
  gfxRectangle(screen, &r, C_FACE);
  
  r.x += d; r.y += d; r.w -= d << 1; r.h -= d << 1;
  
  if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
  
// Umieszczamy kontrolki opcji rozmiaru planszy  

  gfxOptElement * opte1, * opte2;
  opte2 = new gfxOptElement(19, 1, 1, false, true, "Plansza gry 19x19", NULL, NULL);  
  opte1 = new gfxOptElement(15, 1, 1, true, true, "Plansza gry 15x15", NULL, opte2);
  opt = new gfxOption(screen, font, &r, opte1);

// Tworzymy przyciski sterujące funkcjami gry

  char * btx[] = {"Nowa gra Go Moku", "Cofnij ruch", "Przywróć ruch",
                  "Odczytaj grę z dysku", "Zapisz grę na dysk", "Zakończ Go Moku"};
  
  r.y += opt->rect.h + 4; r.h = font->h << 1;
  
  for(int i = 0; i < MAXBUTTON; i++)
  {
    btn[i] = new gfxButton(i, true, screen, font, &r, btx[i], BtnClick);
    r.y += r.h + 4;
  }

// Tworzymy kontrolkę listy ruchów

  for( int i = 0; i < 180; i++) pl[i][0] = pl[i][1] = 0;
  r.h = screen->h - r.y - d;
  d   = font -> h + (font->h >> 1);
  plh = r.h / d;
  r.h = plh * d;
  r.w -= d + 2;
  plr = r;
  r.x += r.w + 2;
  r.w = d;
  sbr = new gfxScrollBar(0, true, screen, &r, 0, 181 - plh, plh, plh - 1, Update_pl);
  Update_pl(sbr);
  SDL_UpdateRect(screen,0,0,0,0);
}

// Na podstawie położenia kursora myszki oraz wymiarów planszy funkcja
// wylicza kolumnę i wiersz wskazywane przez kursor myszki
//--------------------------------------------------------------------

void ColRow(Sint32 x, Sint32 y, Sint32 &row, Sint32 &col)
{
  x -= pfx; y -= pfy;
  col = (x / pwh) + 1;
  row = pfsize - (y / pwh);
  if((col < 1) || (col > pfsize) || (row < 1) || (row > pfsize)) row = col = 0;
}

// Obsługa zdarzeń na planszy
//---------------------------

void pfDoEvents(SDL_Event * e)
{
  Sint32 col, row, col2, row2;
  
  if(playing)
  {
    switch(e->type)
    {
      case SDL_MOUSEMOTION:
        ColRow((Sint32)e->motion.x, (Sint32)e->motion.y, row, col);
        col2 = row2 = 0;
        for(int i = 1; i <= pfsize; i++)
        {
          for(int j = 1; j <= pfsize; j++)
          if(pf[i][j] & C_CODE)
          {
            row2 = i; col2 = j; break;
          }
          if(col2) break;
        }
        if(col2)
        {
          pf[row2][col2] &= C_CODE^0xff;;
          if((col2 != col) || (row2 != row))
            DrawCell(col2, row2, pf[row2][col2]);
        }
        if(col)
        {
          pf[row][col] |= C_CODE;
          DrawCell(col, row, pf[row][col]);
        }
        break;
      case SDL_MOUSEBUTTONDOWN:
        ColRow(e->button.x, e->button.y, row, col);
        if(col && !(pf[row][col] & P_CODE))
        {
          row2 = plnr >> 1; col2 = plnr % 2;
          pl[row2][col2] = (col << 8) | row;

// kasujemy ewentualne dalsze ruchy
          
          do
          {
            col2++;
            if(col2 == 2)
            {
              col2 = 0; row2++;
            }
            pl[row2][col2] = 0;
          } while(row2 < 181);
          InsertMove();
        }
        break;
    }
  }     
}

// Obsługa zdarzeń w grze
//-----------------------

bool DoEvents(SDL_Event * e)
{
  pfDoEvents(e);            // plansza
  opt->DoEvents(e);         // opcje
  for(int i = 0; i < MAXBUTTON; i++)
    btn[i]->DoEvents(e);    // przyciski
  sbr->DoEvents(e);         // pasek przewijania z listą ruchów
  return true;     
}

// **********************
// *** 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 | SDL_FULLSCREEN))) exit(-1);
  
// ustalamy tryb 15 x 15

  InitControls();
      
// Umieszczamy na planszy przyciski sterujące trybem gry

  InitGame();
  
// obsługujemy wszystkie zdarzenia w grze

  SDL_Event event;      // Unia zawierająca struktury zdarzeń

  while(true)
  {
    SDL_WaitEvent(&event); // czekamy na zdarzenie    
    if(DoEvents(&event) && (event.type == SDL_QUIT)) break;
  }

// zamykamy czcionkę

  gfxCloseFont(font);
  
  return 0;

}    



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.