用C写的俄罗斯方块游戏 By: hoodlum1980 编程论坛

时间:2023-03-09 18:46:50
用C写的俄罗斯方块游戏  By:    hoodlum1980   编程论坛
/************************************
* Desc: 俄罗斯方块游戏
* By: hoodlum1980
* Email: jinfd@126.com
* Date: 2008.03.12 22:30
************************************/
#include <stdio.h>
#include <bios.h>
#include <dos.h>
#include <graphics.h>
#include <string.h>
#include <stdlib.h>
#define true 1
#define false 0
#define BoardWidth 12
#define BoardHeight 23
#define _INNER_HELPER /*inner helper method */
/*Scan Codes Define*/
enum KEYCODES
{
K_ESC =0x011b,
K_UP =0x4800, /* upward arrow */
K_LEFT =0x4b00,
K_DOWN =0x5000,
K_RIGHT =0x4d00,
K_SPACE =0x3920,
K_P =0x1970
}; /* the data structure of the block */
typedef struct tagBlock
{
char c[][]; /* cell fill info array, 0-empty, 1-filled */
int x; /* block position cx [ 0,BoardWidht -1] */
int y; /* block position cy [-4,BoardHeight-1] */
char color; /* block color */
char size; /* block max size in width or height */
char name; /* block name (the block's shape) */
} Block; /* game's global info */
int FrameTime= ;
int CellSize= ;
int BoardLeft= ;
int BoardTop= ; /* next block grid */
int NBBoardLeft= ;
int NBBoardTop= ;
int NBCellSize= ; /* score board position */
int ScoreBoardLeft= ;
int ScoreBoardTop=;
int ScoreBoardWidth=;
int ScoreBoardHeight=;
int ScoreColor=LIGHTCYAN; /* infor text postion */
int InfoLeft=;
int InfoTop=;
int InfoColor=YELLOW; int BorderColor=DARKGRAY;
int BkGndColor=BLACK;
int GameRunning=true;
int TopLine=BoardHeight-; /* top empty line */
int TotalScore=;
char info_score[];
char info_help[];
char info_common[]; /* our board, Board[x][y][0]-isFilled, Board[x][y][1]-fillColor */
unsigned char Board[BoardWidth][BoardHeight][];
char BufferCells[][]; /* used to judge if can rotate block */
Block curBlock; /* current moving block */
Block nextBlock; /* next Block to appear */ /* function list */
int GetKeyCode();
int CanMove(int dx,int dy);
int CanRotate();
int RotateBlock(Block *block);
int MoveBlock(Block *block,int dx,int dy);
void DrawBlock(Block *block,int,int,int);
void EraseBlock(Block *block,int,int,int);
void DisplayScore();
void DisplayInfo(char* text);
void GenerateBlock(Block *block);
void NextBlock();
void InitGame();
int PauseGame();
void QuitGame(); /*Get Key Code */
int _INNER_HELPER GetKeyCode()
{
int key=;
if(bioskey())
{
key=bioskey();
}
return key;
} /* display text! */
void _INNER_HELPER DisplayInfo(char *text)
{
setcolor(BkGndColor);
outtextxy(InfoLeft,InfoTop,info_common);
strcpy(info_common,text);
setcolor(InfoColor);
outtextxy(InfoLeft,InfoTop,info_common);
} /* create a new block by key number,
* the block anchor to the top-left corner of 4*4 cells
*/
void _INNER_HELPER GenerateBlock(Block *block)
{
int key=(random()*random()+random()+random())%;
block->size=;/* because most blocks' size=3 */
memset(block->c,,);
switch(key)
{
case :
block->name='T';
block->color=RED;
block->c[][]=;
block->c[][]=, block->c[][]=;
block->c[][]=;
break;
case :
block->name='L';
block->color=YELLOW;
block->c[][]=;
block->c[][]=;
block->c[][]=, block->c[][]=;
break;
case :
block->name='J';
block->color=LIGHTGRAY;
block->c[][]=;
block->c[][]=;
block->c[][]=, block->c[][]=;
break;
case :
block->name='z';
block->color=CYAN;
block->c[][]=, block->c[][]=;
block->c[][]=, block->c[][]=;
break;
case :
block->name='';
block->color=LIGHTBLUE;
block->c[][]=, block->c[][]=;
block->c[][]=, block->c[][]=;
break;
case :
block->name='o';
block->color=BLUE;
block->size=;
block->c[][]=, block->c[][]=;
block->c[][]=, block->c[][]=;
break;
case :
block->name='I';
block->color=GREEN;
block->size=;
block->c[][]=;
block->c[][]=;
block->c[][]=;
block->c[][]=;
break;
}
} /* get next block! */
void NextBlock()
{
/* copy the nextBlock to curBlock */
curBlock.size=nextBlock.size;
curBlock.color=nextBlock.color;
curBlock.x=(BoardWidth-)/;
curBlock.y=-curBlock.size;
memcpy(curBlock.c,nextBlock.c,);
/* generate nextBlock and show it */
EraseBlock(&nextBlock,NBBoardLeft,NBBoardTop,NBCellSize);
GenerateBlock(&nextBlock);
nextBlock.x=,nextBlock.y=;
DrawBlock(&nextBlock,NBBoardLeft,NBBoardTop,NBCellSize);
} /* rotate the block, update the block struct data */
int _INNER_HELPER RotateCells(char c[][],char blockSize)
{
char temp,i,j;
switch(blockSize)
{
case :
temp=c[][];
c[][]=c[][], c[][]=c[][], c[][]=c[][], c[][]=temp;
temp=c[][];
c[][]=c[][], c[][]=c[][], c[][]=c[][], c[][]=temp;
break;
case : /* only 'I' block arived here! */
c[][]=-c[][], c[][]=-c[][], c[][]=-c[][];
c[][]=-c[][], c[][]=-c[][], c[][]=-c[][];
break;
}
} /* judge if the block can move toward the direction */
int CanMove(int dx,int dy)
{
int i,j,tempX,tempY;
for(i=;i<curBlock.size;i++)
{
for(j=;j<curBlock.size;j++)
{
if(curBlock.c[i][j])
{
/* cannot move leftward or rightward */
tempX = curBlock.x + i + dx;
if(tempX< || tempX>(BoardWidth-)) return false; /* make sure x is valid! */
/* cannot move downward */
tempY = curBlock.y + j + dy;
if(tempY>(BoardHeight-)) return false; /* y is only checked lower bound, maybe negative!!!! */
/* the cell already filled, we must check Y's upper bound before check cell ! */
if(tempY>= && Board[tempX][tempY][]) return false;
}
}
}
return true;
} /* judge if the block can rotate */
int CanRotate()
{
int i,j,tempX,tempY;
/* update buffer */
memcpy(BufferCells, curBlock.c, );
RotateCells(BufferCells,curBlock.size);
for(i=;i<curBlock.size;i++)
{
for(j=;j<curBlock.size;j++)
{
if(BufferCells[i][j])
{
tempX=curBlock.x+i;
tempY=curBlock.y+j;
if(tempX< || tempX>(BoardWidth-))
return false;
if(tempY>(BoardHeight-))
return false;
if(tempY>= && Board[tempX][tempY][])
return false;
}
}
}
return true;
} /* draw the block */
void _INNER_HELPER DrawBlock(Block *block,int bdLeft,int bdTop,int cellSize)
{
int i,j;
setfillstyle(SOLID_FILL,block->color);
for(i=;i<block->size;i++)
{
for(j=;j<block->size;j++)
{
if(block->c[i][j] && (block->y+j)>=)
{
floodfill(
bdLeft+cellSize*(i+block->x)+cellSize/,
bdTop+cellSize*(j+block->y)+cellSize/,
BorderColor);
}
}
}
} /* Rotate the block, if success, return true */
int RotateBlock(Block *block)
{
char temp,i,j;
int b_success;
if(block->size==)
return true;
if(( b_success=CanRotate()))
{
EraseBlock(block,BoardLeft,BoardTop,CellSize);
memcpy(curBlock.c,BufferCells,);
DrawBlock(block,BoardLeft,BoardTop,CellSize);
}
return b_success;
} /* erase a block, only fill the filled cell with background color */
void _INNER_HELPER EraseBlock(Block *block,int bdLeft,int bdTop,int cellSize)
{
int i,j;
setfillstyle(SOLID_FILL,BkGndColor);
for(i=;i<block->size;i++)
{
for(j=;j<block->size;j++)
{
if(block->c[i][j] && (block->y+j>=))
{
floodfill(
bdLeft+cellSize*(i+block->x)+cellSize/,
bdTop+cellSize*(j+block->y)+cellSize/,
BorderColor);
}
}
}
} /* move by the direction if can, donothing if cannot
* return value: true - success, false - cannot move toward this direction
*/
int MoveBlock(Block *block,int dx,int dy)
{
int b_canmove=CanMove(dx,dy);
if(b_canmove)
{
EraseBlock(block,BoardLeft,BoardTop,CellSize);
curBlock.x+=dx;
curBlock.y+=dy;
DrawBlock(block,BoardLeft,BoardTop,CellSize);
}
return b_canmove;
} /* drop the block to the bottom! */
int DropBlock(Block *block)
{
EraseBlock(block,BoardLeft,BoardTop,CellSize);
while(CanMove(,))
{
curBlock.y++;
}
DrawBlock(block,BoardLeft,BoardTop,CellSize);
return ;/* return value is assign to the block's alive */
} /* init the graphics mode, draw the board grid */
void InitGame()
{
int i,j,gdriver=DETECT,gmode;
struct time sysTime;
/* draw board cells */
memset(Board,,BoardWidth*BoardHeight*);
memset(nextBlock.c,,);
strcpy(info_help,"P: Pause Game. --by hoodlum1980");
initgraph(&gdriver,&gmode,"");
setcolor(BorderColor);
for(i=;i<=BoardWidth;i++)
{
line(BoardLeft+i*CellSize, BoardTop, BoardLeft+i*CellSize, BoardTop+ BoardHeight*CellSize);
}
for(i=;i<=BoardHeight;i++)
{
line(BoardLeft, BoardTop+i*CellSize, BoardLeft+BoardWidth*CellSize, BoardTop+ i*CellSize);
}
/* draw board outer border rect */
rectangle(BoardLeft-CellSize/, BoardTop-CellSize/,
BoardLeft+BoardWidth*CellSize+CellSize/,
BoardTop+BoardHeight*CellSize+CellSize/); /* draw next block grids */
for(i=;i<=;i++)
{
line(NBBoardLeft+i*NBCellSize, NBBoardTop, NBBoardLeft+i*NBCellSize, NBBoardTop+*NBCellSize);
line(NBBoardLeft, NBBoardTop+i*NBCellSize, NBBoardLeft+*NBCellSize, NBBoardTop+ i*NBCellSize);
} /* draw score rect */
rectangle(ScoreBoardLeft,ScoreBoardTop,ScoreBoardLeft+ScoreBoardWidth,ScoreBoardTop+ScoreBoardHeight);
DisplayScore(); /* set new seed! */
gettime(&sysTime);
srand(sysTime.ti_hour*+sysTime.ti_min*+sysTime.ti_sec); GenerateBlock(&nextBlock);
NextBlock(); /* create first block */
setcolor(DARKGRAY);
outtextxy(InfoLeft,InfoTop+,"Up -rotate Space-drop");
outtextxy(InfoLeft,InfoTop+,"Left-left Right-right");
outtextxy(InfoLeft,InfoTop+,"Esc -exit");
DisplayInfo(info_help);
} /* set the isFilled and fillcolor data to the board */
void _INNER_HELPER FillBoardData()
{
int i,j;
for(i=;i<curBlock.size;i++)
{
for(j=;j<curBlock.size;j++)
{
if(curBlock.c[i][j] && (curBlock.y+j)>=)
{
Board[curBlock.x+i][curBlock.y+j][]=;
Board[curBlock.x+i][curBlock.y+j][]=curBlock.color;
}
}
}
} /* draw one line of the board */
void _INNER_HELPER PaintBoard()
{
int i,j,fillcolor;
for(j=max((TopLine-),);j<BoardHeight;j++)
{
for(i=;i<BoardWidth;i++)
{
fillcolor=Board[i][j][]? Board[i][j][]:BkGndColor;
setfillstyle(SOLID_FILL,fillcolor);
floodfill(BoardLeft+i*CellSize+CellSize/,BoardTop+j*CellSize+CellSize/,BorderColor);
}
}
} /* check if one line if filled full and increase the totalScore! */
void _INNER_HELPER CheckBoard()
{
int i,j,k,score=,sum=,topy,lines=;
/* we find the top empty line! */
j=topy=BoardHeight-;
do
{
sum=;
for(i=;i< BoardWidth; i++)
{
sum+=Board[i][topy][];
}
topy--;
} while(sum> && topy>); /* remove the full filled line (max remove lines count = 4) */
do
{
sum=;
for(i=;i< BoardWidth; i++)
sum+=Board[i][j][]; if(sum==BoardWidth)/* we find this line is full filled, remove it! */
{
/* move the cells data down one line */
for(k=j; k > topy;k--)
{
for(i=;i<BoardWidth;i++)
{
Board[i][k][]=Board[i][k-][];
Board[i][k][]=Board[i][k-][];
}
}
/*make the top line empty! */
for(i=;i<BoardWidth;i++)
{
Board[i][topy][]=;
Board[i][topy][]=;
}
topy++; /* move the topline downward one line! */
lines++; /* lines <=4 */
TotalScore+=score;
score*=; /* adding: 10, 30, 70, 150 */
}
else
j--;
} while(sum> && j>topy && lines<);
/* speed up the game when score is high, minimum is 400 */
FrameTime=max(-*(TotalScore/), );
TopLine=topy;/* update the top line */
/* if no lines remove, only add 1: */
if(lines==)
TotalScore++;
} /* display the score */
void _INNER_HELPER DisplayScore()
{
setcolor(BkGndColor);
outtextxy(ScoreBoardLeft+,ScoreBoardTop+,info_score);
setcolor(ScoreColor);
sprintf(info_score,"Score: %d",TotalScore);
outtextxy(ScoreBoardLeft+,ScoreBoardTop+,info_score);
} /* we call this function when a block is inactive. */
void UpdateBoard()
{
FillBoardData();
CheckBoard();
PaintBoard();
DisplayScore();
} /* pause the game, and timer handler stop move down the block! */
int PauseGame()
{
int key=;
DisplayInfo("Press P to Start or Resume!");
while(key!=K_P && key!=K_ESC)
{
while(!(key=GetKeyCode())){}
}
DisplayInfo(info_help);
return key;
} /* quit the game and do cleaning work. */
void QuitGame()
{
closegraph();
} /* the entry point function. */
void main()
{
int i,flag=,j,key=,tick=;
InitGame();
if(PauseGame()==K_ESC)
goto GameOver;
/* wait until a key pressed */
while(key!=K_ESC)
{
/* wait until a key pressed */
while(!(key=GetKeyCode()))
{
tick++;
if(tick>=FrameTime)
{
/* our block has dead! (can't move down), we get next block */
if(!MoveBlock(&curBlock,,))
{
UpdateBoard();
NextBlock();
if(!CanMove(,))
goto GameOver;
}
tick=;
}
delay();
}
switch(key)
{
case K_LEFT:
MoveBlock(&curBlock,-,);
break;
case K_RIGHT:
MoveBlock(&curBlock,,);
break;
case K_DOWN:
MoveBlock(&curBlock,,);
break;
case K_UP:
RotateBlock(&curBlock);
break;
case K_SPACE:
DropBlock(&curBlock);
break;
case K_P:
PauseGame();
break;
}
}
GameOver:
DisplayInfo("GAME OVER! Press any key to exit!");
getch(); /* wait the user Press any key. */
QuitGame();
}