tetris.cpp

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#include <windows.h>

enum { BLACK, DARK_BLUE, DARK_GREEN, DARK_SKY_BLUE, DARK_RED, DARK_VIOLET, DARK_YELLOW, GRAY, DARK_GRAY, BLUE, GREEN, SKY_BLUE, RED, VIOLET, YELLOW, WHITE};


#define BOARD_WIDTH 10

#define BOARD_HEIGHT 22

#define CLOCKWISE           0

#define COUNTER_CLOCKWISE   1

#define MAX_BLOCK_LIST      5

#define LATENCY             200

typedef struct _TBlock{

    int type;

    int len;

    int state;

    COORD vector[4];

}TBlock;


int start_x = 1, start_y = 2;

char board_buffer[BOARD_HEIGHT][BOARD_WIDTH];

char board[BOARD_HEIGHT][BOARD_WIDTH];

TBlock block_list[MAX_BLOCK_LIST];

int statitics[7];

unsigned int gScore = 0;

unsigned int gLines = 0;


unsigned int gSpeed = 1000;

unsigned int gMoveDelay = 100;

unsigned int gRotateDelay = 80;

unsigned int gSpaceDelay = 220;

clock_t gLatency = 0;

int tspin_weight = 1;

bool tspin_opt = true;

bool rotation_opt = CLOCKWISE;

//clock_t gMovedBlock = 0;


enum { BLANK, I_BLOCK, J_BLOCK, L_BLOCK, O_BLOCK, S_BLOCK, T_BLOCK, Z_BLOCK};


void setColor(int ForeColor, int BackColor)

{

    int color = ForeColor | (BackColor << 4);

    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);

}

void gotoxy(int x, int y)

{

    COORD coord = {x, y};

    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);

}

void cursorToggle(BOOL _cursor)

{

CONSOLE_CURSOR_INFO curInfo;

curInfo.bVisible=_cursor;

curInfo.dwSize=1;

SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &curInfo);

}

void drawTile(int tile)

{

    static char fcolor_set[10] = {BLACK, RED, BLUE, WHITE, YELLOW, VIOLET, SKY_BLUE, GREEN };

    static char bcolor_set[10] = {BLACK, DARK_RED, DARK_BLUE, BLACK, DARK_YELLOW, DARK_VIOLET, BLACK, DARK_GREEN };

    setColor(fcolor_set[tile], bcolor_set[tile]);

    printf("■");

    setColor(GRAY, BLACK);

}

void drawScore(int x, int y){

    setColor(YELLOW,BLACK);

    gotoxy(x,y);

    printf("Score :%6d",gScore);

    gotoxy(x,y+2);

    printf("Lines :%6d",gLines);

}

void drawFrame()

{

    int x,y;

    setColor(WHITE,BLACK);

    for(y=0; y<BOARD_HEIGHT; y++){

        gotoxy(2*(start_x-1), y+start_y);

        printf("│");

        gotoxy(2*(start_x+BOARD_WIDTH), y+start_y);

        printf("│");

    }

    gotoxy(2*(start_x-1), y+start_y);

    printf("└");

    gotoxy(2*(start_x+BOARD_WIDTH), y+start_y);

    printf("┘");

    for(x=0; x<BOARD_WIDTH; x++){

        gotoxy(2*(start_x+x), start_y+BOARD_HEIGHT);

        printf("─");

    }

    setColor(WHITE, BLACK);

}

void drawInfo(){

    int x=44,y=6;

    gotoxy(x, y); printf("      T-spin : %s", tspin_opt?"possible":"impossible");

    gotoxy(x,y+1);printf("  rotatation : %s", rotation_opt?"conuter-clockwise":"clockwise");

    gotoxy(x,y+2);printf("       speed : %d", gSpeed);

    gotoxy(x,y+3);printf("     m-delay : %d", gMoveDelay);

    gotoxy(x,y+4);printf("     r-delay : %d", gRotateDelay);

    gotoxy(x,y+5);printf("     d-delay : %d", gSpaceDelay);

    gotoxy(x,y+6);printf("     latency : %d", LATENCY);

}

void drawDebug(char board[BOARD_HEIGHT][BOARD_WIDTH], int x_pos){

    int x,y;

    for(y=0; y<BOARD_HEIGHT; y++){

        gotoxy(2*(start_x+x)+x_pos+BOARD_WIDTH, start_y+y);

        for(x=0; x<BOARD_WIDTH; x++){

            if( board[y][x]!= BLANK )

                setColor(RED, BLACK);

            else

                setColor(GRAY, BLACK);

            printf("%02d", board[y][x]);

        }

    }

    setColor(GRAY, BLACK);

}

void drawBoard()

{

    int x,y;

    for(y=0; y<BOARD_HEIGHT; y++){

        for(x=0; x<BOARD_WIDTH; x++){

            if( board[y][x] != board_buffer[y][x] ){

                gotoxy(2*(start_x+x), start_y+y);

                drawTile(board_buffer[y][x]);

                board[y][x] = board_buffer[y][x];

            }

        }

    }

}


void drawBlock(TBlock *block)

{

    int x = block->vector[0].X, y = block->vector[0].Y;

    int i=0;

    gotoxy(2*(start_x+x),start_y+y);

    drawTile(block->type);

    for(i=1; i<block->len; i++){

        x = block->vector[0].X+block->vector[i].X;

        y = block->vector[0].Y+block->vector[i].Y;

        gotoxy(2*(start_x+x),start_y+y);

        drawTile(block->type);

    }

}


void eraseBlock(TBlock *block)

{

    int x = block->vector[0].X, y = block->vector[0].Y;

    int i=0;

    gotoxy(2*(start_x+x),start_y+y);

    drawTile(BLANK);

    for(i=1; i<block->len; i++){

        x = block->vector[0].X+block->vector[i].X;

        y = block->vector[0].Y+block->vector[i].Y;

        gotoxy(2*(start_x+x),start_y+y);

        drawTile(BLANK);

    }

}

void drawPreview(int n){


    TBlock block;

    int i=0;

    for(i=0; i<n; i++){

        eraseBlock(&block_list[i]);

        block_list[i].vector[0] = {BOARD_WIDTH+start_x+3, i*3};

        drawBlock(&block_list[i]);

    }

}

void makeBlock(int nBlock, TBlock *block)

{

    static COORD block_init[7][3] = {

        {   // I_BLOCK

            {-1,0},{1,0},{2,0},

        },

        {   // J_BLOCK

            {-1,0},{1,0},{1,1},

        },

        {   // L_BLOCK

            {-1,0},{1,0},{-1,1},

        },

        {   // O_BLOCK

            {1,0},{0,1},{1,1},

        },

        {   // S_BLOCK

            {1,0},{-1,1},{0,1},

        },

        {   // T_BLOCK

            {-1,0},{1,0},{0,1},

        },

        {   // Z_BLOCK

            {-1,0},{0,1},{1,1},

        },

    };

    int i=0;

    block->vector[0] = {2*(start_x+BOARD_WIDTH)+4, 7*3};

    block->len = 4;

    block->type = nBlock;

    for(i=0; i<3; i++){

        block->vector[i+1] = block_init[nBlock-1][i];

    }

}

void drawStatitic(void){


    static char fcolor_set[10] = {BLACK, RED, BLUE, WHITE, YELLOW, VIOLET, SKY_BLUE, GREEN };

    static char tetrinomix[7] = {'I','J','L','O','S','T','Z'};

    int i=1;

    for(i=0; i<7; i++){

        gotoxy(2*BOARD_WIDTH+6,17+i);

        setColor(fcolor_set[i+1],BLACK);

        printf("%c -%3d",tetrinomix[i],statitics[i]);

    }

}

void generate_block(TBlock *block)

{

    static TBlock buf[MAX_BLOCK_LIST];

    memcpy(buf,block_list,sizeof(TBlock)*MAX_BLOCK_LIST);

    *block = block_list[0];

    eraseBlock(block);

    block->vector[0] = {4, 2};

    statitics[block->type-1]++;

    memcpy(block_list,buf+1,sizeof(TBlock)*(MAX_BLOCK_LIST-1));

    makeBlock(rand()%7+1, &block_list[MAX_BLOCK_LIST-1]);

    drawStatitic();

}

void moveDown(int y, int n){

    memcpy( board_buffer[y+n], board_buffer[y], sizeof(char)*10 );

    memset( board_buffer[y], 0, sizeof(char)*10 );

}

void destoryLine(){

    static int line[4];

    int x,y;

    int score;

    static int n=0,i=0;

    bool flag = true;

    for(i=0; i<n; i++){

        gotoxy(2*(BOARD_WIDTH+start_x)+4,line[i]+start_y);

        printf(" ");

    }

    n=0;

    for(y=BOARD_HEIGHT-1; y>=0; y--){

        flag = true;

        for(x=0; x<BOARD_WIDTH; x++){

            if(board[y][x] == BLANK){

                flag = false;

                break;

            }

        }

        if(flag)

            line[n++] = y;

    }

    for(i=0; i<n; i++){

        gotoxy(2*(BOARD_WIDTH+start_x)+4,line[i]+start_y);

        printf("<");

    }

    for(i=n-1; i>=0; i--){

        for(y=line[i]; y>0; y--){

            moveDown(y-1,1);

        }

    }

    if(n){

        gLines += n;

        score = 100*n*tspin_weight+(n==4?600:0);

        gScore += score;

        drawScore(2*BOARD_WIDTH+18,18);

        setColor(RED,BLACK);

        gotoxy(2*BOARD_WIDTH+22,19); printf("(+%4d)",score);

    }

    tspin_weight = 1;

}


bool isCrash(TBlock block){

    int i=0;

    int x,y;

    x = block.vector[0].X;

    y = block.vector[0].Y;


    if( board_buffer[y][x] != BLANK ){

        gotoxy(45,1);printf("point :%2d,%2d",x,y);

        return true;

    }

    if( x<0 || x>=BOARD_WIDTH || y<0 || y>=BOARD_HEIGHT ){

        gotoxy(45,1);printf("point :%2d,%2d",x,y);

        return true;

    }

    for( i=1; i<block.len; i++){

        x = block.vector[0].X;

        y = block.vector[0].Y;

        x += block.vector[i].X;

        y += block.vector[i].Y;

        if( board_buffer[y][x] != BLANK ){

            gotoxy(45,1);printf("point :%2d,%2d",x,y);

            return true;

        }

        if( x<0 || x>=BOARD_WIDTH || y<0 || y>=BOARD_HEIGHT ){

            gotoxy(45,1);printf("point :%2d,%2d",x,y);

            return true;

        }

    }

    return false;

}

bool checkCrash(TBlock block, int dir){

    switch(dir){

    case VK_RIGHT:

        block.vector[0].X++;

        break;

    case VK_LEFT:

        block.vector[0].X--;

        break;

    case VK_DOWN:

        block.vector[0].Y++;

        break;

    }

    return isCrash(block);

}

int rotate(TBlock *block){

    static COORD block_rotate[7][4][3] = {

        {   // I_BLOCK

            {{-1,0},{1,0},{2,0}},

            {{0,-1},{0,1},{0,2}},

            {{-2,0},{-1,0},{1,0}},

            {{0,-2},{0,-1},{0,1}}

        },

        {   // J_BLOCK

            {{-1,0},{1,0},{1,1}},

            {{0,-1},{-1,1},{0,1}},

            {{-1,-1},{-1,0},{1,0}},

            {{0,-1},{1,-1},{0,1}}

        },

        {   // L_BLOCK

            {{-1,0},{1,0},{-1,1}},

            {{-1,-1},{0,-1},{0,1}},

            {{-1,0},{1,0},{1,-1}},

            {{0,-1},{0,1},{1,1}}

        },

        {   // O_BLOCK

            {{1,0},{0,1},{1,1}},

            {{1,0},{0,1},{1,1}},

            {{1,0},{0,1},{1,1}},

            {{1,0},{0,1},{1,1}}

        },

        {   // S_BLOCK

            {{1,0},{-1,1},{0,1}},

            {{-1,-1},{-1,0},{0,1}},

            {{1,0},{-1,1},{0,1}},

            {{-1,-1},{-1,0},{0,1}}

        },

        {   // T_BLOCK

            {{-1,0},{1,0},{0,1}},

            {{0,-1},{-1,0},{0,1}},

            {{0,-1},{-1,0},{1,0}},

            {{0,-1},{1,0},{0,1}}

        },

        {   // Z_BLOCK

            {{-1,0},{0,1},{1,1}},

            {{1,-1},{1,0},{0,1}},

            {{-1,0},{0,1},{1,1}},

            {{1,-1},{1,0},{0,1}}

        },

    };

    static int a = 0;

    TBlock blockBuf = *block;


    int state = (block->state+1)%4;

    int type = blockBuf.type;

    int i=0;

    for(i=1; i<blockBuf.len; i++){

        blockBuf.vector[i] = block_rotate[type-1][state][i-1];

    }

    if(!isCrash(blockBuf)){

        blockBuf.state = state;

        *block = blockBuf;

        gotoxy(27,0); printf("rotate : success");

        return 1;

    }

    else{

        blockBuf.state = state;

        gotoxy(27,0); printf("rotate : fail   ");


        if( type == T_BLOCK && checkCrash(blockBuf, VK_DOWN)){

            if( rotation_opt == CLOCKWISE && state == 3 ){

                blockBuf.vector[0].X--;

                blockBuf.vector[0].Y += 2;

            }

            else if( rotation_opt == CLOCKWISE && state == 2){

                blockBuf.vector[0].X--;

                blockBuf.vector[0].Y++;

            }

            else if( rotation_opt == COUNTER_CLOCKWISE && state == 1){

                blockBuf.vector[0].X++;

                blockBuf.vector[0].Y += 2;

            }

            else if( rotation_opt == COUNTER_CLOCKWISE && state == 2){

                blockBuf.vector[0].X++;

                blockBuf.vector[0].Y++;

            }

            if(!isCrash(blockBuf)){

                blockBuf.state = state;

                *block = blockBuf;

                setColor(RED,BLACK);

                gotoxy(25,0); printf("rotate : T-spin");

                tspin_weight = 3;

                setColor(GRAY,BLACK);

                return 1;

            }

        }


    }

    gotoxy(27,1); printf(" state : %d,%2d", state,a++);

    gLatency = LATENCY;

    return 0;

}

int key_handler(){

    static char *cKey[2]={"down","up  "};

    SHORT key;

    int nKey;

    static int vkey[] = {VK_ESCAPE,VK_SPACE,VK_LEFT,VK_RIGHT,VK_DOWN,VK_UP};

    for(nKey=0; nKey<6; nKey++){

        key = GetAsyncKeyState(vkey[nKey]);

        gotoxy(60,0); printf("key : %s",cKey  [(key&0x8000)>0?0:1]);

        if( 0x8000 & key ){

            return vkey[nKey];

        }

    }

    return 0;

}


int key_wait(){

    static clock_t moveCooldown = 0;

    static clock_t spaceCooldown = 0;

    static clock_t rotateCooldown = 0;

    static clock_t begin = clock();

    static clock_t end = clock();

    static clock_t latency = 0;

    int key = 0;

    gotoxy(45,2); printf("begin :%5d", begin);

    gotoxy(45,3); printf("  end :%5d", end);

    while( (end-begin) < gSpeed+gLatency ){

        end = clock();

        gotoxy(60,2); printf("time :%5d", (int)end);

        gotoxy(60,3); printf("tick :%5d ", (int)(end-begin));

        if( clock() - moveCooldown > gMoveDelay ){

            key = key_handler();

            moveCooldown = clock();

        }

        if( key == VK_UP ){

            if( clock() - rotateCooldown < gRotateDelay ){

                key = 0;

            }

            else{

                rotateCooldown = clock();

            }

        }

        if( key == VK_SPACE ){

            begin = clock();

            end = clock();

            if( clock() - spaceCooldown < gSpaceDelay ){

                key = 0;

            }

            else{

                spaceCooldown = clock();

            }

        }

        if(key!=0){

            gLatency = 0;

            return key;

        }

    }

    begin = clock();

    end = clock();

    gLatency = 0;

    return VK_DOWN;

}

void solid(TBlock block){

    int x = block.vector[0].X, y = block.vector[0].Y;

    int i=0;

    int type = block.type;

    board_buffer[y][x] = type;

    for(i=1; i < block.len; i++){

        x = block.vector[0].X+block.vector[i].X;

        y = block.vector[0].Y+block.vector[i].Y;

        board_buffer[y][x] = type;

    }

}

void initBlockList(){

    int i=0;

    for(i=0; i<MAX_BLOCK_LIST; i++){

        makeBlock(rand()%7+1, &block_list[i]);

        block_list[i].vector[0] = {BOARD_WIDTH+start_x+3, i*3};

    }

}

void drawGhost(TBlock block){

    static TBlock ghost;

    ghost = block;

    while(!isCrash(ghost)){

        ghost.vector[0].Y++;

    }

    ghost.vector[0].Y--;

    int x = ghost.vector[0].X, y = ghost.vector[0].Y;

    int i=0;

    gotoxy(2*(start_x+x),start_y+y);

    setColor(GRAY, BLACK);

    printf("■");

    for(i=1; i<block.len; i++){

        x = ghost.vector[0].X+ghost.vector[i].X;

        y = ghost.vector[0].Y+ghost.vector[i].Y;

        gotoxy(2*(start_x+x),start_y+y);

        printf("■");

    }

}

void eraseGhost(TBlock block){

    static TBlock ghost;

    ghost = block;

    while(!isCrash(ghost)){

        ghost.vector[0].Y++;

    }

    ghost.vector[0].Y--;

    int x = ghost.vector[0].X, y = ghost.vector[0].Y;

    int i=0;

    gotoxy(2*(start_x+x),start_y+y);

    setColor(GRAY, BLACK);

    printf("  ");

    for(i=1; i<block.len; i++){

        x = ghost.vector[0].X+ghost.vector[i].X;

        y = ghost.vector[0].Y+ghost.vector[i].Y;

        gotoxy(2*(start_x+x),start_y+y);

        printf("  ");

    }

}

int main(int argc, char *argv[])

{

    int x, y;

    srand(time(NULL));

    drawFrame();

    drawInfo();

    initBlockList();

    cursorToggle(false);

    for(y=0; y<20; y++){

        for(x=0; x<10; x++){

            board_buffer[y][x]=BLANK;

            board[y][x]=BLANK;

        }

    }


    TBlock curBlock;

    SHORT key;

    drawScore(2*BOARD_WIDTH+18,18);

    drawPreview(5);

    generate_block(&curBlock);

    drawGhost(curBlock);

    drawBlock(&curBlock);

    while(1){


        if(isCrash(curBlock)){

            break;

        }

//        drawDebug(board,-5);

//        drawDebug(board_buffer,16);

        if((key = key_wait())){

            gotoxy(60,1); printf("key : 0x%X",key);

            if( checkCrash(curBlock, key) ){

                if( key == VK_DOWN ){

//                    if(clock()-gMovedBlock < 900) continue;

                    solid(curBlock);

                    drawBlock(&curBlock);

                    drawBoard();

                    destoryLine();

                    drawBoard();

                    drawPreview(5);

                    generate_block(&curBlock);

                    drawGhost(curBlock);

                }

                gotoxy(45,0); printf("crash  ");

                continue;

            }

            else{

                gotoxy(45,0); printf("movable");

            }

            switch(key){

            case VK_UP:

                eraseBlock(&curBlock);

                eraseGhost(curBlock);

                rotate(&curBlock);

//                gMovedBlock = clock();

                break;

            case VK_RIGHT:

                eraseBlock(&curBlock);

                eraseGhost(curBlock);

                curBlock.vector[0].X++;

//                gMovedBlock = clock();

                break;

            case VK_LEFT:

                eraseBlock(&curBlock);

                eraseGhost(curBlock);

                curBlock.vector[0].X--;

//                gMovedBlock = clock();

                break;

            case VK_DOWN:

                eraseBlock(&curBlock);

                eraseGhost(curBlock);

                curBlock.vector[0].Y++;

//                gMovedBlock = clock();

                break;

            case VK_SPACE:

                eraseBlock(&curBlock);

                while(!checkCrash(curBlock,VK_DOWN)){

                    curBlock.vector[0].Y++;

                }

                eraseGhost(curBlock);

                solid(curBlock);

                drawBlock(&curBlock);

                drawBoard();

                destoryLine();

                drawPreview(5);

                generate_block(&curBlock);

                break;

            }

            drawBoard();

            drawGhost(curBlock);

            drawBlock(&curBlock);

        }

    }


    gotoxy(30,1);

    setColor(YELLOW,BLACK);

    printf(" - game over - ");

    gotoxy(0,21);

    setColor(GRAY,BLACK);

    system("pause>nul");

}



Posted by Нуеоп