Mit Minetest den Retroklassiker Snake programmieren

Was mit Minetest alles machbar ist, beweist schön das von SGVsbG8K programmierte Snake-Spiel, bei dem es darum geht, eine Schlange den nächsten Stein fressen zu lassen, wodurch sie immer länger wird. Mit seiner Genehmigung dokumentieren wir hier den Code und ermutigen zum Nachmachen .

Man erreicht es über den POI “Snakespiel” im Inventar (P anklicken).

Programmierung

Code für die Leuchtsteine auf der Anzeigetafel, unterster Controller:

if event.type=="program" then
mem.pos={0,7}
end
local function is_this(pos)
return (pos[1]==mem.pos[1] and pos[2]==mem.pos[2]) or pos=="all"
end

if event.type=="digiline" and event.channel=="screen" and is_this(event.msg[1]) then
port.c=event.msg[2]
end

Code des Spiels:

local function reset()
mem.cursor={0,0}
digiline_send("screen", {"all",false})
digiline_send("screen", {mem.cursor,true})
mem.length=0
mem.tail={{0,0}}
mem.dir={1,0}
mem.dirname="left"
mem.food={math.random(0,mem.w-1), math.random(0,mem.h-1)}
digiline_send("screen", {mem.food,true})
if not mem.running then interrupt(1);mem.running=true end


end
if event.type=="program" then
mem.name="anonym"
mem.w=8
mem.h=8
mem.dirs={["up"]={0,1},
["down"]={0,-1},
["left"]={1,0},
["right"]={-1,0},}
reset()
end
local function random_pos()
return {math.random(0,mem.w-1), math.random(0,mem.h-1)}

end
local function touching_tail(pos)
for i,j in ipairs(mem.tail) do
if j[1]..","..j[2] ==pos[1]..","..pos[2] then return true end
end
return false
end
local function get_vh(dir)
test={["right"]="h",
["left"]="h",
["up"]="v",
["down"]="v"}
return test[dir]
end
local function is_valid(dir)
return not (get_vh(dir) == get_vh(mem.dirname))
end
if event.type=="digiline" and event.channel=="name" then
name=event.msg
mem.name=name
end

if event.type=="digiline" and event.channel=="buttons" then
dir=mem.dirs[event.msg]
if not dir then
if event.msg=="middle" then
--[[mem.cursor={0,0}
digiline_send("screen", {"all",false})
digiline_send("screen", {mem.cursor,true})
mem.length=0
mem.dir={1,0}
mem.tail={{0,0}}
mem.dirname="left"
mem.food={math.random(0,mem.w-1), math.random(0,mem.h-1)}
digiline_send("screen", {mem.food,true})]]
reset()
end
return
end
if is_valid(event.msg) then
mem.dir=dir
mem.dirname=event.msg

end
end
local function equal_pos(p1,p2)
return (p1[1]..","..p1[2] == p2[1]..","..p2[2])
end
local function select_new_food_pos()
pos=random_pos()
if touching_tail(pos) then
while touching_tail(pos) do
pos=random_pos()
end
end
mem.food=pos
digiline_send("screen", {mem.food,true})

end
local function die()
mem.running=false
digiline_send("image_manager", "dead")--die
digiline_send("highscore", {mem.name,mem.length})
end
--main loop
if event.type=="interrupt" then
if (mem.cursor[1]+mem.dir[1]<0 or mem.cursor[1]+mem.dir[1]>mem.w-1) or (mem.cursor[2]+mem.dir[2]<0 or mem.cursor[2]+mem.dir[2]>mem.h-1) then
die()

return
end

--digiline_send("screen", {mem.cursor,false})
if #mem.tail-1>mem.length then
digiline_send("screen", {mem.tail[1],false})
table.remove(mem.tail,1)
end
mem.cursor={mem.cursor[1]+mem.dir[1],mem.cursor[2]+mem.dir[2]}
if touching_tail(mem.cursor) then
mem.running=false
die()
return
end
if equal_pos(mem.cursor, mem.food) then
mem.length=mem.length+1
select_new_food_pos()
end
digiline_send("screen", {mem.cursor,true})
--[[for i,j in ipairs(mem.tail) do
digiline_send("screen", {j,true})
end]]
digiline_send("screen", {mem.food,true})
table.insert(mem.tail, mem.cursor)
interrupt(1)
end





--[[test-program
if event.type=="program" then
mem.w=6
mem.h=6
mem.cursor={0,0}
digiline_send("screen", {"all",false})
digiline_send("screen", {mem.cursor,true})
mem.dirs={["up"]={0,1},
["down"]={0,-1},
["left"]={1,0},
["right"]={-1,0},}
end
if event.type=="digiline" and event.channel=="buttons" then
dir=mem.dirs[event.msg]
if not dir then
if event.msg=="middle" then
digiline_send("screen", {mem.cursor,false})
mem.cursor={0,0}
digiline_send("screen", {mem.cursor,true})
end
return
end
if (mem.cursor[1]+dir[1]<0 or mem.cursor[1]+dir[1]>mem.w-1) or (mem.cursor[2]+dir[2]<0 or mem.cursor[2]+dir[2]>mem.h-1) then return end
digiline_send("screen", {mem.cursor,false})
mem.cursor={mem.cursor[1]+dir[1],mem.cursor[2]+dir[2]}
digiline_send("screen", {mem.cursor,true})
end--]]

Mittlerer Controller:

if event.type=="program" then
mem.images={["404"]={},
["dead"]={{2,2}, {5,2}, {3,3}, {4,3}, {2,5}, {5,5}}, 
["clear"]={}}

end

if event.type == "digiline" and event.channel=="image_manager" then
digiline_send("screen", {"all",false})
image=mem.images[event.msg] or mem.images["404"]
for i,j in ipairs(image) do
digiline_send("screen",{j,true})
end
end

Oberer Controller:

if event.type=="program" then
mem.list={}
end
local function update()
digiline_send("display", "\n\n\n\n\n\n")
for i,j in pairs(mem.list) do
digiline_send("display", j[1]..": "..j[2])
end

end

if event.type=="digiline" and event.channel=="highscore" then
name=event.msg[1]
points=event.msg[2]
if #mem.list==0 then
table.insert(mem.list,{name,points})
elseif mem.list[#mem.list][2]<=points then
for i,j in ipairs(mem.list) do
if j[2]<=points then
table.insert(mem.list,i,{name,points})
break
end
end
if #mem.list > 6 then 
table.remove(mem.list,#mem.list)
end
elseif #mem.list<7 then
table.insert(mem.list,{name,points})
end
update()
end

Schalter für neues Spiel:

if event.type=="program" then
mem.list={}
end
local function update()
digiline_send("display", "\n\n\n\n\n\n")
for i,j in pairs(mem.list) do
digiline_send("display", j[1]..": "..j[2])
end

end

if event.type=="digiline" and event.channel=="highscore" then
name=event.msg[1]
points=event.msg[2]
if #mem.list==0 then
table.insert(mem.list,{name,points})
elseif mem.list[#mem.list][2]<=points then
for i,j in ipairs(mem.list) do
if j[2]<=points then
table.insert(mem.list,i,{name,points})
break
end
end
if #mem.list > 6 then 
table.remove(mem.list,#mem.list)
end
elseif #mem.list<7 then
table.insert(mem.list,{name,points})
end
update()
end

Schalter “nach links”:

if event.type=="on" and pin.c then
digiline_send("buttons", "left")
end