-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b96ad90
Showing
9 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: Test | ||
|
||
on: | ||
push: | ||
branches: [main] | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
permissions: | ||
contents: write | ||
concurrency: | ||
group: ${{ github.workflow }}-${{ github.ref }} | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Setup mdBook | ||
uses: peaceiris/actions-mdbook@v1 | ||
with: | ||
mdbook-version: 'latest' | ||
|
||
- run: mdbook build | ||
|
||
- name: Deploy | ||
uses: peaceiris/actions-gh-pages@v3 | ||
if: ${{ github.ref == 'refs/heads/main' }} | ||
with: | ||
github_token: ${{ secrets.GITHUB_TOKEN }} | ||
publish_dir: ./book |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
book |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[book] | ||
title = "Sokoban Tutorial" | ||
authors = ["ShenMian <sms_school@outlook.com>"] | ||
language = "cn" | ||
multilingual = false | ||
src = "src" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Summary | ||
|
||
[介绍](introduction.md) | ||
|
||
- [关卡](level/README.md) | ||
- [解析](level/parse.md) | ||
- [标准化](level/normalization.md) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# 介绍 | ||
|
||
本文将描述如何实现一个推箱子游戏, 代码示例使用 Rust 语言编写. | ||
推箱子游戏具有以下特点: | ||
|
||
- 规则简单. 可以专注于实现功能, 而非理解复杂的游戏规则和机制. | ||
- 基本功能易于实现. | ||
- 有具有挑战的高级功能. 比如纯鼠标控制(也称为点推), 逆推等. | ||
- 有需要深入钻研的求解器, 用于自动求解推箱子关卡. | ||
|
||
本文将由浅入深的介绍上面功能并提供实现的思路. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# 关卡 | ||
|
||
推箱子关卡使用最广泛的格式为 XSB, 最初由 XSokoban 所使用. 该格式使用 ASCII 字符来表示地图元素, 支持注释和附加元数据. | ||
以关卡 `Boxworld #1` 为例: | ||
|
||
<img src="../assets/boxworld_1.png" alt="Boxworld #1" width="70%" style="display: block; margin: 0 auto"/> | ||
|
||
其 XSB 格式关卡的数据如下: | ||
|
||
```txt | ||
;Level 1 | ||
__###___ | ||
__#.#___ | ||
__#-#### | ||
###$-$.# | ||
#.-$@### | ||
####$#__ | ||
___#.#__ | ||
___###__ | ||
Title: Boxworld 1 | ||
Author: Thinking Rabbit | ||
``` | ||
|
||
- 第 1 行, 以 `;` 开头的单行注释. | ||
- 第 2-9 行, 使用 ASCII 字符表示的地图数据. | ||
- 第 10-11 行, 包括关卡标题和作者的元数据. | ||
|
||
| ASCII 符号 | 描述 | | ||
| ----------------- | -------------- | | ||
| `<SPACE>`/`-`/`_` | Floor | | ||
| `#` | Wall | | ||
| `$` | Box | | ||
| `.` | Goal | | ||
| `@` | Player | | ||
| `+` | Player on goal | | ||
| `*` | Box on goal | | ||
|
||
## 表示地图 | ||
|
||
地图共包含 5 种元素, 其中部分元素可能叠加(比如玩家位于目标上). 因此可以使用比特位来表示地图中的每个格子包含哪些元素. | ||
创建用于表示地图元素的比特位: | ||
|
||
```rs | ||
use bitflags::bitflags; | ||
|
||
bitflags! { | ||
pub struct Tiles: u8 { | ||
const Floor = 1 << 0; | ||
const Wall = 1 << 1; | ||
const Box = 1 << 2; | ||
const Goal = 1 << 3; | ||
const Player = 1 << 4; | ||
} | ||
} | ||
``` | ||
|
||
使用一维数组来存储地图数据并使用二维向量存储地图尺寸. | ||
|
||
```rs | ||
use nalgebra::Vector2; | ||
|
||
pub struct Map { | ||
data: Vec<Tiles>, | ||
dimensions: Vector2<i32>, | ||
// ... SKIP ... | ||
} | ||
``` | ||
|
||
使用一维数组而非二维数组是因为一维数组更平坦, 进行部分操作时更简单高效: | ||
|
||
```rs | ||
impl Map { | ||
pub fn with_dimensions(dimensions: Vector2<i32>) -> Self { | ||
Self { | ||
data: vec![Tiles::empty(); (dimensions.x * dimensions.y) as usize], | ||
dimensions, | ||
// ... SKIP ... | ||
} | ||
} | ||
// ... SKIP ... | ||
} | ||
``` | ||
|
||
## 表示关卡 | ||
|
||
关卡数据可分为三个部分: 地图数据, 元数据和注释. 其中注释可以作为元数据. | ||
元数据是一个键值对的集合, 因此可以使用 HashMap 来存储. | ||
可以使用下面的结构体存储关卡数据: | ||
|
||
```rs | ||
pub struct Level { | ||
map: Map, | ||
metadata: HashMap<String, String>, | ||
// ... SKIP ... | ||
} | ||
``` | ||
|
||
## 行程编码(Run length encoding) | ||
|
||
行程编码(Run length encoding, RLE)经常被用于压缩推箱子的关卡和解决方案. | ||
|
||
```txt | ||
### | ||
#.### | ||
#*$ # | ||
# @ # | ||
##### | ||
``` | ||
|
||
经 RLE 编码后可得: | ||
|
||
```txt | ||
3# | ||
#.3# | ||
#*$-# | ||
#--@# | ||
5# | ||
``` | ||
|
||
可以看出, 虽然编码后的关卡有更小的体积, 但不再能直观地看出关卡的结构. | ||
|
||
RLE 编码后的关卡通常还会使用 `|` 来分割行, 而非 `\n`. 使其看上去更加紧凑: | ||
|
||
```txt | ||
3#|#.3#|#*$-#|#--@#|5# | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# 标准化 | ||
|
||
TODO | ||
|
||
## 激进的标准化 | ||
|
||
激进的标准化可能改变关卡的解. | ||
以最简单的关卡为例: | ||
|
||
```txt | ||
##### | ||
#@$.# | ||
##### | ||
``` | ||
|
||
因为玩家开始的位置三面有墙, 位于死胡同, 只能向右移动. 得到结果: | ||
|
||
```txt | ||
##### | ||
# @*# | ||
##### | ||
``` | ||
|
||
玩家左侧死胡同属于无用的区域, 使用墙体填充. 玩家右侧位于目标上的箱子处于死锁状态, 属于无用的箱子和目标, 使用墙体填充. 最后去除多余的墙体得到激进的标准化结果: | ||
|
||
```txt | ||
### | ||
#@# | ||
### | ||
``` | ||
|
||
这是一个最简单的非标准关卡, 因为其没有箱子和目标, 解为空. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# 解析 | ||
|
||
## 单个关卡解析 | ||
|
||
## 多个关卡解析 |