c++ Graphics 实现俄罗斯方块

俄罗斯方块

(一)游戏规则

(1) 方块种类

六种方块图

(2) 操作规则

玩家可以通过

按键功能
a向左一格
d向右一格
s顺时针旋转90度
w逆时针旋转90度

(3) 积分规则

玩家根据消除的行列数量获取得分

数量得分
1行10分
2行30分
3行60分
4行100分

(5) 输赢规则

当玩家某一列触碰到屏幕顶端而无法被消除时游戏结束

(二)开发环境配置

编辑器: 小熊猫c++ ——1.1.2
第三方库: easyx ——graphics.h

(三)项目思路设计

(1)方块形状的储存

考虑方块的大小不会超过4*4所以用整型变量进行状压储存更加方便
具体如下

int Blocklist[] = {
	0b1000100010001000,
	0b1100100010000000,
	0b1100010001000000,
	0b1000110001000000,
	0b0100110010000000,
	0b1100110000000000,
	0b0100111000000000
};
// c++ 用0b表示二进制数

这里将所有的方块的摆放位置尽量靠左,为了使其刚刚出现时不需要进行坐标调整
从坐标转换到下标

int getIndex(int x, int y) {
	return 16 - ((x - 1) * 4 + y);
}
(2) 方块的旋转

这里只实现了顺时针旋转90度 其实只需要将行和列交换一下(注意每一列要从下往上顺着赋值)

int TurnRight(int Block) {
	int newBlock = 0;
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int indexo = getIndex(i, j), indexn = getIndex(j, 4 - i + 1); // 4 - i + 1 即为从下往上
			newBlock |= ((Block >> indexo) & 1) << indexn;
		}
	}
	return newBlock;
}
(3) 方块的消除

考虑到格子较少采用暴力算法,从下往上一行一行的扫,每次让上面的行下落一行

void CheckHoleLine() {
	int linecnt = 0;
	for (int i = Boardcol; i >= 1; i --) {
		int cnt = 0;
		for (int j = 1; j <= Boardrow; j ++) {
			if (BlockMp[i][j]) cnt ++;
		}
		if (cnt == Boardrow) {
			linecnt ++;
			i ++; // 注意因为少了一行所以i要回到上一行
			for (int j = i; j >= 1; j --) {
				for (int k = 1; k <= Boardrow; k ++) {
					BlockMp[j][k] = BlockMp[j - 1][k];
				}
			}
		}
	}
	if (linecnt == 1) Score += 10;
	else if (linecnt == 2) Score += 30;
	else if (linecnt == 3) Score += 60;
	else if (linecnt == 4) Score += 100;
}
(4) 画棋盘格

外面用一个长方形保住,里面用Boardcol * Boardrow个小方块填充,如下:

void InitGame() {
	NextBlockT = rand() % 7;
	setcolor(WHITE);
	rectangle(BoardLeft - Boardedge, BoardTop - Boardedge, BoardRight + Boardedge, BoardBottom + Boardedge);
	rectangle(ShowBoardLeft - Boardedge, ShowBoardTop - Boardedge, ShowBoardRight + Boardedge, ShowBoardBottom + Boardedge);
	setcolor(BLACK);
	for (int i = 1; i <= Boardcol; i ++) {
		for (int j = 1; j <= Boardrow; j ++) {
			int xpos = BoardLeft + (j - 1) * Squaresize, ypos = BoardTop + (i - 1) * Squaresize;
			DisplayBlock(xpos, ypos, BlockMp[i][j] != 0);
		}
	}
	for (int i = 1; i <= ShowBoardcol; i ++) {
		for (int j = 1; j <= ShowBoardrow; j ++) {
			int xpos = ShowBoardLeft + (j - 1) * Squaresize, ypos = ShowBoardTop + (i - 1) * Squaresize;
			int index = getIndex(i, j);
			DisplayBlock(xpos, ypos, (Blocklist[NextBlockT] >> index) & 1);
		}
	}
}

DisplayBlock 用于画每一个单元格,如下:

void DisplayBlock(int x, int y, bool show) {
	if (show) setfillcolor(WHITE), setcolor(BLACK);
	else setfillcolor(BLACK), setcolor(WHITE);
	fillrect(x, y, x + Squaresize, y + Squaresize);
}
(5)方块的下落

因为要响应鼠标消息,这里将一次下落一行拆分成Grades步 使用Grades方便调整速度
下落时要检测到底没有:

bool canDown(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (x + i > Boardcol || BlockMp[x + i][y + j - 1] == 1) return false;
			}
		}
	}
	return true;
}

左右是否可以移动 (注意4*4的模型中空格不算)


bool canRight(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (y + j > Boardrow || BlockMp[x + i - 1][y + j] == 1) return false;
			}
		}
	}
	return true;
}

bool canLeft(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (y + j - 2 < 1 || BlockMp[x + i - 1][y + j - 2] == 1) return false;
			}
		}
	}
	return true;
}

int GetFirstBlock(int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) return i;
		}
	}
}

int GetLeftBlock(int Block) {
	int ans = 1e9;
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				ans = min(ans, j);
			}
		}
	}
	return ans;
}

移动后还要将左上角的空格处理掉 (直接做一个减法即可)

int oldc = GetFirstBlock(NowBlock), oldr = GetLeftBlock(NowBlock);
int oldBlock = NowBlock;
NowBlock = TurnRight(TurnRight(TurnRight(NowBlock)));
int nwc = GetFirstBlock(NowBlock), nwr = GetLeftBlock(NowBlock);
NowBlockx += oldc - nwc;
NowBlocky += oldr - nwr;

完整代码如下:

#include <bits/stdc++.h>
#include <windows.h>
#include <graphics.h>
#include <conio.h>

using namespace std;

const int WINDOWS_WIDTH = 500, WINDOWS_HEIGHT = 540;
const int Squaresize = 15;
const int BoardLeft = 30, BoardTop = 20, ShowBoardLeft = 350, ShowBoardTop = 20;
const int Boardcol = 30, Boardrow = 15, ShowBoardcol = 4, ShowBoardrow = 4;
const int BoardRight = BoardLeft + Boardrow * Squaresize, BoardBottom = BoardTop + Boardcol * Squaresize, ShowBoardRight = ShowBoardLeft + ShowBoardrow * Squaresize, ShowBoardBottom = ShowBoardTop + ShowBoardcol * Squaresize;
const int Boardedge = 10;
const int Grades = 300;
const int TEXT_X = 350, TEXT_Y = 150;

int Score;
bool GameRun = true;
int BlockMp[Boardcol + 5][Boardrow + 5];
/*
	0 没有方块
	1 有还没有到位的方块
	2 当前方块
*/
int Blocklist[] = {
	0b1000100010001000,
	0b1100100010000000,
	0b1100010001000000,
	0b1000110001000000,
	0b0100110010000000,
	0b1100110000000000,
	0b0100111000000000
};

int NowBlockx, NowBlocky, NowBlockT, NowBlock, NextBlockT;

int getIndex(int x, int y) {
	return 16 - ((x - 1) * 4 + y);
}

void SetBlock(int x, int y, int Block, int status) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if (Block & (1 << index)) {
				BlockMp[x + i - 1][y + j - 1] = status;
			}
		}
	}
}

void DisplayBlock(int x, int y, bool show) {
	if (show) setfillcolor(WHITE), setcolor(BLACK);
	else setfillcolor(BLACK), setcolor(WHITE);
	fillrect(x, y, x + Squaresize, y + Squaresize);
}

void DrawGame() {
	setcolor(WHITE);
	for (int i = 1; i <= Boardcol; i ++) {
		for (int j = 1; j <= Boardrow; j ++) {
			int xpos = BoardLeft + (j - 1) * Squaresize, ypos = BoardTop + (i - 1) * Squaresize;
			DisplayBlock(xpos, ypos, BlockMp[i][j] != 0);
		}
	}
}

void InitGame() {
	NextBlockT = rand() % 7;
	setcolor(WHITE);
	rectangle(BoardLeft - Boardedge, BoardTop - Boardedge, BoardRight + Boardedge, BoardBottom + Boardedge);
	rectangle(ShowBoardLeft - Boardedge, ShowBoardTop - Boardedge, ShowBoardRight + Boardedge, ShowBoardBottom + Boardedge);
	setcolor(BLACK);
	for (int i = 1; i <= Boardcol; i ++) {
		for (int j = 1; j <= Boardrow; j ++) {
			int xpos = BoardLeft + (j - 1) * Squaresize, ypos = BoardTop + (i - 1) * Squaresize;
			DisplayBlock(xpos, ypos, BlockMp[i][j] != 0);
		}
	}
	for (int i = 1; i <= ShowBoardcol; i ++) {
		for (int j = 1; j <= ShowBoardrow; j ++) {
			int xpos = ShowBoardLeft + (j - 1) * Squaresize, ypos = ShowBoardTop + (i - 1) * Squaresize;
			int index = getIndex(i, j);
			DisplayBlock(xpos, ypos, (Blocklist[NextBlockT] >> index) & 1);
		}
	}
}

void DrawNextBlock() {
	setcolor(WHITE);
	for (int i = 1; i <= ShowBoardcol; i ++) {
		for (int j = 1; j <= ShowBoardrow; j ++) {
			int xpos = ShowBoardLeft + (j - 1) * Squaresize, ypos = ShowBoardTop + (i - 1) * Squaresize;
			int index = getIndex(i, j);
			DisplayBlock(xpos, ypos, (Blocklist[NextBlockT] >> index) & 1);
		}
	}
}

int TurnRight(int Block) {
	int newBlock = 0;
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int indexo = getIndex(i, j), indexn = getIndex(j, 4 - i + 1);
			newBlock |= ((Block >> indexo) & 1) << indexn;
		}
	}
	return newBlock;
}

bool canDown(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (x + i > Boardcol || BlockMp[x + i][y + j - 1] == 1) return false;
			}
		}
	}
	return true;
}

bool canRight(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (y + j > Boardrow || BlockMp[x + i - 1][y + j] == 1) return false;
			}
		}
	}
	return true;
}

bool canLeft(int x, int y, int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				if (y + j - 2 < 1 || BlockMp[x + i - 1][y + j - 2] == 1) return false;
			}
		}
	}
	return true;
}

int GetFirstBlock(int Block) {
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) return i;
		}
	}
}

int GetLeftBlock(int Block) {
	int ans = 1e9;
	for (int i = 1; i <= 4; i++) {
		for (int j = 1; j <= 4; j++) {
			int index = getIndex(i, j);
			if ((Block >> index) & 1) {
				ans = min(ans, j);
			}
		}
	}
	return ans;
}


void DownNowBlock() {
	NowBlockT = NextBlockT;
	NextBlockT = rand() % 7;
	DrawNextBlock();
	NowBlockx = 1;
	NowBlocky = 1;
	NowBlock = Blocklist[NowBlockT];
	SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
	DrawGame();
	while (canDown(NowBlockx, NowBlocky, NowBlock)) {
		SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
		NowBlockx += 1;
		SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
		DrawGame();
		for (int i = 1; i <= Grades; i ++) {
			if (kbhit()) {
				char op = getch();
				if (op == 'w') { // 逆时针旋转
					SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
					int oldc = GetFirstBlock(NowBlock), oldr = GetLeftBlock(NowBlock);
					int oldBlock = NowBlock;
					NowBlock = TurnRight(TurnRight(TurnRight(NowBlock)));
					int nwc = GetFirstBlock(NowBlock), nwr = GetLeftBlock(NowBlock);
					NowBlockx += oldc - nwc;
					NowBlocky += oldr - nwr;
					if (!canLeft(NowBlockx, NowBlocky + 1, NowBlock) || !canRight(NowBlockx, NowBlocky - 1, NowBlock)) {
						NowBlock = oldBlock;
						NowBlockx -= oldc - nwc;
						NowBlocky -= oldr - nwr;
					}
					SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
					DrawGame();
				} else if (op == 's') {
					SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
					int oldc = GetFirstBlock(NowBlock), oldr = GetLeftBlock(NowBlock);
					int oldBlock = NowBlock;
					NowBlock = TurnRight(NowBlock);
					int nwc = GetFirstBlock(NowBlock), nwr = GetLeftBlock(NowBlock);
					NowBlockx += oldc - nwc;
					NowBlocky += oldr - nwr;
					if (!canLeft(NowBlockx, NowBlocky + 1, NowBlock) || !canRight(NowBlockx, NowBlocky - 1, NowBlock)) {
						NowBlock = oldBlock;
						NowBlockx -= oldc - nwc;
						NowBlocky -= oldr - nwr;
					}
					SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
					DrawGame();
				} else if (op == 'a') {
					SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
					if (canLeft(NowBlockx, NowBlocky, NowBlock)) NowBlocky --;
					SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
					DrawGame();
				} else if (op == 'd') {
					SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
					if (canRight(NowBlockx, NowBlocky, NowBlock)) NowBlocky ++;
					SetBlock(NowBlockx, NowBlocky, NowBlock, 2);
					DrawGame();
				}
			}
			Sleep(1);
		}
	}
	SetBlock(NowBlockx, NowBlocky, NowBlock, 0);
	SetBlock(NowBlockx, NowBlocky, NowBlock, 1);
}

bool checkOver() {
	for (int i = 1; i <= Boardrow; i ++) {
		if (BlockMp[1][i]) return 1;
	}
	return 0;
}

void CheckHoleLine() {
	int linecnt = 0;
	for (int i = Boardcol; i >= 1; i --) {
		int cnt = 0;
		for (int j = 1; j <= Boardrow; j ++) {
			if (BlockMp[i][j]) cnt ++;
		}
		if (cnt == Boardrow) {
			linecnt ++;
			i ++;
			for (int j = i; j >= 1; j --) {
				for (int k = 1; k <= Boardrow; k ++) {
					BlockMp[j][k] = BlockMp[j - 1][k];
				}
			}
		}
	}
	if (linecnt == 1) Score += 10;
	else if (linecnt == 2) Score += 30;
	else if (linecnt == 3) Score += 60;
	else if (linecnt == 4) Score += 100;
}

void ShowScore() {
	ostringstream output;
	output << "得分: " << Score;
	setcolor(RED);
	setfont(25, 0, "黑体");
	outtextxy(TEXT_X, TEXT_Y, output.str().c_str());
}

int main() {
	srand(time(0));
	initgraph(WINDOWS_WIDTH, WINDOWS_HEIGHT);
	InitGame();
	DrawGame();
	ShowScore();
	while (GameRun && is_run()) {
		DownNowBlock(); // 控制当前方块落下
		CheckHoleLine(); // 删除完整的行
		ShowScore();
		if (checkOver()) {
			GameRun = false;
		}
	}
	getch();
	closegraph();
	cleardevice();
	return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值