본문 바로가기

Native/C

테트리스

[code]
//
// 설명 읽지말고 분석하란다 OTL
//

#include <Turboc.h>

// 아스키 키 코드
#define LEFT    75
#define RIGHT    77
#define UP        72
#define DOWN    80
#define ESC        27

// 가로 세로 블럭 설정
#define BX        5
#define BY        1
#define BW        10
#define BH        20

void DrawScreen();
void DrawBoard();
BOOL ProcessKey();
void PrintBrick(BOOL Show);
int GetAround(int x,int y,int b,int r);
BOOL MoveDown();
void TestFull();

struct Point
{
    int x,y;
};

//
// 원래는...
// {{0,0},{1,0},{2,0},{-1,0}} 이렇게 해야된다..
//
// -1,0, 1.0, 0,0, 2,0
//    
// ㅡ 모양의 블럭이 완성된다.
// 이후 것은
//     0,1,
//     0,0,
//     0,-1,
//     0,-2
// 식으로 회전 한다. 일명 제일 좋아하는 짝대기...
//
Point Shape[][4][4] = {
    { {0,0,1,0,2,0,-1,0},    {0,0,0,1,0,-1,0,-2},    {0,0,1,0,2,0,-1,0},        {0,0,0,1,0,-1,0,-2} },
    { {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} },
    { {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} },
    { {0,0,-1,-1,0,-1,1,0}, {0,0,-1,0,-1,1,0,-1},    {0,0,-1,-1,0,-1,1,0},    {0,0,-1,0,-1,1,0,-1} },
    { {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} },
    { {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} },
    { {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} },
};

enum { EMPTY, BRICK, WALL };
char *arTile[]={". ","■","□"};
int board[BW+2][BH+2];
int nx,ny;
int brick,rot;

void main()
{
    int nFrame, nStay;
    int x,y;

    setcursortype(NOCURSOR);

    randomize();
    clrscr();

    // □ 으로 테두리 변수에 대입하기
    for (x=0;x<BW+2;x++) {
        for (y=0;y<BH+2;y++) {
            board[x][y] = (y==0 || y==BH+1 || x==0 || x==BW+1) ? WALL:EMPTY;
        }
    }

    DrawScreen();
    nFrame=20;

    for (;1;) {
        brick=random(sizeof(Shape)/sizeof(Shape[0]));    // 896 / 128 = 7가지의 종류!!
        nx=BW/2;    // 5
        ny=3;        // 3
        rot=0;
        PrintBrick(TRUE);

        if (GetAround(nx,ny,brick,rot) != EMPTY) break;

        nStay=nFrame;

        //
        // for (;2;) 루프의 끝에서 0.05초씩 시간을 지연시키고 있으므로 이 루프는 초당 20번 반복된다.
        // 벽돌 하나가 내려오는 주기인 nFrame이 20으로 초기화되어 있고 nStay가 이 값을 역카운트하면서
        // 0이 될 때 벽돌을 한칸 내린다. 그래서 벽돌은 정확하게 1초에 한 번 한칸 아래로 이동할 것이다.
        // 이렇게 할 바에야 아예 delay(1000)을 주고 1초에 한 번씩 벽돌을 이동시킬 수도 있지만 그렇게
        // 할 경우 키 입력도 1초에 한 번밖에 못 받으므로 반응성이 떨어진다. 그래서 시간을 1/20초로
        // 분할하고 벽돌은 20프레임에 한 번씩 움직이되 키는 매 프레임마다 입력받을 수 있도록 했다
        //
        for (;2;) {
            if (--nStay == 0) {
                nStay=nFrame;
                if (MoveDown()) break;
            }
            if (ProcessKey()) break;
            delay(1000/20);
        }
    }
    clrscr();
    gotoxy(30,12);puts("G A M E O V E R");
    setcursortype(NORMALCURSOR);
}

//
// 화면 전체를 그린다. 게임판과 게임 이름, 벽까지 한꺼번에 그린다
//
void DrawScreen()
{
    int x,y;

    // 변수의 내용을 직접 뿌려주기 puts
    for (x=0;x<BW+2;x++) {
        for (y=0;y<BH+2;y++) {
            gotoxy(BX+x*2,BY+y);
            puts(arTile[board[x][y]]);
        }
    }

    gotoxy(50,3);puts("ShTetris Ver 1.0");
    gotoxy(50,5);puts("좌우:이동, 위:회전, 아래:내림");
    gotoxy(50,6);puts("공백:전부 내림");
//    gotoxy(50,8);
    //printf("%d, %d",sizeof(Shape),sizeof(Shape[0]));
    
}

//
// 게임판만 그린다. 즉 외부벽과 문자열들은 빼고 쌓여 있는 벽돌만 그린다
//
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]]);
        }
    }
}

//
// 키 입력을 처리하는데 main 함수의 부담을 덜어주기 위해 별도의 함수로 분리해 놓았다. 이동중인 벽돌이 바닥에 닿으면 TRUE를 리턴한다
//
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;
}

//
// 벽돌을 출력하거나 삭제하는데 이동중인 벽돌을 대상으로 하므로 전역 변수 brick, rot, nx, ny값을 참조한다
//
void PrintBrick(BOOL Show) // 불쑈 ㅡ.ㅡ;;;; 미친소가 생각나따!
{
    int i;
    
    //
    // 하나의 블럭을 완성시킨다... 4번 for를 돌리므로서의 완성(배열정보가 4개므로..)
    //
    for (i=0;i<4;i++) {
        //
        // 0: 5+(0+5)*2, 1+0+3    = 15 4
        // 1: 5+(1+5)*2, 1+0+3    = 17 4
        // 2: 5+(2+5)*2, 1+0+3    = 19 4
        // 3: 5+(-1+5)*2, 1+0+3 = 13 4
        //
        // [벽돌 종류], [회전번호], [일련번호]
        gotoxy(BX+(Shape[brick][0][i].x+nx)*2,BY+Shape[brick][rot][i].y+ny);
        puts(arTile[Show ? BRICK:EMPTY]);
    }
}

//
// x:5, y:3, b:random, r:0
// 벽돌 주변에 무엇이 있는지 검사하여 벽돌의 이동 및 회전 가능성을 조사한다.
// 이동중인 벽돌의 주변을 조사하는 것이 아니므로 인수로 전달된 위치의 벽돌 모양을 참조한다.
//
// □ □ □ □ □ □ □ □ □ □ □ □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ . . . . . . . . . . □
// □ □ □ □ □ □ □ □ □ □ □ □
//
int GetAround(int x,int y,int b,int r)
{
    int i,k=EMPTY;

    for (i=0;i<4;i++) {
        // EMPTY보다는 BRICK을, BRICK보다는 WALL을 더 큰 값으로 평가한다
        // board[5+0][3+0]
        // board[5+1][3+0]
        // board[5+2][3+0]
        // board[5+-1][3+0]
        k=max(k,board[x+Shape[r][i].x][y+Shape[r][i].y]);
    }
    return k;
}

//
// 벽돌을 한칸 아래로 이동시킨다. 만약 바닥에 닿았다면 TestFull 함수를 호출한 후 TRUE를 리턴한다.
//
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++) {
        // 한줄 전체가 BRICK이면
        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();
            delay(200);
        }
    }
}
[/code]

'Native > C' 카테고리의 다른 글

함수 포인터 상에서의 캐스트 연산자  (0) 2013.10.02
Function pointer(함수포인터)  (0) 2013.10.02
turbo.h  (0) 2013.10.02
Matrix Logo  (0) 2013.10.02
공용체 Union  (0) 2013.10.02