[C++] 테트리스 (콘솔)

0시0분·2024년 6월 7일
0

C++

목록 보기
2/2

예전에 만들었던 콘솔 테트리스 코드를 발견해서 저장 겸 기록해놓는다.
뭔가 정석적으로 이것저것 써보려고 했던 것 같다.

실행은 잘 안된다..
기능자체는 동작하는데 칸수가 안맞다. 인코딩이 깨지는 것 같다.


(예전)
(지금 😫😥😭)

🔽 메인 코드 (게임 실행)

#include <iostream>
#include <conio.h>
#include <time.h>
#include "block.h"
#include "board.h"

using namespace std;

#define BOARDWIDTH 15
#define BOARDHEIGHT 25

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define SPACE 32

int keyCode = 0;

int main()
{
	Board gameBoard(BOARDHEIGHT, BOARDWIDTH);
	Block currBlock;
	bool nextTurn = true;
	bool gameEnd = false;

	clock_t start, end;
	while (!gameEnd)  {
		gameBoard.ClearLine();

		// ** 새 블럭 생성
		if (nextTurn) {
			start = clock();

			currBlock = Block();
			gameBoard.DrawCurrBlock(currBlock, keyCode, nextTurn, gameEnd);
			nextTurn = false;
		}

		// ** 키 입력
		if (_kbhit()) {
			keyCode = _getch();
			if (keyCode == 224) {	// 방향키이면
				keyCode = _getch();
				switch (keyCode)
				{
				case UP:
					currBlock.Rotate();
					break;
				case DOWN:
					currBlock.Down();
					break;
				case LEFT:
					currBlock.Left();
					break;
				case RIGHT:
					currBlock.Right();
					break;
				}
			}
			else if (keyCode == SPACE) {
				do {
					currBlock.Down();
				} while (gameBoard.DrawCurrBlock(currBlock, keyCode, nextTurn, gameEnd));
			}

			// ** 보드판 영역을 벗어났을 경우 위치 롤백
			if (!gameBoard.DrawCurrBlock(currBlock, keyCode, nextTurn, gameEnd)) {
				currBlock.RollbackMove(keyCode);
			}
		}

		// ** 자동 낙하
		end = clock();
		if ((end - start) > 1 * CLOCKS_PER_SEC) {
			start = clock();
			currBlock.Down();
			gameBoard.DrawCurrBlock(currBlock, keyCode, nextTurn, gameEnd);
		}
	}

	gameBoard.DrawGameEnd();
}

🔽 보드판 헤더

#pragma once
#include <iostream>
#include <vector>
#include <stack>
#include <Windows.h>

#define ENDLINE 4

void gotoxy(int x, int y) 
{
	COORD pos;

	pos.X = x;
	pos.Y = y;

	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

/*
* 1 : 현재 블럭
* 2 : 보드 테두리(양옆위)
* 3 : 보드 바닥
* 4 : 쌓인 블럭
* 5 : 게임 종료
*/

class Board
{
private:
	int height, width;
	vector<vector<int>> board;

	int score;

public:
	Board(int x, int y) : height(x), width(y), score(0)
	{
		SetBoardFrame();
		DrawBoard();
		DrawScore();
	}

	void SetBoardFrame()
	{
		vector<vector<int>> tmp(height, vector<int>(width, 0));

		for (int i = 1; i < height; i++) {
			tmp[i][0] = 2;
			tmp[i][width - 1] = 2;
		}

		for (int i = 0; i < width; i++) {
			tmp[height - 1][i] = 3;
		}

		for (int i = 1; i < width - 1; i++) {
			tmp[ENDLINE][i] = 5;
		}

		board = tmp;
	}

	void DrawBoard()
	{
		gotoxy(0, 0);

		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				if (board[i][j] == 2 || board[i][j] == 3) {
					cout << "▦";
				}
				else if (board[i][j] == 1 || board[i][j] == 4) {
					cout << "■";
				}
				else if (board[i][j] == 5) {
					cout << "- ";
				}
				else {
					cout << "  ";
				}
			}
			cout << "\n";
		}
	}

	void DrawScore()
	{
		gotoxy(width * 2 + 5, 2);
		cout << "SCORE\t" << score;
	}

	void DrawGameEnd()
	{
		gotoxy(width * 2 + 5, 4);
		cout << "GAME END!";
		gotoxy(0, height + 1);
	}

	bool DrawCurrBlock(Block blk, int key, bool& nextTurn, bool& gameEnd)
	{
		int X = blk.getX();
		int Y = width / 2 - 2 + blk.getY();

		///*
		for (int i = 1; i < width - 1; i++) {
			if (board[ENDLINE][i] != 1)
				board[ENDLINE][i] = 5;
		}
		//*/

		// ** 유효한지 확인
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (blk.CurrBlock()[i][j] == 1 && (board[i + X][j + Y] == 2 || board[i + X][j + Y] == 3 || board[i + X][j + Y] == 4)) {

					if (!nextTurn && (board[i + X][j + Y] == 3 || board[i + X][j + Y] == 4)) {
						score += 10;
						nextTurn = true;

						for (int m = 0; m < height; m++) {
							for (int n = 0; n < width; n++) {
								if (board[m][n] == 1) {
									board[m][n] = 4;
								}
							}
						}

						if (IsGameEnd()) {
							gameEnd = true;
						}

						DrawScore();
					}

					return false;
				}
			}
		}

		// ** 이전 위치 지우기
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				if (board[i][j] == 1) {
					board[i][j] = 0;
				}
			}
		}

		// ** 현재 블럭 위치 표시
		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				if (blk.CurrBlock()[i][j] == 1)
				//if (board[i + X][j + Y] != 2 && board[i + X][j + Y] != 3 && board[i + X][j + Y] != 4)
					board[i + X][j + Y] = blk.CurrBlock()[i][j];
			}
		}

		DrawBoard();

		return true;
	}

	void ClearLine()
	{
		// 다 찬 줄 검사
		stack<int> lineIndex;
		for (int i = height - 2; i > 1; i--) {
			for (int j = 1; j < width - 1; j++) {
				if (board[i][j] != 4)	break;

				if (j == width - 2)
					lineIndex.push(i);
			}
		}

		while (!lineIndex.empty()) {
			// 줄 지우기
			int line = lineIndex.top();
			lineIndex.pop();
			for (int j = 1; j < width - 1; j++) {
				board[line][j] = 0;
			}
			 
			score += 100;

			// 위 블럭 내리기
			for (int i = line - 1; i > ENDLINE; i--) {
				for (int j = 1; j < width - 1; j++) {
						board[i + 1][j] = board[i][j];
						board[i][j] = 0;
				}
			}

			DrawBoard();
			DrawScore();
		}
	}

	bool IsGameEnd()
	{
		for (int i = 1; i < width - 1; i++) {
			if (board[ENDLINE][i] == 4)
				return true;
		}

		return false;
	}
};

🔽 블록 헤더

#pragma once
#include <cstdlib>

#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77

using namespace std;

int blocks[7][4][4][4] = {
// zBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 0, 0 }
	}
},
// sBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 0 }
	}
},
// oBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 0, 0 }
	}
},
// iBlock
{
	{
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 0 },
		{ 1, 1, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 0 },
		{ 1, 1, 1, 1 },
		{ 0, 0, 0, 0 }
	}
},
// tBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 1, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 0, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 1 },
		{ 0, 0, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 0 }
	}
},
//lBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 1 },
		{ 0, 1, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 0, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 1 },
		{ 0, 1, 0, 0 }
	}
},
// rBlock
{
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 0, 1, 0 },
		{ 0, 1, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 1, 0, 0 },
		{ 0, 1, 1, 1 },
		{ 0, 0, 0, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 1, 1 },
		{ 0, 0, 1, 0 },
		{ 0, 0, 1, 0 }
	},
	{
		{ 0, 0, 0, 0 },
		{ 0, 0, 0, 0 },
		{ 0, 1, 1, 1 },
		{ 0, 0, 0, 1 }
	}
}
};

class Block
{
private:
	int moveX;
	int moveY;
	int rotateIndex;
	int currBlock[4][4][4];
public:
	Block() : moveX(0), moveY(0), rotateIndex(0)
	{
		GenerateRandomBlock();
	}

	void GenerateRandomBlock()
	{
		srand((unsigned)time(NULL));
		int random = rand() % 7;

		for (int i = 0; i < 4; i++) {
			for (int j = 0; j < 4; j++) {
				for (int k = 0; k < 4; k++) {
					currBlock[i][j][k] = blocks[random][i][j][k];
				}
			}
		}
	}

	int getX() { return moveX; }
	int getY() { return moveY; }

	auto CurrBlock() { return currBlock[rotateIndex]; }

	Block RollbackMove(int key)
	{
		switch (key)
		{
		case UP:
			rotateIndex--;
			break;
		case DOWN:
			moveX--;
			break;
		case LEFT:
			moveY++;
			break;
		case RIGHT:
			moveY--;
			break;
		}

		return *this;
	}

	void Rotate()
	{
		rotateIndex++;
		rotateIndex = rotateIndex % 4;
	}
	
	void Down() { moveX++; }
	void Left() { moveY--; }
	void Right() { moveY++; }
};

0개의 댓글