我有一个“想法”,可以使一个着色器(一个帧缓冲纹理)同时运行俄罗斯方块的最大数量。
以下是对所得代码如何工作的简短描述。
这是什么
每个俄罗斯方块都以三个像素工作,对于1920x1080
分辨率,您一次可以运行619200
份。 还制作了一个用于自动播放的简单机器人。
在帖子末尾,可以链接到运行和源。
视频已更新,显示剩余字段数,最多为零。
资料储存
尺寸为[10, 22]
(10宽,22高度)的俄罗斯方块表。
每个单元格可以为空或不为空。
存储整个表总共需要22 * 10 = 220
位。
一个“像素”是四个24位浮点数,每个像素96位。
在视觉上(调试框架的一部分),三个像素用红色突出显示,这是一个保存的字段:

2 * 96 + 24 + 4
两个像素,第三像素的一个浮点数,第三像素的第二个浮点数的4位
第三个像素pixel3.zw中有两个未使用的浮点数,它们存储了逻辑状态 ,更精确地讲
- z存储三个八位数字
[a,b,c]
-当前块的位置,作为数组中该位置的ID(大小为220位的数组,最大位置为220,小于0xff)
-b时间,直到每帧-1
自动下降(定时器)到该数字为止,当它变为0时它就落在块上
-当前块的c ID - w也是
[a,b,c]
,而且整个浮动的符号(正号或负号) 也是当前表中游戏结束的标志(以免浪费该字段,避免浪费资源)
- 一个动作,无动作(0),向左(1),向右(2),依此类推,完整代码在Common中 ,动作具有两种状态, 检查左并检查是否可以向左移动,然后将动作设置为left_ move 。
-当前表的[b,c]
0xffff (16位)点,已刻录的行数
在第三像素的第二浮点数中还有20
未使用。
调试框架显示保存逻辑正常工作
左侧有一个大小为3个像素的白场,专门设置为显示间隙已正确处理(如果分辨率不是3的倍数,则条带将成一定角度)
75行的条件缓冲区A

为什么需要动作ID:
- 数据存储在三个像素中,不可能同时检查逻辑并在一帧中更改数据 (如果不执行所有逻辑并在每个像素中加载整个地图,则负载将增加数十倍)。
- 因此, 数据存储逻辑在每个像素中工作,并执行接收到的命令left_mov ,验证命令left_check仅在一个像素(第三个像素) 中执行。
慢的地方
- 每三个像素(逻辑像素)解压缩整个地图(读取所有三个像素)。
- 其余两个像素仅解压缩“自身”(一个像素)以执行保存的动作。
- 在操作过程中, 线烧了,另一个像素被加载,因为桌子掉下来了,桌子的下部应该知道上面是什么。
存储算法性能
对于测试,也将#define debug设置为Common和AI 0 。
我得到了这样的结果 -渲染和处理所有619200字段时为10FPS ,
在12万场(25fps)

机器人逻辑
逻辑非常糟糕 ,该僵尸程序在一分钟内就会耗尽,最高可升至60分。
考虑到基于所有可能的跌落的最佳位置,我无法通过许多周期来检查孔洞,壁架和可燃区域来开始一个良好的逻辑 ...
好的逻辑对我来说最多可以工作100份,并且在绕过所有循环时会产生很大的延迟。
我的机器人逻辑是这样的
所有逻辑都在缓冲区A的AI_pos_gen函数中,共有十行。
伪代码:
检查模块安装的高度是否等于当前列中字段的最大值(检查一行的高度)
(4 ){ ( (10)){ ( ){ ( , ) best ID() best POS } } } ( ) ( ) 0 1
事实证明,三个循环很常见-他们将块放置在使得高度最小的位置。
当出现新块时会调用AI_pos_gen函数,并返回从上方下落的位置,获取块ID并使其旋转,该函数在第三个像素(逻辑)中工作,即,它具有一个已满载的地图(地图数组)。
如果愿意,您可以轻松尝试编写您的机器人。
最慢的地方
仅增加一个循环即可测试漏洞 ,当机器人数量超过1万时,我的视频卡驱动程序崩溃了……我编写的机器人是我能做到的最“简约”的机器人版本,不幸的是它非常糟糕。
UI界面/渲染
所有呈现在Image中 ,UI逻辑在缓冲区B中。
渲染:
将屏幕拆分为小块,并在每个小块中绘制一张桌子,以最小的负荷。
加载地图的逻辑-未解压缩每个像素,未解压缩每个像素,仅解压缩了“必需位”(字面意思),功能代码为:
int maptmp(int id, int midg) { int nBits = 8; ivec4 pixeldata = loadat(id, midg); int itt = (id / 24) / 4; //data pixel id 0-2 int jtt = (id - itt * 24 * 4) / 24; //component in data pizel id 0-3 int ott = (id - itt * 24 * 4 - jtt * 24) / 8; //component in unpacked value 0-2 int ttt = (id - itt * 24 * 4 - jtt * 24 - ott * 8); //bit after int2bit 0-7 ivec3 val = decodeval16(pixeldata[jtt]); int n = val[ott]; for (int i = 0; i < nBits; ++i, n /= 2) { if (i == ttt) { if ((n % 2) == 0)return 0; else return 1; //switch + return does not work on windows(Angle) /*switch (n % 2) { case 0:return 0;break; case 1:return 1;break; }*/ } } return 0; }
为了避免滚动时的像素化 ,从43000开始,浮点数的小数部分会丢失,并且无法计算出将619千添加到UV进行滚动(会有像素而不是表格)。
所有滚动都分为一个大图块,并旋转一圈,最多向UV添加32。 ( 图片中的 207行)。
确定字段ID的方法相同。 ( 图片中的第215行)
用户界面
号码:
黄色是俄罗斯方块字段的数量。
左大-当前字段的编号。
在右下方-当前字段的点。
源和启动
Bufer A逻辑, Bufer B是UI控件, 图像渲染
来源https://www.shadertoy.com/view/3dlSzs (通过Angle编译的时间为16秒)
该机器人在此处被禁用(您可以启用它),并且所有字段都可以通过键盘进行播放 。
控制左/右/上/下箭头。
UI红色矩形重置,移动(单击LMB拖动鼠标),然后单击字段以滚动或选择要显示的字段。
从网络浏览器启动:
- 使用chrome.exe运行chrome --use-angle = gl
- 跟随链接到shadertoy
- 在网站上的编辑器中,选择“通用”并删除#define no_AI
- (也是常见的)将#define AI 199设置为0,即#define AI 0
- 单击编译按钮(在着色器上的编辑器窗口下),然后单击全屏
第二个选项是在任何“着色器启动器”中运行着色器,这里是指向带有该着色器的* .exe文件的存档( 下载 )的链接 。
OpenGL的编译时间约为10秒。
更新 :添加了带有孔检查的着色器https://www.shadertoy.com/view/wsXXzH
而不是在相同高度下获得更好位置的条件。 check_block_at_wh
功能check_block_at_wh
(第380 BufA行),同时检查位置的有效性,计算孔数,未添加新的循环以及条件行442至459 BufA。
它也可以在一分钟之内迅速燃烧30-60点(显然,您需要检查较大的区域是否有孔,但这会产生强大的制动作用)
还有两张图片解释了这项工作:
位置选择https://i.imgur.com/e0uENgV.png
该条件的块位置为https://i.imgur.com/ORECXUW.png