天天游戏攻略网

您现在的位置是: 首页 > 单机游戏

文章内容

sokoban推箱子攻略_推箱子1~15关图解

tamoadmin 2024-07-21
1.如何制作推箱子动画答案?2.关于Haskell推箱子程序的问题。3.推箱子的手机游戏//**********************************

1.如何制作推箱子动画答案?

2.关于Haskell推箱子程序的问题。

3.推箱子的手机游戏

sokoban推箱子攻略_推箱子1~15关图解

//*******************************************************

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();

}

else

Sokoban::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:确定

左软键:系统菜单/确定

右软键:状态菜单/返回