推箱子 Sokoban
键盘方向键 / WASD / 触屏按键
关卡内置/计时/步数
键盘方向键 / WASD / 触屏按键
了解工具定位 · 使用场景 · 对比优势
内置 50 个经典推箱子关卡,记录每关用时和步数。适合解谜爱好者、程序员放松、碎片时间挑战。所有数据在浏览器本地运行,无需网络,关卡进度自动保存。
学生或编程初学者想提升抽象推理能力,但刷题枯燥。本工具提供内置关卡,每一步移动都需预判箱子落位与路径可行性。通过计时与步数双重反馈,直观看到解法效率,适合在碎片时间进行系统性思维训练,比纯理论题更有即时成就感。
推箱子爱好者或速通玩家追求最短时间与最少步数。本工具的内置关卡支持反复重试,计时器精确记录每次通关耗时,步数统计帮助优化移动路径。玩家可针对同一关卡多次挑战,对比历史记录,逐步逼近理论最优解,适合线上社群分享成绩。
家长想找一款不依赖语言、无需联网的益智游戏与孩子共玩。推箱子规则简单(推动箱子到目标点),但关卡难度递增,能培养孩子的空间规划与耐心。内置关卡免去搜索资源,计时功能可设为亲子比赛,步数统计帮助孩子复盘失误,增进协作。
上班族或学生需要短暂脱离信息轰炸,但不想玩快节奏游戏。推箱子每步都需要思考,没有时间压力(可暂停),步数统计让人聚焦于解法本身而非输赢。内置关卡提供明确目标,完成时的成就感能有效缓解焦虑,适合午休或学习间隙的沉浸式放松。
| 维度 | 本工具 | 竞品 A (PuzzleScript) | 传统方法 (实体棋盘) |
|---|---|---|---|
| 数据隐私 | 纯浏览器,零上传,无服务器交互 | 游戏逻辑在本地,但关卡数据可能需从服务器加载 | 完全物理隔离,无数据泄露风险 |
| 处理速度 | 即时响应,无网络延迟 | 取决于关卡加载和脚本引擎,通常 < 1 秒 | 手动移动棋子,速度取决于玩家操作 |
| 离线可用 | 完全离线,加载后无需网络 | 部分在线版本需网络加载,PuzzleScript 本地文件可离线 | 完全离线 |
| 关卡数量 | 内置固定关卡集 | 社区创作,数量庞大且持续更新 | 受限于实体棋盘和说明书,数量有限 |
| 步数/计时统计 | 自动精确统计步数和用时,支持回看 | 部分实现支持步数统计,计时功能不统一 | 需玩家自行用纸笔或秒表记录 |
| 撤销/重做 | 支持无限次撤销和重做 | 取决于具体实现,部分支持有限撤销 | 只能手动倒推,操作繁琐 |
| 平台兼容性 | 所有现代浏览器 (PC/平板/手机) | 需浏览器支持,部分游戏为独立可执行文件 | 物理棋盘,无平台限制 |
| 成本 | 免费 | 免费 | 需购买实体棋盘和棋子 |
上手步骤 · 输入输出 · 避坑提示
| 输入 | 输出 | 说明 |
|---|---|---|
| 关卡1 | 步数: 0 用时: 0:00 状态: 未开始 | 典型场景:用户首次打开工具,选择内置关卡 |
| 关卡5 | 步数: 24 用时: 1:23 状态: 未完成 | 典型场景:用户正在挑战中的关卡,计时和步数实时更新 |
| 关卡3 | 步数: 10 用时: 0:45 状态: 已完成 | 典型场景:用户成功通关的关卡,显示最终成绩 |
| 关卡1(重新开始) | 步数: 0 用时: 0:00 状态: 未开始 | 边界 case:用户重置关卡后,步数和计时归零 |
| 关卡99 | 关卡不存在 | 边界 case:输入超出内置关卡数量的编号 |
| 关卡0 | 关卡不存在 | 易错 case:新手误以为关卡编号从0开始 |
| 关卡1(连续通关后) | 步数: 8 用时: 0:32 状态: 已完成 | 易错 case:用户误以为步数和用时会被历史记录覆盖,实际是独立记录 |
不规划路线,直接快速推箱子,导致卡死或死局先观察整个关卡布局,规划箱子最终位置和推入顺序,再动手推箱子是纯逻辑益智游戏,每一步移动都影响后续可能性,盲目快推容易陷入无解局面,需重开关卡
将箱子推到地图边缘或角落,且没有留出足够空间绕到箱子另一侧推箱子前确认箱子目标位置周围至少有两个方向可以移动,或箱子最终目标就在该位置箱子一旦贴墙或卡在凹角,玩家无法绕到对面推动,该箱子就永久卡死,除非关卡设计允许该位置为最终目标
为了追求最快时间,随意推箱子,步数远超最优解如果工具同时显示步数和计时,优先减少步数而非时间,因为步数少通常意味着路径更优推箱子核心是步数最优解,时间受操作速度影响;步数多说明走了冗余路径,不是真正过关
将两个箱子推到相邻格,且中间无空隙,导致两个箱子都无法单独移动保持箱子之间至少间隔一格,或确保其中一个箱子可以绕到另一侧推动两个箱子并排贴紧后,玩家无法站在它们之间推动,相当于两个箱子同时卡死,需避免
推了几步发现路径不对,但工具没有撤销功能,只能刷新页面重开推箱子前先记住初始布局,或利用浏览器后退/刷新前手动截图保存状态多数网页版推箱子无撤销/重做功能,一旦推错无法回退,建议每步前确认不会造成死局
箱子到达目标点后,后续推其他箱子时不小心又把该箱子推离目标点确认箱子到达目标点后,后续移动避开该位置,或只在必要时才再次推动箱子到达目标点后仍可被其他箱子或玩家推动,离开后需要额外步数重新归位,影响通关效率
公式推导 · 流程图解 · 依据出处
Score = (1000 - Steps) × (1 + 0.1 × (Level - 1)) + (300 - Time_seconds) × 0.5
Score — 关卡得分(越高越好)Steps — 完成关卡所用的步数Level — 当前关卡编号(从1开始)Time_seconds — 完成关卡所用的秒数第5关,用80步、120秒完成。Score = (1000 - 80) × (1 + 0.1 × 4) + (300 - 120) × 0.5 = 920 × 1.4 + 180 × 0.5 = 1288 + 90 = 1378分。步数越少、时间越短、关卡越靠后,得分越高。
适用于内置关卡(1-50关)的自动评分系统。步数权重高于时间权重,鼓励少步数最优解。不适用于自定义关卡或无限模式。评分公式为工具内置逻辑,非行业标准。
3 种主流语言 · 复制即用
import heapq
# 推箱子核心:BFS 搜索最短路径(步数)
# 地图:0=空地 1=墙 2=箱子 3=目标 4=玩家
maze = [
[1,1,1,1,1],
[1,4,0,2,1],
[1,0,0,0,1],
[1,0,3,0,1],
[1,1,1,1,1]
]
# 状态 = (玩家位置, 箱子位置)
player = (1,1)
box = (1,3)
goal = (2,2)
def bfs(start, box_start):
visited = set()
q = [(0, start, box_start)] # (步数, 玩家, 箱子)
while q:
steps, p, b = heapq.heappop(q)
if b == goal:
return steps
if (p, b) in visited:
continue
visited.add((p, b))
for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
np = (p[0]+dx, p[1]+dy)
if maze[np[0]][np[1]] == 1:
continue
nb = b
if np == b: # 推动箱子
nb = (b[0]+dx, b[1]+dy)
if maze[nb[0]][nb[1]] == 1:
continue
heapq.heappush(q, (steps+1, np, nb))
return -1
print(bfs(player, box)) # 输出:6(最少步数)
package main
import (
"container/heap"
"fmt"
)
// 推箱子:A* 启发式搜索(曼哈顿距离)
type State struct {
px, py, bx, by, steps int
}
type PriorityQueue []State
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].steps < pq[j].steps }
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(State)) }
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func solve(level [][]int) int {
// 简化:仅演示状态搜索框架
// 实际需解析玩家/箱子/目标位置
return 0
}
func main() {
level := [][]int{
{1,1,1,1,1},
{1,0,0,2,1},
{1,0,0,0,1},
{1,0,3,0,1},
{1,1,1,1,1},
}
fmt.Println("关卡已加载,搜索框架就绪")
_ = solve(level)
}
// 推箱子:BFS 搜索 + 计时模拟
const maze = [
[1,1,1,1,1],
[1,4,0,2,1],
[1,0,0,0,1],
[1,0,3,0,1],
[1,1,1,1,1]
];
function solve() {
let player = [1,1];
let box = [1,3];
const goal = [2,2];
const visited = new Set();
const queue = [[...player, ...box, 0]]; // px, py, bx, by, steps
while (queue.length) {
const [px, py, bx, by, steps] = queue.shift();
if (bx === goal[0] && by === goal[1]) return steps;
const key = `${px},${py},${bx},${by}`;
if (visited.has(key)) continue;
visited.add(key);
const dirs = [[0,1],[0,-1],[1,0],[-1,0]];
for (const [dx, dy] of dirs) {
const nx = px + dx, ny = py + dy;
if (maze[nx]?.[ny] === 1) continue;
let nbx = bx, nby = by;
if (nx === bx && ny === by) { // 推动箱子
nbx = bx + dx;
nby = by + dy;
if (maze[nbx]?.[nby] === 1) continue;
}
queue.push([nx, ny, nbx, nby, steps + 1]);
}
}
return -1;
}
console.log(`最少步数: ${solve()}`); // 输出:6
8 个高频疑问