Sokoban: Difference between revisions

3,567 bytes added ,  3 months ago
m
(Sokoban en Befunge)
m (→‎{{header|Wren}}: Minor tidy)
 
(5 intermediate revisions by 3 users not shown)
Line 10:
* + is the player on a goal
* * is a box on a goal
<syntaxhighlight lang="text">#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######</syntaxhighlight>
 
Sokoban solutions are usually stored in the LURD format, where lowercase l, u, r and d represent a move in that ('''l'''eft, '''u'''p, '''r'''ight, '''d'''own) direction and capital LURD represents a push.
Line 17 ⟶ 25:
For more information, see [http://www.sokobano.de/wiki/index.php?title=Main_Page the Sokoban wiki].
 
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">[String] data
V nrows = 0
V px = 0
V py = 0
V sdata = ‘’
V ddata = ‘’
 
F init(board)
:data = board.split("\n")
:nrows = max(:data.map(r -> r.len))
 
V maps = [‘ ’ = ‘ ’, ‘.’ = ‘.’, ‘@’ = ‘ ’, ‘#’ = ‘#’, ‘$’ = ‘ ’]
V mapd = [‘ ’ = ‘ ’, ‘.’ = ‘ ’, ‘@’ = ‘@’, ‘#’ = ‘ ’, ‘$’ = ‘*’]
 
L(row) :data
V r = L.index
L(ch) row
V c = L.index
:sdata ‘’= maps[ch]
:ddata ‘’= mapd[ch]
I ch == ‘@’
:px = c
:py = r
 
F push(x, y, dx, dy, &data)
I :sdata[(y + 2 * dy) * :nrows + x + 2 * dx] == ‘#’
| data[(y + 2 * dy) * :nrows + x + 2 * dx] != ‘ ’
data = ‘’
R
 
data[y * :nrows + x] = ‘ ’
data[(y + dy) * :nrows + x + dx] = ‘@’
data[(y + 2 * dy) * :nrows + x + 2 * dx] = ‘*’
 
F is_solved(data)
L(i) 0 .< data.len
I (:sdata[i] == ‘.’) != (data[i] == ‘*’)
R 0B
R 1B
 
F solve()
V open = Deque([(:ddata, ‘’, :px, :py)])
V visited = Set([:ddata])
V dirs = ((0, -1, ‘u’, ‘U’), ( 1, 0, ‘r’, ‘R’),
(0, 1, ‘d’, ‘D’), (-1, 0, ‘l’, ‘L’))
 
L !open.empty
V (cur, csol, x, y) = open.pop_left()
 
L(di) dirs
V temp = copy(cur)
V (dx, dy) = (di[0], di[1])
 
I temp[(y + dy) * :nrows + x + dx] == ‘*’
push(x, y, dx, dy, &temp)
I temp != ‘’ & temp !C visited
I is_solved(temp)
R csol‘’di[3]
open.append((temp, csol‘’di[3], x + dx, y + dy))
visited.add(temp)
E
I :sdata[(y + dy) * :nrows + x + dx] == ‘#’ | temp[(y + dy) * :nrows + x + dx] != ‘ ’
L.continue
 
temp[y * :nrows + x] = ‘ ’
temp[(y + dy) * :nrows + x + dx] = ‘@’
 
I temp !C visited
I is_solved(temp)
R csol‘’di[2]
open.append((temp, csol‘’di[2], x + dx, y + dy))
visited.add(temp)
 
R ‘No solution’
 
V level =
|‘#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######’
 
init(level)
print(level"\n\n"solve())</syntaxhighlight>
 
{{out}}
<pre>
#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######
 
ulULLulDDurrrddlULrruLLrrUruLLLulD
</pre>
 
=={{header|Befunge}}==
El código no es mío, sólo lo reproduzco.
<langsyntaxhighlight lang="befunge">
589*+0g"0"-25**689*+0g"0"-+50p v # Sokoban - (c) Matthew Westcott 2000 # 03
83*>10p99*2->:00p10gg"x"-#v_v p01g<> ## # # # # # # # # # # # # # # # # # # #
Line 46 ⟶ 159:
#0 x 0#
########
</syntaxhighlight>
</lang>
 
 
=={{header|C}}==
Long, long, long C99 code (plus GNU C nested functions). Doesn't output the movement keys, instead it animates the sequence for you. Solution is move optimized. For an even longer solution, see [[Sokoban/C]].
<langsyntaxhighlight lang="c">#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Line 452 ⟶ 564:
 
return 0;
}</langsyntaxhighlight>
 
=={{header|C sharp|C#}}==
<langsyntaxhighlight lang="csharp">using System.Collections.Generic;
using System.Linq;
using System.Text;
Line 628 ⟶ 740:
}
}
}</langsyntaxhighlight>
Output:
<pre>
Line 653 ⟶ 765:
 
This performs a breadth-first search by moves, so the results should be a move-optimal solution.
<langsyntaxhighlight lang="cpp">#include <iostream>
#include <string>
#include <vector>
Line 806 ⟶ 918:
cout << level << endl << endl << b.solve() << endl;
return 0;
}</langsyntaxhighlight>
 
Output:
Line 826 ⟶ 938:
{{works with|GCC 4.6}}
Alternative version, about twice faster (about 2.1 seconds runtime), same output.
<langsyntaxhighlight lang="cpp">#include <iostream>
#include <string>
#include <vector>
Line 965 ⟶ 1,077:
cout << board.solve() << endl;
return 0;
}</langsyntaxhighlight>
 
=={{header|D}}==
Line 971 ⟶ 1,083:
{{trans|C++}}
This version uses the queue defined in the Queue/Usage task.
<langsyntaxhighlight lang="d">import std.string, std.typecons, std.exception, std.algorithm;
import queue_usage2; // No queue in Phobos 2.064.
 
Line 1,115 ⟶ 1,227:
immutable b = immutable(Board)(level.splitLines);
writeln(level, "\n\n", b.solve);
}</langsyntaxhighlight>
{{out}}
<pre>#######
Line 1,132 ⟶ 1,244:
{{trans|C}}
This code is not idiomatic D, it retains most of the style of the C version.
<langsyntaxhighlight lang="d">import core.stdc.stdio: printf, puts, fflush, stdout, putchar;
import core.stdc.stdlib: malloc, calloc, realloc, free, alloca, exit;
 
Line 1,545 ⟶ 1,657:
 
return 0;
}</langsyntaxhighlight>
 
=={{header|Elixir}}==
{{works with|Elixir|1.3}}
{{trans|Ruby}}
<langsyntaxhighlight lang="elixir">defmodule Sokoban do
defp setup(level) do
{leng, board} = normalize(level)
Line 1,730 ⟶ 1,842:
"""
IO.puts level
Sokoban.solve(level)</langsyntaxhighlight>
 
{{out}}
Line 1,749 ⟶ 1,861:
{{trans|C++}}
Well, it started as a C++ translation, but turned out different. It's still the breadth-first set-based algorithm, but I dropped the sdata/ddata optimization and just maintained a single string as the board representation. Also dropped the code to handle non-rectangular boards, and probably some other stuff too.
<langsyntaxhighlight lang="go">package main
 
import (
Line 1,865 ⟶ 1,977:
}
return string(buffer)
}</langsyntaxhighlight>
{{out}}
<pre>
Line 1,882 ⟶ 1,994:
 
=={{header|Haskell}}==
<langsyntaxhighlight Haskelllang="haskell">import Control.Monad (liftM)
import Data.Array
import Data.List (transpose)
Line 2,013 ⟶ 2,125:
mapM_ putStrLn exampleA
putStrLn ""
putStrLn $ concatMap show solution</langsyntaxhighlight>
{{out}}
<pre>#######
Line 2,029 ⟶ 2,141:
Translation of [[Sokoban#C++|C++]] via [[Sokoban#D|D]]
{{works with|Java|7}}
<langsyntaxhighlight lang="java">import java.util.*;
 
public class Sokoban {
Line 2,166 ⟶ 2,278:
System.out.println(new Sokoban(level.split(",")).solve());
}
}</langsyntaxhighlight>
 
<pre>ulULLulDDurrrddlULrruLLrrUruLLLulD</pre>
Line 2,172 ⟶ 2,284:
=={{header|Julia}}==
{{trans|Go}}
<langsyntaxhighlight lang="julia">struct BoardState
board::String
csol::String
Line 2,265 ⟶ 2,377:
#######""")
println("For sokoban level:\n$testlevel\n...solution is :\n$(solve(testlevel))")
</langsyntaxhighlight>{{output}}<pre>
For sokoban level:
#######
Line 2,281 ⟶ 2,393:
=={{header|Kotlin}}==
{{trans|Java}}
<langsyntaxhighlight lang="scala">// version 1.2.0
 
import java.util.LinkedList
Line 2,396 ⟶ 2,508:
println()
println(Sokoban(level).solve())
}</langsyntaxhighlight>
 
{{out}}
Line 2,416 ⟶ 2,528:
We have chosen to use a double queue (deque) instead of a linked list.
 
<langsyntaxhighlight Nimlang="nim">import deques, sets, strutils
 
type
Line 2,514 ⟶ 2,626:
echo Level.join("\n")
echo()
echo initSokoban(Level).solve()</langsyntaxhighlight>
 
{{out}}
Line 2,531 ⟶ 2,643:
{{trans|Python}}
This uses a breadth-first move search, so will find a move-optimal solution.
<langsyntaxhighlight OCamllang="ocaml">type dir = U | D | L | R
type move_t = Move of dir | Push of dir
 
Line 2,624 ⟶ 2,736:
"#.# @#";
"#######"] in
solve level</langsyntaxhighlight>
Output:
<pre>luULLulDDurrrddlULrruLLrrUruLLLulD</pre>
Line 2,644 ⟶ 2,756:
would still be a valid solution. I could fix it, but at the cost of speed and memory.
 
<langsyntaxhighlight Perllang="perl">#!perl
use strict;
use warnings qw(FATAL all);
Line 2,848 ⟶ 2,960:
}
__END__
</syntaxhighlight>
</lang>
{{out}}
<pre>Solution found!
Line 2,871 ⟶ 2,983:
Push-optimised, prunes (breadth-first) search space to reachable pushable-to-live boxes.<br>
Fairly fast, but often produces same-push-tally but longer results than move-optimised.
<langsyntaxhighlight Phixlang="phix">-- demo\rosetta\Sokoban.exw
integer w, h -- (set from parsing the input grid)
sequence moves -- "", as +/-w and +/-1 (udlr)
Line 3,041 ⟶ 3,153:
printf(1,"solution of %d pushes (%s)\n",{pop,elapsed(time()-t0)})
plays(level,pushset)
end if</langsyntaxhighlight>
{{out}}
Note that a full solution in LURD format would show as 48 moves, as opposed to
Line 3,067 ⟶ 3,179:
 
Other tests:
<langsyntaxhighlight Phixlang="phix">constant input = """
#############
# # #
Line 3,073 ⟶ 3,185:
#....... #
#############
"""</langsyntaxhighlight>
{{out}}
<pre>
Line 3,102 ⟶ 3,214:
</pre>
Test #3
<langsyntaxhighlight Phixlang="phix">constant input = """
####
##. ##
Line 3,111 ⟶ 3,223:
###### ##
####
"""</langsyntaxhighlight>
{{out}}
<pre>
Line 3,134 ⟶ 3,246:
</pre>
Test #4
<langsyntaxhighlight Phixlang="phix">constant input = """
#############
#... # #
Line 3,140 ⟶ 3,252:
#... #
#############
"""</langsyntaxhighlight>
{{out}}
<pre>
Line 3,158 ⟶ 3,270:
</pre>
Test #5
<langsyntaxhighlight Phixlang="phix">constant input = """
#####
# #
Line 3,170 ⟶ 3,282:
# #########
#######
"""</langsyntaxhighlight>
{{out}}
<pre>
Line 3,203 ⟶ 3,315:
=={{header|PicoLisp}}==
This searches for a solution, without trying for the push-optimal one. The player moves between the pushes, however, are minimized.
<langsyntaxhighlight PicoLisplang="picolisp">(load "@lib/simul.l")
 
# Display board
Line 3,284 ⟶ 3,396:
(pushes) )
(display) # Display solution
(pack (flip Res)) ) ) )</langsyntaxhighlight>
Test:
<langsyntaxhighlight PicoLisplang="picolisp">(main
(quote
"#######"
Line 3,297 ⟶ 3,409:
"#######" ) )
(prinl)
(go)</langsyntaxhighlight>
Output:
<pre> 8 # # # # # # #
Line 3,324 ⟶ 3,436:
{{works with|Psyco}}
{{works with|Python 2.6}}
<langsyntaxhighlight lang="python">from array import array
from collections import deque
import psyco
Line 3,419 ⟶ 3,531:
psyco.full()
init(level)
print level, "\n\n", solve()</langsyntaxhighlight>
Output:
<pre>#######
Line 3,436 ⟶ 3,548:
This was originally inspired by PicoLisp's solution. Modified to use a priority queue as mentioned on the Sokoban wiki for the main breadth first search on pushes but just a plain queue for the move bfs. This uses personal libraries. Vector2 isn't strictly needed but the math/array library is not currently optimized for untyped Racket. push! is comparable to lisp's, awhen is anaphoric when, ret uses the bound value as the result of its expression, and tstruct is short for struct with the #:transparent option.
 
<syntaxhighlight lang="racket">
<lang Racket>
#lang racket
(require data/heap
Line 3,544 ⟶ 3,656:
(pushes s clear))
(bfs)])))
</syntaxhighlight>
</lang>
 
{{out}}
Line 3,557 ⟶ 3,669:
(formerly Perl 6)
{{trans|Go}}
<syntaxhighlight lang="raku" perl6line>sub MAIN() {
my $level = q:to//;
#######
Line 3,657 ⟶ 3,769:
}
return "No solution";
}</langsyntaxhighlight>
{{out}}
<pre>Level:
Line 3,672 ⟶ 3,784:
 
=={{header|Ring}}==
<langsyntaxhighlight lang="ring">
#--------------------------------------------------#
# Sokoban Game #
Line 4,055 ⟶ 4,167:
UpdateGameMap(oGame)
lPlayerWin = False
</syntaxhighlight>
</lang>
 
Output image:
Line 4,066 ⟶ 4,178:
===Simple Version===
{{trans|Python}}
<langsyntaxhighlight lang="ruby">require 'set'
 
class Sokoban
Line 4,124 ⟶ 4,236:
"No solution"
end
end</langsyntaxhighlight>
'''Test:'''
<langsyntaxhighlight lang="ruby">level = <<EOS
#######
# #
Line 4,136 ⟶ 4,248:
#######
EOS
puts level, "", Sokoban.new(level).solve</langsyntaxhighlight>
 
{{out}}
Line 4,157 ⟶ 4,269:
When a box is pushed there, it doesn't process after that.
 
<langsyntaxhighlight lang="ruby">class Sokoban
def initialize(level)
board = level.lines.map(&:rstrip)
Line 4,251 ⟶ 4,363:
"No solution"
end
end</langsyntaxhighlight>
Runtime: about 0.20 seconds.
 
Line 4,257 ⟶ 4,369:
This code does a breadth-first search so it finds a solution with a minimum number of moves.
{{trans|OCaml}}<!-- the big difference is that this is all in one procedure for speed as it reduces the amount of packing/unpacking of tuples/lists, and the queue isn't shortened as that's significantly slower for these sorts of board sizes -->
<langsyntaxhighlight lang="tcl">package require Tcl 8.5
 
proc solveSokoban b {
Line 4,326 ⟶ 4,438:
}
error "no solution"
}</langsyntaxhighlight>
Demonstration code:
<langsyntaxhighlight lang="tcl">set level {
"#######"
"# #"
Line 4,338 ⟶ 4,450:
"#######"
}
puts [solveSokoban $level]</langsyntaxhighlight>
Output: <pre>ulULLulDDurrrddlULrruLLrrUruLLLulD</pre>
Runtime with stock Tcl 8.5 installation: ≅2.2 seconds<!-- on what is now a fairly old machine -->
Line 4,348 ⟶ 4,460:
{{libheader|Wren-set}}
This works but at a rather sedate pace - 26.7 seconds.
<langsyntaxhighlight ecmascriptlang="wren">import "./dynamic" for Tuple
import "./llist" for DLinkedList
import "./set" for Set
 
var Board = Tuple.create("Board", ["cur", "sol", "x", "y"])
Line 4,454 ⟶ 4,566:
System.print(level.join("\n"))
System.print()
System.print(Sokoban.new(level).solve())</langsyntaxhighlight>
 
{{out}}
9,476

edits