手撸俄罗斯方块(一)——简单介绍
手撸俄罗斯方块
简单介绍
《俄罗斯方块》(俄语:Тетрис,英语:Tetris),是1980
年末期至1990
年代初期风靡全世界的电脑游戏,是落下型益智游戏的始祖,电子游戏领域的代表作之一,为苏联首个在美国发布的娱乐软件。此游戏最初由阿列克谢·帕基特诺夫
在苏联设计和编写,于1984年6月6日首次发布,当时他正在苏联科学院电算中心工作。此游戏的名称是由希腊语数字“四”的前缀“tetra-”
(因所有落下方块皆由四块组成)和帕基特诺夫最喜欢的运动网球(“tennis”
)拼接而成,华语地区则因游戏为俄罗斯人发明普遍称为“俄罗斯方块”。
数字“四”,显而易见,最初的图形都是由4个小方块组成的,这也是俄罗斯方块的一个特点。游戏的目标是通过控制不同形状的方块,使它们在一个矩形的游戏区域中排列成完整的一行或多行,然后这些完整的行就会消除,给玩家得分。
游戏规则
- 游戏开始时,游戏区域是一个空白的矩形,玩家通过控制方块的移动和旋转,使它们在游戏区域中排列成完整的一行或多行;
- 当一行或多行被完整填满时,这些行就会消除,给玩家得分;
- 当方块堆积到游戏区域的顶部时,游戏结束。
游戏特点
- 游戏简单易上手,但是难度逐渐增加;
- 游戏的速度会随着游戏的进行而逐渐加快;
- 游戏的随机性较大,玩家需要根据当前的情况,灵活地调整方块的位置和旋转。
形状
俄罗斯方块中的方块有7种不同的形状,分别是:
I
型:一字型;J
型:J型;L
型:L型。O
型:方块型;S
型:S型;T
型:T型;Z
型:Z型;
这些形状是由4个小方块组成的,每个小方块都可以旋转,但是旋转的中心不同。如下图:
各种四格骨牌由左至右,上至下的英文字母代号:I、J、L、O、S、T、Z
操作
玩家通过键盘控制方块的移动和旋转,具体操作如下:
←
:向左移动方块;→
:向右移动方块;↑
:旋转方块;↓
:加速下落。
得分
- 消除一行:得
100
分; - 消除两行:得
300
分; - 消除三行:得
700
分; - 消除四行:得
1500
分。
游戏结束
- 当方块堆积到游戏区域的顶部时,游戏结束;
- 游戏结束后,显示游戏结束的提示,并显示玩家的得分。
从开发角度看俄罗斯方块
坐标系
对于所有二维游戏来说,坐标系是一个非常重要的概念。在俄罗斯方块中,我们可以使用一个二维数组来表示游戏区域,数组的每一个元素表示一个方块,数组的索引表示方块的坐标。如下图:
图中区域表示正数的范围。但是实际情况下,我们可以使用一个二维数组来表示游戏区域。而且计算机在绘画过程中也是自上而下自左而右进行的,因此我们需要将坐标进行变换。如下图:
x
轴表示列,y
轴表示行。这样我们就可以通过一个二维数组来表示游戏区域。
对于执行的一个元素,我们可以通过二维数组快速定位到它。
const points = [[]]; // 二维数组
const x = 1; // 列
const y = 2; // 行
const point = points[y][x]; // 获取坐标为(1, 2)的元素
需要注意的是,数组的索引是从0
开始的,因此在实际过程中我们将y = 0看成第一行,x = 0看成第一列。
方块的表示
按照上述坐标体系的定义,我们可以表示任何一个方块,如当表示I
型方块时,我们可以定义一个数组来表示它的形状:
const IHorizonal = [[0, 1, 2, 3]
]
const IVertical = [[0],[1],[2],[3]
]
如下图:
这样我们就可以通过一个数组来表示一个方块的形状。通过类似的方法,我们可以表示其他的6个方块。但是,I方块有两种形态,我们到底使用哪一种作为初始形态呢?
这里面需要做一个约定,我们定义每个方块的初始化形态。定义如下:
I
型:
// 口口口口
J
型:
// 口
// 口口口
L
型:
// 口
// 口口口
O
型:
// 口口
// 口口
S
型:
// 口口
// 口口
T
型:
// 口
// 口口口
Z
型:
// 口口
// 口口
方块的移动
在游戏过程中,方块是可以移动的。分别可以向左移动、向右移动、向下移动。同时移动必须满足如下条件:
- 移动后的方块不能超出游戏区域;即不能超出游戏区域的左边界、右边界和底边界;翻译成程序语言为:
// 向左移动
if (x - 1 >= 0) {x -= 1;
}
// 向右移动
if (x + 1 < width) {x += 1;
}
// 向下移动
if (y + 1 < height) {y += 1;
}
- 移动后的方块不能与其他方块重叠;即不能与其他方块的坐标重叠;翻译成程序语言为:
// 向左移动
if (x - 1 >= 0 && !isOverlap(x - 1, y)) {x -= 1;
}
// 向右移动
if (x + 1 < width && !isOverlap(x + 1, y)) {x += 1;
}
// 向下移动
if (y + 1 < height && !isOverlap(x, y + 1)) {y += 1;
}
上面是描述某个点的情况,实际上判断方块情况,需要所有的点都满足条件。
方块的转换
在游戏过程中,方块是可以旋转的。如下图:
同样的,在旋转过程中,也必须满足如下条件:
-
旋转后的方块不能超出游戏区域;即不能超出游戏区域的左边界、右边界和底边界;
-
旋转后的方块不能与其他方块重叠;即不能与其他方块的坐标重叠。
使用程序语言表述如下:
// 旋转
const isValid = (newShape) => {for (let y = 0; y < newShape.length; y++) {for (let x = 0; x < newShape[y].length; x++) {if (newShape[y][x] && (x < 0 || x >= width || y >= height || isOverlap(x, y))) {return false;}}}return true;
}
const newShape = rotate(shape);
if (isValid(newShape)) {shape = newShape;
}
接下来问题来,如何实现rotate
函数呢?这里面就涉及到了方块的旋转。对于不同的方块,旋转的方式是不同的。
如I型方块,其旋转形态只有两种,分别是横向和纵向。
// I型方块
// 横向
// 口口口口
// 纵向
// 口
// 口
// 口
// 口
但是对于T型等其他方块,其旋转形态是四种的。
// T型方块
// 1
// 口
// 口口口
// 2
// 口
// 口口
// 口
// 3
// 口口口
// 口
// 4
// 口
// 口口
// 口
我们当然可以通过枚举的方式将所有的旋转形态都列出来,但是这样的方式是不可取的。因为这样的方式会使得代码变得复杂,而且不易维护。因此我们需要找到一种更好的方式来实现方块的旋转。
实际上,我们可以通过观察发现,方块的旋转是围绕一个中心点进行的。且旋转方向和角度是固定的。
因此我们可以得出如下结论:
-
方块的旋转是围绕一个中心点进行的;选择的中心点是不动的。
-
方块的旋转方向和角度是固定,我们可以选取逆时针旋转,相对我们定义的坐标系统旋转角度为 π / 2。 故根据旋转公式,新坐标定义如下:
const newX = Math.cos(Math.PI / 2) * (x - centerX) - Math.sin(Math.PI / 2) * (y - centerY) + centerX;const newY = Math.cos(Math.PI / 2) * (y - centerY) + Math.sin(Math.PI / 2) * (x - centerX) + centerY;
即
const newX = -y + centerY + centerX;const newY = x - centerX + centerY;
此处不太了解的可以去看看坐标旋转。
小结
上面我们讨论了俄罗斯方块的基本介绍,从开发的角度来看,我们讨论了坐标系、方块的表示、方块的移动和方块的旋转。那么我们可以通过如下两个实体在表示方块的基本情况。
- Point,坐标点
interface PointAttr {color?: string; // 颜色isReadyToClean?: boolean; // 是否准备消除[key: string]: any; // 其他属性
}class Point {private x: number;private y: number;private attr: PointAttr;constructor(x, y, attr?: PointAttr) {this.x = x;this.y = y;this.attr = attr || {};}
}
每个坐标点均包含了x、y坐标,以及其他属性,如颜色等;
- Block,方块
我们通过Block
来抽象方块,定义如下:
class Block {private points: Point[];private rotateIndex: number; /// 旋转因子constructor(points: Point[]) {this.points = points;}abstract getCenterIndex(): number; // 获取中心点abstract getRotateArray(); number[]; // 获取旋转数组
}
其他的方块均继承自Block,实现getCenterIndex
和getRotateArray
方法。
如IBlcok,其实现如下:
class IBlock extends Block {getCenterIndex() {return 1;}getRotateArray() {return [Math.PI / 2, // 第一次旋转相对于初始位置的角度-Math.PI / 2 // 第二次旋转相对于第一次旋转的角度]}
}
通过继承的关系,分别实现LBlock
、JBlock
、OBlock
、TBlock
、SBlock
、ZBlock
。
本章内容暂时就到这里,后续章节我们将继续讨论如何实现俄罗斯方块的游戏逻辑。
详细内容可以关注我的github账号: https://github.com/shushanfx/tetris
接下来我将从如下几个方面来阐述:
- 手撸俄罗斯方块——游戏设计
- 手撸俄罗斯方块——游戏核心模块设计
- 手撸俄罗斯方块——渲染与交互
- 手撸俄罗斯方块——游戏主题
相关文章:
手撸俄罗斯方块(一)——简单介绍
手撸俄罗斯方块 简单介绍 《俄罗斯方块》(俄语:Тетрис,英语:Tetris),是1980年末期至1990年代初期风靡全世界的电脑游戏,是落下型益智游戏的始祖,电子游戏领域的代表作之一&a…...
构建LangChain应用程序的示例代码:61、如何使用 LangChain 和 LangSmith 优化链
本示例介绍如何使用 LangChain 和 LangSmith 优化链。 设置 我们将为 LangSmith 设置环境变量,并加载相关数据 import osos.environ["LANGCHAIN_PROJECT"] "movie-qa" # 设置 LANGCHAIN_PROJECT 环境变量为 "movie-qa"import pan…...
Android系统通过属性设置来控制log输出的方案
Android系统通过属性设置来控制log输出的方案 背景 项目中经常需要在针对性的模块或者文件,分析问题的时候输出Log,但问题分析完成后,又由于性能问题,需要关闭这些log输出。当前大多数情况下是控制整个系统的log等级来实现&#…...
JavaDoc的最佳实践
文章目录 一、JavaDoc 使用说明1.1 什么是 JavaDoc1.2 文档注释结构1.3 常见的 Javadoc 标签 二、文档最佳实践2.1 注释原则2.2 实际案例 参考资料 一、JavaDoc 使用说明 1.1 什么是 JavaDoc JavaDoc 是一款能根据源代码中的文档注释来产生 HTML 格式的 API 文档的工具。 Jav…...
数字力量助西部职教全面提升——唯众品牌大数据、人工智能系列产品中标甘肃庆阳职院数字经济人才培养基地!
近日,唯众品牌凭借在大数据和人工智能领域深耕多年的技术积累和卓越产品,成功中标庆阳职业技术学院全国一体化算力网络国家枢纽节点数字经济人才培养基地项目,标志着唯众在助力西部职业教育与数字经济融合发展的新征程上迈出了坚实的一步。 …...
Swagger的原理及应用详解(四)
本系列文章简介: 在当今快速发展的软件开发领域,特别是随着微服务架构和前后端分离开发模式的普及,API(Application Programming Interface,应用程序编程接口)的设计与管理变得愈发重要。一个清晰、准确且易于理解的API文档不仅能够提升开发效率,还能促进前后端开发者之…...
Elasticsearch7.10集群搭建
Elasticsearch详细介绍: Elasticsearch 是一个分布式、RESTful 风格的搜索和分析引擎。它的核心基于 Apache Lucene,能够处理海量的数据,并支持实时的全文搜索。以下是关于 Elasticsearch 的详细介绍。 一、基本概念 索引(Index…...
SMU Summer 2024 Contest Round 3
A.Hcode OnlineJudge 先用欧拉筛把质数预处理出来,然后枚举左端点的质数,只需要询问右端点是不是质数并取差值的min就行了 #include<bits/stdc.h> #define endl \n #define mk make_pair #define int long long using namespace std; typedef lon…...
uniapp 封装瀑布流组件
思路: 1.coulumns:需要分成几列 2.如何分布数据 3.计算每列的宽度 4.图片进行高度自适应 <template><view :style"{ margin: boxM }"><view class"flex flex-justify-start bg-red" style"background-colo…...
pd虚拟机去虚拟化是什么意思?pd虚拟机去虚拟化教程 PD虚拟机优化设置
Parallels Desktop for Mac(PD虚拟机)去虚拟化是指在虚拟机(Virtual Machine,简称 VM)中禁用或减少虚拟化层的影响,使其表现更接近于物理机。这种操作通常用于提高虚拟机的性能或解决某些软件兼容性问题。具…...
低代码研发项目管理流程优化:提效与创新的双重驱动
随着信息技术的迅猛发展,软件项目的规模和复杂度日益增加,传统的软件开发方式已经难以满足快速迭代和高效交付的需求。在这一背景下,低代码平台应运而生,以其高效、灵活、易用的特点,迅速成为软件行业的新宠。然而&…...
32位版 C 库函数time 将在 2038 年溢出,那到时候,它该何去何从
简单地说,通常不必担心,在64位操作系统已经成为主流的今天这基本上不是问题(在写这篇回答的时候,我才发现我甚至找不到32位的机器来测试)刚好我有一些资料,是我根据网友给的问题精心整理了一份「32库函数的…...
C语言 printf函数缓冲机制
printf不立即打印到stdout的原因 printf函数使用了缓冲机制。当我们调用printf时,输出通常不会立即显示在屏幕上,而是先存储在一个缓冲区中。这是为了提高I/O操作的效率。 缓存数据输出的原理 stdio库维护了一个缓冲区。当缓冲区满了,或者在特定条件下,缓冲区的内容会被刷新…...
【Linux进阶】文件系统8——硬链接和符号连接:ln
在Linux下面的链接文件有两种, 一种是类似Windows的快捷方式功能的文件,可以让你快速地链接到目标文件(或目录);另一种则是通过文件系统的inode 链接来产生新文件名,而不是产生新文件,这种称为硬链接&…...
代码随想录算法训练营Day64|拓扑排序(卡码网117)、dijkstra朴素版
拓扑排序 117. 软件构建 (kamacoder.com) 拓扑排序简单的说是将一个有向图转为线性的排序。 它将图中的所有结点排序成一个线性序列,使得对于任何的边uv,结点u在序列中都出现在结点v之前,这样的序列满足图中所有的前驱-后继关系。 拓扑排…...
neo4j 图数据库:Cypher 查询语言、医学知识图谱
neo4j 图数据库:Cypher 查询语言、医学知识图谱 Cypher 查询语言创建数据查询数据查询并返回所有节点查询并返回所有带有特定标签的节点查询特定属性的节点及其所有关系和关系的另一端节点查询从名为“小明”的节点到名为“小红”的节点的路径 更新数据更新一个节点…...
数据结构基础--------【二叉树基础】
二叉树基础 二叉树是一种常见的数据结构,由节点组成,每个节点最多有两个子节点,左子节点和右子节点。二叉树可以用来表示许多实际问题,如计算机程序中的表达式、组织结构等。以下是一些二叉树的概念: 二叉树的深度&a…...
数据开源 | Magic Data大模型高质量十万轮对话数据集
能够自然的与人类进行聊天交谈,是现今的大语言模型 (LLM) 区别于传统语言模型的重要能力之一,近日OpenAI推出的GPT-4o给我们展示了这样的可能性。 对话于人类来说是与生俱来的,但构建具备对话能力的大模型是一项不小的挑战,收集高…...
webpack之ts打包
tsconfig.json配置 // 是否对js文件进行编译,默认false"allowJs": true,// 是否检查js代码是否符合语法规范,默认false(引入的外部文件有可能语法有问题)"checkJs": true, allowJs和checkJs基本是同时出现,因为有了allowJs 这个检查…...
MATLAB数据统计描述和分析
描述性统计就是搜集、整理、加工和分析统计数据, 使之系统化、条理化,以显示出数据资料的趋势、特征和数量关系。它是统计推断的基础,实用性较强,在数学建模的数据描述部分经常使用。 目录 1.频数表和直方图 2 .统计量 3.统计…...
设计分享—国外后台界面设计赏析
国外后台界面设计将用户体验放在首位,通过直观易懂的布局和高效的交互设计,提升用户操作效率和满意度。 设计不仅追求美观大方,还注重功能的实用性和数据的有效展示,通过图表和图形化手段使数据更加直观易懂。 采用响应式布局&a…...
最小生成树(算法篇)
算法之最小生成树 最小生成树 概念: 最小生成树是一颗连接图G所有顶点的边构成的一颗权最小的树,最小生成树一般是在无向图中寻找。最小生成树共有N-1条边(N为顶点数)。 算法: Prim算法 概念: Prim(普里姆)算法是生成最小生…...
教师管理小程序的设计
管理员账户功能包括:系统首页,个人中心,教师管理,个人认证管理,课程信息管理,课堂记录管理,课堂统计管理,留言板管理 微信端账号功能包括:系统首页,课程信息…...
Selenium 等待
环境: Python 3.8 selenium3.141.0 urllib31.26.19 Chromium 109.0.5405.0 (32 位) # 1 固定等待(time) # 固定待是利用python语言自带的time库中的sleep()方法,固定等待几秒。 # 这种方式会导致这个脚本运…...
安装easy-handeye
一、aruco_ros配置 mkdir -p ~/ros_ws/src cd ~/ros_ws/src git clone -b melodic-devel https://github.com/pal-robotics/aruco_ros.git cd .. catkin_make 二、visp配置(需要联外网下载东西,不然会一直出问题) sudo apt-get install ros-melodic-…...
【面试题】MySQL 索引(第二篇)
1.索引 索引是数据库中的一个核心概念,它对于提高数据库查询效率至关重要。以下是索引的详细概念解析: 一、索引的定义 基本定义:索引是一个排序的列表,其中存储着索引的值和包含这些值的数据所在行的物理地址(或逻…...
4. 小迪安全v2023笔记 javaEE应用
4. 小迪安全v2023笔记 javaEE应用 大体上跟随小迪安全的课程,本意是记录自己的学习历程,不能说是完全原创吧,大家可以关注一下小迪安全。 若有冒犯,麻烦私信移除。 默认有java基础。 文章目录 4. 小迪安全v2023笔记 javaEE应…...
anaconda修改安装的默认环境
📚博客主页:knighthood2001 ✨公众号:认知up吧 (目前正在带领大家一起提升认知,感兴趣可以来围观一下) 🎃知识星球:【认知up吧|成长|副业】介绍 ❤️如遇文章付费,可先看…...
MySQL 9.0 正式发行Innovation创新版已支持向量
从 MySQL 8.1 开始,官方启用了新的版本模型:MySQL 创新版 (Innovation) 和长期支持版 (LTS)。 根据介绍,两者的质量都已达到可用于生产环境级别。区别在于: 如果希望尝试最新的功能和改进,并喜欢与最新技术保持同步&am…...
基于Java+SpringMvc+Vue技术的智慧校园系统设计与实现
博主介绍:硕士研究生,专注于信息化技术领域开发与管理,会使用java、标准c/c等开发语言,以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年,拥有近12年的管理工作经验,拥有较丰富的技术架…...
模板手机网站建设多少钱/郑州竞价代运营公司
撰文 | JZ专栏 | 九章算法题目描述 有一个集合组成的list,如果有两个集合有相同的元素,将他们合并。返回最后还剩下几个集合。 思路点拨 先遍历所有元素,以元素为key,元素所属的集合id为value(是一个集合)建立hashmap࿰…...
做网站公司高端/搜狗站长工具综合查询
原标题:django打造电商项目本课程的特点是实战,老师将手把手教大家搞个“大”项目,开发一套功能完备的系统!全程采用企业级标准电商平台实战项目,以互联网公司标准开发流程,带领大家实践从0到项目正式上线。…...
互联网装修服务平台/新人学会seo
本文为 AI 研习社编译的技术博客,原标题 :Python: How To Reduce Memory Consumption By Half By Adding Just One Line Of Code?作者 | Alex Maison翻译 | 邓普斯•杰弗校对 | 酱番梨 整理 | 菠萝妹原文链接:https://medium.com/alexmaisiu…...
h5网站开发软件下载/站长之家官网登录入口
问题 Push failed: fatal: Authentication failed for httpspycharm invalid authentication data couldn’t kickstart handshaking 解决...
2015做哪些网站能致富/网络推广怎么做才有效
GPU显存管理 GPU:有两种方式访问GDDR5,一种是HUB统一接口进行分配,另一种是直接调Controller,比如Depth block,color block,texture block等都是直接Controller。直接调用的方式肯定快一些,我觉…...
如何上传网站到空间/怎么建立自己的企业网站
树的定义 二叉树 二叉树中的节点最多只能有两个节点,一个左侧节点,一个右侧节点。 二叉搜索树(BST) 二叉搜索树是二叉树的一种,只允许左侧节点小于右侧节点 创建二叉搜索树 function BinarySearchTree(){let Nodefunction(key…...