winapi.co.kr 에 있는 테트리스 강좌를 실행 했습니다.
실행하는데 필요한 함수 및 라이브러리를 추가했습니다.
[소스코드]
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <conio.h> #include <Windows.h> typedef enum { false, true } BOOL; #define TOTAL_BLOCK 7 // 전체블럭수 #define BW 10 // 테트리스판의 가로 #define BH 20 // 테트리스판의 높이 #define BX 5 // #define BY 1 #define LEFT 75 #define RIGHT 77 #define UP 72 #define DOWN 80 #define ESC 27 #define clscr() system("cls") typedef enum { NOCURSOR , SOLIDCURSOR , NORMALCURSOR } CURSOR_TYPE; enum { EMPTY , BRICK , WALL }; void DrawScreen(); void DrawBoard(); void setCursorType(CURSOR_TYPE c); void TestFull(); void gotoxy(int x, int y); void PrintBrick(BOOL Show); int GetAround(int x, int y, int b, int r); BOOL MoveDown(); BOOL ProcessKey(); struct Point { int x, y; }; char *arTile[] = { ".", "■", "□" }; // 테트리스 판의 전체크기 int board[BW + 2][BH + 2]; // 현재 진행중인 블럭 int brick; // 회전수 int rot; int nx, ny; struct Point Shape[][4][4] = { // // 0.■■■■ { { 0, 0, 1, 0, 2, 0, 0, 1 }, { 0, 0, 0, 1, 0, 2, -1, 0 }, { 0, 0, 0, -1, -1, 0, -2, 0 }, { 0, 0, 1, 0, 0, -1, 0, -2 } }, // 1. ■■ // ■■ { { 0, 0, 1, 0, 0, 1, 1, 1 }, { 0, 0, 1, 0, 0, 1, 1, 1 }, { 0, 0, 1, 0, 0, 1, 1, 1 }, { 0, 0, 1, 0, 0, 1, 1, 1 } }, // // 2.■■ // ■■ { { 0, 0, -1, 0, 0, -1, 1, -1 }, { 0, 0, 0, 1, -1, 0, -1, -1 }, { 0, 0, -1, 0, 0, -1, 1, -1 }, { 0, 0, 0, 1, -1, 0, -1, -1 } }, // // 3. ■■ // ■■ { { 0, 0, 1, 0, 0, -1, -1, -1 }, { 0, 0, 0, 1, 1, 0, 1, -1 }, { 0, 0, 1, 0, 0, -1, -1, -1 }, { 0, 0, 0, 1, 1, 0, 1, -1 } }, // // 4.■■■ // ■ { { 0, 0, -1, 0, 1, 0, -1, -1 }, { 0, 0, 0, -1, 0, 1, -1, 1 }, { 0, 0, -1, 0, 1, 0, 1, 1 }, { 0, 0, 0, -1, 0, 1, 1, -1 } }, // // 5.■■■ // ■ { { 0, 0, 0, 1, -1, 1, -2, 1 }, { 0, 0, 1, 0, 1, 1, 1, 2 }, { 0, 0, 0, -1, 1, -1, 2, -1 }, { 0, 0, -1, 0, -1, -1, -1, -2 } }, // // 6. ■■■ // ■ { { 0, 0, -1, 0, 1, 0, 0, 1 }, { 0, 0, 0, -1, 0, 1, 1, 0 }, { 0, 0, -1, 0, 1, 0, 0, -1 }, { 0, 0, -1, 0, 0, -1, 0, 1 } } }; int main() { int nFrame, nStay; int x, y; setCursorType(NOCURSOR); // // 초기 화면 배열을 생성한다. // for (x = 0; x<BW + 2; x++) { for (y = 0; y<BH + 2; y++) { board[x][y] = EMPTY; //블럭의 테두리를 그리게 채운다. if (y == 0 || y == BH + 1 || x == 0 || x == BW + 1) { board[x][y] = WALL; } //board[x][y] = (y==0||y==BH+1|| x==0 || x ==BW+1) ? WALL: EMPTY; } } // 초기 화면을 그린다. DrawScreen(); nFrame = 20; // // 게임을 진행한다. // // while (true) { // // 무한 루프를 반복하며 게임을 진행한다. // 무한 루프에서 빠져 나오면 게임을 종료한다. // srand(time(NULL)); // 랜덤하게 벽돌을 생성한다. brick = rand() % ((sizeof(Shape) / sizeof(Shape[0])) + 1); // 현재 가로 위치 nx = BW / 2; // 생성된 세로 위치 ny = 2; // 회전수 rot = 0; PrintBrick(true); // 만약 벽돌이 화면에 가득차면 게임을 종료한다. if (GetAround(nx, ny, brick, rot) != EMPTY) break; nStay = nFrame; // 키 입력을 받아 이벤트를 처리한다. while (true) { if (--nStay == 0) { nStay = nFrame; if (MoveDown()) break; } if (ProcessKey()) break; Sleep(1000 / 20); } } clscr(); gotoxy(30, 12); puts("G A M E O V E R"); setCursorType(NORMALCURSOR); } BOOL ProcessKey() { // 전체 회전수 int ch, trot; if (kbhit()) { ch = getch(); if (ch == 0xE0 || ch == 0) { ch = getch(); switch (ch) { case LEFT: if (GetAround(nx - 1, ny, brick, rot) == EMPTY) { PrintBrick(false); nx--; PrintBrick(true); } break; case RIGHT: if (GetAround(nx + 1, ny, brick, rot) == EMPTY) { PrintBrick(false); nx++; PrintBrick(true); } break; case UP: trot = (rot == 3 ? 0 : rot + 1); if (GetAround(nx, ny, brick, trot) == EMPTY) { PrintBrick(false); rot = trot; PrintBrick(true); } break; case DOWN: if (MoveDown()) { return true; } break; } } else { switch (ch) { case ' ': while (MoveDown() == false) { } return true; } } } return false; } // // 블럭을 그리고 그려진 블럭을 지운다. // void PrintBrick(BOOL Show) { int i; for (i = 0; i<4; i++) { gotoxy(BX + (Shape[brick][rot][i].x + nx) * 2, BY + Shape[brick][rot][i].y + ny); puts(arTile[Show ? BRICK : EMPTY]); } } // // 테트리스 화면을 그린다. // void DrawScreen() { //int x, y; for (int x = 0; x<BW + 2; x++) { for (int y = 0; y<BH + 2; y++) { gotoxy(BX + x * 2, y); puts(arTile[board[x][y]]); } } // COORD coord; coord.X = 50; coord.Y=3; // SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); gotoxy(50, 3); puts("Tetris Ver 1.0"); gotoxy(50, 5); puts("좌우:이동, 위:회전, 아래:내림"); gotoxy(50, 6); puts("공백: 전부 내림"); gotoxy(1, 25); } // // 배경을 뺀 나머지 부분을 그린다. // void DrawBoard() { int x, y; // 테두리를 제외한 부분을 그린다. for (x = 1; x < BW + 1; x++) { for (y = 1; y < BH + 1; y++) { gotoxy(BX + x * 2, BY + y); puts(arTile[board[x][y]]); } } } // // 주변에 벽돌이 있는지 확인한다. // int GetAround(int x, int y, int brick, int rotation) { int k = EMPTY; for (int i = 0; i < 4; i++) { int t = board[x + Shape[brick][rotation][i].x][y + Shape[brick][rotation][i].y]; if (t > k) { k = t; } } return k; } // // 커서의 종류를 변경한다. // void setCursorType(CURSOR_TYPE c) { CONSOLE_CURSOR_INFO curInfo; switch (c) { case NOCURSOR: curInfo.dwSize = 1; curInfo.bVisible = false; break; case SOLIDCURSOR: curInfo.dwSize = 100; curInfo.bVisible = true; break; case NORMALCURSOR: curInfo.dwSize = 20; curInfo.bVisible = true; break; } } // // 콘솔창의 좌표를 파라미터 값으로 받은 x, y로 이동한다. // void gotoxy(int x, int y) { COORD coord; coord.X = x; coord.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); } // // 블럭을 아래로 내린다. // BOOL MoveDown() { if (GetAround(nx, ny + 1, brick, rot) != EMPTY) { TestFull(); return true; } PrintBrick(false); ny++; PrintBrick(true); return false; } void TestFull() { int i, x, y, ty; for (i = 0; i < 4; i++) { board[nx + Shape[brick][rot][i].x][ny + Shape[brick][rot][i].y] = BRICK; } for (y = 1; y < BH + 1; y++) { for (x = 1; x < BW + 1; x++) { if (board[x][y] != BRICK) break; } if (x == BW + 1) { for (ty = y; ty > 1; ty--) { for (x = 1; x < BW + 1; x++) { board[x][ty] = board[x][ty - 1]; } } DrawBoard(); Sleep(200); } } }