[TetrisManager 클래스의 Class Diagram]


테트리스의 전반적인 로직을 담당하는 TetrisManager 클래스에 대해서 살펴보겠습니다. TetrisManager.c 에서 변경된 부분 위주로 설명드리겠습니다. 추가, 삭제, 변경 이력에 대해서 말씀드리겠습니다.


[TetrisManager.c 버전 비교 (1)]


8, 9, 12 라인 (변경)


[TetrisManager.c 버전 비교 (2)]


49 라인 (삭제)

70 라인 (삭제)


[TetrisManager.c 버전 비교 (3)]


149, 151 라인 (추가)

154, 156 라인 (추가)

171, 173 ~ 186 라인 (추가)




이제는, 위에서 언급한 부분들을 분석해 보겠습니다.


[TetrisManager.c]

#include <stdio.h>
#include <string.h>
#include <windows.h>
#include "TetrisManager.h"
#include "Util.h"
#include "Constant.h"

#define INITIAL_SPEED 400
#define SPEED_LEVEL_OFFSET 50
#define LEVELP_UP_CONDITION 3
#define STATUS_POSITION_X_TO_PRINT 40
#define STATUS_POSITION_Y_TO_PRINT 3

static void _TetrisManager_ClearBoard(TetrisManager* tetrisManager);
static void _TetrisManager_ChangeBoardByStatus(TetrisManager* tetrisManager, int status);
static void _TetrisManager_UpSpeedLevel(TetrisManager* tetrisManager);
static void _TetrisManager_SearchLineIndexesToDelete(TetrisManager* tetrisManager, int* indexes, int* count);
static void _TetrisManager_DeleteLines(TetrisManager* tetrisManager, int* indexes, int count);

void TetrisManager_Init(TetrisManager* tetrisManager, int speedLevel){
	Block block;
	block.current = -1;
	memset(tetrisManager->board, 0, sizeof(char)* BOARD_ROW_SIZE * BOARD_COL_SIZE);
	_TetrisManager_ClearBoard(tetrisManager);
	tetrisManager->block = Block_Make(True, block);
	tetrisManager->deletedLineCount = 0;
	tetrisManager->speedLevel = speedLevel;
}

int TetrisManager_CheckValidPosition(TetrisManager* tetrisManager, int direction){
	Block temp = Block_Move(tetrisManager->block, direction);
	int i;
	for (i = 0; i < POSITIONS_SIZE; i++){
		int x = Block_GetPositions(temp)[i].x;
		int y = Block_GetPositions(temp)[i].y;
		if (tetrisManager->board[x][y] != EMPTY && tetrisManager->board[x][y] != MOVING_BLOCK){
			return tetrisManager->board[x][y];
		}
	}
	return EMPTY;
}

void TetrisManager_ChangeBoardByDirection(TetrisManager* tetrisManager, int direction){
	int tempDirection = DOWN;
	int tempCheckResult = EMPTY;
	_TetrisManager_ClearBoard(tetrisManager);
	int checkResult = TetrisManager_CheckValidPosition(tetrisManager, direction);
	if (checkResult == EMPTY){
		tetrisManager->block = Block_Move(tetrisManager->block, direction);
	}
	else{
		if (direction == UP){
			switch (checkResult){
			case TOP_WALL:
				tempDirection = DOWN;
				tempCheckResult = TOP_WALL;
				break;
			case RIGHT_WALL:
				tempDirection = LEFT;
				tempCheckResult = RIGHT_WALL;
				break;
			case LEFT_WALL:
				tempDirection = RIGHT;
				tempCheckResult = LEFT_WALL;
				break;
			}
			do{
				tetrisManager->block = Block_Move(tetrisManager->block, tempDirection);
			} while (TetrisManager_CheckValidPosition(tetrisManager, direction) == tempCheckResult);
			tetrisManager->block = Block_Move(tetrisManager->block, direction);
		}
	}
	_TetrisManager_ChangeBoardByStatus(tetrisManager, MOVING_BLOCK);
}

void TetrisManager_ChangeBoardByAuto(TetrisManager* tetrisManager){
	TetrisManager_ChangeBoardByDirection(tetrisManager, DOWN);
}

void TetrisManager_ProcessDirectDown(TetrisManager* tetrisManager){
	while (!TetrisManager_IsReachedToBottom(tetrisManager)){
		TetrisManager_ChangeBoardByDirection(tetrisManager, DOWN);
	}
}

void TetrisManager_ProcessDeletingLines(TetrisManager* tetrisManager){
	int indexes[BOARD_ROW_SIZE];
	int count;
	int i;
	_TetrisManager_SearchLineIndexesToDelete(tetrisManager, indexes, &count);
	if (count > 0){
		_TetrisManager_DeleteLines(tetrisManager, indexes, count);
		for (i = tetrisManager->speedLevel; i <= tetrisManager->deletedLineCount / LEVELP_UP_CONDITION; i++){
			_TetrisManager_UpSpeedLevel(tetrisManager);
		}
	}
}

int TetrisManager_IsReachedToBottom(TetrisManager* tetrisManager){
	int i;
	for (i = 0; i < POSITIONS_SIZE; i++){
		int x = Block_GetPositions(tetrisManager->block)[i].x;
		int y = Block_GetPositions(tetrisManager->block)[i].y;
		if (tetrisManager->board[x + 1][y] != EMPTY && tetrisManager->board[x + 1][y] != MOVING_BLOCK){
			return True;
		}
	}
	return False;
}

int TetrisManager_ProcessReachedCase(TetrisManager* tetrisManager){
	_TetrisManager_ChangeBoardByStatus(tetrisManager, FIXED_BLOCK);
	tetrisManager->block = Block_Make(False, tetrisManager->block);
	if (TetrisManager_IsReachedToBottom(tetrisManager)){
		return END;
	}
	else{
		return PLAYING;
	}
}

void TetrisManager_Sleep(TetrisManager* tetrisManager){
	Sleep(TetrisManager_GetDownMilliSecond(tetrisManager));
}

void TetrisManager_Print(TetrisManager* tetrisManager){
	int i;
	int j;
	CursorUtil_GotoXY(0, 0);
	for (i = 0; i < BOARD_ROW_SIZE; i++){
		for (j = 0; j < BOARD_COL_SIZE; j++){
			switch (tetrisManager->board[i][j]){
			case LEFT_TOP_EDGE:
				printf("┎");
				break;
			case RIGHT_TOP_EDGE:
				printf("┒");
				break;
			case LEFT_BOTTOM_EDGE:
				printf("┖");
				break;
			case RIGHT_BOTTOM_EDGE:
				printf("┚");
				break;
			case EMPTY:
				printf("  ");
				break;
			case MOVING_BLOCK:
				FontUtil_ChangeFontColor(tetrisManager->block.color);
				printf("■");
				FontUtil_ChangeFontColor(WHITE);
				break;
			case FIXED_BLOCK:
				FontUtil_ChangeFontColor(JADE);
				printf("▧");
				FontUtil_ChangeFontColor(WHITE);
				break;
			case LEFT_WALL:
			case RIGHT_WALL:
				printf("|");
				break;
			case TOP_WALL:
			case BOTTOM_WALL:
				printf("―");
				break;
			}
		}
		printf("\n");
	}
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT);
	printf("********* Tetris *********\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 1);
	printf("[%d level / %d lines deleted]\n", tetrisManager->speedLevel, tetrisManager->deletedLineCount);
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 3);
	printf("[Key Description]\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 4);
	printf("← : move left\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 5);
	printf("→ : move right\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 6);
	printf("↓ : move down\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 7);
	printf("↑ : rotate\n");
	CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 8);
	printf("SpaceBar : direct down\n");
	Block_PrintNext(tetrisManager->block, STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 11);
	CursorUtil_Hide();
}

DWORD TetrisManager_GetDownMilliSecond(TetrisManager* tetrisManager){
	int i;
	DWORD milliSecond = INITIAL_SPEED;
	for (i = MIN_SPEED_LEVEL; i < tetrisManager->speedLevel; i++){
		if (i < MAX_SPEED_LEVEL / 2){
			milliSecond -= SPEED_LEVEL_OFFSET;
		}
		else{
			milliSecond -= (SPEED_LEVEL_OFFSET / 5);
		}
	}
	return milliSecond;
}

static void _TetrisManager_ClearBoard(TetrisManager* tetrisManager){
	int i;
	int j;
	for (i = 0; i < BOARD_ROW_SIZE; i++){
		tetrisManager->board[i][0] = LEFT_WALL;
		tetrisManager->board[i][BOARD_COL_SIZE - 1] = RIGHT_WALL;
	}
	for (i = 0; i < BOARD_COL_SIZE; i++){
		tetrisManager->board[0][i] = TOP_WALL;
		tetrisManager->board[BOARD_ROW_SIZE - 1][i] = BOTTOM_WALL;
	}
	for (i = 1; i < BOARD_ROW_SIZE - 1; i++){
		for (j = 1; j < BOARD_COL_SIZE - 1; j++){
			if (tetrisManager->board[i][j] != FIXED_BLOCK){
				tetrisManager->board[i][j] = EMPTY;
			}
		}
	}
	tetrisManager->board[0][0] = LEFT_TOP_EDGE;
	tetrisManager->board[0][BOARD_COL_SIZE - 1] = RIGHT_TOP_EDGE;
	tetrisManager->board[BOARD_ROW_SIZE - 1][0] = LEFT_BOTTOM_EDGE;
	tetrisManager->board[BOARD_ROW_SIZE - 1][BOARD_COL_SIZE - 1] = RIGHT_BOTTOM_EDGE;
}

static void _TetrisManager_ChangeBoardByStatus(TetrisManager* tetrisManager, int status){
	int i;
	for (i = 0; i < POSITIONS_SIZE; i++){
		int x = Block_GetPositions(tetrisManager->block)[i].x;
		int y = Block_GetPositions(tetrisManager->block)[i].y;
		tetrisManager->board[x][y] = status;
	}
}

static void _TetrisManager_UpSpeedLevel(TetrisManager* tetrisManager){
	if (tetrisManager->speedLevel < MAX_SPEED_LEVEL){
		tetrisManager->speedLevel++;
	}
}

static void _TetrisManager_SearchLineIndexesToDelete(TetrisManager* tetrisManager, int* indexes, int* count){
	int i;
	int j;
	int toDelete;
	memset(indexes, -1, sizeof(int)* (BOARD_ROW_SIZE - 2));
	*count = 0;
	for (i = 1; i < BOARD_ROW_SIZE - 1; i++){
		toDelete = True;
		for (j = 1; j < BOARD_COL_SIZE - 1; j++){
			if (tetrisManager->board[i][j] != FIXED_BLOCK){
				toDelete = False;
				break;
			}
		}
		if (toDelete){
			indexes[(*count)++] = i;
		}
	}
}

static void _TetrisManager_DeleteLines(TetrisManager* tetrisManager, int* indexes, int count){
	int i;
	int j;
	int k = BOARD_ROW_SIZE - 2;
	int toDelete;
	char temp[BOARD_ROW_SIZE][BOARD_COL_SIZE] = { EMPTY };
	for (i = BOARD_ROW_SIZE - 2; i > 0; i--){
		toDelete = False;
		for (j = 0; j < BOARD_COL_SIZE; j++){
			if (i == indexes[j]){
				toDelete = True;
				break;
			}
		}
		if (!toDelete){
			for (j = 0; j < BOARD_COL_SIZE; j++){
				temp[k][j] = tetrisManager->board[i][j];
			}
			k--;
		}
	}
	for (i = 1; i < BOARD_ROW_SIZE - 1; i++){
		for (j = 1; j < BOARD_COL_SIZE - 1; j++){
			tetrisManager->board[i][j] = temp[i][j];
		}
	}
	tetrisManager->deletedLineCount += count;
}


8, 9, 12 라인 (변경)

INITIAL_SPEED 값을 500 에서 400 으로 내려서, 초기 시작 속도를 조금 빠르게 변경하였고, SPEED_LEVEL_OFFSET 값을 100 에서 50 으로 조정하였습니다. 12 라인에서는 STATUS_POSITION_Y_TO_PRINT 값을 6 에서 3 으로 변경하였습니다. (출력 위치의 Y 값 조정)


49 라인 (삭제)

사용자가 연속으로 UP 키를 누르고 있을 때의 처리였는데, main.c 에서의 소스 코드 변경으로 인해 필요가 없어져서 삭제했습니다.


70 라인 (삭제)

양쪽 벽에 닿았을 때 계속 벽 방향 키를 연속으로 누르고 있을 때의 처리였는데, 마찬가지로 main.c 에서의 소스 코드 변경으로 인해 필요가 없어져서 삭제했습니다.


149, 151 라인 (추가)

이동중인 블럭을 출력하기 위해 149 라인에서 먼저 해당 block 의 color 를 가지고 콘솔 출력 커서의 색상을 변경합니다 (FontUtil_ChangeFontColor 함수를 통해). 그리고 이동중인 블럭을 150 라인에서 출력하고 난뒤에는, 151 라인에서 다시 원래 색상인 흰색으로 출력 커서의 색상을 되돌려 놓습니다. (FontUtil_ChangeFontcolor 함수 호출 통해서)


154, 156 라인 (추가)

고정된 블럭을 출력하기 위해 154 라인에서 먼저 JADE 색상 (비취색) 을 가지고 콘솔 출력 커서의 색상을 변경합니다 (FontUtil_ChangeFontColor 함수를 통해). 그리고 고정 블럭을 155 라인에서 출력하고 난뒤에는, 156 라인에서 다시 원래 색상인 흰색으로 출력 커서의 색상을 되돌려 놓습니다. (FontUtil_ChangeFontcolor 함수 호출 통해서)


171, 173 ~ 186 라인 (추가)

사용자 키 설명 가이드를 화면에 출력합니다. (상, 하, 좌, 우, 스페이스 바)


by kkikkodev 2015. 8. 21. 01:31