Gra "2048"

Wątek przeniesiony 2023-09-02 17:10 z C/C++ przez Althorion.

1

Napisałem grę "2048" i chciałbym,żeby ktoś przetestował, czy mój kod działa.
Dziękuje.

#include<iostream>
#include<vector>
#include<string>
#include<thread>
#include<conio.h>
#include<random>

std::string map[4][4]{
	{" "," "," "," "},
	{" "," "," "," "},
	{" "," "," "," "},
	{" "," "," "," "}
};
int DetectionMap[4][4]{
	{-1,-1,-1,-1},
	{-1,-1,-1,-1},
	{-1,-1,-1,-1},
	{-1,-1,-1,-1}
};

struct CollisionHolder {
	std::vector<int> left = {0,0};
	std::vector<int> right = { 0,0 };
	std::vector<int> up = { 0,0 };
	std::vector<int> down = { 0,0 };
};
struct NumberHolder {
	std::vector<int> X = { 0,1 };
	std::vector<int> Y = { 0,0 };
	std::vector<int> Number_Val = { 2,2 };
	std::vector<CollisionHolder> interactions = { CollisionHolder(),CollisionHolder() };
	std::vector<bool> interacted = { false,false };
};
struct get_input {
	char key;
	int left = 0;
	int right = 0;
	int up = 0;
	int down = 0;
};
bool IsPlaying = true;
bool can_move = false;
bool push_number = false;
bool won = false;
int delay = 0;
NumberHolder holder;
get_input get_key;
void Rendering() {
	bool empty = false;
	for (int i = 0; i < holder.X.size(); i++) {
		map[holder.Y[i]][holder.X[i]] = std::to_string(holder.Number_Val[i]);
		DetectionMap[holder.Y[i]][holder.X[i]] = holder.Number_Val[i];
	}
	
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			for (int k = 0; k < holder.X.size(); k++) {
				if (i != holder.Y[k] or j != holder.X[k]) {
					empty = true;
				}
				else if(i==holder.Y[k] && j==holder.X[k]) {
					empty = false;
					break;
				}
			}
			if (empty == true) {
				map[i][j] = " ";
				DetectionMap[i][j] = -1;
				empty = false;
			}
		}
	}
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			
			std::cout << map[i][j];
			std::string get = map[i][j];
			int size = get.size();
			int scale = 5 - size;
			for (int i = 0; i < scale; i++) { std::cout << " "; }
			std::cout << "|";
		}
		std::cout << "\n";
		for (int k = 0; k < 4*6; k++) { std::cout << "-"; }
		std::cout << "\n";
	}
}
void Collision() {
	for (int i = 0; i < holder.X.size(); i++) {
		if (holder.X[i] + 1 > 3) {
			holder.interactions[i].right[0] = -2;
			holder.interactions[i].right[1] = 0;
		}
		else {
			holder.interactions[i].right[0] = DetectionMap[holder.Y[i]][holder.X[i] + 1];
			for (int j = 0; j < holder.X.size(); j++) {
				if (holder.X[i] + 1 == holder.X[j] && holder.Y[i] == holder.Y[j]) {
					holder.interactions[i].right[1] = j;
				}
			}
		}
		if (holder.X[i] - 1 < 0) {
			holder.interactions[i].left[0] = -2;
			holder.interactions[i].left[1] = 0;
		}
		else {
			holder.interactions[i].left[0] = DetectionMap[holder.Y[i]][holder.X[i] - 1];
			for (int j = 0; j < holder.X.size(); j++) {
				if (holder.X[i] - 1 == holder.X[j] && holder.Y[i] == holder.Y[j]) {
					holder.interactions[i].left[1] = j;
				}
			}
		}
		if (holder.Y[i] - 1 < 0) {
			holder.interactions[i].up[0] = -2;
			holder.interactions[i].up[1] = 0;
		}
		else {
			holder.interactions[i].up[0] = DetectionMap[holder.Y[i] - 1][holder.X[i]];
			for (int j = 0; j < holder.X.size(); j++) {
				if (holder.X[i]  == holder.X[j] && holder.Y[i] - 1 == holder.Y[j]) {
					holder.interactions[i].up[1] = j;
				}
			}
		}
		if (holder.Y[i] + 1 > 3) {
			holder.interactions[i].down[0] = -2;
			holder.interactions[i].down[1] = 0;
		}
		else {
			holder.interactions[i].down[0] = DetectionMap[holder.Y[i] + 1][holder.X[i]];
			for (int j = 0; j < holder.X.size(); j++) {
				if (holder.X[i]  == holder.X[j] && holder.Y[i] + 1 == holder.Y[j]) {
					holder.interactions[i].down[1] = j;
				}
			}
		}

	}
}
void Input() {
	while (IsPlaying == true) {
		std::this_thread::sleep_for(std::chrono::milliseconds(delay));
		delay = 0;
		get_key.key = _getch();
		if (get_key.key == 'd') {
			get_key.right = 1;
			get_key.left = 0;
			get_key.up = 0;
			get_key.down = 0;
		}
		if (get_key.key == 'a') {
			get_key.right = 0;
			get_key.left = 1;
			get_key.up = 0;
			get_key.down = 0;
		}
		if (get_key.key == 's') {
			get_key.right = 0;
			get_key.left = 0;
			get_key.up = 0;
			get_key.down = 1;
		}
		if (get_key.key == 'w') {
			get_key.right = 0;
			get_key.left = 0;
			get_key.up = 1;
			get_key.down = 0;
		}
		get_key.key = '.';
	}
}


int main() {
	std::random_device device;
	std::mt19937 rng(device());
	std::uniform_int_distribution<std::mt19937::result_type> res(0, 3);
	holder.X[0] = res(rng);
	holder.Y[0] = res(rng);
	holder.X[1] = res(rng);
	holder.Y[1] = res(rng);
	while (holder.X[0] == holder.X[1] && holder.Y[0] == holder.Y[1]) {
		holder.X[1] = res(rng);
		holder.Y[1] = res(rng);
	}

	std::thread t1(Input);

	t1.detach();
	while (IsPlaying) {

		can_move = false;
		system("cls");
		Rendering();
		Collision();
		for (int i = 0; i < holder.Number_Val.size(); i++) {
			if (holder.Number_Val[i] == 2048) {
				won = true;
			}
		}
		for (int i = 0; i < holder.interactions.size(); i++) {
			if (holder.interactions[i].right[0] == -1 or holder.interactions[i].left[0] == -1 or holder.interactions[i].up[0] == -1 or holder.interactions[i].down[0] == -1 or holder.interactions[i].up[0] == -1) {
				can_move = true;
			}
			if (holder.interactions[i].right[0] == holder.Number_Val[i] or holder.interactions[i].left[0] == holder.Number_Val[i] or holder.interactions[i].up[0] == holder.Number_Val[i] or holder.interactions[i].down[0] == holder.Number_Val[i] or holder.interactions[i].up[0] == holder.Number_Val[i]) {
				can_move = true;
			}
		}
		if (can_move == false) {
			IsPlaying = false;
		}

		if (push_number == true && can_move==true) {
			push_number = false;
			std::random_device device;
			std::mt19937 rng(device());
			std::uniform_int_distribution<std::mt19937::result_type> res(0, 3);
			int X = res(rng);
			int Y = res(rng);
			while (DetectionMap[Y][X] != -1) {
				X = res(rng);
				Y = res(rng);
			}
			holder.X.push_back(X);
			holder.Y.push_back(Y);
			holder.interacted.push_back(false);
			holder.Number_Val.push_back(2);
			holder.interactions.push_back(CollisionHolder());
			Rendering();
			Collision();

		}

		if (get_key.right == 1) {
			bool moved = true;
			while (moved == true) {
				moved = false;
				for (int j = 3; j >= 0; j--) {
					for (int i = 0; i < holder.X.size(); i++) {
						if (holder.interactions[i].right[0] == -1 && holder.X[i] == j) {
							holder.X[i] += 1;
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
						else if (holder.interactions[i].right[0] == holder.Number_Val[i] && holder.X[i] == j && holder.interacted[holder.interactions[i].right[1]] == false && holder.interacted[i] == false) {
							holder.Number_Val[holder.interactions[i].right[1]] *= 2;
							holder.interacted[holder.interactions[i].right[1]] = true;
							holder.interactions.erase(holder.interactions.begin() + i);
							holder.X.erase(holder.X.begin() + i);
							holder.Y.erase(holder.Y.begin() + i);
							holder.Number_Val.erase(holder.Number_Val.begin() + i);
							holder.interacted.erase(holder.interacted.begin() + i);
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
					}
				}

			}

			delay = 1000;
			get_key.right = 0;
			for (int i = 0; i < holder.interacted.size(); i++) {
				holder.interacted[i] = false;
			}
		}
		else if (get_key.left == 1) {
			bool moved = true;
			while (moved == true) {
				moved = false;
				for (int j = 0; j < 4; j++) {
					for (int i = 0; i < holder.X.size(); i++) {
						if (holder.interactions[i].left[0] == -1 && holder.X[i] == j) {
							holder.X[i] -= 1;
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
						else if (holder.interactions[i].left[0] == holder.Number_Val[i] && holder.X[i] == j && holder.interacted[holder.interactions[i].left[1]] == false && holder.interacted[i] == false) {
							holder.Number_Val[holder.interactions[i].left[1]] *= 2;
							holder.interacted[holder.interactions[i].left[1]] = true;
							holder.interactions.erase(holder.interactions.begin() + i);
							holder.X.erase(holder.X.begin() + i);
							holder.Y.erase(holder.Y.begin() + i);
							holder.Number_Val.erase(holder.Number_Val.begin() + i);
							holder.interacted.erase(holder.interacted.begin() + i);
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
					}
				}


			}
			delay = 1000;

			get_key.left = 0;
			for (int i = 0; i < holder.interacted.size(); i++) {
				holder.interacted[i] = false;
			}
		}
		else if (get_key.up == 1) {
			bool moved = true;
			while (moved == true) {
				moved = false;
				for (int j = 0; j < 4; j++) {
					for (int i = 0; i < holder.X.size(); i++) {
						if (holder.interactions[i].up[0] == -1 && holder.Y[i] == j) {
							holder.Y[i] -= 1;
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
						else if (holder.interactions[i].up[0] == holder.Number_Val[i] && holder.Y[i] == j && holder.interacted[holder.interactions[i].up[1]] == false && holder.interacted[i] == false) {
							holder.Number_Val[holder.interactions[i].up[1]] *= 2;
							holder.interacted[holder.interactions[i].up[1]] = true;
							holder.interactions.erase(holder.interactions.begin() + i);
							holder.X.erase(holder.X.begin() + i);
							holder.Y.erase(holder.Y.begin() + i);
							holder.Number_Val.erase(holder.Number_Val.begin() + i);
							holder.interacted.erase(holder.interacted.begin() + i);
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
					}
				}


			}
			delay = 1000;

			get_key.up = 0;
			for (int i = 0; i < holder.interacted.size(); i++) {
				holder.interacted[i] = false;
			}


		}
		else if (get_key.down == 1) {
			bool moved = true;
			while (moved == true) {
				moved = false;
				for (int j = 3; j >= 0; j--) {
					for (int i = 0; i < holder.X.size(); i++) {
						if (holder.interactions[i].down[0] == -1 && holder.Y[i] == j) {
							holder.Y[i] += 1;
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
						else if (holder.interactions[i].down[0] == holder.Number_Val[i] && holder.Y[i] == j && holder.interacted[holder.interactions[i].down[1]] == false && holder.interacted[i] == false) {
							holder.Number_Val[holder.interactions[i].down[1]] *= 2;
							holder.interacted[holder.interactions[i].down[1]] = true;
							holder.interactions.erase(holder.interactions.begin() + i);
							holder.X.erase(holder.X.begin() + i);
							holder.Y.erase(holder.Y.begin() + i);
							holder.Number_Val.erase(holder.Number_Val.begin() + i);
							holder.interacted.erase(holder.interacted.begin() + i);
							system("cls");
							Rendering();
							Collision();
							moved = true;
							push_number = true;
						}
					}
				}


			}
			delay = 1000;
			get_key.down = 0;
			for (int i = 0; i < holder.interacted.size(); i++) {
				holder.interacted[i] = false;
			}
		}
	}
	if (won == true) {
		std::cout << "You win!";
	}
	else if (can_move == false) {
		std::cout << "Game Over!";
	}
}
3

Zaczyna się fajnie, strukturami itd...
A potem dwie j..tne funkcje.

Kwiatki, które "lubimy" jak while (moved == true)
Magiczne stałe, o ile zero nie robi problemu, to -2 już tak (nie, nie znam się na grach najmniejszym stopniu)
Pętle w C++ do 4-ch, jestem prawie pewien kontenery C++ da się lepiej oprogramować, być może używając lepiej dobranego
Jak mówię, nie wnikałem, na grach sie nie znam. Mam jednak przeczucie że niektóre zmiany stanu struktur czytelnie by się robiło metodami

ps. nmie broń się "wydajnością" jak używasz system("cls");

0

@johnny_Be_good: Spoko,postaram się następnym razem X)

1
Ktoś_Tam napisał(a):

@johnny_Be_good: Spoko,postaram się następnym razem X)

Hmm ... jakby ci @Ktoś_Tam to powiedzieć ... do wypowiedzi @johnny_Be_good trzeba się odnosić "ze specyfiką"

0

@ZrobieDobrze: Ta.... wiem ;|

1
Ktoś_Tam napisał(a):

Napisałem grę "2048" i chciałbym,żeby ktoś przetestował, czy mój kod działa.
Dziękuje.

Polecam spróbować Test Driven Development (TDD). 2048 jest idealną okazja by spróbować.
Catch2 (biblioteka do pisania testów) jest bardzo przyjemny w użyciu. Tu masz piaskownice gdzie można spróbować: https://godbolt.org/z/8GEGTK6TM
Im szybciej zainteresujesz się TDD tym lepiej, na początku będzie wydawać się bezsensu.

0

Testy napisz. Dobry trening.

0

Fajnie że napisałeś tę grę! Sam kiedyś zrobiłem to samo. Możesz porównać sobie kod i podejście - mam wrażenie, że mam znacznie mniej kodu (co prawda to Groovy),
Obczaj https://github.com/AleksanderGrzybowski/2048/blob/master/src/main/groovy/game/core/State.groovy
No i koniecznie zrób testy :) https://github.com/AleksanderGrzybowski/2048/tree/master/src/test/groovy/game

0
kelog napisał(a):

Fajnie że napisałeś tę grę! Sam kiedyś zrobiłem to samo. Możesz porównać sobie kod i podejście - mam wrażenie, że mam znacznie mniej kodu (co prawda to Groovy),
Obczaj https://github.com/AleksanderGrzybowski/2048/blob/master/src/main/groovy/game/core/State.groovy
No i koniecznie zrób testy :) https://github.com/AleksanderGrzybowski/2048/tree/master/src/test/groovy/game

Fajnie! Ja nie jestem dobry w pisaniu małego,reużywalnego kodu :/

2

Tak naprawdę, polecam tutaj spróbować ogarnąć jak podzielić kod na więcej funkcji. Np. jak jest ruch w prawo/lewo, to na każdym wierszu indywidualnie próbujesz złączyć kafelki. Więc wydziel logikę łączenia w jednej tablicy czteroelementowych do osobnej funkcji (która dodatkowo zwraca informację, czy coś zmodyfikowała). Potem obsługa ruchu, to będzie pętla po 4 wierszach/kolumnach.

W tym momencie, obsługa ruchu, to jest wykonanie operacji przesuwania na każdej kolumnie/wierszu. Jak którykolwiek z wyników to informacja, że się udało poruszyć, fajnie, koniec obsługi, dodajesz nowy losowy kafelek. Jak nie, nic nie robisz. Zrób sobie funkcję co zwraca puste kafelki - wtedy będziesz mógł po prostu jeden z nich zapełnić.
Takie wartości jak 2048 mogłyby być ukryte w funkcji DidWin, czy coś.

Wcale nie musisz zapisywać sobie wartości "interacted". Załóżmy, że wiersz ma kafelki 32 32 32 32 i przesuwane jest w lewo. No to robisz transformacje -> 64 0 32 32 -> 64 0 64 0 -> 64 64 0 0. To znaczy, każdą liczbę przetwarzasz maks raz, a następnie usuwasz wszystkie zera ze środka.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.