Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ShenMian committed Mar 1, 2024
0 parents commit b2dfb0e
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/deploy.yml
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
6 changes: 6 additions & 0 deletions book.toml
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"
7 changes: 7 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Summary

[介绍](introduction.md)

- [关卡](level/README.md)
- [解析](level/parse.md)
- [标准化]()
Binary file added src/assets/boxworld_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 介绍

本文将描述如何实现一个推箱子游戏, 代码示例使用 Rust 语言编写.
推箱子游戏具有以下特点:

- 规则简单. 可以专注于实现功能, 而非理解复杂的游戏规则和机制.
- 基本功能易于实现.
- 有具有挑战的高级功能. 比如纯鼠标控制(也称为点推), 逆推等.
- 有需要深入钻研的求解器, 用于自动求解推箱子关卡.

本文将由浅入深的介绍上面功能并提供实现的思路.
96 changes: 96 additions & 0 deletions src/level/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# 关卡

推箱子关卡使用最广泛的格式为 XSB, 最初由 XSokoban 所使用. 该格式使用 ASCII 字符来表示地图元素, 支持注释和附加元数据.
以关卡 `Boxworld #1` 为例:

![Boxworld #1](../assets/boxworld_1.png)

其 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 ...
}
```
1 change: 1 addition & 0 deletions src/level/parse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# 解析

0 comments on commit b2dfb0e

Please sign in to comment.