eight-puzzle-search 0.0.71
一款用于辅助学习八数码问题搜索求解的 Python 包 | A Python package for learning search algorithms of eight puzzle problem
Requires Python: >=3.7
Keywords: eight puzzle,search,八数码,搜索,文朔

# 八数码问题练习包

Language: [English](https://github.com/VincentSHI1230/eight-puzzle-search/blob/main/README_en.md) | 简体中文 (Simplified Chinese)

---

> 创建日期：2022-10-16  
> 更新日期：2022-10-24  
> 作者：Vincent SHI | 史文朔  
> blog：https://blog.vincent1230.top/  
> GitHub：[VincentSHI1230/eight-puzzle-search: 一款用于辅助学习八数码问题搜索求解的 Python 包 (github.com)](https://github.com/VincentSHI1230/eight-puzzle-search)  
> Gitee：[eight-puzzle-search: 一款用于辅助学习八数码问题搜索求解的 Python 包 (gitee.com)](https://gitee.com/Vincent1230/eight-puzzle-search)

一款用于辅助学习八数码问题搜索求解的 Python 包

## 安装

```bash
pip install --upgrade eight-puzzle-search
```

## 引入

```python
import eight_puzzle_search as eps
```

## 输入和输出

### eps.input_box() 交互式输入九宫格对象

`input_box(prompt: str = '') -> 'Box'`

使用高鲁棒性的交互式命令行输入九宫格对象。具有如下特性 (无需详阅)：

1. 允许分三行输入八数码问题的九宫格，也允许在第一行一次性输入；
1. 同时支持以任意数量空格 ` ` 和英文逗号 `,` 作为间隔输入；当分三行输入时，亦支持不间隔连续输入；
1. 同时支持以 `0`、`*`、`9` 表示九宫格的空格；当以英文逗号 `,` 作为间隔输入时亦支持以两个连续逗号 (`,,` 或 `, ,`) 表示；当不间隔连续输入时亦支持以空格 ` ` 表示；
1. 能够自动排除错误或不合理的输入；
1. 具有完善的提示文本，用户无需注意输入细则。

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明     |
| --- | ------ | -------- | -------- | ------ | -------- |
| 1   | prompt | str      | 否       | ''     | 提示信息 |

#### 返回值

| \   | 数据类型 | 说明                 |
| --- | -------- | -------------------- |
| 1   | `Box`    | 返回新建的九宫格对象 |

#### 示例 1

```python
a = eps.input_box()
# 输入 283
# 输入 164
# 输入 7 5
print(a)

```

```text
请按提示直接输入数值. 0 或 * 可代表空位.
enter the value directly as prompted.
0 or * can be used to represent blank.
第 1 行 | row 1: 283
第 2 行 | row 2: 164
第 3 行 | row 3: 7 5
[ 2 8 3
  1 6 4
  7 * 5 ]

moved via -> :
[ 2 8 3
  1 6 4
  7 * 5 ]

```

#### 示例 2

```python
b = eps.input_box('请输入变量 b 的值: ')
# 输入 1, 2, 3, 8,  , 4, 7, 6, 5
print(b)

```

```text
请输入变量 b 的值:
第 1 行 | row 1: 1, 2, 3, 8,  , 4, 7, 6, 5
[ 1 2 3
  8 * 4
  7 6 5 ]

moved via -> :
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### Box() 九宫格的实例化

`Box(value: list, history: str = '') -> 'Box'`

由于求解的基本单位，将在后文中详述。可以直接实例化该对象以输入九宫格。
若要使用该方式，必须保证 value 是由 0 - 9 九个 整型 `int` 数字组成的数组，其中 `0` 代表空格。

#### 传入参数

| \   | 参数名  | 数据类型  | 是否必填 | 默认值 | 说明                 |
| --- | ------- | --------- | -------- | ------ | -------------------- |
| 1   | value   | List[int] | 是       | -      | 九宫格对象的值       |
| 2   | history | str       | 否       | ''     | 九宫格对象的移动历史 |

#### 返回值

| \   | 数据类型 | 说明                   |
| --- | -------- | ---------------------- |
| 1   | `Box`    | 返回实例化的九宫格对象 |

#### 示例

```python
c = eps.Box([0, 2, 3, 1, 8, 4, 7, 6, 5])
print(c)

```

```text
moved via -> :
[ * 2 3
  1 8 4
  7 6 5 ]

```

### 九宫格对象的格式化输出

在上文中已经可见，九宫格对象经过优化，可以直接使用 `print()` 内置函数输出。

<div STYLE="page-break-after: always;"></div>

## 基础用法：使用预置函数进行盲目搜索

盲目搜索是最基础的搜索算法。在本代码包中，你可以直接使用函数运行盲目搜索，并观察它们。

### breadth_first_search() 宽度优先搜索

`breadth_first_search(start: 'Box', end: 'Box') -> None`
`bfs(start: 'Box', end: 'Box') -> None`

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明           |
| --- | ------ | -------- | -------- | ------ | -------------- |
| 1   | start  | `Box`    | 是       | -      | 起始九宫格对象 |
| 2   | end    | `Box`    | 是       | -      | 目标九宫格对象 |

#### 返回值 `None`

搜索结果直接打印，无返回值

#### 示例

```python
eps.bfs(a, b)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### depth_first_search() 宽度优先搜索

`depth_first_search(start: 'Box', end: 'Box') -> None`
`dfs(start: 'Box', end: 'Box') -> None`

**警告：深度优先搜索是不完备的搜索算法，在八数码问题中具有严重缺陷，本函数仅供展示，不可用于求解。**

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明           |
| --- | ------ | -------- | -------- | ------ | -------------- |
| 1   | start  | `Box`    | 是       | -      | 起始九宫格对象 |
| 2   | end    | `Box`    | 是       | -      | 目标九宫格对象 |

#### 返回值 `None`

搜索结果直接打印，无返回值

#### 示例

```python
eps.dfs(a, b)
# 输入: y

```

```text
SyntaxWarning: 深度优先搜索是不完备的搜索算法, 在八数码问题中具有严重缺陷, 本函数仅供展示, 不可用于求解.
The typical depth-first search is an incomplete search algorithm, which has serious defects here.
This function is only for demonstration and cannot be used for search.

是否仍要继续? (y/n) | continue? (y/n): y
->
-> D
-> DU
-> DUD
-> DUDU
...
-> DUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUD
-> DUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDUDU
Traceback (most recent call last):
    eps.dfs(a, b)
RecursionError: maximum recursion depth exceeded in comparison

```

### depth_limited_search() 有限深度优先搜索

`depth_limited_search(start: 'Box', end: 'Box') -> None`
`dls(start: 'Box', end: 'Box') -> None`

**警告：有限深度优先搜索是不完备的搜索算法**

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明           |
| --- | ------ | -------- | -------- | ------ | -------------- |
| 1   | start  | `Box`    | 是       | -      | 起始九宫格对象 |
| 2   | end    | `Box`    | 是       | -      | 目标九宫格对象 |
| 3   | limit  | int      | 是       | -      | 搜索深度限制   |

#### 返回值 `None`

搜索结果直接打印，无返回值

#### 示例 1

```python
eps.dls(a, b, 1)

```

```text
SyntaxWarning: 有限深度优先搜索是不完备的搜索算法 | depth limited search is an incomplete search algorithm
->
-> D
-> L
-> R
未能在限定深度内找到解 | cannot find solution within the depth limit

```

#### 示例 2

```python
eps.dls(a, b, 5)

```

```text
SyntaxWarning: 有限深度优先搜索是不 完备的搜索算法 | depth limited search is an incomplete search algorithm
->
-> D
-> DU
-> DUD
-> DUDU
-> DUDUD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### double_breadth_first_search() 双向宽度优先搜索

`double_breadth_first_search(start: 'Box', end: 'Box') -> None`
`dbfs(start: 'Box', end: 'Box') -> None`

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明           |
| --- | ------ | -------- | -------- | ------ | -------------- |
| 1   | start  | `Box`    | 是       | -      | 起始九宫格对象 |
| 2   | end    | `Box`    | 是       | -      | 目标九宫格对象 |

#### 返回值 `None`

搜索结果直接打印，无返回值

#### 示例

```python
eps.dbfs(a, b)

```

```text
start ->
forward -> D
forward -> L
forward -> R
reverse -> U
reverse -> D
...
reverse -> UD
reverse -> UL
reverse -> UR
...
forward -> DDU
forward -> DDL
forward -> DDR
forward moved via -> DDR:
[ * 2 3
  1 8 4
  7 6 5 ]

reverse moved via -> RD:
[ * 2 3
  1 8 4
  7 6 5 ]

totally moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

## 进阶用法：构建和使用启发式搜索

启发式搜索是搜索算法的核心之一。利用提供的 `search()` 函数和预置的评估函数，我们可以尝试实现启发式搜索。

**注意：评估函数 `fn` 由于是直接传入，故参数列表中只写函数名，不写括号。**

### search() 通用启发式搜索函数

`search(start: 'Box', end: 'Box', fn: Callable[[Dict[str, any]], int]) -> None`

`search` 函数需要你直接传入一个评价函数 `fn` 来决定搜索前沿的下一步动作。你可以使用预置的几个评价函数，也可以自己构建 `fn` 评价函数。构建方法将在稍后介绍。

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明           |
| --- | ------ | -------- | -------- | ------ | -------------- |
| 1   | start  | `Box`    | 是       | -      | 起始九宫格对象 |
| 2   | end    | `Box`    | 是       | -      | 目标九宫格对象 |
| 3   | fn     | function | 是       | -      | 启发函数       |

#### 返回值 `None`

搜索结果直接打印，无返回值

### lowest_step() 最小步数

`lowest_step`
`ls`

为 `search()` 函数构建的估价函数。采用历史记录的长度作为评价标准，单独使用时类似于广度优先搜索。

#### 示例

```python
eps.search(a, b, eps.ls)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### most_at_place() 最多在位

`most_at_place`
`mp`

为 `search()` 函数构建的估价函数。采用当前状态与目标状态的相同元素个数作为评价标准。

#### 示例

```python
eps.search(a, b, eps.mp)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### manhattan_distance() 曼哈顿距离

`manhattan_distance`
`mhd`

为 `search()` 函数构建的估价函数。采用当前状态与目标状态的曼哈顿距离作为评价标准。

#### 示例

```python
eps.search(a, b, eps.mhd)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

### 自己构建 fn 评估函数

`search()` 函数需要传入一个评估函数。你可以自己构造它。

`fn` 评估函数需要遵守以下输入输出规则：

`fn(task: dict) -> int`

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明                   |
| --- | ------ | -------- | -------- | ------ | ---------------------- |
| 1   | task   | dict     | 是       | -      | 用于完成评估的基本信息 |

| task 字段       | 数据类型  | 说明                 |
| --------------- | --------- | -------------------- |
| task['start']   | List[int] | 求解的起始值         |
| task['end']     | List[int] | 求解的目标值         |
| task['now']     | List[int] | 需评价的当前节点的值 |
| task['history'] | str       | 当前节点的历史记录   |

#### 返回值

| \   | 数据类型 | 说明               |
| --- | -------- | ------------------ |
| 1   | int      | 评估得出的搜索代价 |

#### 示例 1 - 利用预置评估函数构建新评估函数

```python
def astar(task):
    return eps.lowest_step(task) + eps.manhattan_distance(task)

eps.search(a, b, astar)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

#### 示例 2 - 完全重新构建估价函数

```python
def astar(task):
    return len(task['history']) + sum(abs(task['now'].index(i) // 3 - task['end'].index(i) // 3)
                                      + abs(task['now'].index(i) % 3 - task['end'].index(i) % 3)
                                      for i in range(1, 9))

eps.search(a, b, astar)

```

```text
->
-> D
-> L
-> R
-> DU
-> DD
...
-> DDRUU
-> DDRUD
-> DDRUL
moved via -> DDRUL:
[ 1 2 3
  8 * 4
  7 6 5 ]

```

## 高级用法：直接操作 Box 对象

`Box` 对象是这个代码包的核心内容，提供了众多的方法以及丰富的嵌套封装，具有很大的可操作空间。你可以详尽阅读本文档，选择合适自己的封装程度，自己操作实现搜索。

## 究极用法：直接阅读本代码包源码

并找出我的 bug。

<div STYLE="page-break-after: always;"></div>

## Box 对象结构说明文档

### Box() 实例化

`Box(value: list, history: str = '') -> 'Box'`

按照格式传入九宫格的值即可实例化 `Box`，需遵守以下规则：

1. 以整型数列表的形式传入；
1. 按照行先列后的顺序传入，即 `[a00, a01, a02, a10, ..., a21, a22]`；
1. 仅限传入 0 - 9 九个数字，其中 0 代表空格，每个数字有且只有一次出现。

可选传入九宫格移动的历史数据，需遵守以下规则：

1. 仅能含有 `U`、`P`、`L`、`R` 四个字符，分别代表上、下、左、右；
1. 例外地，字符串开头允许含有 `->` 或 `-> `，但该片段会在传入后被自动删除。

#### 传入参数

| \   | 参数名  | 数据类型  | 是否必填 | 默认值 | 说明                 |
| --- | ------- | --------- | -------- | ------ | -------------------- |
| 1   | value   | List[int] | 是       | -      | 九宫格对象的值       |
| 2   | history | str       | 否       | ''     | 九宫格对象的移动历史 |

#### 返回值

| \   | 数据类型 | 说明                   |
| --- | -------- | ---------------------- |
| 1   | `Box`    | 返回实例化的九宫格对象 |

#### 示例

```python
c = eps.Box([0, 2, 3, 1, 8, 4, 7, 6, 5])
print(c)

```

```text
moved via -> :
[ * 2 3
  1 8 4
  7 6 5 ]

```

### value 九宫格对象的值

`'Box'.value -> list[int]`

#### 示例

```python
print(a.value)

```

```text
[2, 8, 3, 1, 6, 4, 7, 0, 5]

```

### set_value() 修改当前九宫格对象的值而不改变其历史记录

`'Box'.set_value(value: List[int]) -> None`

#### 传入参数

| \   | 参数名 | 数据类型  | 是否必填 | 默认值 | 说明   |
| --- | ------ | --------- | -------- | ------ | ------ |
| 1   | value  | List[int] | 是       | -      | 新的值 |

#### 返回值 `None`

本方法不返回任何值。

### history 九宫格对象的移动历史

`'Box'.history -> str`

### add_history() 添加历史记录而不改变九宫格对象的值

`'Box'.add_history(history: str) -> None`

#### 传入参数

| \   | 参数名  | 数据类型 | 是否必填 | 默认值 | 说明         |
| --- | ------- | -------- | -------- | ------ | ------------ |
| 1   | history | str      | 是       | -      | 新的历史记录 |

#### 返回值 `None`

本方法不返回任何值。

### del_history() 删除历史记录而不改变九宫格对象的值

`'Box'.del_history(length: int = 1) -> str`

#### 传入参数

| \   | 参数名 | 数据类型 | 是否必填 | 默认值 | 说明       |
| --- | ------ | -------- | -------- | ------ | ---------- |
| 1   | length | int      | 是       | -      | 删除的长度 |

#### 返回值

| \   | 数据类型 | 说明                 |
| --- | -------- | -------------------- |
| 1   | str      | 返回被删除的历史记录 |

### copy() 复制当前的九宫格对象

`'Box'.copy() -> 'Box'`

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值

| \   | 数据类型 | 说明                       |
| --- | -------- | -------------------------- |
| 1   | `Box`    | 返回复制后的九宫格对象拷贝 |

### up() 在当前的九宫格对象内向上移牌

`'Box'.up() -> None`

注意：该方法直接修改当前对象，不返回值。

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值 `None`

本方法不返回任何值。

### upped 返回向上移牌后的九宫格对象

`'Box'.upped -> 'Box'`

注意：该参数返回操作后的对象，不会改变当前对象。

### down() 在当前的九宫格对象内向下移牌

`'Box'.down() -> None`

注意：该方法直接修改当前对象，不返回值。

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值 `None`

本方法不返回任何值。

### downed 返回向下移牌后的九宫格对象

`'Box'.downed -> 'Box'`

注意：该参数返回操作后的对象，不会改变当前对象。

### left() 在当前的九宫格对象内向左移牌

`'Box'.left() -> None`

注意：该方法直接修改当前对象，不返回值。

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值 `None`

本方法不返回任何值。

### lefter 返回向左移牌后的九宫格对象

`'Box'.lefter -> 'Box'`

注意：该参数返回操作后的对象，不会改变当前对象。

### rigth() 在当前的九宫格对象内向右移牌

`'Box'.right() -> None`

注意：该方法直接修改当前对象，不返回值。

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值 `None`

本方法不返回任何值。

### righter 返回向上右移牌后的九宫格对象

`'Box'.righter -> 'Box'`

注意：该参数返回操作后的对象，不会改变当前对象。

### able 查询并返回可用的移动方向

`'Box'.able -> Set[str]`

以字符串集合的形式返回，含有 `U`、`P`、`L`、`R` 四个字符，分别代表上、下、左、右四个可能的方向。

### expand() 拓展下一层

`'Box'.expand() -> List['Box']`

运行本方法，会自动检查可移动的方向，并返回该节点所有可能的下一层节点。

#### 传入参数 `None`

本方法不传入任何值。

#### 返回值

| \   | 数据类型    | 说明                   |
| --- | ----------- | ---------------------- |
| 1   | List[`Box`] | 返回新的九宫格对象列表 |

#### 示例

```python
print(a.expand())

```

```text
[
    moved via -> D:
    [ 2 8 3
      1 * 4
      7 6 5 ],

    moved via -> L:
    [ 2 8 3
      1 6 4
      7 5 * ],

    moved via -> R:
    [ 2 8 3
      1 6 4
      * 7 5 ]
]

```

<div STYLE="page-break-after: always;"></div>

## 辅助工具

### @run_time 用于计算函数运行时间的装饰器

`@run_time`
`@rt`

这是一个装饰器，用法是新建一个函数作为需要计时的片段，然后添加装饰器正常运行。

#### 示例

```python
import eight_puzzle_search as eps

@eps.run_time
def main():
    a = eps.Box([2, 8, 3, 1, 6, 4, 7, 0, 5])
    b = eps.Box([1, 0, 2, 8, 4, 3, 7, 6, 5])
    eps.bfs(a, b)

if __name__ == '__main__':
    main()

```

```test
->
-> D
-> L
-> R
-> DU
...
-> DDRULLDU
-> DDRULLDR
moved via -> DDRULLDR:
[ 1 * 2
  8 4 3
  7 6 5 ]

运行时间 | run time: 0.0156250s

```

### @run_time_5 重复运行 5 次并计算平均运行时间的装饰器

`@run_time_5`
`@rt5`

这是一个装饰器，用法是新建一个函数作为需要计时的片段，然后添加装饰器正常运行。

#### 示例

```python
import eight_puzzle_search as eps

@eps.rt5
def main():
    a = eps.Box([2, 8, 3, 1, 6, 4, 7, 0, 5])
    b = eps.Box([1, 0, 2, 8, 4, 3, 7, 6, 5])
    eps.bfs(a, b)

if __name__ == '__main__':
    main()

```

```test
->
-> D
-> L
...
->
-> D
-> L
...
->
-> D
-> L
...
->
-> D
-> L
...
->
-> D
-> L
...
-> DDRULLDU
-> DDRULLDR
moved via -> DDRULLDR:
[ 1 * 2
  8 4 3
  7 6 5 ]

--------------------------------
  1  |         0.0312500s
  2  |         0.1093750s
  3  |         0.0781250s
  4  |         0.0625000s
  5  |         0.0468750s
--------------------------------
平均时间 | average time: 0.0656250s

```