• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[C++] ASCII Timberman!

Object Storage Arubacloud
+2 głosów
657 wizyt
pytanie zadane 27 lutego 2019 w Nasze projekty przez Eminiox Obywatel (1,500 p.)

Kolega mnie wyzwał i musiałem zrobić Timbermana w c++ w ASCII. Pod koniec aż się zaskoczyłem, bo wyszło 440 linijek kodu (plus 60 na animacje i postać drwala), ale w sumie starałem się to zrobić jak najprościej, a nie jak najkrócej. Dodałem nawet "animacje" spadania gałęzi oraz oldschoolowy spadający napis "game over".

Główna pętla gry troche brzydka, bo pisałem ją bylejak. Z bugów i niedociągnięć to zauważyłem, że spadające gałęzie nie nadążają i są doganiane przez gałęzie na drzewie gdy przytrzymuje jakiś przycisk oraz zapomniałem wyczyścić bufora, którego używa getch() z conio.h przez co na ekranie wyników jak się pospami trochę A i D to wszystkie te zaległe wciśnięcia zostaną natychmiostowo przetworzone i prawdopodobnie zginiemy. 

Zasady gry: unikaj gałęzi oraz zetnij jak najwiecej drzewa zanim skończy sie czas (każde ścięcie go troche dodaje) 

Screen: https://ibb.co/C5dKzcX

Kod:
Main.cpp

#include <Windows.h>
#include <iostream>
#include <conio.h>
#include <string>
#include <random>
#include <ctime>

#include "Graphics.h"

std::random_device rd;
std::mt19937 mt(rd());

int Random(int min, int max)
{
	std::uniform_int_distribution<int> distibution(min, max);
	return distibution(mt);
}

//====================================

const HANDLE CONSOLE_HANDLE = GetStdHandle(STD_OUTPUT_HANDLE);

void ResizeConsole(short width, short height, short fontWidth, short fontHeight)
{
	COORD size = { width, height };
	SMALL_RECT bounds = { 0, 0, width - 1, height - 1 };

	SMALL_RECT smallRect = { 0,0,1,1 };
	SetConsoleWindowInfo(CONSOLE_HANDLE, TRUE, &smallRect);

	SetConsoleScreenBufferSize(CONSOLE_HANDLE, size);

	SetConsoleActiveScreenBuffer(CONSOLE_HANDLE);

	CONSOLE_FONT_INFOEX cfi;
	cfi.cbSize = sizeof(cfi);
	cfi.nFont = 0;
	cfi.dwFontSize.X = fontWidth;
	cfi.dwFontSize.Y = fontHeight;
	cfi.FontFamily = FF_DONTCARE;
	cfi.FontWeight = FW_NORMAL;

	wcscpy_s(cfi.FaceName, L"Lucida Console");

	SetCurrentConsoleFontEx(CONSOLE_HANDLE, false, &cfi);

	SetConsoleWindowInfo(CONSOLE_HANDLE, TRUE, &bounds);

}

void SetConsoleCursorVisible(bool state)
{
	CONSOLE_CURSOR_INFO cursorInfo;
	GetConsoleCursorInfo(CONSOLE_HANDLE, &cursorInfo);
	cursorInfo.bVisible = state;
	SetConsoleCursorInfo(CONSOLE_HANDLE, &cursorInfo);
}

char GetInput()
{
	while (!_kbhit())
		return NULL;

	return _getch();
}

char GetChoice()
{
	while (!_kbhit())
		return _getch();

	return _getch();
}

//====================================


const unsigned SCREEN_SIZE = 42;
char buffer[SCREEN_SIZE * SCREEN_SIZE];
char screen[SCREEN_SIZE * SCREEN_SIZE];

void ClearBuffer()
{
	std::memset(&buffer, NULL, SCREEN_SIZE * SCREEN_SIZE * sizeof(char));
}

void ClearScreen()
{
	std::memset(&screen, NULL, SCREEN_SIZE * SCREEN_SIZE * sizeof(char));
}

void Draw(int x, int y, char ascii)
{
	buffer[y * SCREEN_SIZE + x] = ascii;
}

void Draw(int x, int y, const char* text, int size)
{
	for (int i = 0; i < size; i++, x++)
		buffer[y * SCREEN_SIZE + x] = text[i];
}

void DrawCharArray(const char array[], int x, int y)
{
	int posX = x, posY = y;
	for (int i = 0; i < strlen(array);)
	{
		while (array[i] != '\n')
		{
			Draw(posX++, posY, array[i++]);
		}

		posX = x;
		posY++;
		i++;
	}
}

void DrawScreen()
{
	for (short y = 0; y < SCREEN_SIZE; y++)
	{
		for (short x = 0; x < SCREEN_SIZE; x++)
		{
			unsigned index = y * SCREEN_SIZE + x;
			if (buffer[index] != screen[index])
			{
				screen[index] = buffer[index];
				COORD position = { x, y };
				SetConsoleCursorPosition(CONSOLE_HANDLE, position);
				putchar(buffer[index]);
			}
		}
	}

	ClearBuffer();
}

//====================================

enum Side
{
	Left,
	Right
};

struct Branch
{
	Side side;
	unsigned height;

	struct Graphics
	{
		unsigned x, y;
		char ascii;
	};

	std::vector<Graphics> graphic;

};

const float TIME_PER_SWING = 0.20;
const float STARTING_TIME= 5.0;
const float MAX_TIME = 10;

const unsigned TREE_WIDTH = 2;
const unsigned MIN_BRANCH_SIZE = 3;
const unsigned MAX_BRANCH_SIZE = 6;
const unsigned BRANCH_PROBABILITY = 60;

const unsigned ANIMATION_TIME_MS = 100;
const unsigned ANIMATION_FALLING_BRANCH_TIME_MS = 50;

int animationTimer = 0;
Side currentSide = Left;

std::vector<Branch> branches;
std::vector<Branch> fallingBranches;
std::vector<int> fallingBranchesTimers;

void DrawTree()
{
	unsigned x = (SCREEN_SIZE / 2) - (TREE_WIDTH / 2);

	for (unsigned i = 0; i < SCREEN_SIZE; i++)
		for (unsigned j = 1; j <= TREE_WIDTH; j++)
			Draw(x + j, i, '|');
}

void GenerateBranch(unsigned height)
{
	Side side = Random(0, 1) ? Left : Right;

	for (int i = branches.size() - 1; i >= 0; i--)
	{
		if (branches[i].side == side)
		{
			if (height + 4 >= branches[i].height) //check if last branch on the same side wont collide with our branch (i assume that we are generating from bottom to top)
				return;
		}
		else
			if (height == branches[i].height)
				return;
	}


	if (Random(0, 100) <= BRANCH_PROBABILITY)
	{
		Branch branch;
		unsigned branchLenght = Random(MIN_BRANCH_SIZE, MAX_BRANCH_SIZE);
		branch.height = height;

		if (side == Left)
		{
			branch.side = side;
			unsigned x = (SCREEN_SIZE / 2) - (TREE_WIDTH / 2); //tree is drawn from left

			for (unsigned i = 0; i < branchLenght; i++)
				branch.graphic.push_back({ x - i, height, '=' });

			branch.graphic.push_back({ x - Random(0, branchLenght - 1), height - 1, '|' });
			branch.graphic.push_back({ x - Random(0, branchLenght - 1), height + 1, '|' });
		}
		else
		{
			branch.side = side;

			unsigned x = (SCREEN_SIZE / 2) + TREE_WIDTH;

			for (unsigned i = 0; i < branchLenght; i++)
				branch.graphic.push_back({ x + i, height, '=' });

			branch.graphic.push_back({ x + Random(0, branchLenght - 1), height - 1, '|' });
			branch.graphic.push_back({ x + Random(0, branchLenght - 1), height + 1, '|' });
		}

		branches.push_back(branch);
	}
}

void DrawBranches()
{
	for (const auto& branch : branches)
		for (const auto& graphic : branch.graphic)
			Draw(graphic.x, graphic.y, graphic.ascii);

	for (const auto& fallingBranch : fallingBranches)
		for (const auto& graphic : fallingBranch.graphic)
			Draw(graphic.x, graphic.y, graphic.ascii);
}

void MoveBranches()
{
	for (auto& branch : branches)
	{
		branch.height++;

		for (auto& graphic : branch.graphic)
			graphic.y++;
	}
}

void DrawLumberjack()
{
	if (currentSide == Left)
	{
		if (clock() - animationTimer >= ANIMATION_TIME_MS)
		{
			DrawCharArray(LUMBERJACK_STAYING_RIGHT, (SCREEN_SIZE / 2) - (TREE_WIDTH / 2) - LUMBERJACK_WIDTH, SCREEN_SIZE - LUMBERJACK_HEIGHT);
		}
		else
		{
			DrawCharArray(LUMBERJACK_SWING_RIGHT, (SCREEN_SIZE / 2) - (TREE_WIDTH / 2) - LUMBERJACK_WIDTH, SCREEN_SIZE - LUMBERJACK_HEIGHT);
		}

	}
	else
	{
		if (clock() - animationTimer >= ANIMATION_TIME_MS)
		{
			DrawCharArray(LUMBERJACK_STAYING_LEFT, (SCREEN_SIZE / 2) + TREE_WIDTH, SCREEN_SIZE - LUMBERJACK_HEIGHT);
		}
		else
		{
			DrawCharArray(LUMBERJACK_SWING_LEFT, (SCREEN_SIZE / 2) + TREE_WIDTH, SCREEN_SIZE - LUMBERJACK_HEIGHT);

		}
	}
}

void UpdateFallingBranches()
{
	for (int i = 0; i < fallingBranches.size(); i++)
	{
		if (fallingBranches[i].graphic.size())
		{
			if (clock() - fallingBranchesTimers[i] >= ANIMATION_FALLING_BRANCH_TIME_MS)
			{
				for (int j = 0; j < fallingBranches[i].graphic.size(); j++)
				{
					if (fallingBranches[i].graphic[j].y >= SCREEN_SIZE - 1)
					{
						fallingBranches[i].graphic.erase(fallingBranches[i].graphic.begin() + j);
					}
					else
					{
						fallingBranches[i].graphic[j].y++;
					}
				}

				fallingBranchesTimers[i] = clock();
			}
		}
		else
		{
			fallingBranches.erase(fallingBranches.begin() + i);
		}
	}
}


int main()
{
	ResizeConsole(SCREEN_SIZE, SCREEN_SIZE, 16, 16);
	SetConsoleCursorVisible(false);
	ClearBuffer();
	ClearScreen();

	float gameTimer = 0;
	float timeLeft = STARTING_TIME;
	unsigned score = 0;
	bool gameOver = false;

	for (int i = SCREEN_SIZE - LUMBERJACK_HEIGHT - 5; i >= 1; i--)
		GenerateBranch(i);

	while (!gameOver)
	{
		char input = GetInput();

		if (input == 'a' || input == 'd')
		{
			currentSide = (input == 'a' ? Left : Right);
			MoveBranches();

			if (branches.size())
			{
				if (branches.front().height  >= SCREEN_SIZE - LUMBERJACK_HEIGHT)
				{
					if (currentSide == branches.front().side)
					{
						gameOver = true;
					}
					else
					{
						fallingBranches.push_back(branches.front());
						fallingBranchesTimers.push_back(clock());
						branches.erase(branches.begin());
					}
				}
			}

			if (!gameOver)
			{
				if (timeLeft + TIME_PER_SWING >= MAX_TIME)
					timeLeft = MAX_TIME;
				else
					timeLeft += TIME_PER_SWING;

				score++;
				animationTimer = clock();
				GenerateBranch(1);
			}
		}

		if (clock() - gameTimer >= 250)
		{
			timeLeft -= 0.250;
			gameTimer = clock();
		}

		if (gameOver || timeLeft <= 0)
		{
			std::string gameOverString = "GAME OVER";
			int x = SCREEN_SIZE / 2 - gameOverString.size() / 2, y = 0;
			int timer = clock();
			while (y < SCREEN_SIZE / 2)
			{
				if (clock() - timer >= 40)
				{
					y++;
					timer = clock();
				}

				Draw(x, y, gameOverString.c_str(), gameOverString.size());
				DrawScreen();
			}

			Sleep(1250);
			system("cls");
			std::cout << "Your score: " << score << "\nTry again? (Y/N)";
			char choice = tolower(GetChoice());
			while (choice != 'y' && choice != 'n')
				choice = tolower(GetChoice());

			if (choice == 'y')
			{
				branches.clear();
				fallingBranches.clear();
				fallingBranchesTimers.clear();

				gameOver = false;
				gameTimer = 0;
				score = 0;
				timeLeft = STARTING_TIME;

				for (int i = SCREEN_SIZE - LUMBERJACK_HEIGHT - 5; i >= 1; i--)
					GenerateBranch(i);

				system("cls");
				ClearScreen();
				ClearBuffer();
			}
		}
		
		UpdateFallingBranches();

		Draw(0, 1, "Time left:", std::string("Time left:").size());
		Draw(std::string("Time left: ").size(), 1, std::to_string(timeLeft).c_str(), 4);

		Draw(0, 0, "Score: ", 7);
		Draw(7, 0, std::to_string(score).c_str(), std::to_string(score).size());
		DrawTree();
		DrawBranches();
		DrawLumberjack();
		DrawScreen();
	}
		return 0;
}

Graphics.h
 

#pragma once

const unsigned LUMBERJACK_HEIGHT = 10;
const unsigned LUMBERJACK_WIDTH = 11;

const char LUMBERJACK_STAYING_RIGHT[] =
"\
  ___ [  ]\n\
 /---\\ ||\n\
 |O O| ||\n\
 | U | ||\n\
  \\_/  /\n\
/|   \\/\n\
||   |\n\
 |===|\n\
 || ||\n\
 || ||\n\
";

const char LUMBERJACK_SWING_RIGHT[] =
"\
  ___      \n\
 /---\\  [  ]\n\
 |O O|  //\n\
 | U | //\n\
  \\_/  /\n\
/|   \\/\n\
||   |\n\
 |===|\n\
 || ||\n\
 || ||\n\
";

const char LUMBERJACK_STAYING_LEFT[] =
"\
 [  ] ___ \n\
  || /---\\\n\
  || |O O|\n\
  || | U |\n\
   \\  \\_/\n\
    \\/   |\\\n\
     |   ||\n\
     |===|\n\
     || ||\n\
     || ||\n\
";


const char LUMBERJACK_SWING_LEFT[] =
"\
      ___\n\
[  ] /---\\\n\
 \\\\  |O O|\n\
  \\\\ | U |\n\
   \\  \\_/\n\
    \\/   |\\\n\
     |   ||\n\
     |===|\n\
     || ||\n\
     || ||\n\
";


Download(exe + kod): https://drive.google.com/file/d/1AMVtIE8Q93LSo8SkInwe1Gqj8iagEGE2/view

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
2 odpowiedzi 3,729 wizyt
pytanie zadane 29 października 2018 w C i C++ przez donekdzwonek Początkujący (420 p.)
0 głosów
0 odpowiedzi 763 wizyt
pytanie zadane 16 listopada 2019 w C i C++ przez Hubertius Bywalec (2,970 p.)
0 głosów
1 odpowiedź 664 wizyt
pytanie zadane 22 listopada 2018 w Visual Basic przez Szonek69 Nowicjusz (170 p.)

92,575 zapytań

141,424 odpowiedzi

319,649 komentarzy

61,960 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...