Just another update
This commit is contained in:
parent
a25938f123
commit
00a190b0a6
6591 changed files with 94363 additions and 23227 deletions
|
|
@ -21,24 +21,18 @@ public:
|
|||
|
||||
int w = 0;
|
||||
vector<string> data;
|
||||
for(; iter != end; ++iter)
|
||||
{
|
||||
for(; iter != end; ++iter){
|
||||
data.push_back((*iter)[1]);
|
||||
w = max(w, (*iter)[1].length());
|
||||
}
|
||||
|
||||
for(int v = 0; v < data.size(); ++v)
|
||||
{
|
||||
for(int v = 0; v < data.size(); ++v){
|
||||
vector<char> sTemp, dTemp;
|
||||
for(int u = 0; u < w; ++u)
|
||||
{
|
||||
if(u > data[v].size())
|
||||
{
|
||||
for(int u = 0; u < w; ++u){
|
||||
if(u > data[v].size()){
|
||||
sTemp.push_back(' ');
|
||||
dTemp.push_back(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
}else{
|
||||
char s = ' ', d = ' ', c = data[v][u];
|
||||
|
||||
if(c == '#')
|
||||
|
|
@ -46,13 +40,11 @@ public:
|
|||
else if(c == '.' || c == '*' || c == '+')
|
||||
s = '.';
|
||||
|
||||
if(c == '@' || c == '+')
|
||||
{
|
||||
if(c == '@' || c == '+'){
|
||||
d = '@';
|
||||
px = u;
|
||||
py = v;
|
||||
}
|
||||
else if(c == '$' || c == '*')
|
||||
}else if(c == '$' || c == '*')
|
||||
d = '*';
|
||||
|
||||
sTemp.push_back(s);
|
||||
|
|
@ -111,32 +103,26 @@ public:
|
|||
dirs[2] = make_tuple(0, 1, 'd', 'D');
|
||||
dirs[3] = make_tuple(-1, 0, 'l', 'L');
|
||||
|
||||
while(open.size() > 0)
|
||||
{
|
||||
while(open.size() > 0){
|
||||
vector<vector<char>> temp, cur = get<0>(open.front());
|
||||
string cSol = get<1>(open.front());
|
||||
int x = get<2>(open.front());
|
||||
int y = get<3>(open.front());
|
||||
open.pop();
|
||||
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
for(int i = 0; i < 4; ++i){
|
||||
temp = cur;
|
||||
int dx = get<0>(dirs[i]);
|
||||
int dy = get<1>(dirs[i]);
|
||||
|
||||
if(temp[y+dy][x+dx] == '*')
|
||||
{
|
||||
if(push(x, y, dx, dy, temp) && (visited.find(temp) == visited.end()))
|
||||
{
|
||||
if(temp[y+dy][x+dx] == '*'){
|
||||
if(push(x, y, dx, dy, temp) && (visited.find(temp) == visited.end())){
|
||||
if(isSolved(temp))
|
||||
return cSol + get<3>(dirs[i]);
|
||||
open.push(make_tuple(temp, cSol + get<3>(dirs[i]), x+dx, y+dy));
|
||||
visited.insert(temp);
|
||||
}
|
||||
}
|
||||
else if(move(x, y, dx, dy, temp) && (visited.find(temp) == visited.end()))
|
||||
{
|
||||
}else if(move(x, y, dx, dy, temp) && (visited.find(temp) == visited.end())){
|
||||
if(isSolved(temp))
|
||||
return cSol + get<2>(dirs[i]);
|
||||
open.push(make_tuple(temp, cSol + get<2>(dirs[i]), x+dx, y+dy));
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const struct Board {
|
|||
private immutable CTable sData, dData;
|
||||
private immutable int playerx, playery;
|
||||
|
||||
this(in string[] board) pure nothrow const
|
||||
this(in string[] board) immutable pure nothrow @safe
|
||||
in {
|
||||
foreach (const row; board) {
|
||||
assert(row.length == board[0].length,
|
||||
|
|
@ -18,8 +18,10 @@ const struct Board {
|
|||
assert(c.inPattern(" #.$@*"), "Not valid input");
|
||||
}
|
||||
} body {
|
||||
immutable sMap = [' ':' ', '.':'.', '@':' ', '#':'#', '$':' '];
|
||||
immutable dMap = [' ':' ', '.':' ', '@':'@', '#':' ', '$':'*'];
|
||||
/*static*/ immutable sMap =
|
||||
[' ':' ', '.':'.', '@':' ', '#':'#', '$':' '];
|
||||
/*static*/ immutable dMap =
|
||||
[' ':' ', '.':' ', '@':'@', '#':' ', '$':'*'];
|
||||
ncols = board[0].length;
|
||||
|
||||
int plx = 0, ply = 0;
|
||||
|
|
@ -43,12 +45,12 @@ const struct Board {
|
|||
|
||||
private bool move(in int x, in int y, in int dx,
|
||||
in int dy, ref CTable data)
|
||||
const pure /*nothrow*/ {
|
||||
if (sData[(y+dy) * ncols + x + dx] == El.wall ||
|
||||
data[(y+dy) * ncols + x + dx] != El.floor)
|
||||
const pure nothrow /*@safe*/ {
|
||||
if (sData[(y + dy) * ncols + x + dx] == El.wall ||
|
||||
data[(y + dy) * ncols + x + dx] != El.floor)
|
||||
return false;
|
||||
|
||||
auto data2 = data.dup; // Not nothrow.
|
||||
auto data2 = data.dup;
|
||||
data2[y * ncols + x] = El.floor;
|
||||
data2[(y + dy) * ncols + x + dx] = El.player;
|
||||
data = data2.assumeUnique; // Not enforced.
|
||||
|
|
@ -57,39 +59,40 @@ const struct Board {
|
|||
|
||||
private bool push(in int x, in int y, in int dx,
|
||||
in int dy, ref CTable data)
|
||||
const pure /*nothrow*/ {
|
||||
if (sData[(y + 2*dy) * ncols + x+2*dx] == El.wall ||
|
||||
data[(y + 2*dy) * ncols + x+2*dx] != El.floor)
|
||||
const pure nothrow /*@safe*/ {
|
||||
if (sData[(y + 2 * dy) * ncols + x + 2 * dx] == El.wall ||
|
||||
data[(y + 2 * dy) * ncols + x + 2 * dx] != El.floor)
|
||||
return false;
|
||||
|
||||
auto data2 = data.dup; // Not nothrow.
|
||||
auto data2 = data.dup;
|
||||
data2[y * ncols + x] = El.floor;
|
||||
data2[(y + dy) * ncols + x + dx] = El.player;
|
||||
data2[(y + 2*dy) * ncols + x + 2*dx] = El.boxOnGoal;
|
||||
data2[(y + 2 * dy) * ncols + x + 2*dx] = El.boxOnGoal;
|
||||
data = data2.assumeUnique; // Not enforced.
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool isSolved(in CTable data) const pure nothrow {
|
||||
private bool isSolved(in CTable data)
|
||||
const pure nothrow @safe @nogc {
|
||||
foreach (immutable i, immutable d; data)
|
||||
if ((sData[i] == El.goal) != (d == El.boxOnGoal))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
string solve() pure {
|
||||
string solve() pure nothrow /*@safe*/ {
|
||||
bool[immutable CTable] visitedSet = [dData: true];
|
||||
|
||||
alias Four = Tuple!(CTable, string, int, int);
|
||||
GrowableCircularQueue!Four open;
|
||||
open.push(Four(dData, "", playerx, playery));
|
||||
|
||||
immutable dirs = [tuple( 0, -1, 'u', 'U'),
|
||||
tuple( 1, 0, 'r', 'R'),
|
||||
tuple( 0, 1, 'd', 'D'),
|
||||
tuple(-1, 0, 'l', 'L')];
|
||||
static immutable dirs = [tuple( 0, -1, 'u', 'U'),
|
||||
tuple( 1, 0, 'r', 'R'),
|
||||
tuple( 0, 1, 'd', 'D'),
|
||||
tuple(-1, 0, 'l', 'L')];
|
||||
|
||||
while (open.length) {
|
||||
while (!open.empty) {
|
||||
//immutable (cur, cSol, x, y) = open.pop;
|
||||
immutable item = open.pop;
|
||||
immutable cur = item[0];
|
||||
|
|
@ -107,14 +110,13 @@ const struct Board {
|
|||
if (push(x, y, dx, dy, temp) && temp !in visitedSet) {
|
||||
if (isSolved(temp))
|
||||
return cSol ~ di[3];
|
||||
open.push(Four(temp, cSol ~ di[3], x+dx, y+dy));
|
||||
open.push(Four(temp, cSol ~ di[3], x + dx, y + dy));
|
||||
visitedSet[temp] = true;
|
||||
}
|
||||
} else if (move(x, y, dx, dy, temp) &&
|
||||
temp !in visitedSet) {
|
||||
} else if (move(x, y, dx, dy, temp) && temp !in visitedSet) {
|
||||
if (isSolved(temp))
|
||||
return cSol ~ di[2];
|
||||
open.push(Four(temp, cSol ~ di[2], x+dx, y+dy));
|
||||
open.push(Four(temp, cSol ~ di[2], x + dx, y + dy));
|
||||
visitedSet[temp] = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +128,7 @@ const struct Board {
|
|||
|
||||
void main() {
|
||||
import std.stdio, core.memory;
|
||||
GC.disable; // Uses about twice the memory
|
||||
GC.disable; // Uses about twice the memory.
|
||||
|
||||
immutable level =
|
||||
"#######
|
||||
|
|
@ -138,6 +140,6 @@ void main() {
|
|||
#.# @#
|
||||
#######";
|
||||
|
||||
const b = const(Board)(level.splitLines);
|
||||
immutable b = immutable(Board)(level.splitLines);
|
||||
writeln(level, "\n\n", b.solve);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ struct State { // Variable length struct.
|
|||
State* prev, next, qNext;
|
||||
CellIndex[0] c_;
|
||||
|
||||
CellIndex get(in size_t i) inout pure nothrow {
|
||||
CellIndex get(in size_t i) inout pure nothrow @nogc {
|
||||
return c_.ptr[i];
|
||||
}
|
||||
|
||||
void set(in size_t i, in CellIndex v) pure nothrow {
|
||||
void set(in size_t i, in CellIndex v) pure nothrow @nogc {
|
||||
c_.ptr[i] = v;
|
||||
}
|
||||
|
||||
CellIndex[] slice(in size_t i, in size_t j) pure nothrow {
|
||||
CellIndex[] slice(in size_t i, in size_t j) pure nothrow @nogc return {
|
||||
return c_.ptr[i .. j];
|
||||
}
|
||||
}
|
||||
|
|
@ -35,8 +35,8 @@ __gshared State*[] buckets;
|
|||
__gshared Thash hashSize, fillLimit, filled;
|
||||
|
||||
|
||||
State* newState(State* parent) nothrow {
|
||||
static State* nextOf(State *s) nothrow {
|
||||
State* newState(State* parent) nothrow @nogc {
|
||||
static State* nextOf(State *s) nothrow @nogc {
|
||||
return cast(State*)(cast(ubyte*)s + stateSize);
|
||||
}
|
||||
|
||||
|
|
@ -64,14 +64,14 @@ State* newState(State* parent) nothrow {
|
|||
}
|
||||
|
||||
|
||||
void unNewState(State* p) nothrow {
|
||||
void unNewState(State* p) nothrow @nogc {
|
||||
p.next = blockHead;
|
||||
blockHead = p;
|
||||
}
|
||||
|
||||
|
||||
/// Mark up positions where a box definitely should not be.
|
||||
void markLive(in size_t c) nothrow {
|
||||
void markLive(in size_t c) nothrow @nogc {
|
||||
immutable y = c / w;
|
||||
immutable x = c % w;
|
||||
if (live[c])
|
||||
|
|
@ -93,8 +93,8 @@ void markLive(in size_t c) nothrow {
|
|||
}
|
||||
|
||||
|
||||
State* parseBoard(in size_t y, in size_t x, in char* s) nothrow {
|
||||
static T[] myCalloc(T)(in size_t n) nothrow {
|
||||
State* parseBoard(in size_t y, in size_t x, in char* s) nothrow @nogc {
|
||||
static T[] myCalloc(T)(in size_t n) nothrow @nogc {
|
||||
auto ptr = cast(T*)calloc(n, T.sizeof);
|
||||
if (ptr == null)
|
||||
exit(1);
|
||||
|
|
@ -150,7 +150,7 @@ State* parseBoard(in size_t y, in size_t x, in char* s) nothrow {
|
|||
|
||||
|
||||
/// K&R hash function.
|
||||
void hash(State* s, in size_t nBoxes) pure nothrow {
|
||||
void hash(State* s, in size_t nBoxes) pure nothrow @nogc {
|
||||
if (!s.h) {
|
||||
Thash ha = 0;
|
||||
foreach (immutable i; 0 .. nBoxes + 1)
|
||||
|
|
@ -160,7 +160,7 @@ void hash(State* s, in size_t nBoxes) pure nothrow {
|
|||
}
|
||||
|
||||
|
||||
void extendTable() nothrow {
|
||||
void extendTable() nothrow @nogc {
|
||||
int oldSize = hashSize;
|
||||
|
||||
if (!oldSize) {
|
||||
|
|
@ -194,7 +194,7 @@ void extendTable() nothrow {
|
|||
}
|
||||
|
||||
|
||||
State* lookup(State *s) nothrow {
|
||||
State* lookup(State *s) nothrow @nogc {
|
||||
s.hash(nBoxes);
|
||||
auto f = buckets[s.h & (hashSize - 1)];
|
||||
for (; f; f = f.next) {
|
||||
|
|
@ -206,7 +206,7 @@ State* lookup(State *s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
bool addToTable(State* s) nothrow {
|
||||
bool addToTable(State* s) nothrow @nogc {
|
||||
if (s.lookup) {
|
||||
s.unNewState;
|
||||
return false;
|
||||
|
|
@ -223,7 +223,7 @@ bool addToTable(State* s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
bool success(in State* s) nothrow {
|
||||
bool success(in State* s) nothrow @nogc {
|
||||
foreach (immutable i; 1 .. nBoxes + 1)
|
||||
if (!goals[s.get(i)])
|
||||
return false;
|
||||
|
|
@ -231,7 +231,7 @@ bool success(in State* s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
State* moveMe(State* s, in int dy, in int dx) nothrow {
|
||||
State* moveMe(State* s, in int dy, in int dx) nothrow @nogc {
|
||||
immutable int y = s.get(0) / w;
|
||||
immutable int x = s.get(0) % w;
|
||||
immutable int y1 = y + dy;
|
||||
|
|
@ -284,7 +284,7 @@ State* moveMe(State* s, in int dy, in int dx) nothrow {
|
|||
}
|
||||
|
||||
|
||||
bool queueMove(State *s) nothrow {
|
||||
bool queueMove(State *s) nothrow @nogc {
|
||||
if (!s || !s.addToTable)
|
||||
return false;
|
||||
|
||||
|
|
@ -300,7 +300,7 @@ bool queueMove(State *s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
bool doMove(State* s) nothrow {
|
||||
bool doMove(State* s) nothrow @nogc {
|
||||
return s.moveMe( 1, 0).queueMove ||
|
||||
s.moveMe(-1, 0).queueMove ||
|
||||
s.moveMe( 0, 1).queueMove ||
|
||||
|
|
@ -308,7 +308,7 @@ bool doMove(State* s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
void showBoard(in State* s) nothrow {
|
||||
void showBoard(in State* s) nothrow @nogc {
|
||||
static immutable glyphs1 = " #@$", glyphs2 = ".#@$";
|
||||
|
||||
auto ptr = cast(ubyte*)alloca(w * h * ubyte.sizeof);
|
||||
|
|
@ -329,19 +329,21 @@ void showBoard(in State* s) nothrow {
|
|||
}
|
||||
|
||||
|
||||
void showMoves(in State* s) nothrow {
|
||||
void showMoves(in State* s) nothrow @nogc {
|
||||
if (s.prev)
|
||||
s.prev.showMoves;
|
||||
"\n".printf;
|
||||
s.showBoard;
|
||||
}
|
||||
|
||||
int main() nothrow @nogc {
|
||||
// Workaround for @nogc.
|
||||
alias ctEval(alias expr) = expr;
|
||||
|
||||
int main() nothrow {
|
||||
enum uint problem = 0;
|
||||
|
||||
static if (problem == 0) {
|
||||
auto s = parseBoard(8, 7,
|
||||
auto s = parseBoard(8, 7, ctEval!(
|
||||
"#######"~
|
||||
"# #"~
|
||||
"# #"~
|
||||
|
|
@ -349,18 +351,18 @@ int main() nothrow {
|
|||
"#. $$ #"~
|
||||
"#.$$ #"~
|
||||
"#.# @#"~
|
||||
"#######");
|
||||
"#######"));
|
||||
|
||||
} else static if (problem == 1) {
|
||||
auto s = parseBoard(5, 13,
|
||||
auto s = parseBoard(5, 13, ctEval!(
|
||||
"#############"~
|
||||
"# # #"~
|
||||
"# $$$$$$$ @#"~
|
||||
"#....... #"
|
||||
"#############");
|
||||
"#############"));
|
||||
|
||||
} else static if (problem == 2) {
|
||||
auto s = parseBoard(11, 19,
|
||||
auto s = parseBoard(11, 19, ctEval!(
|
||||
" ##### "~
|
||||
" # # "~
|
||||
" # # "~
|
||||
|
|
@ -371,7 +373,7 @@ int main() nothrow {
|
|||
"# $ $ ..#"~
|
||||
"##### ### #@## .#"~
|
||||
" # #########"~
|
||||
" ####### ");
|
||||
" ####### "));
|
||||
} else {
|
||||
asset(0, "Not present problem.");
|
||||
}
|
||||
|
|
|
|||
132
Task/Sokoban/Haskell/sokoban.hs
Normal file
132
Task/Sokoban/Haskell/sokoban.hs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import Control.Monad (liftM)
|
||||
import Data.Array
|
||||
import Data.List (transpose)
|
||||
import Data.Maybe (mapMaybe)
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Set as Set
|
||||
import Prelude hiding (Left, Right)
|
||||
|
||||
data Field = Space | Wall | Goal
|
||||
deriving (Eq)
|
||||
|
||||
data Action = Up | Down | Left | Right | PushUp | PushDown | PushLeft | PushRight
|
||||
|
||||
instance Show Action where
|
||||
show Up = "u"
|
||||
show Down = "d"
|
||||
show Left = "l"
|
||||
show Right = "r"
|
||||
show PushUp = "U"
|
||||
show PushDown = "D"
|
||||
show PushLeft = "L"
|
||||
show PushRight = "R"
|
||||
|
||||
type Index = (Int, Int)
|
||||
type FieldArray = Array Index Field
|
||||
type BoxArray = Array Index Bool
|
||||
type PlayerPos = Index
|
||||
type GameState = (BoxArray, PlayerPos)
|
||||
type Game = (FieldArray, GameState)
|
||||
|
||||
toField :: Char -> Field
|
||||
toField '#' = Wall
|
||||
toField ' ' = Space
|
||||
toField '@' = Space
|
||||
toField '$' = Space
|
||||
toField '.' = Goal
|
||||
toField '+' = Goal
|
||||
toField '*' = Goal
|
||||
|
||||
toPush :: Action -> Action
|
||||
toPush Up = PushUp
|
||||
toPush Down = PushDown
|
||||
toPush Left = PushLeft
|
||||
toPush Right = PushRight
|
||||
toPush n = n
|
||||
|
||||
toMove :: Action -> Index
|
||||
toMove PushUp = ( 0, -1)
|
||||
toMove PushDown = ( 0, 1)
|
||||
toMove PushLeft = (-1, 0)
|
||||
toMove PushRight = ( 1, 0)
|
||||
toMove n = toMove $ toPush n
|
||||
|
||||
-- Parse the string-based game board into an easier-to-use format.
|
||||
-- Assume that the board is valid (rectangular, one player, etc).
|
||||
parseGame :: [String] -> Game
|
||||
parseGame fieldStrs = (field, (boxes, player))
|
||||
where
|
||||
width = length $ head fieldStrs
|
||||
height = length fieldStrs
|
||||
bound = ((0, 0), (width - 1, height - 1))
|
||||
flatField = concat $ transpose fieldStrs
|
||||
charField = listArray bound flatField
|
||||
field = fmap toField charField
|
||||
boxes = fmap (`elem` "$*") charField
|
||||
player = fst $ head $ filter (flip elem "@+" . snd) $ assocs charField
|
||||
|
||||
add :: (Num a, Num b) => (a, b) -> (a, b) -> (a, b)
|
||||
add (a, b) (x, y) = (a + x, b + y)
|
||||
|
||||
-- Attempt to perform an action, returning the updated game and adjusted
|
||||
-- action if the action was legal.
|
||||
tryAction :: Game -> Action -> Maybe (Game, Action)
|
||||
tryAction (field, (boxes, player)) action
|
||||
| field ! vec == Wall = Nothing
|
||||
| boxes ! vec =
|
||||
if boxes ! vecB || field ! vecB == Wall
|
||||
then Nothing
|
||||
else Just ((field, (boxes // [(vec, False), (vecB, True)], vec)),
|
||||
toPush action)
|
||||
| otherwise = Just ((field, (boxes, vec)), action)
|
||||
where
|
||||
actionVec = toMove action
|
||||
vec = player `add` actionVec
|
||||
vecB = vec `add` actionVec
|
||||
|
||||
-- Search the game for a solution.
|
||||
solveGame :: Game -> Maybe [Action]
|
||||
solveGame (field, initState) =
|
||||
liftM reverse $ bfs (Seq.singleton (initState, [])) (Set.singleton initState)
|
||||
where
|
||||
goals = map fst $ filter ((== Goal) . snd) $ assocs field
|
||||
isSolved st = all (st !) goals
|
||||
possibleActions = [Up, Down, Left, Right]
|
||||
|
||||
-- Breadth First Search of the game tree.
|
||||
bfs :: Seq.Seq (GameState, [Action]) -> Set.Set GameState -> Maybe [Action]
|
||||
bfs queue visited =
|
||||
case Seq.viewl queue of
|
||||
Seq.EmptyL -> Nothing
|
||||
(game@(boxes, _), actions) Seq.:< queueB ->
|
||||
if isSolved boxes
|
||||
then Just actions
|
||||
else
|
||||
let newMoves = filter (flip Set.notMember visited . fst) $
|
||||
map (\((_, g), a) -> (g, a)) $
|
||||
mapMaybe (tryAction (field, game)) possibleActions
|
||||
visitedB = foldl (flip Set.insert) visited $
|
||||
map fst newMoves
|
||||
queueC = foldl (Seq.|>) queueB $
|
||||
map (\(g, a) -> (g, a:actions)) newMoves
|
||||
in bfs queueC visitedB
|
||||
|
||||
exampleA :: [String]
|
||||
exampleA =
|
||||
["#######"
|
||||
,"# #"
|
||||
,"# #"
|
||||
,"#. # #"
|
||||
,"#. $$ #"
|
||||
,"#.$$ #"
|
||||
,"#.# @#"
|
||||
,"#######"]
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
case solveGame $ parseGame exampleA of
|
||||
Nothing -> putStrLn "Unsolvable"
|
||||
Just solution -> do
|
||||
mapM_ putStrLn exampleA
|
||||
putStrLn ""
|
||||
putStrLn $ concatMap show solution
|
||||
63
Task/Sokoban/Ruby/sokoban-1.rb
Normal file
63
Task/Sokoban/Ruby/sokoban-1.rb
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
require 'set'
|
||||
|
||||
class Sokoban
|
||||
def initialize(level)
|
||||
board = level.each_line.map(&:chomp)
|
||||
@nrows = board.map(&:size).max
|
||||
board = board.map{|line| line.ljust(@nrows)}
|
||||
board.each_with_index do |row, r|
|
||||
row.each_char.with_index do |ch, c|
|
||||
@px, @py = c, r if ch == '@' or ch == '+'
|
||||
end
|
||||
end
|
||||
goal = board.join.tr(' .@#$+*', ' . ..')
|
||||
@goal = goal.each_char.with_index.select{|ch, c| ch == '.'}.map(&:last)
|
||||
@data = board.join.tr(' .@#$+*', ' @#$ $')
|
||||
end
|
||||
|
||||
def pos(x, y)
|
||||
y * @nrows + x
|
||||
end
|
||||
|
||||
def push(x, y, dx, dy, data)
|
||||
return data if data[pos(x+2*dx, y+2*dy)] != ' '
|
||||
data[pos(x , y )] = ' '
|
||||
data[pos(x + dx, y + dy)] = '@'
|
||||
data[pos(x+2*dx, y+2*dy)] = '$'
|
||||
data
|
||||
end
|
||||
|
||||
def solved?(data)
|
||||
@goal.all?{|i| data[i] == '$'}
|
||||
end
|
||||
|
||||
DIRS = [[0, -1, 'u', 'U'], [ 1, 0, 'r', 'R'], [0, 1, 'd', 'D'], [-1, 0, 'l', 'L']]
|
||||
def solve
|
||||
open = [[@data, "", @px, @py]]
|
||||
visited = Set[@data]
|
||||
|
||||
until open.empty?
|
||||
cur, csol, x, y = open.shift
|
||||
|
||||
for dx, dy, cmove, cpush in DIRS
|
||||
temp = cur.dup
|
||||
ps = pos(x+dx, y+dy)
|
||||
if temp[ps] == '$'
|
||||
temp = push(x, y, dx, dy, temp)
|
||||
next if visited.include?(temp)
|
||||
visited.add(temp)
|
||||
return csol + cpush if solved?(temp)
|
||||
open << [temp, csol + cpush, x+dx, y+dy]
|
||||
else
|
||||
next if @data[ps] == '#' or temp[ps] != ' '
|
||||
temp[pos(x, y)] = ' '
|
||||
temp[ps] = '@'
|
||||
next if visited.include?(temp)
|
||||
visited.add(temp)
|
||||
open << [temp, csol + cmove, x+dx, y+dy]
|
||||
end
|
||||
end
|
||||
end
|
||||
"No solution"
|
||||
end
|
||||
end
|
||||
11
Task/Sokoban/Ruby/sokoban-2.rb
Normal file
11
Task/Sokoban/Ruby/sokoban-2.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
level = <<EOS
|
||||
#######
|
||||
# #
|
||||
# #
|
||||
#. # #
|
||||
#. $$ #
|
||||
#.$$ #
|
||||
#.# @#
|
||||
#######
|
||||
EOS
|
||||
puts level, "", Sokoban.new(level).solve
|
||||
95
Task/Sokoban/Ruby/sokoban-3.rb
Normal file
95
Task/Sokoban/Ruby/sokoban-3.rb
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
class Sokoban
|
||||
def initialize(level)
|
||||
board = level.lines.map(&:rstrip)
|
||||
leng = board.map(&:length).max
|
||||
board = board.map{|line| line.ljust(leng)}.join
|
||||
@goal = []
|
||||
board.each_char.with_index do |c, i|
|
||||
@player = i if c == '@' or c == '+'
|
||||
@goal << i if c == '.' or c == '+' or c == '*'
|
||||
end
|
||||
@board = board.tr(' .@#$+*', ' @#$ $')
|
||||
@lurd = [[-1, 'l', 'L'], [-leng, 'u', 'U'], [1, 'r', 'R'], [leng, 'd', 'D']]
|
||||
@dirs = @lurd.map(&:first)
|
||||
set_dead_zone(board.tr('^#', ' '))
|
||||
end
|
||||
|
||||
def set_dead_zone(wall)
|
||||
corner = search_corner(wall)
|
||||
@dead = corner.dup
|
||||
begin
|
||||
size = @dead.size
|
||||
corner.each do |pos|
|
||||
@dirs.each do |dir|
|
||||
next if wall[pos + dir] == '#'
|
||||
@dead.concat(check_side(wall, pos+dir, dir))
|
||||
end
|
||||
end
|
||||
end until size == @dead.size
|
||||
end
|
||||
|
||||
def search_corner(wall)
|
||||
wall.size.times.with_object([]) do |i, corner|
|
||||
next if wall[i] == '#' or @goal.include?(i)
|
||||
case count_wall(wall, i)
|
||||
when 2
|
||||
corner << i if wall[i-1] != wall[i+1]
|
||||
when 3
|
||||
corner << i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_side(wall, pos, dir)
|
||||
wk = []
|
||||
until wall[pos] == '#' or count_wall(wall, pos) == 0 or @goal.include?(pos)
|
||||
return wk if @dead.include?(pos)
|
||||
wk << pos
|
||||
pos += dir
|
||||
end
|
||||
[]
|
||||
end
|
||||
|
||||
def count_wall(wall, pos)
|
||||
@dirs.count{|dir| wall[pos + dir] == '#'}
|
||||
end
|
||||
|
||||
def push_box(pos, dir, board)
|
||||
return board if board[pos + 2*dir] != ' '
|
||||
board[pos ] = ' '
|
||||
board[pos + dir] = '@'
|
||||
board[pos + 2*dir] = '$'
|
||||
board
|
||||
end
|
||||
|
||||
def solved?(board)
|
||||
@goal.all?{|i| board[i] == '$'}
|
||||
end
|
||||
|
||||
def solve
|
||||
queue = [[@board, "", @player]]
|
||||
# When the key doesn't exist in Hash, it subscribes a key but it returns false.
|
||||
visited = Hash.new{|h,k| h[k]=true; false}
|
||||
visited[@board] # first subscription
|
||||
|
||||
until queue.empty?
|
||||
board, route, pos = queue.shift
|
||||
@lurd.each do |dir, move, push|
|
||||
work = board.dup
|
||||
case work[pos+dir]
|
||||
when '$' # push
|
||||
work = push_box(pos, dir, work)
|
||||
next if visited[work]
|
||||
return route+push if solved?(work)
|
||||
queue << [work, route+push, pos+dir] unless @dead.include?(pos+2*dir)
|
||||
when ' ' # move
|
||||
work[pos ] = ' '
|
||||
work[pos+dir] = '@'
|
||||
next if visited[work]
|
||||
queue << [work, route+move, pos+dir]
|
||||
end
|
||||
end
|
||||
end
|
||||
"No solution"
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue