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

[C++] ASCII Timberman!

Aruba Cloud VPS - 50% taniej przez 3 miesiące!
+2 głosów
778 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,928 wizyt
pytanie zadane 29 października 2018 w C i C++ przez donekdzwonek Początkujący (420 p.)
0 głosów
0 odpowiedzi 987 wizyt
pytanie zadane 16 listopada 2019 w C i C++ przez Hubertius Bywalec (2,970 p.)
0 głosów
1 odpowiedź 810 wizyt
pytanie zadane 22 listopada 2018 w Visual Basic przez Szonek69 Nowicjusz (170 p.)

93,158 zapytań

142,171 odpowiedzi

321,881 komentarzy

62,487 pasjonatów

Advent of Code 2024

Top 15 użytkowników

  1. 224p. - Marcin Putra
  2. 224p. - nidomika
  3. 223p. - dia-Chann
  4. 221p. - ssynowiec
  5. 217p. - Mikbac
  6. 216p. - CC PL
  7. 215p. - Łukasz Piwowar
  8. 212p. - zmmz89
  9. 210p. - Adrian Wieprzkowicz
  10. 208p. - rafalszastok
  11. 206p. - Michal Drewniak
  12. 204p. - Łukasz Eckert
  13. 202p. - rucin93
  14. 200p. - robwarsz
  15. 198p. - TheLukaszNs
Szczegóły i pełne wyniki

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

Wprowadzenie do ITsec, tom 1 Wprowadzenie do ITsec, tom 2

Można już zamawiać dwa tomy książek o ITsec pt. "Wprowadzenie do bezpieczeństwa IT" - mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy aż 15% zniżki! Dziękujemy ekipie Sekuraka za fajny rabat dla naszej Społeczności!

...