Just another update

This commit is contained in:
Ingy döt Net 2015-02-20 00:35:01 -05:00
parent a25938f123
commit 00a190b0a6
6591 changed files with 94363 additions and 23227 deletions

View file

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

View file

@ -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);
}

View file

@ -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.");
}

View 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

View 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

View file

@ -0,0 +1,11 @@
level = <<EOS
#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######
EOS
puts level, "", Sokoban.new(level).solve

View 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