Data update
This commit is contained in:
parent
4bb20c9b71
commit
cbaf4c4b64
12390 changed files with 318560 additions and 27248 deletions
233
Task/Sokoban/FreeBASIC/sokoban.basic
Normal file
233
Task/Sokoban/FreeBASIC/sokoban.basic
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
Const NULL As Any Ptr = 0
|
||||
|
||||
' HashTable with open addressing
|
||||
Type HashTable
|
||||
Dim As Integer size
|
||||
Dim As String entries(Any)
|
||||
End Type
|
||||
|
||||
Sub HashInit(Byref ht As HashTable, sz As Integer)
|
||||
ht.size = sz
|
||||
Redim ht.entries(0 To sz-1)
|
||||
For i As Integer = 0 To sz-1
|
||||
ht.entries(i) = "" ' empty slot marker
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Function HashString(s As String, tableSize As Integer) As Integer
|
||||
Dim As Uinteger h = 5381
|
||||
For i As Integer = 0 To Len(s)-1
|
||||
h = ((h Shl 5) + h) + Asc(Mid(s,i+1,1)) ' h*33 + c
|
||||
Next
|
||||
Return h Mod tableSize
|
||||
End Function
|
||||
|
||||
Sub HashAdd(Byref ht As HashTable, s As String)
|
||||
Dim As Integer idx = HashString(s, ht.size)
|
||||
While ht.entries(idx) <> "" And ht.entries(idx) <> s
|
||||
idx = (idx + 1) Mod ht.size
|
||||
Wend
|
||||
ht.entries(idx) = s
|
||||
End Sub
|
||||
|
||||
Function HashContains(Byref ht As HashTable, s As String) As Boolean
|
||||
Dim As Integer idx = HashString(s, ht.size)
|
||||
While ht.entries(idx) <> ""
|
||||
If ht.entries(idx) = s Then Return True
|
||||
idx = (idx + 1) Mod ht.size
|
||||
Wend
|
||||
Return False
|
||||
End Function
|
||||
|
||||
' Sokoban solver
|
||||
Type State
|
||||
board As String
|
||||
moves As String
|
||||
posic As Integer
|
||||
End Type
|
||||
|
||||
Type DirType
|
||||
moveStr As String
|
||||
pushStr As String
|
||||
dPos As Integer
|
||||
End Type
|
||||
|
||||
Function getChar(s As String, lug As Integer) As String
|
||||
If lug < 0 Or lug >= Len(s) Then Return "#"
|
||||
Return Mid(s, lug + 1, 1)
|
||||
End Function
|
||||
|
||||
Function setChar(s As String, lug As Integer, c As String) As String
|
||||
If lug < 0 Or lug >= Len(s) Then Return s
|
||||
Return Left(s, lug) & c & Mid(s, lug + 2)
|
||||
End Function
|
||||
|
||||
Function isWall(c As String) As Boolean
|
||||
Return c = "#" Or c = Chr(10) Or c = ""
|
||||
End Function
|
||||
|
||||
' --- Corner deadlock pruning (discard states where a box is stuck in a non-goal corner) ---
|
||||
Function isDeadlock(board As String, ancho As Integer) As Boolean
|
||||
For i As Integer = 0 To Len(board)-1
|
||||
Dim c As String = Mid(board, i+1, 1)
|
||||
If c = "$" Then
|
||||
' Read neighbor cells around the box
|
||||
Dim izda As String = getChar(board, i-1)
|
||||
Dim dcha As String = getChar(board, i+1)
|
||||
Dim up As String = getChar(board, i-(ancho+1))
|
||||
Dim down As String = getChar(board, i+(ancho+1))
|
||||
If (isWall(izda) Or isWall(dcha)) And (isWall(up) Or isWall(down)) Then
|
||||
If getChar(board, i) <> "." Then Return True
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Return False
|
||||
End Function
|
||||
|
||||
Function Move(s As State Ptr, dPos As Integer) As String
|
||||
Dim As Integer newPos = s->posic + dPos
|
||||
If newPos < 0 Or newPos >= Len(s->board) Then Return ""
|
||||
|
||||
Dim As String targetChar = getChar(s->board, newPos)
|
||||
If isWall(targetChar) Then Return ""
|
||||
If targetChar <> " " And targetChar <> "." Then Return ""
|
||||
|
||||
Dim As String tmp = s->board
|
||||
Dim As Integer lug = s->posic
|
||||
Dim As String currentChar = getChar(tmp, lug)
|
||||
|
||||
tmp = Iif(currentChar = "@", setChar(tmp, lug, " "), setChar(tmp, lug, "."))
|
||||
tmp = setChar(tmp, newPos, Iif(targetChar = " ", "@", "+"))
|
||||
Return tmp
|
||||
End Function
|
||||
|
||||
Function Push(s As State Ptr, dPos As Integer) As String
|
||||
Dim As Integer newPos = s->posic + dPos
|
||||
Dim As Integer boxPos = newPos + dPos
|
||||
If newPos < 0 Or boxPos < 0 Or boxPos >= Len(s->board) Then Return ""
|
||||
|
||||
Dim As String boxChar = getChar(s->board, newPos)
|
||||
Dim As String targetChar = getChar(s->board, boxPos)
|
||||
|
||||
If boxChar <> "$" And boxChar <> "*" Then Return ""
|
||||
If isWall(targetChar) Then Return ""
|
||||
If targetChar <> " " And targetChar <> "." Then Return ""
|
||||
|
||||
Dim As String tmp = s->board
|
||||
Dim As Integer lug = s->posic
|
||||
Dim As String currentChar = getChar(tmp, lug)
|
||||
|
||||
tmp = Iif(currentChar = "@", setChar(tmp, lug, " "), setChar(tmp, lug, "."))
|
||||
tmp = setChar(tmp, newPos, Iif(boxChar = "$", "@", "+"))
|
||||
tmp = setChar(tmp, boxPos, Iif(targetChar = " ", "$", "*"))
|
||||
Return tmp
|
||||
End Function
|
||||
|
||||
Function isComplete(board As String) As Boolean
|
||||
Return Instr(board, ".") = 0 And Instr(board, "$") = 0
|
||||
End Function
|
||||
|
||||
Function Solve(board As String) As String
|
||||
Dim As Integer boardLen = Len(board)
|
||||
Dim As Integer ancho = Instr(board, Chr(10)) - 1
|
||||
If ancho <= 0 Then Return "Invalid board"
|
||||
|
||||
Dim As DirType dirs(3)
|
||||
dirs(0).moveStr = "u": dirs(0).pushStr = "U": dirs(0).dPos = -ancho-1
|
||||
dirs(1).moveStr = "r": dirs(1).pushStr = "R": dirs(1).dPos = 1
|
||||
dirs(2).moveStr = "d": dirs(2).pushStr = "D": dirs(2).dPos = ancho+1
|
||||
dirs(3).moveStr = "l": dirs(3).pushStr = "L": dirs(3).dPos = -1
|
||||
|
||||
Dim As HashTable visited
|
||||
HashInit(visited, 200000) ' initial size (tune for fewer collisions)
|
||||
|
||||
HashAdd(visited, board)
|
||||
|
||||
Dim As Integer playerPos = Instr(board, "@") - 1
|
||||
If playerPos < 0 Then playerPos = Instr(board, "+") - 1
|
||||
If playerPos < 0 Then Return "No player"
|
||||
|
||||
Dim As State Ptr openStates = Callocate(100000, Sizeof(State))
|
||||
Dim As Integer head = 0, tail = 0, capacity = 100000
|
||||
|
||||
openStates[tail].board = board
|
||||
openStates[tail].moves = ""
|
||||
openStates[tail].posic = playerPos
|
||||
tail += 1
|
||||
|
||||
While head < tail
|
||||
If tail > 1000000 Then
|
||||
Deallocate(openStates)
|
||||
Return "Too complex"
|
||||
End If
|
||||
|
||||
Dim As State current = openStates[head]
|
||||
head += 1
|
||||
|
||||
For i As Integer = 0 To 3
|
||||
Dim As DirType currentDir = dirs(i)
|
||||
Dim As Integer newPos = current.posic + currentDir.dPos
|
||||
|
||||
If newPos < 0 Or newPos >= boardLen Then Continue For
|
||||
Dim As String targetChar = getChar(current.board, newPos)
|
||||
If isWall(targetChar) Then Continue For
|
||||
|
||||
Dim As String newBoard = ""
|
||||
Dim As String newSol = current.moves
|
||||
|
||||
If targetChar = "$" Or targetChar = "*" Then
|
||||
newBoard = Push(@current, currentDir.dPos)
|
||||
If Len(newBoard) > 0 Then newSol += currentDir.pushStr
|
||||
Elseif targetChar = " " Or targetChar = "." Then
|
||||
newBoard = Move(@current, currentDir.dPos)
|
||||
If Len(newBoard) > 0 Then newSol += currentDir.moveStr
|
||||
End If
|
||||
|
||||
If Len(newBoard) > 0 Andalso newBoard <> current.board Then
|
||||
If isDeadlock(newBoard, ancho) Then Continue For
|
||||
|
||||
If isComplete(newBoard) Then
|
||||
Deallocate(openStates)
|
||||
Return newSol
|
||||
End If
|
||||
If Not HashContains(visited, newBoard) Then
|
||||
HashAdd(visited, newBoard)
|
||||
If tail >= capacity Then
|
||||
capacity += 50000
|
||||
Dim As State Ptr tmpPtr = Reallocate(openStates, capacity * Sizeof(State))
|
||||
If tmpPtr = NULL Then
|
||||
Deallocate(openStates)
|
||||
Return "Out of memory"
|
||||
End If
|
||||
openStates = tmpPtr
|
||||
End If
|
||||
openStates[tail].board = newBoard
|
||||
openStates[tail].moves = newSol
|
||||
Dim As Integer np = Instr(newBoard, "@") - 1
|
||||
If np < 0 Then np = Instr(newBoard, "+") - 1
|
||||
openStates[tail].posic = np
|
||||
tail += 1
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Wend
|
||||
|
||||
Deallocate(openStates)
|
||||
Return "No solution"
|
||||
End Function
|
||||
|
||||
' Main program
|
||||
Dim level As String = _
|
||||
"#######" & Chr(10) & _
|
||||
"# #" & Chr(10) & _
|
||||
"# #" & Chr(10) & _
|
||||
"#. # #" & Chr(10) & _
|
||||
"#. $$ #" & Chr(10) & _
|
||||
"#.$$ #" & Chr(10) & _
|
||||
"#.# @#" & Chr(10) & _
|
||||
"#######"
|
||||
|
||||
Print "level:" & Chr(10) & level
|
||||
Print "Solution:" & Chr(10) & Solve(level)
|
||||
|
||||
Sleep
|
||||
108
Task/Sokoban/Pluto/sokoban.pluto
Normal file
108
Task/Sokoban/Pluto/sokoban.pluto
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
require "map"
|
||||
require "list"
|
||||
|
||||
class board
|
||||
function __construct(public cur, public sol, public x, public y) end
|
||||
end
|
||||
|
||||
class sokoban
|
||||
function __construct(brd)
|
||||
self.dest_board = ""
|
||||
self.curr_board = ""
|
||||
self.ncols = #brd[1]
|
||||
self.player_x = 0
|
||||
self.player_y = 0
|
||||
for r = 1, #brd do
|
||||
for c = 1, self.ncols do
|
||||
local ch = brd[r][c]
|
||||
self.dest_board ..= (ch != "$" and ch != "@") ? ch : " "
|
||||
self.curr_board ..= (ch != ".") ? ch : " "
|
||||
if ch == "@" then
|
||||
self.player_x = c - 1
|
||||
self.player_y = r - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function move(x, y, dx, dy, trial_board)
|
||||
local new_player_pos = (y + dy) * self.ncols + x + dx + 1
|
||||
if trial_board[new_player_pos] != " " then return "" end
|
||||
local trial = trial_board:split("")
|
||||
trial[y * self.ncols + x + 1] = " "
|
||||
trial[new_player_pos] = "@"
|
||||
return trial:concat("")
|
||||
end
|
||||
|
||||
function push(x, y, dx, dy, trial_board)
|
||||
local new_box_pos = (y + 2 * dy) * self.ncols + x + 2 * dx + 1
|
||||
if trial_board[new_box_pos] != " " then return "" end
|
||||
local trial = trial_board:split("")
|
||||
trial[y * self.ncols + x + 1] = " "
|
||||
trial[(y + dy) * self.ncols + x + dx + 1] = "@"
|
||||
trial[new_box_pos] = "$"
|
||||
return trial:concat("")
|
||||
end
|
||||
|
||||
function is_solved(trial_board)
|
||||
for i = 1, #trial_board do
|
||||
if (self.dest_board[i] == ".") != (trial_board[i] == "$") then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function solve()
|
||||
local dir_labels = { {"u", "U"}, {"r", "R"}, {"d", "D"}, {"l", "L"} }
|
||||
local dirs = { {0, -1}, {1, 0}, {0, 1}, {-1, 0} }
|
||||
local history = new set()
|
||||
history:add(self.curr_board)
|
||||
local open = new dlist()
|
||||
open:push(new board(self.curr_board, "", self.player_x, self.player_y))
|
||||
|
||||
while !open:empty() do
|
||||
local b = open:remove(1)
|
||||
for i = 1, #dirs do
|
||||
local trial = b.cur
|
||||
local dx = dirs[i][1]
|
||||
local dy = dirs[i][2]
|
||||
|
||||
-- Are we standing next to a box ?
|
||||
if trial[(b.y + dy) * self.ncols + b.x + dx + 1] == "$" then
|
||||
-- Can we push it ?
|
||||
trial = self:push(b.x, b.y, dx, dy, trial)
|
||||
if trial != "" then
|
||||
-- Or did we already try this one ?
|
||||
if !history:contains(trial) then
|
||||
local newsol = b.sol .. dir_labels[i][2]
|
||||
if self:is_solved(trial) then return newsol end
|
||||
open:push(new board(trial, newsol, b.x + dx, b.y + dy))
|
||||
history:add(trial)
|
||||
end
|
||||
end
|
||||
else -- otherwise try changing position
|
||||
trial = self:move(b.x, b.y, dx, dy, trial)
|
||||
if trial != "" and !history:contains(trial) then
|
||||
local newsol = b.sol .. dir_labels[i][1]
|
||||
open:push(new board(trial, newsol, b.x + dx, b.y + dy))
|
||||
history:add(trial)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return "No solution"
|
||||
end
|
||||
end
|
||||
|
||||
local level = {
|
||||
"#######",
|
||||
"# #",
|
||||
"# #",
|
||||
"#. # #",
|
||||
"#. $$ #",
|
||||
"#.$$ #",
|
||||
"#.# @#",
|
||||
"#######"
|
||||
}
|
||||
print(level:concat("\n"))
|
||||
print()
|
||||
print(new sokoban(level):solve())
|
||||
175
Task/Sokoban/V-(Vlang)/sokoban.v
Normal file
175
Task/Sokoban/V-(Vlang)/sokoban.v
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
struct Board {
|
||||
cur string
|
||||
sol string
|
||||
xir int
|
||||
yir int
|
||||
}
|
||||
|
||||
struct Direction {
|
||||
dxir int
|
||||
dyir int
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
mut:
|
||||
data []Board
|
||||
}
|
||||
|
||||
fn (mut que Queue) push(brd Board) {
|
||||
que.data << brd
|
||||
}
|
||||
|
||||
fn (mut que Queue) pop() ?Board {
|
||||
if que.data.len == 0 { return none }
|
||||
item := que.data[0]
|
||||
que.data = que.data[1..]
|
||||
return item
|
||||
}
|
||||
|
||||
fn (que Queue) is_empty() bool {
|
||||
return que.data.len == 0
|
||||
}
|
||||
|
||||
struct Sokoban {
|
||||
n_cols int
|
||||
dest_board string
|
||||
curr_board string
|
||||
player_x int
|
||||
player_y int
|
||||
}
|
||||
|
||||
fn new_sokoban(board []string) Sokoban {
|
||||
n_cols := board[0].len
|
||||
mut dest_buf := []u8{}
|
||||
mut curr_buf := []u8{}
|
||||
mut player_x := 0
|
||||
mut player_y := 0
|
||||
for ral in 0 .. board.len {
|
||||
for cal in 0 .. n_cols {
|
||||
ch := board[ral][cal]
|
||||
dest_buf << if ch != `$` && ch != `@` { ch } else { ` ` }
|
||||
curr_buf << if ch != `.` { ch } else { ` ` }
|
||||
if ch == `@` {
|
||||
player_x = cal
|
||||
player_y = ral
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Sokoban{
|
||||
n_cols: n_cols
|
||||
dest_board: dest_buf.bytestr()
|
||||
curr_board: curr_buf.bytestr()
|
||||
player_x: player_x
|
||||
player_y: player_y
|
||||
}
|
||||
}
|
||||
|
||||
fn (skn Sokoban) move(xir int, yir int, dxir int, dyir int, trial_board string) string {
|
||||
new_pos := (yir + dyir) * skn.n_cols + xir + dxir
|
||||
if trial_board[new_pos] != ` ` { return "" }
|
||||
mut trial := trial_board.bytes()
|
||||
trial[yir * skn.n_cols + xir] = ` `
|
||||
trial[new_pos] = `@`
|
||||
return trial.bytestr()
|
||||
}
|
||||
|
||||
fn (skn Sokoban) push(xir int, yir int, dxir int, dyir int, trial_board string) string {
|
||||
new_box_pos := (yir + 2 * dyir) * skn.n_cols + xir + 2 * dxir
|
||||
if trial_board[new_box_pos] != ` ` { return "" }
|
||||
mut trial := trial_board.bytes()
|
||||
trial[yir * skn.n_cols + xir] = ` `
|
||||
trial[(yir + dyir) * skn.n_cols + xir + dxir] = `@`
|
||||
trial[new_box_pos] = `$`
|
||||
return trial.bytestr()
|
||||
}
|
||||
|
||||
fn (skn Sokoban) is_solved(trial_board string) bool {
|
||||
for ial in 0 .. trial_board.len {
|
||||
if (skn.dest_board[ial] == `.`) != (trial_board[ial] == `$`) { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn (skn Sokoban) solve() string {
|
||||
dir_labels := [`u`, `r`, `d`, `l`]
|
||||
dir_labels_caps := [`U`, `R`, `D`, `L`]
|
||||
dirs := [
|
||||
Direction{dxir: 0, dyir: -1},
|
||||
Direction{dxir: 1, dyir: 0},
|
||||
Direction{dxir: 0, dyir: 1},
|
||||
Direction{dxir: -1, dyir: 0},
|
||||
]
|
||||
mut trial := ""
|
||||
mut open := Queue{}
|
||||
mut history := map[string]bool{}
|
||||
history[skn.curr_board] = true
|
||||
open.push(Board{
|
||||
cur: skn.curr_board
|
||||
sol: ""
|
||||
xir: skn.player_x
|
||||
yir: skn.player_y
|
||||
})
|
||||
for !open.is_empty() {
|
||||
cur_board := open.pop() or { break }
|
||||
cur := cur_board.cur
|
||||
sol := cur_board.sol
|
||||
xir := cur_board.xir
|
||||
yir := cur_board.yir
|
||||
|
||||
for i in 0 .. dirs.len {
|
||||
dxir := dirs[i].dxir
|
||||
dyir := dirs[i].dyir
|
||||
trial = cur
|
||||
if trial[(yir + dyir) * skn.n_cols + xir + dxir] == `$` {
|
||||
trial = skn.push(xir, yir, dxir, dyir, trial)
|
||||
if trial != "" && !history[trial] {
|
||||
new_sol := sol + dir_labels_caps[i].str()
|
||||
if skn.is_solved(trial) { return new_sol }
|
||||
open.push(Board{
|
||||
cur: trial
|
||||
sol: new_sol
|
||||
xir: xir + dxir
|
||||
yir: yir + dyir
|
||||
})
|
||||
history[trial] = true
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
trial = skn.move(xir, yir, dxir, dyir, trial)
|
||||
if trial != "" && !history[trial] {
|
||||
new_sol := sol + dir_labels[i].str()
|
||||
open.push(Board{
|
||||
cur: trial
|
||||
sol: new_sol
|
||||
xir: xir + dxir
|
||||
yir: yir + dyir
|
||||
})
|
||||
history[trial] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "No solution"
|
||||
}
|
||||
|
||||
fn main() {
|
||||
level := [
|
||||
"#######",
|
||||
"# #",
|
||||
"# #",
|
||||
"#. # #",
|
||||
"#. $$ #",
|
||||
"#.$$ #",
|
||||
"#.# @#",
|
||||
"#######",
|
||||
]
|
||||
|
||||
for line in level {
|
||||
println(line)
|
||||
}
|
||||
println("")
|
||||
sokoban := new_sokoban(level)
|
||||
println(sokoban.solve())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue