c++ Graphics 实现俄罗斯方块

2023-11-18

俄罗斯方块

(一)游戏规则

(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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++ Graphics 实现俄罗斯方块 的相关文章

随机推荐

  • 数据结构与算法2--数组常见操作

    数据结构与算法2 数组常见操作 数组是最常见也是我们使用最多的数据结构了 它是一块连续的内存空间 以下标来描述空间的位置 C 中int arr len 表示的的数组一旦配置后大小就无法改变 vector
  • CUDA-同步

    主机与设备之间的同步 分为隐式和显式 一 隐式 cudaMemcpy函数的作用在于传输传输 但在执行结束之前会产生阻塞 许多与内存相关的操作都会产生阻塞 这些不必要的阻塞会对性能产生较大的影响 如 锁页主机内存分配 设备内存分配 设备内存初
  • 2021年中国航天发射列表(55发见证历史)

    序号 有效载荷 发射时间 火箭型号 发射场 状态 1 天通一号03星 2021 01 20 00 25 CZ 3B 西昌 成功 2 遥感三十一号02组 2021 01 29 12 47 CZ 4C 酒泉 成功 3 方舟2号 2021 02
  • C++三目运算

    三目运算也叫条件运算或三元运算 可以实现简单if语句的功能 但是书写更简洁 语法 表达式一 表达式二 表达式三 先计算表达式一的值 如果为真 整个表达式的结果为表达式二的值 如果为假 整个表达式的结果为表达式三的值 int a b c a
  • BP神经网络与Python实现

    人工神经网络是一种经典的机器学习模型 随着深度学习的发展神经网络模型日益完善 联想大家熟悉的回归问题 神经网络模型实际上是根据训练样本创造出一个多维输入多维输出的函数 并使用该函数进行预测 网络的训练过程即为调节该函数参数提高预测精度的过程
  • three.js(相机 (Camera))

    相机类型 1透视相机 PerspectiveCamera 透视相机模拟人眼观察物体时的效果 具有近大远小的景深效果 创建透视相机需要设置视角FOV 画布宽高比 近平面和远平面距离等参数 2正交相机 OrthographicCamera 正交
  • 实验五循环结构程序设计

    一 实验目的 1 掌握在程序设计条件型循环结构时 如何正确地设定循环条件 以及如何控制循环的次数 2 了解条件型循环结构的基本测试方法 3 掌握如何正确地控制计数型循环结构进行测试的基本方法 4 了解对计数型循环结构进行测试的基本方法 5
  • linux nginx安装【docker容器怎么绑定域名?docker装nginx,nginx域名反向代理转发】centos7 docker系列

    一 安装Docker 1 查看你当前的内核版本 命令 uname r 系统的内核版本高于 3 10 2 确保 yum 包更新到最新 命令 yum update 最新此步骤可省略 3 检查本机是否已经安装docker 命令 docker v
  • 常用内存数据库三

    4 1 2 哪些场合适合使用其他的关系型数据库管理系统 RDBMS 客户端 服务器程序 如果你有许多的客户端程序要通过网络访问一个共享的数据库 你应当考虑用一个客户端 服务器数据库来替代SQLite SQLite可以通过网络文件系统工作 但
  • 压力测试工具Apache JMeter:3:压力测试报告的生成方法

    版权声明 本文为博主原创文章 遵循 CC 4 0 BY SA 版权协议 转载请附上原文出处链接和本声明 本文链接 https blog csdn net liumiaocn article details 101362941 Apache
  • 微信小程序css篇----边框(Border)

    一 边框 border 设置对象边框的特性 1 语法 border length style color 2 style none hidden dotted dashed solid double groove ridge inset o
  • 7种有效安全的网页抓取方法,如何避免被禁止?

    网页抓取是一种从互联网上抓取网页内容的过程 但在网络抓取种相信您也经常遇到障碍 尤其是做跨境业务的 在抓取国外的网站时更有难度 但我们站在您的立场上 提供七种有效的方法来进行网页抓取而不被阻止 最大限度地降低网站禁令的风险 并帮助您应对相关
  • linux 快速安装最新稳定版nginx(CentOS8.1 64位)

    目录 CentOS8 1 安装完成已具备 yum 指令操作 低版本缺少yum 指令自行安装 启动nginx遇到错误 nginx emerg bind to 0 0 0 0 7070 failed 13 Permission denied 配
  • 不用看网课就能学到python的文章(第五天)

    目录 类 对象 实例 类中的成员 面向对象 变量 不用看网课就能学到python的文章 第二天 Why does it work的博客 CSDN博客 不用看网课就能学到python的文章 第三天 Why does it work的博客 CS
  • 自己编写DLL文件——注册——VB工程引用——标准EXE调用(含例子)

    VB6 0的 工程 引用 中有很多控件供用户使用 但有时需要自己编写dll文件以实现某些功能 本文介绍如何自己编写一个简单的dll文件 然后在win7 64位下使用regsvr32注册 并在VB6 0中引用 最后在标准EXE中调用实现某些功
  • ES 版本,及重要特性

    参考 https www cnblogs com flyrock ES release 地址 https www elastic co cn downloads past releases elasticsearch ES版本 发布日期 版
  • 熊啸锋:精准营销及推广的四个步骤,倍增你的利润

    哈喽 我是熊啸锋老师 今天分享的主题是精准营销及推广的四个步骤 作为营销人 企业老板 项目负责人 市场开发人员等 你会经常面临 如何开发客户 如何获得大量的潜在客户名单 等很多的问题 还经常有人抱怨说 我们获取的潜在客户名单不精准 成交率非
  • Linux中清空文件的方法

    Linux中清空文件的方法 平时工作过程中 经常会遇到需要清空linux中某个日志文件的方法 下面总结一下几个常用的方法 以下待清空的文件名统一使用 test txt 表示 方法1 vi 中使用 d 1 输入 vi test txt 回车
  • word添加gif

    word添加gif动图最简单的方法 无需链接无需插件 X to Y的博客 CSDN博客 word插入动图 原文链接 https blog csdn net X To Y article details 124415532 文章目录 word
  • c++ Graphics 实现俄罗斯方块

    俄罗斯方块 一 游戏规则 1 方块种类 2 操作规则 玩家可以通过 按键 功能 a 向左一格 d 向右一格 s 顺时针旋转90度 w 逆时针旋转90度 3 积分规则 玩家根据消除的行列数量获取得分 数量 得分 1行 10分 2行 30分 3