sokoban推箱子攻略_推箱子1~15关图解
1.如何制作推箱子动画答案?
2.关于Haskell推箱子程序的问题。
3.推箱子的手机游戏
//*******************************************************
Sokoban.h:类定义?Sokoban.c:类成员函数实现
Use_Sokoban.c:主函数
请用VC6(别编译器的也行)先运行Use_Sokoban.c文件,要编译该文件一下,
再点Project->?Add?To?Project->?Files?选择Sokoban.c文件,
即将Sokoban.c加载到工程里,最后运行就OK拉。
//*******************************************************
Sokoban.h
//*******************************************************
#ifndef?SOKOBAN_H_//防止文件重复包含
#define?SOKOBAN_H_
#include?<queue>
using?std::queue;
//每一步的数据类型
struct?node?
{
int?bx,?by;?//箱子的坐标 int?px,?py;?//人的坐标};
//推箱子类
class?Sokoban?
{
private:
enum?{L?=?15,?H?=?7}; char?GameMap[H][L];?//地图 int?Pex,?Pey;//人的位置 int?Boxx,?Boxy;//箱子的位置int?Succeed,?Prove;?//是否成功到目的地,?是否可玩性
int?dx[4],?dy[4];?//方向数组
protected:
char?Empty; char?People; char?Box; char?Block; char?Target; int?dir;//记录按键方向 node?s,?e;public:
Sokoban();//构建函数 ~Sokoban()?{}?//析构函数,即为inline //地图初始化函数 void?Initial(); //箱子路劲验证函数,参数为箱子坐标(bx,by),人坐标(px,py) void?Box_Bfs(int?bx,?int?by,?int?px,?int?py); //人路劲验证函数,人所到的目的地(ex,ey) bool?People_Bfs(int?ex,?int?ey); //地图刷新函数 void?Show(); //按键判断函数 void?Button(); //箱子人移动函数 void?Move(); //验证越界函数 bool?Check(int?x,?int?y);?};
#endif
//*******************************************************
Sokoban.cpp
//*******************************************************
#include?"Sokoban.h"
#include?<cstring>
#include?<cstdlib>
#include?<ctime>
#include?<iostream>
#include?<conio.h>
using?std::cout;
using?std::endl;
Sokoban::Sokoban()//构建函数即对变量初始化
{
dir?=?-1; Succeed?=?Prove?=?0; memset(GameMap,?'.',?sizeof(GameMap)); Empty?=?'.'; People?=?'P'; Box?=?'#'; Block?=?'*'; Target?=?'T'; //方向依次为上右下左 dx[0]?=?-1;dx[1]?=?0;dx[2]?=?1;dx[3]?=?0; dy[0]?=?0;?dy[1]?=?1;dy[2]?=?0;dy[3]?=?-1;//随机,使程序每次运行时所产生的随机数不同
srand(time(0));}
//地图初始化函数
void?Sokoban::Initial()
{
int?count?=?0,?x,?y; //对地图中随机产生25个阻碍物 while(count?!=?25) {x?=?rand()%H;
y?=?rand()%L;
if(GameMap[x][y]?==?Empty)
{
GameMap[x][y]?=?Block;count++;
}
} while(true)?//随机产生人开始的位置 {x?=?rand()%H;
y?=?rand()%L;
if(GameMap[x][y]?==?Empty){
GameMap[x][y]?=?People; Pex?=?x; Pey?=?y; break;}
} while(true)?//随机产生箱子开始的位置 {x?=?rand()%H;
y?=?rand()%L;
//不让箱子在地图的边界处
if(GameMap[x][y]?==?Empty?&&?x?!=?0?&&?y?!=?0&&?x?!=?H-1?&&?y?!=?L-1)
{
GameMap[x][y]?=?Box; Boxx?=?x; Boxy?=?y; break;}
} while(true)?//随机产生目标的位置 {x?=?rand()%H;
y?=?rand()%L;
if(GameMap[x][y]?==?Empty){
GameMap[x][y]?=?Target; break;}
} //对游戏地图检查是否可将箱子推到目的地,即判断游戏可玩性 Sokoban::Box_Bfs(Boxx,?Boxy,?Pex,?Pey);? //如游戏不可玩,即再随机产生地图 if(!Prove) {memset(GameMap,?'.',?sizeof(GameMap));
Sokoban::Initial();
} elseSokoban::Show();
}
//箱子路劲验证函数
//用BFS算法对箱子验证是否可到目的地
void?Sokoban::Box_Bfs(int?bx,?int?by,?int?px,?int?py)
{
queue<node>_Box;?//创建箱子队列 //visit对上一步走到下一步的记录,防止箱子走重复路劲 //visit[i][j][z][k]表示箱子从点(i,j)到点(z,k) //visit[][][][]为0时表示为走过,1时表示已走过 int?visit[H][L][H][L]; memset(visit,?0,?sizeof(visit));?//visit数组初始化? s.bx?=?bx;s.by?=?by;//将起始的箱子、人位置放入队列 s.px?=?px;s.py?=?py; _Box.push(s); int?pe_x,?pe_y; while(!_Box.empty())?//队列为空时跳出{
s?=?_Box.front();
_Box.pop();
if(GameMap[s.bx][s.by]?==?Target)//到达目的地
{
Prove?=?1;
break;
}
for(int?i?=?0;?i?<?4;?i++)
{
e.bx?=?s.bx?+?dx[i];e.by?=?s.by?+?dy[i];
switch(i)?//人推箱子的位置
{
case?0:pe_x?=?s.bx?+?dx[2];?pe_y?=?s.by?+?dy[2];?break;
case?1:pe_x?=?s.bx?+?dx[3];?pe_y?=?s.by?+?dy[3];?break;
case?2:pe_x?=?s.bx?+?dx[0];?pe_y?=?s.by?+?dy[0];?break;
case?3:pe_x?=?s.bx?+?dx[1];?pe_y?=?s.by?+?dy[1];?break;
}
//验证箱子和人的位置的合法性if(!Check(e.bx,?e.by)?||?!Check(pe_x,?pe_y)
||?GameMap[e.bx][e.by]?==?Block?||?GameMap[pe_x][pe_y]?==?Block
||?visit[s.bx][s.by][e.bx][e.by]?)
continue;
//如人可推箱子即进入队列if(Sokoban::People_Bfs(pe_x,?pe_y))?
{
//保存人推箱子后的位置
e.px?=?pe_x;e.py?=?pe_y;
_Box.push(e);
visit[s.bx][s.by][e.bx][e.by]?=?1;?//箱子路劲的标记
}
}
}}
//人路劲验证函数
//用BFS算法对人验证是否可推箱子
bool?Sokoban::People_Bfs(int?ex,?int?ey)
{
queue<node>_People;node?t,?end;
//visit数组对人的路劲进行标记,0为未走过,1为走过int?visit[H][L];?
//visit数组初始化为0 memset(visit,?0,?sizeof(visit));t.px?=?s.px;t.py?=?s.py;//人初始位置进入队列
_People.push(t);
visit[t.px][t.py]?=?1;
while(!_People.empty())?//对立为空时跳出
{
t?=?_People.front();
_People.pop();
if(t.px?==?ex?&&?t.py?==?ey)//人可到达(ex,ey)该点
return?1;for(int?i?=?0;?i?<?4;?i++)
{
end.px?=?t.px?+?dx[i];end.py?=?t.py?+?dy[i];
//检查人的位置合法性if(!Check(end.px,?end.py)?||?GameMap[end.px][end.py]?==?Block
||?GameMap[end.px][end.py]?==?Box?||?visit[end.px][end.py]) continue; //进入队列_People.push(end);
visit[end.px][end.py]?=?1;?//记录
}
}
return?0;
}
//地图刷新函数
void?Sokoban::Show()
{
int?i,?j; while(true) { //每半秒刷新一次地图clock_ts?=?clock();
while(clock()?-?s?<?CLOCKS_PER_SEC/2)
;//先判断按键在移动
Sokoban::Button();
Sokoban::Move();
system("cls");
for(i?=?0;?i?<?H;?i++)
{
for(j?=?0;?j?<?L;?j++)cout?<<?GameMap[i][j];
cout?<<?endl;
}
cout?<<?endl;
cout?<<?"\n**********************************"?<<?endl;
cout?<<?"*?小小C++语言推箱子游戏*"?<<?endl;
cout?<<?"*?游戏规则:*"?<<?endl;
cout?<<?"*?P:?人#:?箱子?*"?<<?endl;
cout?<<?"*?*:?障碍物T:?目的地?*"?<<?endl;
cout?<<?"**********************************"?<<?endl;
cout?<<?"*?每次游戏地图不一样?*"?<<?endl;
cout?<<?"*人将箱子推到目的地即过关*"?<<?endl;
cout?<<?"*所给地图,一定可过关,请慎重移箱子*"?<<?endl;
cout?<<?"*?箱子无路可走时,机器不会提示*"?<<?endl;
cout?<<?"**********************************"?<<?endl;
//箱子成功到达目的地
if(Succeed)
{
cout?<<?"\n?^_^>_<"?<<?endl; cout?<<?"恭喜过关成功!?再来一盘吧"?<<?endl; getchar(); break;}
}}
//按键判断函数
void?Sokoban::Button()
{
int?key; if(kbhit()?!=?0)?//检查当前是否有键盘输入,若有则返回一个非0值,否则返回0 {?while(kbhit()?!=?0)//可能存在多个按键,要全部取完,以最后一个为主
key?=?getch();?//将按键从控制台中取出并保存到key中
switch(key)
{
//上 case?72:dir?=?0;break;
//右case?77:dir?=?1;?
break;
//下
case?80:dir?=?2;?break;
//左 case?75:dir?=?3;?break;
}
}}
//人推箱子移动函数
void?Sokoban::Move()
{
int?x,?y; //有按键时 if(dir?!=?-1)? {//人所推向的位置坐标
x?=?Pex?+?dx[dir];y?=?Pey?+?dy[dir];
//人所推位置为空,即走向该位置
if(Check(x,?y)?&&?GameMap[x][y]?==?'.')
{
GameMap[Pex][Pey]?=?'.';//人的位置改变 GameMap[x][y]?=?'P'; Pex?=?x;Pey?=?y; dir?=?-1;//按键记录为无即-1}
else?//人所推位置为箱子,即将箱子推向该方向的前面这点
if(Check(x,?y)?&&?GameMap[x][y]?==?'#' &&?Check(x+dx[dir],?y+dy[dir]) &&?GameMap[?x+dx[dir]?][?y+dy[dir]?]?==?'.') {GameMap[Boxx][Boxy]?=?'.';//箱子的位置改变
GameMap[x+dx[dir]?][?y+dy[dir]?]?=?'#';
Boxx?=?x?+?dx[dir];Boxy?=?y?+?dy[dir];
GameMap[Pex][Pey]?=?'.';//人的位置改变
GameMap[x][y]?=?'P'; Pex?=?x;Pey?=?y;dir?=?-1;
} else//将箱子推向该方向的前面这点为目的地if(Check(x,?y)?&&?GameMap[x][y]?==?'#'
&&?Check(x+dx[dir],?y+dy[dir]) &&?GameMap[?x+dx[dir]?][?y+dy[dir]?]?==?'T'){
GameMap[Boxx][Boxy]?=?'.';//箱子的位置改变GameMap[x+dx[dir]?][?y+dy[dir]?]?=?'#';
Boxx?=?x?+?dx[dir];Boxy?=?y?+?dy[dir];
GameMap[Pex][Pey]?=?'.';//人的位置改变
GameMap[x][y]?=?'P'; Pex?=?x;Pey?=?y;dir?=?-1;
Succeed?=?1;//记录成功到达目的地}
}}
//判断越界情况
bool?Sokoban::Check(int?x,?int?y)
{
if(x?<?0?||?x?>=?H?||?y?<?0?||?y?>=?L)return?0;
else
return?1;
}
//*************************************************
Use_Sokoban.cpp
//*************************************************
#include?<iostream>
#include?"Sokoban.h"
using?namespace?std;
int?main()
{
Sokoban?s; s.Initial(); return?0;}
//*************************************************
如何制作推箱子动画答案?
推箱子存档功能当然是视呼 你用的推箱子程序。
有些推箱子程序可以存档, 有些不可以。
手机版本推箱子我不用, 不清楚什么推箱子程序有存档功能。
PC版本的推箱子程序我用了不少。
点睛推箱子可以自动保存答案。
老封推箱子可以自动保存答案。
国外版本:
YSokoban (中文翻译是歪推箱子) 可以自动保存答案。
YASC 可以自动保存答案。
SokoSe 可以自动保存答案。
SokoMind 可以自动保存答案。
SokoFan 可以自动保存答案。
JSoko 可以自动保存答案。
Sokoban for Windows v3 可以自动保存答案。
关于Haskell推箱子程序的问题。
sudoku52 说的对, 他给的参考资料链接有教程。
魔方吧论坛推箱子专区也有。
魔方吧里面给的其实就是动画制作过程。
动画制作过程三个步骤:
(1) 截图 -- 那个教程是用YSOKOBANBMP做截图。
但你是可以用你喜欢用的软件做截图的。 ALT+PRINT SCREEN热键也可以。
基本上是, 答案的每一步, 做一个截图。
你应该也看到, 如果答案有200步, 你就需要做200个截图。 有点麻烦。
(2) 剪裁 (如果有需要, 可以删除你不需要的部分)
(3) 用动画软件制作动画。不同软件, 用法有点不同。
做出来的动画文件大小也不同。
那个教程是用UNFREEZ。
但你也可以用其它软件,
如: ULEAD GIF ANIMATION, GIF MAKER, GIFSICLE,...等。
教程里面用YSOKOBANBMP做截图, 那是因为它可以自动每一步做一个截图。
但在它可以做截图之前, 它当然需要知道答案。
如果你用YSOKOBANBMP推过关卡, 它就会自动记下答案。
按F2就可以重播答案。
像教程里面说, 让它自动截图,
按: CTRL+F3 (开动自动截图的功能)
然后按 F2 就可以了。
教程其实已经很清楚。
如果还有疑问, 可以在那个教程跟帖发问。
补充回复:
可能你误会了, YSOKOBANBMP本身是一个推箱子软件。
截图功能只是截它自己窗口的图。
它不是一个截图软件, 不可以截其它软件窗口的图。
如果你想做其它软件的窗口截图, 你需要用其它的截图软件。
做好截图后就可以跟着教程做剩下的步骤。
如果你的目的不是GIF动画, 而是录影,
那你需要的工具就不同了, 步骤也不同了。
你需要“SCREEN RECORDER”软件。(画面录影软件)
这个我少用, 没有好的软件推荐。
推箱子的手机游戏
下面是全部代码。可复制存为一个lurd2xsb.hs文件,然后用ghc –make lurd2xsb.hs命令编译,会生成一个lurd2xsb可执行文件。之后用 ./lurd2xsb 或 cat [lurd file] | ./lurd2xsb 命令来运行。也可以用 runhaskell lurd2xsb.hs 直接即时编译并运行。
module Main where
import Data.List (transpose, intercalate)
import System.IO
type Sokoban = [[Char]]
type Solution = String
main = do
putStrLn "\nHaskell LURD2XSB\n\n\t usage: paste LURD solution in one line and press enter.\n"
lurd <- getLine
putStrLn $ intercalate "\n" $ reconstruct lurd
b = ["###", "#@#", "###"]
isLeftLeak :: Sokoban -> Bool
isLeftLeak = foldl (\acc xs -> if head xs /= '#' then True else acc) False
isRightLeak :: Sokoban -> Bool
isRightLeak = foldl (\acc xs -> if last xs /= '#' then True else acc) False
prePad :: Sokoban -> Sokoban
prePad s = if (isLeftLeak s) then map (\x -> ('#':x) ) s else s
sufPad :: Sokoban -> Sokoban
sufPad s = if (isRightLeak s) then map (\x -> x ++ "#") s else s
lastIsBox :: String -> Bool
lastIsBox xs = elem (last xs) "$*"
lastToMan :: String -> String
lastToMan xs = init xs ++ m
where m = (if last xs == '.' then "+" else "@")
headToFloor :: String -> String
headToFloor [] = []
headToFloor (x:xs) = (f:xs)
where f = (if x == '+' || x == '*' || x== '#' then '.' else ' ')
isMan :: Char -> Bool
isMan '@' = True
isMan '+' = True
isMan _ = False
undoRowL :: String -> String
undoRowL xs = if (elem '@' xs || elem '+' xs)
then lastToMan xs1 ++ headToFloor xs2
else xs
where (xs1,xs2) = span (not . isMan ) xs
undoRowPushL :: String -> String
undoRowPushL xs = if (elem '@' xs || elem '+' xs)
then lastToMan xs1 ++ b ++ headToFloor xs3
else xs
where (xs1,xs2@(h:xs3)) = span (not . isMan ) xs
b = (if h == '@' then "$" else "*" )
undoL :: Sokoban -> Sokoban
undoL s = prePad $ map undoRowL s
undoPushL :: Sokoban -> Sokoban
undoPushL s = prePad $ sufPad $ map undoRowPushL s
undo :: Sokoban -> Char -> Sokoban
undo s 'r' = undoL s
undo s 'R' = undoPushL s
undo s 'l' = map reverse $ undoL $ map reverse s
undo s 'L' = map reverse $ undoPushL $ map reverse s
undo s 'd' = transpose . undoL .transpose $ s
undo s 'D' = transpose . undoPushL . transpose $ s
undo s 'u' = transpose . (map reverse) . undoL . (map reverse) . transpose $ s
undo s 'U' = transpose . (map reverse) . undoPushL . (map reverse) . transpose $ s
reconstruct :: Solution -> Sokoban
reconstruct s = foldl (\acc x -> undo acc x) b $ reverse s
2012年4月24日更新:
今天换了一种实现方法,使得看上去更“数学”一些。主要是用 foldl 方法重新实现了 undoRowL 和 undoRowPushL 函数。另外输入输出改用 interact 函数,输入lurd串之后需要用 ctrl – D 执行。
module Main where
import Data.List (transpose, intercalate)
import System.IO (interact)
type Sokoban = [[Char]]
type Solution = String
main = interact $ (++"\n") . (intercalate "\n") . reconstruct
b = ["###", "#@#", "###"]
isLeftLeak :: Sokoban -> Bool
isLeftLeak = foldl (\acc xs -> if head xs /= '#' then True else acc) False
isRightLeak :: Sokoban -> Bool
isRightLeak = foldl (\acc xs -> if last xs /= '#' then True else acc) False
prePad :: Sokoban -> Sokoban
prePad s = if (isLeftLeak s) then map (\x -> ('#':x) ) s else s
sufPad :: Sokoban -> Sokoban
sufPad s = if (isRightLeak s) then map (\x -> x ++ "#") s else s
undoRowLStep :: String -> Char -> String
undoRowLStep (' ':xs) '@' = ' ':'@':xs
undoRowLStep ('.':xs) '@' = ' ':'+':xs
undoRowLStep ('#':xs) '@' = ' ':'@':xs
undoRowLStep (' ':xs) '+' = '.':'@':xs
undoRowLStep ('.':xs) '+' = '.':'+':xs
undoRowLStep ('#':xs) '+' = '.':'@':xs
undoRowLStep xs c = c:xs
undoRowPushLStep :: String -> Char -> String
undoRowPushLStep (' ':xs) '@' = '$':'@':xs
undoRowPushLStep ('.':xs) '@' = '$':'+':xs
undoRowPushLStep ('#':xs) '@' = '$':'@':xs
undoRowPushLStep (' ':xs) '+' = '*':'@':xs
undoRowPushLStep ('.':xs) '+' = '*':'+':xs
undoRowPushLStep ('#':xs) '+' = '*':'@':xs
undoRowPushLStep xs@('$':'@':ys) '$' = ' ':xs
undoRowPushLStep xs@('*':'@':ys) '$' = ' ':xs
undoRowPushLStep xs@('$':'+':ys) '$' = ' ':xs
undoRowPushLStep xs@('*':'+':ys) '$' = ' ':xs
undoRowPushLStep xs@('$':'@':ys) '*' = '.':xs
undoRowPushLStep xs@('*':'@':ys) '*' = '.':xs
undoRowPushLStep xs@('$':'+':ys) '*' = '.':xs
undoRowPushLStep xs@('*':'+':ys) '*' = '.':xs
undoRowPushLStep xs@('$':'@':ys) '#' = '.':xs
undoRowPushLStep xs@('*':'@':ys) '#' = '.':xs
undoRowPushLStep xs@('$':'+':ys) '#' = '.':xs
undoRowPushLStep xs@('*':'+':ys) '#' = '.':xs
undoRowPushLStep xs c = c:xs
undoRowL :: String -> String
undoRowL xs = reverse $ foldl undoRowLStep [] xs
undoRowPushL :: String -> String
undoRowPushL xs = reverse $ foldl undoRowPushLStep [] xs
undoL :: Sokoban -> Sokoban
undoL s = prePad $ map undoRowL s
undoPushL :: Sokoban -> Sokoban
undoPushL s = prePad $ sufPad $ map undoRowPushL s
undo :: Sokoban -> Char -> Sokoban
undo s 'r' = undoL s
undo s 'R' = undoPushL s
undo s 'l' = map reverse $ undoL $ map reverse s
undo s 'L' = map reverse $ undoPushL $ map reverse s
undo s 'd' = transpose . undoL .transpose $ s
undo s 'D' = transpose . undoPushL . transpose $ s
undo s 'u' = transpose . (map reverse) . undoL . (map reverse) . transpose $ s
undo s 'U' = transpose . (map reverse) . undoPushL . (map reverse) . transpose $ s
undo s _ = s
reconstruct :: Solution -> Sokoban
reconstruct s = foldl (\acc x -> undo acc x) b $ reverse s
2012年4月28日更新:
前面的实现都不考虑异常情况。事实上,并不是每一个lurd答案都能合法地还原成一个关卡。在利用答案逆推关卡过程中,有三种异常:一是撤销移动时,退回来的位置被箱子占领(如drrulL);二是撤销推动时,退回来的位置被箱子占领(如LURD,这和第一种本质一样);三是撤销推动时,要回退的箱子并不存在(如Rrr)。于是,重新修改了一下程序,使用了 Haskell 的 Maybe Monad 这一特性和 Monad 的 >>= 运算。当lurd答案合法有效时,返回 Just Sokoban,否则返回 Nothing 。
module Main where
import Data.List (transpose)
type Sokoban = [[Char]]
type Solution = String
main = do
putStrLn "\nHaskell LURD2XSB\n\n\t usage: paste LURD solution in one line and press enter.\n"
lurd <- getLine
putStrLn $ map reformat $ show $ reconstruct lurd
reformat :: Char -> Char
reformat '[' = '\n'
reformat ']' = '\n'
reformat ',' = '\n'
reformat '"' = ' '
reformat x = x
b = ["###", "#@#", "###"]
isLeftLeak :: Sokoban -> Bool
isLeftLeak = foldl (\acc xs -> if head xs /= '#' then True else acc) False
isRightLeak :: Sokoban -> Bool
isRightLeak = foldl (\acc xs -> if last xs /= '#' then True else acc) False
prePad :: Sokoban -> Sokoban
prePad s = if (isLeftLeak s) then map (\x -> ('#':x) ) s else s
sufPad :: Sokoban -> Sokoban
sufPad s = if (isRightLeak s) then map (\x -> x ++ "#") s else s
undoRowLStep :: String -> Char -> String
undoRowLStep (' ':xs) '@' = ' ':'@':xs
undoRowLStep ('.':xs) '@' = ' ':'+':xs
undoRowLStep ('#':xs) '@' = ' ':'@':xs
undoRowLStep ('$':xs) '@' = ' ':'e':xs
undoRowLStep ('*':xs) '@' = ' ':'e':xs
undoRowLStep (' ':xs) '+' = '.':'@':xs
undoRowLStep ('.':xs) '+' = '.':'+':xs
undoRowLStep ('#':xs) '+' = '.':'@':xs
undoRowLStep ('$':xs) '+' = ' ':'e':xs
undoRowLStep ('*':xs) '+' = ' ':'e':xs
undoRowLStep xs c = c:xs
undoRowPushLStep :: String -> Char -> String
undoRowPushLStep (' ':xs) '@' = '$':'@':xs
undoRowPushLStep ('.':xs) '@' = '$':'+':xs
undoRowPushLStep ('#':xs) '@' = '$':'@':xs
undoRowPushLStep ('$':xs) '@' = '$':'e':xs
undoRowPushLStep ('*':xs) '@' = '$':'e':xs
undoRowPushLStep (' ':xs) '+' = '*':'@':xs
undoRowPushLStep ('.':xs) '+' = '*':'+':xs
undoRowPushLStep ('#':xs) '+' = '*':'@':xs
undoRowPushLStep ('$':xs) '+' = '*':'e':xs
undoRowPushLStep ('*':xs) '+' = '*':'e':xs
undoRowPushLStep xs@('$':'@':ys) '$' = ' ':xs
undoRowPushLStep xs@('*':'@':ys) '$' = ' ':xs
undoRowPushLStep xs@('$':'+':ys) '$' = ' ':xs
undoRowPushLStep xs@('*':'+':ys) '$' = ' ':xs
undoRowPushLStep xs@('$':'@':ys) '*' = '.':xs
undoRowPushLStep xs@('*':'@':ys) '*' = '.':xs
undoRowPushLStep xs@('$':'+':ys) '*' = '.':xs
undoRowPushLStep xs@('*':'+':ys) '*' = '.':xs
undoRowPushLStep xs@('$':'@':ys) '#' = '.':xs
undoRowPushLStep xs@('*':'@':ys) '#' = '.':xs
undoRowPushLStep xs@('$':'+':ys) '#' = '.':xs
undoRowPushLStep xs@('*':'+':ys) '#' = '.':xs
undoRowPushLStep xs@('$':'@':ys) ' ' = 'e':xs
undoRowPushLStep xs@('*':'@':ys) ' ' = 'e':xs
undoRowPushLStep xs@('$':'+':ys) ' ' = 'e':xs
undoRowPushLStep xs@('*':'+':ys) ' ' = 'e':xs
undoRowPushLStep xs@('$':'@':ys) '.' = 'e':xs
undoRowPushLStep xs@('*':'@':ys) '.' = 'e':xs
undoRowPushLStep xs@('$':'+':ys) '.' = 'e':xs
undoRowPushLStep xs@('*':'+':ys) '.' = 'e':xs
undoRowPushLStep xs c = c:xs
undoRowL :: String -> String
undoRowL xs = reverse $ foldl undoRowLStep [] xs
undoRowPushL :: String -> String
undoRowPushL xs = reverse $ foldl undoRowPushLStep [] xs
undoL :: Sokoban -> Sokoban
undoL s = prePad $ map undoRowL s
undoPushL :: Sokoban -> Sokoban
undoPushL s = prePad $ sufPad $ map undoRowPushL s
undo :: Char -> Sokoban -> Sokoban
undo 'r' s = undoL s
undo 'R' s = undoPushL s
undo 'l' s = map reverse $ undoL $ map reverse s
undo 'L' s = map reverse $ undoPushL $ map reverse s
undo 'd' s = transpose . undoL .transpose $ s
undo 'D' s = transpose . undoPushL . transpose $ s
undo 'u' s = transpose . (map reverse) . undoL . (map reverse) . transpose $ s
undo 'U' s = transpose . (map reverse) . undoPushL . (map reverse) . transpose $ s
undo _ s = s
undo2 :: Char -> Sokoban -> Maybe Sokoban
undo2 x s =
let s' = undo x s
in if elem 'e' (concat s') then Nothing else Just s'
reconstruct :: Solution -> Maybe Sokoban
reconstruct [] = Just b
reconstruct (x:xs) = reconstruct xs >>= undo2 x
经典的手机游戏,现在电脑上也可以玩了。 以推出高品质Symbian游戏闻名的唐图科技,这次是将经典的《推箱子》游戏搬上了手机。在这款已经被多次翻新的经典益智游戏中,唐图科技的开发人员仍然没有让我们失望。独特的创意,精美的画面,丰富的效果,以及方便的操作,处处体现出唐图科技对于玩家的了解和独到的设计。
这款基于X-Factory游戏引擎开发的《推箱子》游戏,首先将玩家熟悉的主角换成了一艘宇宙飞船,游戏的场景也相应地移到了浩瀚的宇宙空间中,而被推动的箱子则变成了不断闪烁的能量块。游戏场景层次非常丰富,令人眼前一亮,可以说是一个完全不一样的推箱子游戏。玩家操纵的飞船,在移动时会从尾部喷出动态的火焰,飞船的飞行动作平滑流畅,操作非常爽快。当能量块被推到正确的位置时,会不断地发出绿光,提示已经完成一部分。这些丰富的效果,都依赖于X-Factory的强大功能和唐图科技开发人员的精彩创意。 宇宙推箱子
推箱子汉化版Boxit!
繁忙推箱子BusyFish
阿蒙推箱子
推箱子
好玩推箱子Sokaban
迷你推箱子Sokoban
仓库番Sokoban
坦克推箱子LaserTank
推箱子 方向键左或数字键4:左移方向键右或数字键6:右移
方向键上或数字键2:上移
方向键下或数字键8:下移
确定键或数字键5:确定
左软键:系统菜单/确定
右软键:状态菜单/返回