글
[TetrisManager 클래스의 Class Diagram]
테트리스의 전반적인 로직 부분을 담당하는 클래스인 TetrisManager 에 대해서 먼저 살펴볼 것입니다. TetrisManager.h 와 TetrisManager.c 에서 변경된 부분 위주로 설명드리겠습니다. 먼저, 추가, 삭제, 변경 이력에 대해서 언급하겠습니다.
[TetrisManager.h 버전 비교]
30 라인 (추가)
[TetrisManager.c 버전 비교 (1)]
50 ~ 53 라인 (추가)
[TetrisManager.c 버전 비교 (2)]
136 라인 (변경)
[TetrisManager.c 버전 비교 (3)]
189 ~ 200 라인 (추가)
이제는 위에서 언급한 부분들을 분석해 보겠습니다.
[TetrisManager.h]
#ifndef _BOARD_H #define _BOARD_H #include "Block.h" enum GameStatus{ PLAYING, END }; #define BOARD_ROW_SIZE 20 #define BOARD_COL_SIZE 14 typedef struct _tetrisManager{ char board[BOARD_ROW_SIZE][BOARD_COL_SIZE]; Block block; int deletedLineCount; int speedLevel; }TetrisManager; void TetrisManager_Init(TetrisManager* tetrisManager, int speedLevel); int TetrisManager_CheckValidPosition(TetrisManager* tetrisManager, int direction); void TetrisManager_ChangeBoardByDirection(TetrisManager* tetrisManager, int direction); void TetrisManager_ChangeBoardByAuto(TetrisManager* tetrisManager); void TetrisManager_ProcessDirectDown(TetrisManager* tetrisManager); void TetrisManager_ProcessDeletingLines(TetrisManager* tetrisManager); int TetrisManager_IsReachedToBottom(TetrisManager* tetrisManager); int TetrisManager_ProcessReachedCase(TetrisManager* tetrisManager); void TetrisManager_Sleep(TetrisManager* tetrisManager); void TetrisManager_Print(TetrisManager* tetrisManager); DWORD TetrisManager_GetDownMilliSecond(TetrisManager* tetrisManager); #endif
30 라인 (추가)
GetDownMilliSecond 함수의 선언부입니다.
[TetrisManager.c]
#include <stdio.h> #include <string.h> #include <windows.h> #include "TetrisManager.h" #include "Util.h" #include "Constant.h" #define INITIAL_SPEED 500 #define SPEED_LEVEL_OFFSET 100 #define LEVELP_UP_CONDITION 3 #define STATUS_POSITION_X_TO_PRINT 40 #define STATUS_POSITION_Y_TO_PRINT 6 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); if (direction == UP){ TetrisManager_Sleep(tetrisManager); tetrisManager->block = Block_Move(tetrisManager->block, DOWN); } } 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); } else{ if (direction == RIGHT && checkResult == RIGHT_WALL || direction == LEFT && checkResult == LEFT_WALL || direction == RIGHT && checkResult == FIXED_BLOCK || direction == LEFT && checkResult == FIXED_BLOCK){ TetrisManager_Sleep(tetrisManager); tetrisManager->block = Block_Move(tetrisManager->block, DOWN); } } } _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: printf("■"); break; case FIXED_BLOCK: printf("▧"); 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("Current speed level : %d level\n", tetrisManager->speedLevel); CursorUtil_GotoXY(STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 2); printf("Deleted lines : %d lines", tetrisManager->deletedLineCount); Block_PrintNext(tetrisManager->block, STATUS_POSITION_X_TO_PRINT, STATUS_POSITION_Y_TO_PRINT + 4); 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; }
50 ~ 53 라인 (추가)
사용자가 연속으로 UP 키를 눌르고 있을 때, 처리를 합니다. 기존과 다르게, UP 키를 누르면, TetrisManager_Sleep 함수를 호출하여, 프로그램을 일정 시간 지연시킨 다음에, Block_Move 함수를 호출하여 현재 이동중인 블럭을, 밑으로 한 칸 이동시킵니다.
136 라인 (변경)
ver 0.1 에서 길게 작성했던 코드를 외부 함수로 추출하는 형태로 변경하였습니다. 먼저, TetrisManager_GetDownMilliSecond 함수를 호출하여 현재 설정된 속도 레벨에 해당하는 적절한 밀리세컨드를 가져옵니다. 이것을 Sleep 함수에 넘기면서, 프로그램을 지연시킵니다.
TetrisManager_GetDownMilliSecond 함수의 정의부입니다. 내용은 ver 0.1 과 같지만, 따로 함수로 외부로 빼내서 정의했다는 것이 차이점입니다. 이 함수에서는, 현재 속도 레벨에 맞는 지연되어야 하는 밀리세컨드를 계산해서 반환하고 있습니다.
190 ~ 200 라인 (추가)
ver 0.1 에서와 동일하게, 현재 속도 레벨에 맞는, 블럭이 지연되어야 하는 밀리세컨드를 계산해서 반환합니다.
'1.2) 프로젝트 > 테트리스' 카테고리의 다른 글
테트리스 ver 0.3 (2) - 요구사항 분석 (0) | 2015.08.20 |
---|---|
테트리스 ver 0.3 (1) - 실행 결과 & 프로젝트 개요 (10) | 2015.06.17 |
테트리스 ver 0.2 (8) - 개발 완료 / 후기 (0) | 2015.06.14 |
테트리스 ver 0.2 (7) - 소스코드 구현 (Main) (0) | 2015.06.14 |
테트리스 ver 0.2 (6) - 소스코드 구현 (TetrisView) (0) | 2015.06.13 |
테트리스 ver 0.2 (4) - 소스코드 변경 내역 (0) | 2015.06.13 |
테트리스 ver 0.2 (3) - 프로그램 설계 (Use Case, Class, Sequence) (0) | 2015.06.12 |
테트리스 ver 0.2 (2) - 요구사항 분석 (0) | 2015.06.12 |
테트리스 ver 0.2 (1) - 실행 결과 & 프로젝트 개요 (0) | 2015.06.12 |
테트리스 ver 0.1 (10) - 개발 완료 / 후기 (0) | 2015.06.12 |
RECENT COMMENT