如何让 Emacs 俄罗斯方块变得更难
创始人
2024-03-02 04:35:22
0

你知道吗,Emacs 捆绑了一个俄罗斯方块的实现?只需要输入 M-x tetris 就行了。

在对文本编辑器的讨论中,Emacs 鼓吹者经常提到这一点。“没错,但是你那个编辑器能运行俄罗斯方块吗?”我很好奇,这会让大家相信 Emacs 更优秀吗?比如,为什么有人会关心他们是否可以在文本编辑器中玩游戏呢?“没错,但是你那台吸尘器能播放 mp3 吗?”

有人说,俄罗斯方块总是很有趣的。像 Emacs 中的所有东西一样,它的源代码是开放的,易于检查和修改,因此 我们可以使它变得更加有趣。所谓更加有趣,我的意思是更难。

让游戏变得更难的一个最简单的方法就是“隐藏下一个块预览”。你无法在知道下一个块会填满空间的情况下有意地将 S/Z 块放在一个危险的位置——你必须碰碰运气,希望出现最好的情况。下面是没有预览的情况(如你所见,没有预览,我做出的某些选择带来了“可怕的后果”):

预览框由一个名为 tetris-draw-next-shape 1 的函数设置:

(defun tetris-draw-next-shape ()
  (dotimes (x 4)
    (dotimes (y 4)
      (gamegrid-set-cell (+ tetris-next-x x)
                         (+ tetris-next-y y)
                         tetris-blank)))
  (dotimes (i 4)
    (let ((tetris-shape tetris-next-shape)
          (tetris-rot 0))
      (gamegrid-set-cell (+ tetris-next-x
                            (aref (tetris-get-shape-cell i) 0))
                         (+ tetris-next-y
                            (aref (tetris-get-shape-cell i) 1))
                         tetris-shape))))

首先,我们引入一个标志,决定是否允许显示下一个预览块 2 :

(defvar tetris-preview-next-shape nil
  "When non-nil, show the next block the preview box.")

现在的问题是,我们如何才能让 tetris-draw-next-shape 遵从这个标志?最明显的方法是重新定义它:

(defun tetris-draw-next-shape ()
  (when tetris-preview-next-shape
    ;; existing tetris-draw-next-shape logic
    ))

但这不是理想的解决方案。同一个函数有两个定义,这很容易引起混淆,如果上游版本发生变化,我们必须维护修改后的定义。

一个更好的方法是使用 advice。Emacs 的 advice 类似于 Python 装饰器,但是更加灵活,因为 advice 可以从任何地方添加到函数中。这意味着我们可以修改函数而不影响原始的源文件。

有很多不同的方法使用 Emacs advice(查看手册),但是这里我们只使用 advice-add 函数和 :around 标志。advice 函数将原始函数作为参数,原始函数可能执行也可能不执行。我们这里,我们让原始函数只有在预览标志是非空的情况下才能执行:

(defun tetris-maybe-draw-next-shape (tetris-draw-next-shape)
  (when tetris-preview-next-shape
    (funcall tetris-draw-next-shape)))

(advice-add 'tetris-draw-next-shape :around #'tetris-maybe-draw-next-shape)

这段代码将修改 tetris-draw-next-shape 的行为,而且它可以存储在配置文件中,与实际的俄罗斯方块代码分离。

去掉预览框是一个简单的改变。一个更激烈的变化是,让块随机停止在空中

本图中,红色的 I 和绿色的 T 部分没有掉下来,它们被固定下来了。这会让游戏变得 极其困难,但却很容易实现。

和前面一样,我们首先定义一个标志:

(defvar tetris-stop-midair t
  "If non-nil, pieces will sometimes stop in the air.")

目前,Emacs 俄罗斯方块的工作方式 类似这样子:活动部件有 x 和 y 坐标。在每个时钟滴答声中,y 坐标递增(块向下移动一行),然后检查是否有与现存的块重叠。如果检测到重叠,则将该块回退(其 y 坐标递减)并设置该活动块到位。为了让一个块在半空中停下来,我们所要做的就是破解检测函数 tetris-test-shape

这个函数内部做什么并不重要 —— 重要的是它是一个返回布尔值的无参数函数。我们需要它在正常情况下返回布尔值 true(否则我们将出现奇怪的重叠情况),但在其他时候也需要它返回 true。我相信有很多方法可以做到这一点,以下是我的方法的:

(defun tetris-test-shape-random (tetris-test-shape)
  (or (and
       tetris-stop-midair
       ;; Don't stop on the first shape.
       (< 1 tetris-n-shapes )
       ;; Stop every INTERVAL pieces.
       (let ((interval 7))
         (zerop (mod tetris-n-shapes interval)))
       ;; Don't stop too early (it makes the game unplayable).
       (let ((upper-limit 8))
         (< upper-limit tetris-pos-y))
       ;; Don't stop at the same place every time.
       (zerop (mod (random 7) 10)))
      (funcall tetris-test-shape)))

(advice-add 'tetris-test-shape :around #'tetris-test-shape-random)

这里的硬编码参数使游戏变得更困难,但仍然可玩。当时我在飞机上喝醉了,所以它们可能需要进一步调整。

顺便说一下,根据我的 tetris-scores 文件,我的 最高分 是:

01389   Wed Dec  5 15:32:19 2018

该文件中列出的分数默认最多为五位数,因此这个分数看起来不是很好。

给读者的练习

  1. 使用 advice 修改 Emacs 俄罗斯方块,使得每当方块下移动时就闪烁显示讯息 “OH SHIT”。消息的大小与块堆的高度成比例(当没有块时,消息应该很小的或不存在的,当最高块接近天花板时,消息应该很大)。
  2. 在这里给出的 tetris-test-shape-random 版本中,每隔七格就有一个半空中停止。一个玩家有可能能计算出时间间隔,并利用它来获得优势。修改它,使间隔随机在一些合理的范围内(例如,每 5 到 10 格)。
  3. 另一个对使用 Tetris 使用 advise 的场景,你可以试试 autotetris-mode
  4. 想出一个有趣的方法来打乱块的旋转机制,然后使用 advice 来实现它。

  1. Emacs 只有一个巨大的全局命名空间,因此函数和变量名一般以包名做前缀以避免冲突。
  2. 很多人会说你不应该使用已有的命名空间前缀而且应该将自己定义的所有东西都放在一个预留的命名空间中,比如像这样 my/tetris-preview-next-shape,然而这样很难看而且没什么意义,因此我不会这么干。

via: https://nickdrozd.github.io/2019/01/14/tetris.html

作者:nickdrozd 选题:lujun9972 译者:lujun9972 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

不使用emacsclien...
要在不使用emacsclient的情况下构建和安装Emacs,可以...
2024-12-28 15:01:45
不清楚EmacsLisp函...
下面是一个 Emacs Lisp 函数示例,可用于比较和演示:(d...
2024-12-27 14:01:19
比较emacs和vim,它...
Emacs和vim都是强大的文本编辑器,具有各自的优点和缺点。Em...
2024-12-13 14:31:36
崩溃:"Ctrl...
在"emacs -nw"中,按下"Ctrl-g"键可以取消当前操作...
2024-11-30 18:00:56
Android俄罗斯方块(...
下面是一个简单的实现Android俄罗斯方块游戏的示例代码:首先,...
2024-10-07 16:02:38
林纳斯•托瓦兹参与了Lin...
Linux内核已经33岁了,它的创造者Linus Torvalds...
2024-10-02 16:27:47

热门资讯

“Linux 中国” 开源社区... 这是一个艰难的决定 —— 这不是调侃,而是当你真正做出改变了你十几年生活轨迹的决定时,你就会明白这个...
硬核观察 #1263 德国铁路... #1 德国铁路公司招聘 Windows for Workgroups 3.11 管理员该职位要求了解...
人工智能教程(七):Sciki... 在本系列的 上一篇文章 中,我们用 TensorFlow 构建了第一个神经网络,然后还通过 Kera...
关闭 WSL 中正在运行的 L... 以下是使用 WSL 关闭在 Windows 内运行的 Linux 系统的不同方法。你使用 WSL 在...
MX Linux 23.2 “... 最新的 MX Linux 23.2 小版本更新带来了基本的改进和新增功能。MX Linux 推出了 ...
硬核观察 #1262 网络上最... #1 网络上最古老的软件存档库之一即将关闭美国新墨西哥州立大学(NMSU)最近宣布,即将于 2024...
如何在 RHEL 上通过绑定配... 在 Linux 中,你可以根据自己的需要创建高级网络设置,如 网络绑定 Network Bondin...
Linux Matrix 消息... Matrix 用户可以升级 Fractal 6 了!Fractal 是目前 最好的 Matrix 客...
如何通过 VLC 使用字幕 使用 VLC 媒体播放器播放和管理字幕的新手指南。我是一个超级动漫迷,如果你想观看最新的剧集,那么你...
硬核观察 #1261 X 计划... #1 X 计划在奥斯汀建立内容审核总部据报道,该部门将招聘 100 名全职员工,将主要关注 CSE ...