自动生成益智游戏关卡——以《推箱子》为例

5,774 阅读6分钟

Automatically Generate Puzzle Game Levels - Take "Sokoban" as An Example

益智类游戏通常需要为玩家提供大量的关卡,而为益智类游戏设计一个优质的关卡通常需要耗费关卡设计师较多的人力。本文提供一种自动生成益智类游戏关卡的技术。以随机生成《推箱子》游戏关卡为例。

Puzzle games usually need to provide a large number of levels for players, and designing a high-quality level for puzzle games usually requires more manpower from the level designer. This article provides a technology for automatically generating puzzle game levels. Take the randomly generated levels of "Sokoban" as an example.

《推箱子》游戏中有墙壁、地板、目标点三类不会发生变动的游戏地图数据,以及箱子、推箱子的小人两种可变的对象。

"Sokoban" has three types of game elements that are fixed throughout each level of the game: wall, floor and target point, as well as two variable objects: boxes and the player.

image.png 《推箱子》的游戏规则是:玩家控制小人在地图上 上下左右 移动,当小人的移动方向上有箱子时,可以推动箱子向相应的方向移动。但是遇到墙壁,小人不能移动;如果推箱子时箱子前面有墙壁或另一个箱子,则小人不能推动箱子前进。

The game is played on a board of squares, where each square is a floor or a wall. Some floor squares contain boxes, and some floor squares are marked as target points (storage locations). The player is confined to the board and may move horizontally or vertically onto empty squares (never through walls or boxes). The player can move a box by walking up to it and push it to the square beyond. Boxes cannot be pulled, and they cannot be pushed to squares with walls or other boxes. The number of boxes equals the number of storage locations.

《推箱子》的胜利条件是所有的箱子都位于目标点之上。失败的条件是没有箱子能够被推动且有箱子没有位于目标点之上。

The puzzle is solved when all boxes are placed at storage locations. The failure conditions are that no boxes can be pushed and not all boxes are above the target points.

一个有意义的益智游戏关卡,通常要满足以下要素:

We can solve a "Sokoban" level by State space search. A meaningful puzzle game usually meets the following requirements:

  1. 关卡要可解:至少有一种推箱子的步骤,能够达到胜利状态。
  1. The level should be solvable: there is at least one series of steps to push the boxes, which can lead to the victory.
  1. 关卡的解状态空间要大:如果一个关卡只有有限的状态空间,就意味着这个关卡是一个一本道的关卡,玩家很容易试出解法。比如下面这个关卡:
  1. The scale of possible states of a level should be large: if a level has only a few possible states, it means that the level is a one-way level, which is easy for players to find out the solution by trial and error. For example, the following level:

image.png 3) 关卡的求解最短路径要足够长:如果一个关卡的最优解路径过短,说明关卡的解谜程度较弱,玩家看一眼就能看出解法。比如下面这个关卡:

  1. The shortest-path from the initial state to the goal state of a level should be long enough: if the optimal path to solving a level is too short, the player can see the solution at a glance. For example, the following level:

image.png

为此,一个自动关卡生成器,通常由三部分组成:
To this end, an automatic level generator usually consists of three parts:

  1. 关卡生成器:采用随机或者过程化的方式增量式地生成关卡。
  1. Level Generator: Generate levels incrementally in a random or procedural way.
  1. 关卡求解器:输入关卡生成器生成的一个关卡,判断其是否有解。
  1. Level Solver: Input a level generated by the Level Generator and determine whether it has at least one solution.
  1. 关卡过滤器:输入一个生成好且有解的关卡,判断其解状态空间与最小求解步数是否满足需求。
  1. Level filter: Input the state space of solving the generated level to determine whether its possible state amounts and minimum solution steps meet the requirements.

三个部分的相互作用如下图所示:

The interaction of the three parts is shown in the following figure:

image.png

下面我们详细阐述一下关卡生成器、求解器与过滤器的具体实现。 Let's elaborate on the specific implementation of the level generator, solver, and filter.

关卡生成器

Level Generator

关卡生成器采用增量式随机生成关卡的策略:

image.png

关卡求解器

我们采用广度优先搜索的算法来求解推箱子谜题。理由是:

  1. 广度优先可以计算出游戏从初始状态到解谜成功状态的最少步骤。
  2. 广度优先可以回溯取得解谜过程的状态序列,这些状态序列就是谜题的最短解题步骤。
  3. 广度优先搜索的过程中会获得一棵解法树,这棵树的深度与广度可以衡量所生成关卡的解状态空间复杂度。

由于游戏中可变的单位只有箱子和小人的位置,所以状态节点只需要记录箱子与小人的位置即可。需要注意的是,在用广度优先搜索的过程中,会发生状态空间爆炸的情况,必须对每一个状态节点进行剪枝,否则求解一次非常慢。可以考虑从以下几点进行剪枝:

  • 去重:将每个状态节点散列到一个Hash临接表上,每次生成新的状态都判断之前是否有同样的游戏状态,如果有,则不添加该状态。

  • 判断死锁:比如以下情况中有箱子没有办法被推动,如果此时箱子没有位于目标点上,则说明该状态之后不可能达到胜利状态,不添加该状态。

死锁情况一:箱子被推到墙角,存在相邻的两个方向均为墙壁。

image.png

死锁情况二:四个箱子挤到一起,动弹不得。其中任意的箱子换成墙壁依然是这种情况。

image.png

  • PI-Corral剪枝:已经有不少文献专门研究推箱子的求解工作,其中比较著名的是PI-Corral剪枝思想: sokobano.de/wiki/index.…
    推箱子的求解器如果要做好,是一个比较庞大的工作,在此不再展开。

关卡过滤器

利用广度优先搜索已经能够计算出生成的每个关卡的解状态空间与最短解谜步数。因此过滤器只需要把迭代过程中生成的各有解的关卡的解状态空间与最短解谜步数进行比较与权衡,选出其中状态空间与最短解谜步数均比较多的关卡,作为最终输出的关卡。

结果展示

以下是采用我们的关卡自动生成器生成的一些推箱子关卡。其中迭代次数为广度优先搜索实现的求解器找到该关卡最优解时经过的迭代次数。最小步数为解谜成功所需要的最少推动箱子的步数 + 1(初始状态也被记为1步)。

image.png

image.png

image.png

image.png

image.png

源代码

github.com/huanggaole/…