"8x8 kółko i krzyżyk" cpp :D   strona główna:
A po co ten Excel ;-)
 
Kursik który czytam ciągnie mnie przez przykłady których wyniki pokazywane są na konsoli. Dla programistów VBA konsola to twór raczej mało atrakcyjny   Gra 8x8.exe i plik main.cpp
(żeby nie powiedzieć niepotrzebny) jednak mnie prowadzi w poznawaniu nowych możliwości języka C++ i nawet się z nią trochu zaprzyjaźniłem :D   8x8.zip
 
    Wykorzystując fakt że zostałem z dzieciakami w domu a że mojej ciągłej obecności nie wymagają to też postawiłem sobie ambitny plan próby napisania  
swojej pierwszej gry w c++ :-DD Cóż.. Interfejs jaki odrobinę poznałem - tj konsola Windows wielkich możliwości mi nie daje ale nam to nie przeszkodzi.  
 
    Gierka będzie czymś na zasadzie gry "Kółko i krzyżyk" tyle że na planszy 8x8 a do wygranej trzeba 4'rech X'ów lub O'łek w linii (pionowej, poziomej lub  
ukośnej) Jak ma to wyglądać na konsoli:
 
 - po uruchomieniu programu (plik 8x8.exe)  
   ukazuje się nam Konsola z elementami planszy.  
   Kolejne elementy są ponumerowane od 1 do 64.  
 - program ma czekać na wprowadzenie liczby z  
   zakresu <1:64> i jeżeli to pole jest puste ma   
   wstawiać na przemian X lub O. (gra jest na  
   dwóch. Oprogramowanie gry komputera, choć  
   pewnie wykonalne to jednak stanowczo nie  
   proste :D i bynajmniej nie przez wzgląd na język  
   który wykorzystuję a algorytm którym program   
   miałby realizować strategię ataku, obrony, …)  
   Mi jednak nie zależy jedynie na napisaniu gry a  
   wręcz przede wszystkim przybliżenie Wam (a dla  
   mnie powtórka) oprogramowywania Konsoli w c++  
 - Pola X i O zostają zapisane w tablicy (w vb   
   powiedziałbym: tablicy poziomu modułu. Tu pewnie  
   też byłoby to prawidłowe ;P) a po postawieniu  
   danego znaku program musi sprawdzić czy nie   
   doszło do zwycięstwa. Jeśli nie to swój znak stawia  
   drugi gracz, jeżeli jednak są już 4'ry pola w linii  
   poinformować o zwycięstwie danego gracza i zakończyć program :D  
 
 
Po co to robię?  
   Zgodnie z teorią że żeby się czegoś nauczyć trzeba to ćwiczyć!! Czy zadanie jest karkołomne? Zapewniam że po 160 stronach lekturki "Od zera do  
gier kodera" zadanie nie jest już takie straszne :-) Prawdą jednak jest że gdyby nie wiedza nt programowania w VBA/VB tak łatwo by nie poszło.   
Traktujcie to jako zachętę. Umiecie jeden język programowania - zapewniam Was że nauka kolejnych to nie nauka "od podstaw". :)  
Czemu akurat po 160 stronach w.w. książki zadanie okaże się wykonalne? Jest kilka powodów: poznałem tablice (choć nie zupełnie dynamiczne :|), i   
możliwość tworzenia ich jako tablice poziomu modułu co jest niezmiernie istotne bo przecież chciałbym przechowywać w niej kolejne wskazane pola, o  
takich tworach jak pętle czy warunki, operatory logiczne ( i inne ) nie wspominam. Reszta to interpretacja zawartości tej tablicy czy to w kwestii  
pokazywania jej na tablicy, czy do weryfikowania stanu gry (kontynuacja rozgrywki, zwycięstwo -> koniec). Jeżeli to Was nie przekonuje to powiem Wam  
jeszcze że na 160 stornie wiadomej książeczki kończy się właśnie przykład takiej gry dla tablicy 3x3 - klasyczne "kółko i krzyżyk". :-DD Rodzi się pytanie  
czy zerżnąłem wszystko… i mam! Zapewniam Was że sprawa nie jest tak prosta. Przykład pomoże Wam w utworzeniu tablicy i jej zawartości ale   
algorytm ukazywania zawartości tablicy w Konsoli jak i algorytm weryfikacji stanu gry (ewentualnego zwycięstwa) i kilka innych rzeczy jest zupełnie moje.  
Zastanówcie się… Jak w tablicy 3x3 sprawdzić "Zwycięstwo" - np.: sprawdzać kolumny, następnie wiersze, następnie obie przekątne. Jeżeli znak  
znaleziony w pierwszym polu jest taki sam jak w całym analizowanym fragmencie tablicy - Zwycięstwo. Całkiem to proste! Jeżeli jednak tablica jest 8x8 a  
do zwycięstwa wystarczy 4'ry takie same elementy (inne niż puste :P) to już nie będzie tak łatwo…  
 
    No więc do pisania :)  
Funkcja main()
Kiedy uruchamiamy nasz program, zaczyna on wykonywać kod zawarty w funkcji main(). Od niej więc rozpoczyna się działanie aplikacji – a nawet więcej: na niej też to działanie się kończy. Zatem program (konsolowy) to przede wszystkim kod zawarty w funkcji main() – determinuje on bezpośrednio jego zachowanie. (..) Występujące na początku słowo kluczowe
void mówi kompilatorowi, że nasz program nie
będzie informował systemu operacyjnego o wyniku swojego działania.
  Megatutorial
 "Od zera do gier kodera"
str. 33
Innymi słowy to nie funkcja a procedura… coś na kształt naszego Auto_Open() W c++ procedura ta jest jednak na końcu a cała reszta "nad nią".   
Poniżej cały kod. Następnie postaram się omówić poszczególne części.  
 
 
#include <iostream>  
#include <conio.h>  
#include <windows.h>  
#include <stdlib.h>  
   
unsigned nX = 8;  
   
enum POLE {P_Puste, P_X = 'X', P_O = 'O'};  
POLE aPlansza [8][8];  
   
enum GRACZ {G_A = P_X, G_B = P_O};  
GRACZ aktywny;  
   
HANDLE hOut;  
   
void TworzPlansze()  
{  
     for (unsigned i = 0;i<nX;i++)  
     {  
          for (unsigned j=0;j<nX;j++)  
          {       
               aPlansza[i][j] = P_Puste;  
          }  
     }  
}  
   
void RysujPlansze()  
{  
     std::cout << "\t\t ---- GRA " << nX << 'x' << nX << " ---- " << std::endl;  
     std::cout << std::endl;  
   
     unsigned iPole = 0;  
   
     for (unsigned i=0; i<nX; i++)  
     {  
          std::cout << "\t";  
          for (unsigned j=0; j<nX; j++)  
          {  
               ++iPole;  
               if(aPlansza[i][j] == P_Puste)  
               {  
                    SetConsoleTextAttribute ( hOut, FOREGROUND_INTENSITY );  
                    std::cout << "|" << (iPole < 10 ? "  " : " ") << iPole ;  
               }  
               else  
               {  
                    std::cout << "|";  
   
                    SetConsoleTextAttribute( hOut, (aPlansza[i][j] == P_X ? BACKGROUND_BLUE : BACKGROUND_RED));  
                    std::cout << " " << static_cast<char>(aPlansza[i][j]) << " ";  
                    SetConsoleTextAttribute ( hOut, FOREGROUND_INTENSITY );  
               }  
          }  
          std::cout << " " << std::endl;  
     }  
   
     std::cout << std::endl;  
     std::cout << "\t -------------------------------" << std::endl;  
}  
   
unsigned IleWLinii(POLE P_wart, unsigned x, unsigned y)  
{  
     //test pionu  
     unsigned iIle = 0; bool bFlag = false;  
   
     for (unsigned i=0; i<nX; i++)  
     {  
          if(aPlansza[i][y] == P_wart)  
          {  
               iIle++;  
               if (iIle == 4) {break;};  
               if (!bFlag) {bFlag = (i == x);}  
          }  
          else  
          {  
               if (!bFlag) {iIle = 0;}  
               else {break;}  
          }  
     }  
     // test poziomu   
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          for (unsigned i=0; i<nX; i++)  
          {  
               if(aPlansza[x][i] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (i == x);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
          }  
     }  
     // przekątna od lewej  
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          unsigned sX; unsigned sY;  
          if (x == y) {sX = 0; sY = 0;}  
          else  
          {  
               if (x > y) {sX = x-y; sY = 0;}  
               else {sX = 0; sY = y-x;}  
          }  
          do  
          {  
               if (aPlansza[sX][sY] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (sX == x && sY == y);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
               sX++; sY++;  
          } while (sX != nX && sY != nX);  
     }  
   
     // przekątna od prawej  
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          unsigned sX; int sY;  
          if ((x + y) == (nX+1)) {sX = 0; sY = nX-1;}  
          else  
          {  
               if ((x + y) < (nX+1)) {sX = 0; sY = x+y;}  
               else {sX = x-(nX-y)+1; sY = nX-1;}  
          }  
               /*  
               std::cout << "sX: " << sX << std::endl;  
               system("PAUSE");  
               */  
          do  
          {  
               if(aPlansza[sX][sY] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (sX == x && sY == y);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
               sX++; sY--;  
          } while (sX != nX && sY != -1);  
     }  
     return iIle;  
}  
   
void main()  
{  
     hOut = GetStdHandle( STD_OUTPUT_HANDLE );  
   
     TworzPlansze();  
     aktywny = G_A;  
   
     unsigned nPole;  
     do   
     {  
          system ("cls"); RysujPlansze();  
   
          std::cout << "\t Zeby postawic: " << static_cast<char>(aktywny) << std::endl;  
          std::cout << "\t Wprowadz nr Pola: " << std::endl;  
          std::cout << "\t (0 zeby zakonczyc): ";  
          std::cin >> nPole;  
   
          if (nPole > 0 && nPole < nX*nX+1)  
          {  
               unsigned uX = (nPole - 1) / nX;  
               unsigned uY = (nPole - 1) % nX;  
               if (aPlansza[uX][uY] == P_Puste)  
               {  
                    if ( aktywny == G_A)   
                    {   
                         aPlansza [uX][uY] = P_X;  
                         aktywny = G_B;  
                    }  
                    else  
                    {  
                         aPlansza [uX][uY] = P_O;  
                         aktywny = G_A;  
                    }  
   
                    unsigned iIle = IleWLinii(aPlansza[uX][uY], uX, uY);  
                    bool bKoniec = false;  
   
                    if( !(iIle < 4))  
                    {  
                         system ("cls"); RysujPlansze();  
                         std::cout << "Zwyciestwo gracza: " << (aktywny == G_A ? 'O' :'X') << " GRATULACJE :-))" << std::endl;  
                         bKoniec = true;  
                    }  
                    /*  
                    else  
                    {  
                         std::cout << iIle << std::endl;  
                         system("PAUSE");  
   
                    }  
                    */  
                    if (bKoniec) {break;}  
               }  
          }  
     } while (nPole != 0);  
   
     _getch();  
}  
 
No i jak to działa :D  
 
#include <iostream>  
#include <conio.h>  
#include <windows.h>  
#include <stdlib.h>  
   
unsigned nX = 8;  
   
enum POLE {P_Puste, P_X = 'X', P_O = 'O'};  
POLE aPlansza [8][8];  
   
enum GRACZ {G_A = P_X, G_B = P_O};  
GRACZ aktywny;  
   
HANDLE hOut;  
 
dyrektywa #include ma za zadanie włączenie do kodu plików iostream i conio.h.
Pierwszy z nich pozwala nam pisać w oknie konsoli za pomocą
std::cout, drugi zaś wywołać funkcję getch(), która czeka na dowolny klawisz.
  Megatutorial
 "Od zera do gier kodera"
str. 35
Dodane jeszcze windows.h i stdlib.h odpowiadają za uzyskanie wskaźnika do strumienia wejścia i określenie koloru tła dla X'ów i O'łek :)  
 
unsigned nX = 8;  
to wielkość krawendzi tablicy  
 
enum POLE {P_Puste, P_X = 'X', P_O = 'O'};  
to dopuszczalne wartości dla tablicy w której będę przechowywał wybrane wartości. Równie dobrze mogłyby to być 0,1,2 jednak złapanie dopuszczalnych  
wartości w enum'eratorze. Tu jednak enum to pewien typ danych dlatego…  
POLE aPlansza [8][8];  
tak zadeklarujemy tablicę 8x8 której elementy mogą przyjmować wartości jedynie z typu POLE - nasze enum :)  
 
enum GRACZ {G_A = P_X, G_B = P_O};  
GRACZ aktywny;  
ten typ przyda mi się do przemiennego określania kolejności gry obu graczy. Będę przełączał zmienną aktywny z P_X na P_O.  
 
HANDLE hOut;  
tu miejsce na uchwyt strumienia wejścia. Raz określę i będę wykorzystywał kolorując te elementy aPlansza które są różne od P_Puste.  
 
void main()  
{  
     hOut = GetStdHandle( STD_OUTPUT_HANDLE );  
   
     TworzPlansze();  
     aktywny = G_A;  
 
Mamy tablicę aPlansza trzeba ją wypełnić wartościami P_Puste. To właśnie zadanie procedury TworzPlansze();  
 
void TworzPlansze()  
{  
     for (unsigned i = 0; i<nX; i++)  
     {  
          for (unsigned j=0;j<nX;j++)  
          {       
               aPlansza[i][j] = P_Puste;  
          }  
     }  
}  
 
elementy tablic liczymy od zera więc pętle lecą od 0 to 7 (<nX gdzie nX = 8)  
 
     aktywny = G_A;  
… i już wiemy kto zaczyna. I dalej w main()..  
 
     unsigned nPole;  
     do   
     {  
          system ("cls"); RysujPlansze();  
 
mamy więc naszą aPlansza z wartościami P_Puste. Narysujmy ją zatem w konsoli…  
system("cls"); - czyści zawartość konsoli. Za bieżący wygląd planszy będzie odpowiadać procedura RysujPlansze();  będzie ona wywoływana w pętli  
po każdym ruchu graczy jednak już teraz chcielibyśmy zobaczyć "pustą" planszę.   
 
void RysujPlansze()  
{  
     std::cout << "\t\t ---- GRA " << nX << 'x' << nX << " ---- " << std::endl;  
     std::cout << std::endl;  
   
     unsigned iPole = 0;  
   
     for (unsigned i=0; i<nX; i++)  
     {  
          std::cout << "\t";  
          for (unsigned j=0; j<nX; j++)  
          {  
               ++iPole;  
               if(aPlansza[i][j] == P_Puste)  
               {  
                    SetConsoleTextAttribute ( hOut, FOREGROUND_INTENSITY );  
                    std::cout << "|" << (iPole < 10 ? "  " : " ") << iPole ;  
               }  
               else  
               {  
                    std::cout << "|";  
   
                    SetConsoleTextAttribute( hOut, (aPlansza[i][j] == P_X ? BACKGROUND_BLUE : BACKGROUND_RED));  
                    std::cout << " " << static_cast<char>(aPlansza[i][j]) << " ";  
                    SetConsoleTextAttribute ( hOut, FOREGROUND_INTENSITY );  
               }  
          }  
          std::cout << " " << std::endl;  
     }  
   
     std::cout << std::endl;
 
     std::cout << "\t -------------------------------" << std::endl;  
}  
 
Po wykonaniu się tej procedury widzimy w oknie konsoli coś takiego…  
 
     std::cout << "\t\t ---- GRA " << nX << 'x' << nX << " ---- " << std::endl;  
     std::cout << std::endl;  
 
nagłówek i pusta linia.. "\t" - to znak tabulacji..  
 
     unsigned iPole = 0;  
   
     for (unsigned i=0; i<nX; i++)  
     {  
          std::cout << "\t";  
 
iPole będzie przechowywała licznik którym będę numerował pola planszy. Numery te, od 1 do 64, będę nadawał elementom == P_Puste. Teraz, tj jeszcze   
przed jakimkolwiek ruchem graczy wszystkie elementy aPlansza spełniają ten warunek to też wszystkie elementy zostaną przedstawione w Konsoli za  
pomocą numeru elementu.  
i (a tak nawiasem… jak Wam się podoba deklarowanie zmiennej będącej licznikiem pętli w strukturze pętli? Bo mi bardzo! :D) ilość wierszy aPlansza.  
zaczynam znakiem tabulacji…  
 
          for (unsigned j=0; j<nX; j++)  
          {  
               ++iPole;  
               if(aPlansza[i][j] == P_Puste)  
 
j - kolumn. Tu zwiększam wartość iPole o 1 i sprawdzamy czy element [i][j] naszej tablicy jest pusty.  
 
               {  
                    std::cout << "|" << (iPole < 10 ? "  " : " ") << iPole ;  
               }  
 
jeżeli tak… do konsoli trafia bieżący numer iPole. To: (iPole < 10 ? "  " : " ") to przez wzgląd że chciałbym żeby było równo - jednostki pod jednostki,  
dziesiątki pod dziesiątkami - dodaję dodatkową spację dla iPole < 10. A sama składnia: to tak naprawdę nasze IIf. W c++ to (war ? true : false) od razu  
mi się spodobało :)  
 
               else  
               {  
                    std::cout << "|";  
   
                    SetConsoleTextAttribute( hOut, (aPlansza[i][j] == P_X ? BACKGROUND_BLUE : BACKGROUND_RED));  
                    std::cout << " " << static_cast<char>(aPlansza[i][j]) << " ";  
                    SetConsoleTextAttribute ( hOut, FOREGROUND_INTENSITY );  
               }  
 
a co jak badany element nie jest ==P_Puste? Ano.. Określamy jaki kolor tła będzie miał ten element na konsoli: P_X - niebieski, P_O - czerwony  
i tu ciekawostka: jak mając wartość typu POLE (element aPlansza) wpisać w konsoli X lub O. Można poprzez IIF… ale można też inaczej :D  
Zauważcie jak wygląda nasz enum POLE:  
 
enum POLE {P_Puste, P_X = 'X', P_O = 'O'};  
Wartościom P_X i P_O zostały przypisane wartości 'X' i 'O' (pojedynczy znak w apostrofy, dłuższe ciągi w cudzysłowy ;) )  
Mając tak określone wartości mogę rzutować wartość elementu aPlansza na typ char. No po prostu zaj…ste :DD Nie?  
 
Ostatnia linijka przywraca ustawienia kolorów dla strumienia wejścia na początkowe. Przez to tylko dany element jest odpowiedniego koloru.  
Resztę w tej procedurze pominę… dalej main();  
 
          std::cout << "\t Zeby postawic: " << static_cast<char>(aktywny) << std::endl;  
          std::cout << "\t Wprowadz nr Pola: " << std::endl;  
          std::cout << "\t (0 zeby zakonczyc): ";  
          std::cin >> nPole;  
   
          if (nPole > 0 && nPole < nX*nX+1)  
          {  
 
Trzeba pobrać od gracza numer elementu w którym chciałby wstawić swój znak (X lub O). Za tę czynność odpowiada instrukcja std::cin >> zmienna  
nasze nPole (nie mylić z iPole) to…  
     unsigned nPole;  
zmienna ta została zadeklarowana jako liczba całkowita dodatnia. Sprawdzimy jednak czy podana liczba jest z interesującego nas zakresu tj 1 - 64 a więc..  
czy (nPole > 0 AND nPole < 64+1)… jasne - nie? :)  
 
               unsigned uX = (nPole - 1) / nX;  
               unsigned uY = (nPole - 1) % nX;  
               if (aPlansza[uX][uY] == P_Puste)  
               {  
 
z nPole - numeru na planszy trzeba okrasić współrzędne danego elementu aPlansza. Zmienne uX i uY to zmiennej typu całkowitego (dodatniego) przez co  
dzielenie przez nX zwróci wynik całkowity (rozszerzenie dziesiętne zostanie utracone) który będzie jedną ze współrzędnych. Drugą zwróci reszta z dzielenia  
(mod - w c++ %).  
Jeżeli element [uX][uY] jest P_Puste  
 
                    if ( aktywny == G_A)   
                    {   
                         aPlansza [uX][uY] = P_X;  
                         aktywny = G_B;  
                    }  
                    else  
                    {  
                         aPlansza [uX][uY] = P_O;  
                         aktywny = G_A;  
                    }  
 
w zależności od tego którego gracza jest teraz kolej, ustawiam wartość elementu aPlansza na odpowiednio P_X lub P_O i zmieniam zmienną aktywny  
 (typ GRACZ) na "drugiego" gracza.  
 
                    unsigned iIle = IleWLinii(aPlansza[uX][uY], uX, uY);  
                    bool bKoniec = false;  
   
                    if( !(iIle < 4))  
                    {  
                         system ("cls"); RysujPlansze();  
                         std::cout << "Zwyciestwo gracza: " << (aktywny == G_A ? 'O' :'X') << " GRATULACJE :-))" << std::endl;  
                         bKoniec = true;  
                    }  
                    /*  
                    else  
                    {  
                         std::cout << iIle << std::endl;  
                         system("PAUSE");  
   
                    }  
                    */  
                    if (bKoniec) {break;}  
 
iIle to zmienna do której zapiszę ilość elementów danego typu w linii. Linia nie oznacza tu wiersza czy kolumny aPlansza a te 4'ry elementy których  
potrzebujemy do zwycięstwa. Wartość tej zmiennej określa funkcja IleWLinii ale o niej później :)  
 
Jeżeli iIle nie jest mniejsza od 4 (ale zakręciłem :) ) - zwycięstwo! Procedura: jeszcze raz narysuje naszą tablicę na konsoli - chcemy widzieć zwycięstwo  
"naocznie" ;-) i info o Zwycięstwie z gratulacjami jednak czemu tu używam IIF'a a nie rzutowania na typ char? Wystarczyłoby wpisać w typie GRACZ   
wartości dla elementów tego typu. Jednak należałoby opisać je inaczej niż są w rzeczywistości. Tj: Pomimo że G_A wstawia X'y trzeba by przypisać mu  
O' - czemu? Bo przed chwilą, przed ustaleniem wartości iIle, zmieniłem aktywnego gracza więc aktywny nie jest już tym samym graczem który przed  
chwilą wstawił swój znak. Teraz myślę że można było to napisać inaczej ale niech już tak pozostanie :P  
bKoniec na true i break; tj wyjście z pętli która zapewniała kontynuację rozgrywki.  
 
pomiędzy /* .. */ pozostałość po testach sprawdzających działanie funkcji IleWLinii  
 
               }  
          }  
     } while (nPole != 0);  
   
     _getch();  
 
warunek nPole różne od 0 na wypadek gdyby nam się gra znudziła i chcielibyśmy ją zakończyć wcześniej (oczywiście można by też przyciskiem [X]  
wyłączyć konsolę - ale to pomińmy :))  
_getch(); zmusi program do czekania na dowolny klawisz przed jego zakończeniem. To żebyśmy mogli napatrzyć się na zwycięstwo :D  
 
Pozostaje nam zatem jeszcze rozgryzienie funkcji IleWLinii
 
 
Wymyśliłem sobie to tak…  
 - powiedzmy że niebieskie elementy to elementy danego typu w jednej  
   linii. Element zaznaczony (x,y) (4,4) to ten wstawiony przed chwilą.  
   Analizujemy kolumnę od góry, tj od elementu (0,4) w dół.   
 - pierwszy (0,4) to nie nasz typ - sprawa czysta. Drugi OK. zmienna   
   zliczająca ilość elementów "w linii" (iIle) +1 jednak nie wiemy czy ten element  
   wchodzi w linię. Jak widzimy tak nie jest ale na razie o tym nie wiemy.  
 - trzeci (2,4) nie nasz! iIle = 0; dalej…  
 - element 4 (3,4) analogicznie jak drugi. iIle++, jednak nie wiemy czy należy  
   do linii.  
 - element 5 (4,4), iIle++, bFlaga = true - to o tę część kolumny nam chodzi  
   bo trafiliśmy na element przed chwilą wskazany.  
 - element 6 (5,4) - nie nasz typ! - koniec zabawy, wyjście z pętli. Czemu już? Bo sprawdzanie kolejnych nie ma już sensu. Gdyby pod tym elementem  
   istniała jeszcze linia spełniająca warunek !(iIle < 4) to byłaby wykryta już w momencie kiedy wstawiliśmy element tworzący ostatecznie linię. :D  
 
Jeżeli ten fragment nie zwróci wartości => 4 to sprawdzamy analogicznie wiersz w którym właśnie wstawiono element. Jak to nie pomoże to obie przekątne.  
Po tym słowie teorii zrozumienie części: //test pionu i //test poziomu powinny być proste..  
 
unsigned IleWLinii(POLE P_wart, unsigned x, unsigned y)  
{  
     //test pionu  
     unsigned iIle = 0; bool bFlag = false;  
   
     for (unsigned i=0; i<nX; i++)  
     {  
          if(aPlansza[i][y] == P_wart)  
          {  
               iIle++;  
               if (iIle == 4) {break;};  
               if (!bFlag) {bFlag = (i == x);}  
          }  
          else  
          {  
               if (!bFlag) {iIle = 0;}  
               else {break;}  
          }  
     }  
 
fragmentami:  
     for (unsigned i=0; i<nX; i++)  
     {  
          if(aPlansza[i][y] == P_wart)  
 
elementy aPlansza[i][y] dla i od 0 do 7 to kolumna y tablicy aPlansza.  
jeżeli dany element jest równy P_wart (P_wart to oczywiście wartość P_X lub P_O typu POLE która pochodzi z pierwszego arg. Funkcji IleWLinii).  
 
               iIle++;  
               if (iIle == 4) {break;};  
               if (!bFlag) {bFlag = (i == x);}  
 
iIle++, gdyby iIle już była równa 4 to koniec zabawy, jeżeli nie bFlag (tj jeżeli bFlag == false) to sprawdzamy czy badany element jest przed momentem  
wstawionym elementem. Tj czy i - kolejny numer elementu danej kolumny, jest równy x - numerowi wiersza aPlansza w którym dokonano zmiany.  
 
          else  
          {  
               if (!bFlag) {iIle = 0;}  
               else {break;}  
          }  
 
jeżeli element aPlansza jest innego typu niż P_wart to: jeżeli nasza bFlag nie została jeszcze ustawiona na true to jedynie iIle = 0 i lecimy dalej. Jeżeli  
jednak bFlag == true a, przypominam że badany element nie jest typu P_wart, wychodzimy z pętli, dalsze przeglądanie kolumny traci sens.  
Sprawdzamy wiersz… zupełnie analogicznie :D  
 
     // test poziomu   
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          for (unsigned i=0; i<nX; i++)  
          {  
               if(aPlansza[x][i] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (i == x);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
          }  
     }  
   
 
Znacznie ciekawiej jest z przekątnymi :)
 
    Zacznijmy na przekątnej "od lewej"…  
Od elementu o jakich współrzędnych zacząć? Ano to zależy :) Obrazek  
nam podpowie (sam też sobie coś takiego rysowałem tyle że na kartce  
w kratkę :D). Z niego wynika kilka zasad..  
 - jeżeli x == y to elementem startowym jest (0,0)  
 - jeżeli x > y to współrzędna y elementu startowego (sY) = 0. Sytuacja  
   taka zaistniała dla elementu o współrzędnych (7,3) strzałka oznacza  
   element plansza od którego powinniśmy zacząć sprawdzać "linię" i jest  
   to element (4,0). A więc y = 0. A sX (współrzędna x elementu startowego)  
   jest równy x-y. :D  
 - jeżeli x < y. Np.: element (2,4): sX = 0; sY = y-x. I koniec!  
 
Mając współrzędne elementu startowego lecimy pętlą do…while zwiększając sX i SY o 1 tak długo dopóki spełniony jest warunek..  
(sX != nX && sY != nX); obie zmienne nie są różne nX.  
 
     // przekątna od lewej  
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          unsigned sX; unsigned sY;  
          if (x == y) {sX = 0; sY = 0;}  
          else  
          {  
               if (x > y) {sX = x-y; sY = 0;}  
               else {sX = 0; sY = y-x;}  
          }  
          do  
          {  
               if (aPlansza[sX][sY] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (sX == x && sY == y);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
               sX++; sY++;  
          } while (sX != nX && sY != nX);  
     }  
 
Powiecie że w drugą stronę to na pewno analogicznie jak "od lewej", a tu jednak nie :P
 
Tu osią symetrii i podziału zasad jest oś zupełnie odwrotna to też zasady którymi  
będziemy określać współrzędne elementu startowego są zupełnie inne.  
i tak:  
 - jeżeli (x+y)=(nX+1); np.: element (2,5) (na osi symetrii) sX = 0; sY = nX-1  
 - jeżeli (x+y)<(nX+1); np.: element (2,2) (powyżej osi) sX = 0, sY = x+y  
 - jeżeli (x+y)>(nX+1); np.: element (7,3) (poniżej osi) sX = x-(nX-y)+1, sY = nX-1  
Tu jednak zmienną sY trzeba zmniejszać o 1 co krok pętli. sY-- co oznacza że warunek  
w while musi się zmienić: (sX != nX && sY != -1) ale czy taka zmienna może być  
typu unsigned? Oczywiście NIE. Dlatego jest int. :)  
   
     // przekątna od prawej  
     if (iIle < 4)  
     {  
          iIle = 0; bFlag = false;  
   
          unsigned sX; int sY;  
          if ((x + y) == (nX+1)) {sX = 0; sY = nX-1;}  
          else  
          {  
               if ((x + y) < (nX+1)) {sX = 0; sY = x+y;}  
               else {sX = x-(nX-y)+1; sY = nX-1;}  
          }  
               /*  
               std::cout << "sX: " << sX << std::endl;  
               system("PAUSE");  
               */  
          do  
          {  
               if(aPlansza[sX][sY] == P_wart)  
               {  
                    iIle++;  
                    if (iIle == 4) {break;};  
                    if (!bFlag) {bFlag = (sX == x && sY == y);}  
               }  
               else  
               {  
                    if (!bFlag) {iIle = 0;}  
                    else {break;}  
               }  
               sX++; sY--;  
          } while (sX != nX && sY != -1);  
     }  
     return iIle;  
}  
 
  Dwa słowa na koniec..  
Dawno jakiś temat którym się zajmowałem nie sprawił mi tak dużej satysfakcji jak to maleństwo. :D Na pewno będę kontynuował naukę c++ a jestem  
pewien że żadna wiedza na marne nie pójdzie. Może ktoś byłby ciekaw na czym to piszę? Dopadłem stare dobre VC++ 6 i jak na razie zupełnie mi  
wystarcza choć jak zabawa mi się spodoba (czego na teraz jestem prawie pewien) to może zaopatrzę się w coś nowszego. Jednak wczesniej trzeba się  
będzie zastanowić jak wytłumaczyć żonie tak duży koszt związany z hobby :) Jednak zapewniam Was że to czym na razie dysponuję sprawia się całkiem  
dobrze i zacząć od czegoś takiego też się da. Do czego serdecznie zachęcam :-)  
 
W załączniku:  
 - main.cpp (plik w którym znajduje się cały kod w wersji sprzed kompilacji. Można odpalić w Notatniku.)  
 - 8x8.exe (gra po kompilacji)