C 테트리스

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);
        }
    }
}
이 글은 C 카테고리로 분류되었고 님에 의해 에 작성됐습니다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중