#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <windows.h>
#define TETRIS_COLS 12
#define TETRIS_ROWS 16
#define WALL 1 // 墙壁
#define BLOCK 2 // 方块
#define BLOCK_COLS 4
#define BLOCK_SHAPE 4
#define FIXEDBLOCK 3 // 固定方块
// 全局的绘制内存数组
unsigned char g_uchMaxTetris[TETRIS_ROWS][TETRIS_COLS] = {0};
unsigned char g_uchBlock[][BLOCK_COLS] = {
/*
■■ ■■■■ ■ ■■■
■■ ■■■ ■
1代表方块 0代表空格
每个方块有四种形状
*/
/*
■■
■■
*/
1,1,0,0,
1,1,0,0,
0,0,0,0,
0,0,0,0,
1,1,0,0,
1,1,0,0,
0,0,0,0,
0,0,0,0,
1,1,0,0,
1,1,0,0,
0,0,0,0,
0,0,0,0,
1,1,0,0,
1,1,0,0,
0,0,0,0,
0,0,0,0,
/*
■■■■
*/
1,1,1,1,
0,0,0,0,
0,0,0,0,
0,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,1,1,1,
0,0,0,0,
0,0,0,0,
0,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
/*
■
■■■
*/
0,1,0,0,
1,1,1,0,
0,0,0,0,
0,0,0,0,
0,1,0,0,
1,1,0,0,
0,1,0,0,
0,0,0,0,
1,1,1,0,
0,1,0,0,
0,0,0,0,
0,0,0,0,
0,1,0,0,
0,1,1,0,
0,1,0,0,
0,0,0,0,
/*
■■■
■
*/
1,1,1,0,
0,0,1,0,
0,0,0,0,
0,0,0,0,
0,1,0,0,
0,1,0,0,
1,1,0,0,
0,0,0,0,
1,0,0,0,
1,1,1,0,
0,0,0,0,
0,0,0,0,
1,1,0,0,
1,0,0,0,
1,0,0,0,
0,0,0,0
};
int g_nBlockOffset = 0; // 当前的方块的偏移
int g_nBlockRow = 0; // 当前的方块在主游戏区域的行
int g_nBlockCol = 0; // 当前的方块在主游戏区域的列
int g_nBlockType = 0; // 第几种方块
int g_nBlockShape = 0; // 第几种形状
/*
绘制墙壁
*/
int InitWall();
/*
显示UI
*/
int ShowUI();
/*
开始游戏
*/
int StartGame();
/*
产生方块
*/
int CreateBlock();
/*
方块旋转
*/
int BlockRoate();
/*
方块向下移动
*/
int BlockMoveDown();
/*
方块向左移动
*/
int BlockMoveLeft();
/*
方块向右移动
*/
int BlockMoveRight();
/*
把小方块写入主游戏区域
*/
int BlockWriteToMaxTetris();
/*
清除页面中的小方块
*/
int ClearBlockFromMaxTetris();
/*
判断能否移动或者旋转
*/
int IsMoveOrRotate(int nBlockRow,int nBlockCol,int nBlockOffset);
/*
清除一条方块
*/
int IsCanClearOneLineFormMaxTetris();
/*
判断游戏是否结束
*/
int IsGameOver();
/*
绘制墙壁
*/
int InitWall(){
// 遍历二维数组
int i = 0,j = 0;
// 循环赋值墙壁
for(i = 0; i < TETRIS_ROWS; i++){
for(j = 0; j < TETRIS_COLS; j++){
if(j == 0 || j == TETRIS_COLS-1
|| i == TETRIS_ROWS - 1){
// 如果是左右下边界
g_uchMaxTetris[i][j] = WALL;
}
}
}
return 0;
}
/*
显示UI
*/
int ShowUI(){
system("cls");
// 遍历二维数组
int i = 0,j = 0;
// 循环绘制墙壁
for(i = 0; i < TETRIS_ROWS; i++){
for(j = 0; j < TETRIS_COLS; j++){
// 如果是墙壁
if(g_uchMaxTetris[i][j] == WALL ||
g_uchMaxTetris[i][j] == BLOCK ||
g_uchMaxTetris[i][j] == FIXEDBLOCK){
// 绘制墙壁
printf("■");
}else{
// 不是墙壁的就画空格
printf(" ");
}
}
printf("\r\n");
}
return 0;
}
/*
把小方块写入主游戏区域
*/
int BlockWriteToMaxTetris(){
// 遍历方块
int i = 0,j = 0;
for(i = 0; i < BLOCK_COLS; i++){
for(j = 0; j < BLOCK_COLS; j++){
// 把正中间的前四行拿来绘制方块
if(g_uchBlock[i][j+g_nBlockOffset] == 1){
// 找到对应的方块形状 如果为1就把对应的背景设置为方块
g_uchMaxTetris[i+g_nBlockRow][j+g_nBlockCol] = BLOCK;
}
}
}
return 0;
}
/*
产生方块
*/
int CreateBlock(){
// 随机生成行和列 即第几种方块 第几种形状
g_nBlockType = (int)(rand() % BLOCK_SHAPE);
g_nBlockShape = (int)(rand() % BLOCK_SHAPE);
// 因为我们是把方块都直接丢到一个二维数组中的 所以我们要通过偏移来找到
// 不然只能用三维数组 太麻烦了
/*
nBlockType * BLOCK_COLS
定位到第几个方块 第一个方块就是 就是 0 * 16 * 4
第二个方块就是1 * 16 * 4
.....
定位到这个方块的第几种形状
第一个形状 + 0 * 16
第二个形状 + 1 * 16
第三个形状 + 2 * 16
*/
g_nBlockOffset = g_nBlockType * 16 * BLOCK_COLS + g_nBlockShape * 16;
// 初始化的时候在主游戏区域的行和列
g_nBlockRow = 0;
g_nBlockCol = TETRIS_COLS / 2 - 2;
// 写入方块到大数组中
if(BlockWriteToMaxTetris() == EOF){
return EOF;
}
return 0;
}
/*
清除页面中的小方块
*/
int ClearBlockFromMaxTetris(){
// 遍历二维数组
int i = 0,j = 0;
// 循环清除方块
for(i = 0; i < BLOCK_COLS; i++){
for(j = 0; j < BLOCK_COLS; j++){
// 如果是小方块实实在在存在的 并且 在主游戏区域中是方块
if(g_uchBlock[i][j+g_nBlockOffset] == 1
&& g_uchMaxTetris[i+g_nBlockRow][j+g_nBlockCol] == BLOCK){
g_uchMaxTetris[i+g_nBlockRow][j+g_nBlockCol] = 0; // 恢复成默认的
}
}
}
return 0;
}
/*
判断游戏是否结束
*/
int IsGameOver(){
int j = 0;
for(j = 0; j < TETRIS_COLS; j++){
if(g_uchMaxTetris[0][j] == FIXEDBLOCK){ // 第一行有一个固定方块就游戏结束
return 1;
}
}
return 0;
}
/*
清除一条方块
*/
int IsCanClearOneLineFormMaxTetris(){
// 遍历二维数组
int i = 0,j = 0;
int k = 0,l = 0;
int fixedBlockNum = 0;
// 判断是否满了 遍历所有的二维数组行和列
for(i = 0; i < TETRIS_ROWS; i++){
fixedBlockNum = 0;
for(j = 0; j < TETRIS_COLS; j++){
// 如果是底部是固定方块
if(g_uchMaxTetris[i][j] == FIXEDBLOCK){
// 加加
fixedBlockNum++; // 如果是固定方块++
}
}
if(fixedBlockNum == TETRIS_COLS-2){ // 如果这一行满了
if(i != 0){
// 满了一行 清除
// 从要清除这行开始 从后往前 复制 把上面的复制下来 就相当于把当前这一行覆盖了
for(k = i; k >= 0; k--){
for(l = 0; l < TETRIS_COLS; l++){
if(g_uchMaxTetris[k][l] == FIXEDBLOCK || g_uchMaxTetris[k][l] == 0){
g_uchMaxTetris[k][l] = g_uchMaxTetris[k-1][l]; // 把上一行的拷贝下来
}
}
}
}else{
// 如果是第一行就直接设置为0 因为第一行上面没有行了
for(j = 0; j < TETRIS_COLS; j++){
g_uchMaxTetris[i][j] = 0;
}
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
}
}
return 0;
}
/*
判断能否移动或者旋转
传入当前坐标的行和列 和 当前方块是那个方块的那种形状
*/
int IsMoveOrRotate(int nBlockRow,int nBlockCol,int nBlockOffset){
// 遍历二维数组
int i = 0,j = 0;
// 循环判断
for(i = 0; i < BLOCK_COLS; i++){
for(j = 0; j < BLOCK_COLS; j++){
// 如果是小方块实实在在存在的 并且 在主游戏区域中是墙壁 就不能移动
// 如果是固定了的 也不能继续动了
if((g_uchBlock[i][j+nBlockOffset] == 1
&& g_uchMaxTetris[i+nBlockRow][j+nBlockCol] == WALL)
|| (g_uchBlock[i][j+nBlockOffset] == 1
&& g_uchMaxTetris[i+nBlockRow][j+nBlockCol] == FIXEDBLOCK)){
return 1;
}
}
}
return 0;
}
/*
方块旋转
*/
int BlockRoate(){
// 判断还能不能旋转
int nRet = 0;
int nBlockOffset = 0; // 要旋转之后的方块的偏移量
// 要旋转的下一个形状 一个方块只有四种形状 所以需要取模
int nBlockShape = (g_nBlockShape + 1) % BLOCK_SHAPE;
nBlockOffset = /*旋转的时候方块的类型是固定的所以不用变,变的是形状*/
g_nBlockType * 16 * BLOCK_COLS + nBlockShape * 16;
// 判断
nRet = IsMoveOrRotate(g_nBlockRow,g_nBlockCol,nBlockOffset);
if(nRet == 0){
if(ClearBlockFromMaxTetris() == EOF){
// 清除方块
return EOF;
}
// 如果可以就旋转
g_nBlockShape = nBlockShape;
g_nBlockOffset = nBlockOffset;
if(BlockWriteToMaxTetris() == EOF){
// 绘制方块
return EOF;
}
}else if(nRet == EOF){
return EOF;
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
return 0;
}
/*
方块向下移动
*/
int BlockMoveDown(){
// 判断还能不能往下
int nRet = 0;
int i = 0,j = 0;
nRet = IsMoveOrRotate(g_nBlockRow+1,g_nBlockCol,g_nBlockOffset);
if(nRet == 0){ // 如果能往下
if(ClearBlockFromMaxTetris() == EOF){
// 清除方块
return EOF;
}
// 如果可以就往下
g_nBlockRow++;
if(BlockWriteToMaxTetris() == EOF){
// 绘制方块
return EOF;
}
}else if(nRet == 1){ // 如果到底部了
// 固定方块
for(i = 0; i < BLOCK_COLS; i++){
for(j = 0; j < BLOCK_COLS; j++){
if(g_uchMaxTetris[i + g_nBlockRow][j + g_nBlockCol] == BLOCK
&& g_uchBlock[i][j+g_nBlockOffset] == 1){
g_uchMaxTetris[i + g_nBlockRow][j + g_nBlockCol] = FIXEDBLOCK;
}
}
}
// 判断是否一行方块 清除
if (IsCanClearOneLineFormMaxTetris() == EOF)
{
return EOF;
}
// 产生新方块
if(CreateBlock() == EOF){
return EOF;
}
return 0;
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
return 0;
}
/*
方块向左移动
*/
int BlockMoveLeft(){
// 判断还能不能往左边
int nRet = 0;
nRet = IsMoveOrRotate(g_nBlockRow,g_nBlockCol-1,g_nBlockOffset);
if(nRet == 0){
if(ClearBlockFromMaxTetris() == EOF){
// 清除方块
return EOF;
}
// 如果可以就向左移动
g_nBlockCol--;
if(BlockWriteToMaxTetris() == EOF){
// 绘制方块
return EOF;
}
}else if(nRet == EOF){
return EOF;
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
return 0;
}
/*
方块向右移动
*/
int BlockMoveRight(){
// 判断还能不能往右边
int nRet = 0;
nRet = IsMoveOrRotate(g_nBlockRow,g_nBlockCol+1,g_nBlockOffset);
if(nRet == 0){
if(ClearBlockFromMaxTetris() == EOF){
// 清除方块
return EOF;
}
// 如果可以就向右移动
g_nBlockCol++;
if(BlockWriteToMaxTetris() == EOF){
// 绘制方块
return EOF;
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
}else if(nRet == EOF){
return EOF;
}
if(ShowUI() == EOF){
// 显示UI
return EOF;
}
return 0;
}
/*
开始游戏
*/
int StartGame(){
char nKey = 0;
// 初始化时间种子
srand((unsigned)time(NULL));
// 画界面
if(InitWall() == EOF){
// 如果绘制失败
return EOF; // 外界接收到EOF就退出程序了
}
// 1.画矩形背景
/* if(ShowUI() == EOF){
// 如果绘制失败
return EOF; // 外界接收到EOF就退出程序了
}*/
// 2.绘制方块
// 产生方块
if(CreateBlock() == EOF){
// 如果绘制失败
return EOF; // 外界接收到EOF就退出程序了
}
// 显示UI
if(ShowUI() == EOF){
// 如果绘制失败
return EOF; // 外界接收到EOF就退出程序了
}
// 控制方块 直到游戏结束
while(1){ // 是否有键盘输入
if(_kbhit()){
nKey = _getch(); // 有键盘输入 获取按下的值
switch(nKey){
// 上
case 'W':
case 'w':
if(BlockRoate() == EOF){
return EOF;
}
break;
// 下
case 'S':
case 's':
if(BlockMoveDown() == EOF){
return EOF;
}
break;
// 左
case 'A':
case 'a':
if(BlockMoveLeft() == EOF){
return EOF;
}
break;
// 右
case 'D':
case 'd':
if(BlockMoveRight() == EOF){
return EOF;
}
break;
}
}
if(IsGameOver() == 1){
return 1;
}
/* Sleep(1500);
if(BlockMoveDown() == EOF){
return EOF;
} */
}
system("pause");
return 0;
}
int main() {
int nRet = 0; // 接收游戏启动的返回结果
// 开始游戏
nRet = StartGame(); // 返回0则代码游戏正常启动r
if(nRet == EOF){ // 游戏启动失败
printf("Game Error!");
return EOF;
}else if (nRet == 1){ // 游戏结束
printf("Game Fail!");
system("pause");
return 1;
}
// 正常退出游戏
printf("Game Exit!");
system("pause");
return 0;
}
C语言俄罗斯方块游戏
最新推荐文章于 2024-01-27 20:37:01 发布