Jak to działa. Tabela 3d w 2d

0

Witam, ostatnio zacząłem uczyć się języka C++ z kursu „Od Zera Do Gier Kodera” i doszedłem do programu kółko krzyżyk.

main.cpp

#include <iostream>
#include <conio.h>
#include "game.h"

main()
{
      StartGry();
      
      for(;;)
      {
             RysujPlansze();
             
             if (g_StanGry == GS_MOVE)
             {
                unsigned uNumerPola;
                std::cin>>uNumerPola;
                Ruch(uNumerPola);
             }
             else if (g_StanGry == GS_WON || g_StanGry == GS_DRAW) break;
      }
      getch();
}

game.cpp

// Właściwy kod gry

#include <iostream>
#include <ctime>
#include "game.h"


// zmienne
//--------

// plansza w postaci tablicy 3x3
FIELD g_aPlansza[3][3] = { { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY },
						   { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY },
						   { FLD_EMPTY, FLD_EMPTY, FLD_EMPTY } };

// stan gry (nie rozpoczęta, ruch gracza, wygrana lub remis)
GAMESTATE g_StanGry = GS_NOTSTARTED;

// znak aktualnego gracza (kółko lub krzyżyk)
SIGN g_AktualnyGracz;


//----------------------------------------------------------------------------------------


// funkcje
//--------

// funkcja wywoływana na starcie gry
bool StartGry()
{
	// najpierw sprawdzamy, czy gra już nie trwa;
	// jeżeli tak, to funkcja kończy się porażką
	if (g_StanGry != GS_NOTSTARTED) return false;

	// losujemy gracza, który będzie zaczynał
	srand (static_cast<unsigned>(time(NULL)));
	g_AktualnyGracz = (rand() % 2 == 0 ? SGN_CIRCLE : SGN_CROSS);

	// ustawiamy stan gry na ruch graczy
	g_StanGry = GS_MOVE;

	// wszystko się udało, zatem zwracamy true
	return true;
}

// rysowanie (albo raczej wypisywanie) pola gry
bool RysujPlansze()
{
	// także sprawdzamy, czy aby na pewno gra rozpoczęłas ię
	if (g_StanGry == GS_NOTSTARTED)	return false;

	// czyścimy okienko konsoli przy pomocy polecenia systemowego CLS
	system ("cls");

	// pokazujemy szyld tytułowy
	std::cout << "   KOLKO I KRZYZYK   " << std::endl;
	std::cout << "---------------------" << std::endl;
	std::cout << std::endl;

	
	// następnie wypisujemy właściwą planszę
	// oczywiście, czynimy to przy pomocy dwóch pętli for
	std::cout << "        -----" << std::endl;
	for (int i = 0; i < 3; ++i)
	{
		// lewa część ramki
		std::cout << "        |";

		// wiersz
		for (int j = 0; j < 3; ++j)
		{
			// sprawdzamy, czy dane pole jest puste
			if (g_aPlansza[i][j] == FLD_EMPTY)
				// wtedy wyświetlamy jego numerek
				// tak, żeby użytkownik wiedział, jaką liczbę wpisać, gdy będzie
				// chciał postawić na nim kółko lub krzyżyk :)
				std::cout << i * 3 + j + 1;
			else
				// jeżeli natomiast pole jest zajęte,
				// wyświetlamy odpowiadający mu znak;
				// korzystamy tu ze sztuczki z odpowiednimi wartościami stałych typu
				// FIELD; dzięki niej unikamy dodatkowego if'a (tudzież operatora ?:)
				std::cout << static_cast<char>(g_aPlansza[i][j]);
		}

		// prawa część ramki
		std::cout << "|" << std::endl;
	}
	std::cout << "        -----" << std::endl;
	std::cout << std::endl;

	// wreszcie, pokazujemy dolny komunikat
	// jest on albo prośbą o podanie ruchu, albo informacją o stanie gry
	// wszystko zależy o tegoż stanu, a zatem stosujemy instrukcję switch
	switch (g_StanGry)
	{
		case GS_MOVE:
			// ruch gracza, a więc gra trwa

			// musimy więc poprosić o ruch
			std::cout << "Podaj numer pola, w ktorym" << std::endl;
			std::cout << "chcesz postawic ";
			std::cout << (g_AktualnyGracz == SGN_CIRCLE ? "kolko" : "krzyzyk") << ": ";

			break;
		case GS_WON:
			// gra zakończona, ktoś wygrał :)

			// wyświetlamy odpowiednią informację
			std::cout << "Wygral gracz stawiajacy ";
			std::cout << (g_AktualnyGracz == SGN_CIRCLE ? "kolka" : "krzyzyki") << "!";

			break;
		case GS_DRAW:
			// może też się zdarzyć remis

			// także pokazujemy wiadomość
			std::cout << "Remis!";

			break;
	}

	// wszystko poszło OK, zatem powiadamiamy o tym
	return true;
}

// wykonanie ruchu i sprawdzenie jego rezultatu
bool Ruch(unsigned uNumerPola)
{
	// znowuż musimy sprawdzić stan gry
	if (g_StanGry != GS_MOVE) return false;

	// następnie sprawdzamy, czy numer pola mieści się w przedziale <1; 9>
	if (!(uNumerPola >= 1 && uNumerPola <= 9)) return false;

	// jeśli doszliśmy dotąd, to wszystko jest w porządku i możemy wykonać ruch;
	// przeliczamy więc numer pola na indeksy tablicy i przypisujemy znak gracza;
	// (tylko wtedy, gdy pole jest puste)
	// po raz któryś-tam z kolei używamy przy tym sztuczki z wartościami enumów
	unsigned uY = (uNumerPola - 1) / 3;
	unsigned uX = (uNumerPola - 1) % 3;
	if (g_aPlansza[uY][uX] == FLD_EMPTY)
		g_aPlansza[uY][uX] = static_cast<FIELD>(g_AktualnyGracz);
	else
		// jeżeli próbowano dwukrotnie postawić znak w tym samym miejscu,
		// to niestety nie możemy na to pozwolić :)
		return false;

	// no i teraz następuje najbardziej zakręcona część programu :))
	// (taaak, poprzednia linijka wcale nią nie była :DD)
	// musimy mianowicie określić stan gry na podstawie planszy;
	// mogło się przecież zdarzyć, że któryś gracz ułożył linię poziomą, pionową
	// lub ukosną i wygrał, prawda? :) tutaj musimy to określić
	// można to zrobić na kilka sposobów: najprostszy do wymyślenia, ale najbardziej
	// pogmatwany jest zwykły ciąg instrukcji warunkowych, kontrolujących każde pole...
	// sprytniejszą metodą jest jednak użycie tablicy przeglądowej i pętli for
	const int LINIE[][3][2] = { { { 0, 0 }, { 0, 1 }, { 0, 2 } },	// górna pozioma
							{ { 1, 0 }, { 1, 1 }, { 1, 2 } },	// środkowa pozioma
							{ { 2, 0 }, { 2, 1 }, { 2, 2 } },	// dolna pozioma
							{ { 0, 0 }, { 1, 0 }, { 2, 0 } },	// lewa pionowa
							{ { 0, 1 }, { 1, 1 }, { 2, 1 } },	// środkowa pionowa
							{ { 0, 2 }, { 1, 2 }, { 2, 2 } },	// prawa pionowa
							{ { 0, 0 }, { 1, 1 }, { 2, 2 } },	// przekątna backslashowa
							{ { 2, 0 }, { 1, 1 }, { 0, 2 } } };	// przekątna slashowa
	FIELD Pole, ZgodnePole;
	unsigned uLiczbaZgodnychPol;
	for (int i = 0; i < 8; ++i)
	{
		// i przebiega po kolejnych możliwych liniach (jest ich osiem)
		
		// zerujemy zmienne pomocnicze
		Pole = ZgodnePole = FLD_EMPTY;	// obie zmienne zostaną ustawione na FLD_EMPTY
		uLiczbaZgodnychPol = 0;

		for (int j = 0; j < 3; ++j)
		{
			// j przebiega po trzech polach w każdej linii

			// pobieramy rzeczone pole
			// to zdecydowanie najbardziej pogmatwane wyrażenie :)
			Pole = g_aPlansza[LINIE[i][j][0]][LINIE[i][j][1]];

			// jeżeli sprawdzane pole różni się od tego, które ma się zgadzać...
			if (Pole != ZgodnePole)
			{
				// to zmieniamy zgadzane pole na to aktualne
				ZgodnePole = Pole;
				uLiczbaZgodnychPol = 1;
			}
			else
				// jeśli natomiast oba pola się zgadzają, no to inkrementujemy
				// licznik takich zgodnych pól
				++uLiczbaZgodnychPol;
		}

		// teraz sprawdzamy, czy udało nam się zgodzić linię
		if (uLiczbaZgodnychPol == 3 && ZgodnePole != FLD_EMPTY)
		{
			// jeżeli tak, no to ustawiamy stan gry na wygraną
			g_StanGry = GS_WON;

			// przerywamy pętlę i funkcję
			return true;
		}
	}

	// istnieje jeszcze możliwość, że nastąpił remis
	// wtedy funkcja dojdzie tutaj i cała plansza będzie pokryta krzyżykami i kółkami
	// sprawdzamy to przy pomocy odpowiedniej pętli, zliczającej zapełnione pola
	// jeżeli jest ich tyle, ile wszystkich, no to kończymy remisem
	unsigned uLiczbaZapelnionychPol = 0;
	for (int i = 0; i < 3; ++i)
		for (int j = 0; j < 3; ++j)
			if (g_aPlansza[i][j] != FLD_EMPTY)
				++uLiczbaZapelnionychPol;
	if (uLiczbaZapelnionychPol == 3*3)
	{
		// ustawiamy stan gry na remis
		g_StanGry = GS_DRAW;

		// kończymy funkcję
		return true;
	}

	// oczywiście, może się zdarzyć, iż gra po prostu toczy się dalej
	// w takim wypadku zmieniamy jedynie aktualnego gracza
	g_AktualnyGracz = (g_AktualnyGracz == SGN_CIRCLE ? SGN_CROSS : SGN_CIRCLE);

	// zwracamy true
	return true;
}

game.h

enum SIGN {SGN_CIRCLE = 'O', SGN_CROSS = 'X'};
enum FIELD {FLD_EMPTY = 0,      //puste;
            FLD_CIRCLE = SGN_CIRCLE,
            GLD_CROSS = SGN_CROSS};
enum GAMESTATE {GS_NOTSTARTED,
                GS_MOVE,
                GS_WON,
                GS_DRAW};
                
                
                
extern GAMESTATE g_StanGry;

bool StartGry();

bool RysujPlansze ();

bool Ruch(unsigned);

I mam problem ze zrozumieniem jednej linijki, mianowicie:

Pole = g_aPlansza[LINIE[i][j][0]][LINIE[i][j][1]];

Nie rozumiem jak to do g_aPlansza jest przypisywana jakaś liczba przez [LINIE[i][j][0]][LINIE[i][j][1]]

Dlatego proszę o jak najbardziej dokładne wytłumaczenie. Pozdrawiam

0

Pole = g_aPlansza[LINIE[i][j][0]][LINIE[i][j][1]];

====

x = LINIE[i][j][0];
y = LINIE[i][j][1];
Pole = g_aPlansza[x][y];

o to chodziło?

0

nom, jak to działa dokładniej.

0

const int LINIE[][3][2]

to jest tablica współrzędnych punktów trzypunktowych linii. pierwszy nawias nie ma rozmiaru w środku bo to jest tablica dynamiczna (może się rozszerzać), [3] bo mamy trzy punkty w linii, [2] bo są dwie współrzędne (x i y)

teraz chcemy wydobyć współrzędne z linii i z punktu j. a więc:

x = LINIE[i][j][0];
y = LINIE[i][j][1];

(albo na odwrót - zależy jak interpretujesz)

i wyciągamy zawartość tego pola z planszy:

Pole = g_aPlansza[x][y];

0

Tak tu się zgadzam, lecz przyjmijmy że postawiłem krzyżyk w
1|2|x
4|5|6
7|8|9

czyli jego współrzędne wynoszą
y=0;
x=2;

i teraz chce sprawdzić tym oto kodem

Pole = g_aPlansza[LINIE[i][j][0]][LINIE[i][j][1]];

gdy dałem odpluskwiacza w Dev-C++

to zobaczyłem że ten punkt wchodzi potem w komendę if gdy
i=0;
j=2;

nie wiem jak to robi ze wychodzi mu to 3;
Komenda obrazkowa wyglądała by tak

                           1|2|3                 1|2|3

Pole = g_aPlansza[[i] 4|5|6 ] [LINIE[i]3|4|5];
x|8|9 6|x|8

w pierwszym dlatego jest na 7 bo x=0
a w drugim x=1;

i jak to on robi że wychodzi mu ze Pole = 3??

0

x w tym znaczy wspołrzędną

w pierwszym dlatego jest na 7 bo x=0
a w drugim x=1;
0

Ok udało mi się opanować :-) . Dzięki za pomoc

0
donkey7 napisał(a)

bo to jest tablica dynamiczna (może się rozszerzać)

nie.. nie jest dynamiczna. jest hardcode'owana i powinna byc w dodatku totalnie-const. zapis [] jest znaczeniowo rownowazny z *, tylko podpowiada wskazanie na tablice nie na jeden element

0

nie.. nie jest dynamiczna.

ok. trochę nie w temacie jestem. sory za zamieszanie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1