sokoban, checkers, maze generator, game of life

master
rnd 2015-05-08 12:12:46 +02:00
commit ccfb5698d1
12 changed files with 2032 additions and 0 deletions

153
checkers.lua Normal file
View File

@ -0,0 +1,153 @@
-- CHECKERS GAME
local checkers ={};
checkers.piece = "";checkers.time = 0;
checkers.pos = {} -- bottom left position of 8x8 checkerboard piece
checkers.piece_pos = {} -- position of pick up piece
--game pieces
local function draw_board() -- pos is bottom left position of checkerboard
local pos = checkers.pos;
local node;
for i = 1,8 do
for j =1,8 do
node = minetest.get_node({x=pos.x+i-1,y=pos.y,z=pos.z-1}).name;
if (i+j) % 2 == 1 then
if node~="games:board_black" then minetest.set_node({x=pos.x+i-1,y=pos.y,z=pos.z+j-1},{name = "games:board_black"}) end
else
if node~="games:board_white" then minetest.set_node({x=pos.x+i-1,y=pos.y,z=pos.z+j-1},{name = "games:board_white"}) end
end
node = minetest.get_node({x=pos.x+i-1,y=pos.y+1,z=pos.z+j-1}).name;
if node~="air" then minetest.set_node({x=pos.x+i-1,y=pos.y+1,z=pos.z+j-1},{name = "air"}) end
end
end
for i = 1,4 do -- place pieces
minetest.set_node({x=pos.x+2*i-1,y=pos.y+1,z=pos.z},{name = "games:checkers_red"})
minetest.set_node({x=pos.x+2*i-2,y=pos.y+1,z=pos.z+1},{name = "games:checkers_red"})
minetest.set_node({x=pos.x+2*i-1,y=pos.y+1,z=pos.z+6},{name = "games:checkers_blue"})
minetest.set_node({x=pos.x+2*i-2,y=pos.y+1,z=pos.z+7},{name = "games:checkers_blue"})
end
for i = 1,8 do -- place kings
node = minetest.get_node({x=pos.x+i-1,y=pos.y+1,z=pos.z-2}).name;
if node~="games:checkers_red_queen" then minetest.set_node({x=pos.x+i-1,y=pos.y+1,z=pos.z-2},{name = "games:checkers_red_queen"}) end
node = minetest.get_node({x=pos.x+i-1,y=pos.y,z=pos.z-2}).name;
if node~="games:board_white" then minetest.set_node({x=pos.x+i-1,y=pos.y,z=pos.z-2},{name = "games:board_white"}) end
node = minetest.get_node({x=pos.x+i-1,y=pos.y+1,z=pos.z+9}).name;
if node~="games:checkers_blue_queen" then minetest.set_node({x=pos.x+i-1,y=pos.y+1,z=pos.z+9},{name = "games:checkers_blue_queen"}) end
node = minetest.get_node({x=pos.x+i-1,y=pos.y,z=pos.z+9}).name;
if node~="games:board_white" then minetest.set_node({x=pos.x+i-1,y=pos.y,z=pos.z+9},{name = "games:board_white"}) end
end
end
minetest.register_node("games:checkers", {
description = "checkers crate",
tiles = {"moreblocks_iron_checker.png","crate.png","crate.png","crate.png","crate.png","crate.png"},
groups = {oddly_breakable_by_hand=1},
is_ground_content = false,
paramtype = "light",
light_source = 14,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext","checkers game block, admin only")
meta:set_int("time", minetest.get_gametime());
checkers.pos = {x = pos.x+1, y=pos.y-1, z=pos.z+1}
end,
on_punch = function(pos, node, player)
local name = player:get_player_name(); if name==nil then return end
local privs = minetest.get_player_privs(name);
if not privs.kick then return end -- only admin
checkers.pos = {x = pos.x+1, y=pos.y, z=pos.z+1}
draw_board()
end
}
)
minetest.register_chatcommand("checkers", {
description = "Start a game of checkers and refresh board display",
privs = {kick=true},
func = function(name,param)
draw_board();checkers.piece = ""
end
}
)
function register_piece(name, desc, tiles, punch)
minetest.register_node(name, {
description = desc,
drawtype = "nodebox",
paramtype = "light",
tiles = tiles,
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3},
sounds = default.node_sound_defaults(),
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
},
selection_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, -0.3, 0.5},
},
on_punch= punch,
})
end
function register_board(name,desc,tiles)
minetest.register_node(name, {
description = desc,
tiles = tiles,
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3},
sounds = default.node_sound_defaults(),
on_punch = function(pos, node, player) -- place piece on board
local name = player:get_player_name(); if name == nil then return end
if checkers.pos.x == nil then minetest.chat_send_player(name,"punch checkers game block before playing.") return end
if checkers.piece == "" then return end
local t = minetest.get_gametime(); if t-checkers.time <1 then return end; checkers.time = t;
local above = {x=pos.x,y=pos.y+1;z=pos.z};
local x,y; x= above.z-checkers.pos.z+1; y=above.x-checkers.pos.x+1;
minetest.set_node(above, {name = checkers.piece});
minetest.chat_send_all(
"#CHECKERS : " .. name .." moved: ".. checkers.piece_pos.z-checkers.pos.z+1 .. "," .. checkers.piece_pos.x-checkers.pos.x+1 .. " to " ..
x .. "," .. y )
checkers.piece = ""
end,
on_rightclick = function(pos, node, player, itemstack, pointed_thing) -- capture piece
local name = player:get_player_name(); if name == nil then return end
if checkers.pos.x == nil then minetest.chat_send_player(name,"punch checkers game block before playing.") return end
if checkers.piece == "" then return end
minetest.chat_send_all(name .." captured piece at ".. checkers.piece_pos.z-checkers.pos.z+1 .. ","..checkers.piece_pos.x-checkers.pos.x+1)
checkers.piece = "" return
end
})
end
local piece_punch = function(pos, node, player) -- pick up piece
local name = player:get_player_name(); if name == nil then return end
if checkers.pos.x == nil then minetest.chat_send_player(name,"punch checkers game block before playing (need kick priv).") return end
if checkers.piece~="" then return end -- dont pick up another piece before last one was put down
local t = minetest.get_gametime(); if t-checkers.time <1 then return end; checkers.time = t;
checkers.piece = node.name; minetest.set_node(pos, {name="air"});
checkers.piece_pos = {x=pos.x,y=pos.y,z=pos.z};
end
register_board("games:board_white","white board",{"wool_white.png"})
register_board("games:board_black","black board",{"wool_black.png"})
register_piece("games:checkers_blue","blue piece",{"wool_blue.png"},piece_punch)
register_piece("games:checkers_blue_queen","blue queen piece",{"queen_blue.png"},piece_punch)
register_piece("games:checkers_red","red piece",{"wool_red.png"},piece_punch)
register_piece("games:checkers_red_queen","red piece",{"queen_red.png"},piece_punch)

1
depends.txt Normal file
View File

@ -0,0 +1 @@
default

6
init.lua Normal file
View File

@ -0,0 +1,6 @@
-- rnd: collection of puzzle games for minetest
dofile(minetest.get_modpath("games").."/maze.lua")
dofile(minetest.get_modpath("games").."/sokoban.lua")
dofile(minetest.get_modpath("games").."/checkers.lua")
dofile(minetest.get_modpath("games").."/life.lua")
print("[games] loaded")

104
life.lua Normal file
View File

@ -0,0 +1,104 @@
-- game of life
local life_table = {};
local dim = 5;
minetest.register_abm(
{nodenames = {"games:life"},
interval = 5.0,
chance = 1,
action = function(pos)
local meta = minetest.get_meta(pos); local infotext = meta:get_string("infotext");
if infotext~="RUN" then return end
if life_table[0]==nil then return end-- punch node again to initialize
local table_new = {};
local i,j,k,c,n,p;
local dp = {{-1,-1},{0,-1},{1,-1},{-1,0},{1,0},{-1,1},{0,1},{1,1}};
for i = -dim,dim do
table_new[i]={};
for j = -dim,dim do
c = life_table[i][j];
n=0;
for k = 1,8 do
p={i+dp[k][1],j+dp[k][2]};
if p[1]<-dim then p[1]=dim elseif p[1]>dim then p[1]=-dim end;
if p[2]<-dim then p[2]=dim elseif p[2]>dim then p[2]=-dim end;
if life_table[ p[1] ][ p[2] ]==1 then n=n+1 end
end
if c == 1 then --alive
if n>=2 and n<=3 then table_new[i][j]=1
else
table_new[i][j]=0
end
else -- dead
if n==3 then table_new[i][j]=1 else table_new[i][j]=0 end
end
end
end
p={x=pos.x,y=pos.y+1,z=pos.z}
for i = -dim,dim do
for j = -dim,dim do
life_table[i][j]=table_new[i][j]
p.x = pos.x+i;p.z=pos.z+j;
if minetest.get_node(p).name=="air" then
if life_table[i][j]==1 then minetest.set_node(p,{name="stairs:slab_stone"}) end
else
if life_table[i][j]==0 then minetest.set_node(p,{name="air"}) end
end
end
end
end,
})
local function init_game(pos)
local name; local i,j;local p = {x=pos.x;y=pos.y+1,z=pos.z};
for i = -dim,dim do
life_table[i]={};
for j = -dim,dim do
p.x = pos.x+i;p.z=pos.z+j
name = minetest.get_node(p).name;
if name == "air" then life_table[i][j]=0 else life_table[i][j]=1 end
if life_table[i][j]==1 then minetest.set_node(p,{name="stairs:slab_stone"}) end
end
end
end
minetest.register_node("games:life", {
description = "game of life, game field is 1 above ",
inventory_image = "pipeworks_red.png",
wield_image = "pipeworks_red.png",
wield_scale = {x=0.8,y=2.5,z=1.3},
tiles = {"pipeworks_red.png"},
stack_max = 1,
groups = {oddly_breakable_by_hand=3},
on_punch = function(pos, node, puncher, pointed_thing)
local name = puncher:get_player_name();if name == nil then return end
local privs = minetest.get_player_privs(name);
if not privs.ban then return end
local meta = minetest.get_meta(pos)
local infotext = meta:get_string("infotext");
if infotext=="RUN" then infotext ="STOP" else infotext="RUN" end
meta:set_string("infotext", infotext)
init_game(pos)
end
}
)
-- minetest.register_craft({
-- output = "games:life",
-- recipe = {
-- {"default:stone","","default:stone"},
-- {"","default:stone",""},
-- {"default:stone","","default:stone"},
-- }
-- })

189
maze.lua Normal file
View File

@ -0,0 +1,189 @@
-- maze generation
-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker
-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m
-- representation of walls: below node k --> k, left of node k --> k+m.n
-- good overview of maze generation algorithms using javascript/html5
-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker
-- helper functions
--stack in lua
local stack={};
function stack.push(s,e) s[#s+1]=e end
function stack.pop(s) local r = s[#s];s[#s]=nil;return r end
--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end
function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders
local steps,maxsteps; steps= 0; maxsteps = 999999;
local maze = {}
maze.m = m; maze.n = n;
maze.unvisited = {};maze.stack = {}; maze.walls = {};
maze.free = maze.m*maze.n;
local i,j,k
local nb,wall -- unvisited neighbbors, walls
--init structures
for i=1,maze.m do
for j =1,maze.n do
k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited
maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there
end
end
math.randomseed(seed)
maze.current = start
maze.unvisited [ maze.current ] = false;
maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current
while maze.free>0 and steps<maxsteps do -- main loop
steps=steps+1
-- check current node neighbors
k=maze.current
j = k % maze.n;i=math.ceil(k/maze.n); -- get coords back from index
if j==0 then j = maze.n end
--print("coords current node "..k .. " = " .. i .. " " ..j)
nb={};wall={}-- check unvisited neighbors & wall removals
if i>1 then -- down
k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end
end
if i<maze.m then -- up
k=(i)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k;nb[#nb+1]=k end
end
if j<maze.n then --right
k=(i-1)*maze.n+j+1; if maze.unvisited[k] then wall[#wall+1]=k+maze.n*maze.m; nb[#nb+1]=k end
end
if j>1 then --left
k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end
end
--print(" unvisited neighbors " .. table2string(nb))
if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node
stack.push(maze.stack,maze.current) -- remember previous current node
k=math.random(#nb); -- pick random unvisited neighbor
maze.walls[wall[k]]=false; -- remove wall
--print(" removed wall ".. wall[k])
k=nb[k];
maze.current = k; -- new current cell
maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited
--print("new explore " .. k);
elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack
maze.current = stack.pop(maze.stack)
--print("backtrack to "..maze.current)
else -- even stack is empty, just pick random unvisited cell
k = math.random(maze.free); j=1;
for i =1,maze.m*maze.n do
if maze.unvisited[i] then
if j==k then k=i; break end -- pick node
j=j+1
end
end
--print(" stack empty, random pick " ..k)
maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1;
end
end -- of do
-- render maze with chars, row by row
maze.ret = {};
local hor;local vert;
local wall = "1"
for i=1,maze.m do
hor="";vert="";
k= (i-1)*maze.n;
-- horizontal
for j = 1, maze.n do
k=k+1;
-- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end
-- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end
if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end
if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end
end
maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall;
end
maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1)
return maze.ret
end
-- RUN PROGRAM
local maze=maze_deep_first_search(10,30,1,2015)
--for _,v in pairs(maze) do print(v) end
core.register_privilege("maze", "Can create mazes")
minetest.register_chatcommand("maze", {
description = "/maze (optional m,n,start,seed) generates maze at players position, directed towards positive x,z axix",
privs = {maze = true},
func = function(name, param)
local m,n,start, seed
if param == "" then m=10;n=10;start=1;seed=1 else
local words = {}; local word
for word in param:gmatch("%w+") do words[1+#words]= word end
if words[1]~=nil then m = tonumber(words[1]) else m = 10 end
if words[2]~=nil then n = tonumber(words[2]) else n = 10 end
if words[3]~=nil then start = tonumber(words[3]) else start = 1 end
if words[4]~=nil then seed = tonumber(words[4]) else seed = 1 end
end
if m*n>1000000 then minetest.chat_send_player(name, "limit maze size to 1000000 cells") return end
local player = minetest.env:get_player_by_name(name); if player==nil then return end
local pos = player:getpos();local p
local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed
local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z};
for i,v in pairs(maze) do
p.x = pos.x+i
for k = 1,string.len(v) do
p.z=pos.z+k
if string.sub(v,k,k)=="1" then
minetest.set_node(p,{name="games:stone_maze"})
else minetest.set_node(p,{name="air"})
end
end
end
end,
})
minetest.register_node("games:glass_maze", {
description = "maze_glass",
drawtype = "glasslike_framed_optional",
tiles = {"maze_glass.png"},
inventory_image = minetest.inventorycube("maze_glass.png"),
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {immortal = 1,disable_jump=1},
sounds = default.node_sound_glass_defaults(),
})
minetest.register_node("games:stone_maze", {
description = "maze_wall",
tiles = {"default_brick.png"},
is_ground_content = true,
groups = {immortal = 1,disable_jump=1},
legacy_mineral = true,
sounds = default.node_sound_stone_defaults(),
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
local name = player:get_player_name(); if name == nil then return end
if minetest.is_protected(pos, name) then
return
end
local player_inv = player:get_inventory()
if player_inv:room_for_item("main", {"games:stone_maze"}) then
player_inv:add_item("main", "games:stone_maze")
minetest.remove_node(pos) -- remove bones
end
end
})

181
sokoban.lua Normal file
View File

@ -0,0 +1,181 @@
-- SOKOBAN GAME
-- basic board game mechanics ( checkers )
-- by rnd
local sokoban = {};
sokoban.push_time = 0
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
sokoban.load=0;sokoban.playername =""
local SOKOBAN_WALL = "games:stone_maze"
local SOKOBAN_FLOOR = "default:stone"
local SOKOBAN_GOAL = "default:tree"
minetest.register_node("games:crate", { -- block that player pushes around, basically main sokoban routine
description = "sokoban crate",
tiles = {"crate.png"},
paramtype = "light",
light_source = 10,
is_ground_content = false,
groups = {immortal = 1},
sounds = default.node_sound_wood_defaults(),
on_punch = function(pos, node, player)
local name = player:get_player_name(); if name==nil then return end
if sokoban.playername~=name then
if sokoban.playername == "" then
minetest.chat_send_player(name,"Please right click level loader block to load and play Sokoban")
return
end
minetest.chat_send_player(name,"Only ".. sokoban.playername .. " can play. To play new level please right click loader block and select level.")
return
end
local time = sokoban.push_time; local t = minetest.get_gametime();
if t-time<1 then return end;sokoban.push_time = t
local p=player:getpos();local q={x=pos.x,y=pos.y,z=pos.z}
p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
if math.abs(p.y+0.5)>0 then return end
if math.abs(p.x)>math.abs(p.z) then
if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then return end
if p.x+q.x>q.x then q.x= q.x-1
else q.x = q.x+1
end
else
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then return end
if p.z+q.z>q.z then q.z= q.z-1
else q.z = q.z+1
end
end
if minetest.get_node(q).name=="air" then -- push crate
sokoban.moves = sokoban.moves+1
local old_infotext = minetest.get_meta(pos):get_string("infotext");
minetest.set_node(pos,{name="air"})
minetest.set_node(q,{name="games:crate"})
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,})
local meta = minetest.get_meta(q);
q.y=q.y-1;
if minetest.get_node(q).name==SOKOBAN_GOAL then
if old_infotext~="GOAL REACHED" then
sokoban.blocks = sokoban.blocks -1;
end
meta:set_string("infotext", "GOAL REACHED")
else
if old_infotext=="GOAL REACHED" then
sokoban.blocks = sokoban.blocks +1
end
meta:set_string("infotext", "push crate on top of goal block")
end
end
local name = player:get_player_name(); if name==nil then return end
if sokoban.blocks~=0 then
minetest.chat_send_player(name,"move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
else minetest.chat_send_all("#SOKOBAN : ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.")
-- if playerdata~=nil then -- award xp if playerdata exist
-- playerdata[name].xp = playerdata[name].xp + (sokoban.level-0.5)*100
-- end
sokoban.playername = ""; sokoban.level = 1
end
end,
})
minetest.register_node("games:sokoban", { -- level loader block
description = "sokoban crate",
tiles = {"default_brick.png","crate.png","crate.png","crate.png","crate.png","crate.png"},
groups = {oddly_breakable_by_hand=1},
is_ground_content = false,
paramtype = "light",
light_source = 14,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local form =
"size[3,1]" .. -- width, height
"field[0,0.5;3,1;level;enter level 1 to 90;1]"..
"button[2.5,0.25;1,1;play;OK]"
meta:set_string("formspec", form)
meta:set_string("infotext","sokoban level loader, right click to select level")
meta:set_int("time", minetest.get_gametime()-300);
end,
on_punch = function(pos, node, player) -- make timer ready to enter
local name = player:get_player_name(); if name==nil then return end
local privs = minetest.get_player_privs(name);
if not privs.ban then return end
local meta = minetest.get_meta(pos)
local t = minetest.get_gametime(); meta:set_int("time", t-500)
minetest.chat_send_all("Sokoban loader reset. Load level now.")
end,
on_receive_fields = function(pos, formname, fields, sender)
local name = sender:get_player_name(); if name==nil then return end
local privs = minetest.get_player_privs(name);
local meta = minetest.get_meta(pos)
local t = minetest.get_gametime();local t_old = meta:get_int("time");
if not privs.ban then
if name~=sokoban.playername then
if t-t_old<300 then
minetest.chat_send_player(name,"Wait at least 5 minutes to load next level. "..300-(t-t_old) .. " seconds left.");
return
end
else
if t-t_old<60 then
minetest.chat_send_player(name,"Wait at least 1 minute to load next level. "..60-(t-t_old) .. " seconds left.");
return
end
end
end
--minetest.chat_send_all("formname " .. formname .. " fields " .. dump(fields))
sokoban.playername = name
meta:set_int("time", t);
local lvl;
if fields.play ~= nil and tonumber(fields.level) ~=nil then
lvl = tonumber(fields.level)-1
end
if lvl == nil then return end
if lvl <0 or lvl >89 then return end
local file = io.open(minetest.get_modpath("games").."/sokoban.txt","r")
if not file then minetest.chat_send_player(name,"failed to open sokoban.txt") return end
local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0;
local lvl_found = false
while str~= nil do
str = file:read("*line");
if str~=nil and str =="; "..lvl then lvl_found=true break end
end
if not lvl_found then file:close();return end
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
while str~= nil do
str = file:read("*line");
if str~=nil then
if string.sub(str,1,1)==";" then
file:close(); minetest.chat_send_all("Sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); return
end
i=i+1;
for j = 1,string.len(str) do
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
p.y=p.y-1;
if minetest.get_node(p).name~=SOKOBAN_FLOOR then minetest.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor
p.y=p.y+1;
if s==" " and minetest.get_node(p).name~="air" then minetest.set_node(p,{name="air"}) end
if s=="#" and minetest.get_node(p).name~=SOKOBAN_WALL then minetest.set_node(p,{name=SOKOBAN_WALL}) end
if s=="$" then minetest.set_node(p,{name="games:crate"});sokoban.blocks=sokoban.blocks+1 end
if s=="." then p.y=p.y-1;minetest.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;minetest.set_node(p,{name="air"}) end
--starting position
if s=="@" then p.y=p.y-1;minetest.set_node(p,{name="default:glass"}); p.y=p.y+1;minetest.set_node(p,{name="air"}) end
if s~="@" then p.y = pos.y+2;minetest.set_node(p,{name="games:glass_maze"});
else p.y=pos.y+2;minetest.set_node(p,{name="default:ladder"})
end -- roof above to block jumps
end
end
end
file:close();
end,
})

1398
sokoban.txt Normal file

File diff suppressed because it is too large Load Diff

BIN
textures/crate.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
textures/maze_glass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 B

BIN
textures/queen_blue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

BIN
textures/queen_red.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B