Revision 4a29a79b8f1cd58a614af82c3bb5d40fdda85d48 authored by g3gg0 on 08 October 2016, 14:22:41 UTC, committed by g3gg0 on 08 October 2016, 14:22:41 UTC
Simplify dng names
sokoban.lua
--[[
Sokoban Game
Ported to Lua from the original PicoC version
This is a playable demo for Lua scripting.
It should help you understand:
- Basic key processing
- Simple graphics
- Lua programming basics
Enjoy!
]]
require("keys")
--printf
function printf(...)
io.write(string.format(...))
end
-- http://www.sokobano.de/wiki/index.php?title=Sok_format
WALL = '#'
SPACE = ' '
PLAYER = '@'
BOX = '$'
TARGET = '.'
PLAYER_ON_TARGET = '+'
BOX_ON_TARGET = '*'
-- from Wikipedia
maze1 = {
" ##### ",
"### # ",
"#.@$ # ",
"### $.# ",
"#.##$ # ",
"# # . ##",
"#$ *$$.#",
"# . #",
"########"
};
-- Level 1 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
maze2 = {
" ##### ",
"### # ",
"# $ # ##",
"# # . #",
"# # #",
"## # #",
" #@ ###",
" ##### "
};
-- Level 2 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
maze3 = {
" ##### ",
"### # ",
"# $ # ##",
"# # . #",
"# # #",
"##$#. #",
" #@ ###",
" ##### "
};
-- Level 23 from http://sneezingtiger.com/sokoban/levels/minicosmosText.html
maze4 = {
" #### ",
"####### # ",
"# # ",
"# $ #.# # ",
"# $## # ##",
"### @ #",
" ### # #",
" ##. ##",
" ##### "
};
-- Level 1 from http://sneezingtiger.com/sokoban/levels/nabokosmosText.html
maze5 = {
" ##### ",
" # ## ",
"## * .##",
"# $$* #",
"# * . #",
"## @ ###",
" ##### "
};
-- Level 2 from http://sneezingtiger.com/sokoban/levels/nabokosmosText.html
maze6 = {
" #### ",
"### ###",
"# *$ #",
"# # # #",
"# ** #",
"### #@#",
" # ** #",
" # # #",
" # *. #",
" # ###",
" #### "
};
-- global variables
maze = nil
LINES = nil
COLUMNS = nil
function string_to_table(str)
local t = {}
for i = 1,#str,1 do
t[i] = string.sub(str,i,i)
end
return t
end
function convert_maze(m)
local r = {}
for i,v in ipairs(m) do
r[i] = string_to_table(v)
end
return r
end
function setup(a)
if (a == 1) then
maze = convert_maze(maze1);
LINES = 9;
COLUMNS = 8;
elseif (a == 2) then
maze = convert_maze(maze2);
LINES = 8;
COLUMNS = 8;
elseif (a == 3) then
maze = convert_maze(maze3);
LINES = 8;
COLUMNS = 8;
elseif (a == 4) then
maze = convert_maze(maze4);
LINES = 9;
COLUMNS = 11;
elseif (a == 5) then
maze = convert_maze(maze5);
LINES = 7;
COLUMNS = 8;
elseif (a == 6) then
maze = convert_maze(maze6);
LINES = 11;
COLUMNS = 8;
end
end
targets = {}
function print_maze()
for i = 1,LINES,1 do
for j = 1,COLUMNS,1 do
local c = ' '
if targets[i*COLUMNS + j] ~= nil then c= c '*' end
printf("%s%s", maze[i][j], c);
end
printf("\n");
end
end
function draw_wall(x, y, a)
display.rect(x, y, a, a, COLOR.BLACK);
end
function draw_space(x, y, a, on_target)
display.rect(x, y, a, a, COLOR.gray(80), COLOR.gray(80));
if (on_target) then
display.circle(x + a//2, y + a//2, a//4, COLOR.ORANGE);
end
end
function draw_box(x, y, a, on_target)
local pad = 3;
for i = 0, pad, 1 do
display.rect(x + i, y + i, a - i*2, a - i*2, COLOR.gray(80));
end
local color = COLOR.YELLOW;
if on_target then color = COLOR.ORANGE end
display.rect(x + pad, y + pad, a - pad*2, a - pad*2, color,color);
display.rect(x + pad, y + pad, a - pad*2, a - pad*2, COLOR.BLACK);
pad = pad+1;
display.rect(x + pad, y + pad, a - pad*2, a - pad*2, COLOR.BLACK);
display.line(x + pad, y + pad, x + a - pad, y + a - pad, COLOR.BLACK);
display.line(x + pad - 1, y + pad, x + a - pad - 1, y + a - pad, COLOR.BLACK);
display.line(x + pad, y + a - pad, x + a - pad, y + pad, COLOR.BLACK);
display.line(x + pad - 1, y + a - pad, x + a - pad - 1, y + pad, COLOR.BLACK);
end
function draw_player(x, y, a, on_target)
draw_space(x, y, a, 0);
local xc = x + a//2;
local yc = y + a//2;
local r = a * 3//8;
local y_eye = yc - r * 3//8;
local y_mouth = yc + r * 3//8;
display.circle(xc, yc, r, COLOR.YELLOW, COLOR.YELLOW);
display.circle(xc, yc, r, COLOR.BLACK);
display.circle(xc, yc, r-1, COLOR.BLACK);
display.circle(xc - r//3, y_eye, r//5, COLOR.BLACK, COLOR.BLACK);
display.circle(xc + r//3, y_eye, r//5, COLOR.BLACK, COLOR.BLACK);
local r_mouth = r*2//3;
for t = 45, 180-45, 2 do
local xp = xc + r_mouth * math.cos(t*math.pi/180);
local yp = yc - r/8 + r_mouth * math.sin(t*math.pi/180);
display.pixel(xp, yp, COLOR.BLACK);
display.pixel(xp, yp+1, COLOR.BLACK);
end
end
function draw_maze()
display.rect(0, 0, 720, 480, COLOR.BLACK, COLOR.BLACK);
local a = math.min(400 // LINES, 700 // COLUMNS);
local total_w = a * COLUMNS;
local total_h = a * LINES;
local x0 = 360 - total_w // 2;
local y0 = 240 - total_h // 2;
for i = 1, LINES, 1 do
for j = 1, COLUMNS, 1 do
-- top-left corner of current cell
local x = x0 + (j-1) * a;
local y = y0 + (i-1) * a;
if maze[i][j] == BOX then
draw_box(x, y, a, targets[i*COLUMNS + j]);
elseif maze[i][j] == SPACE then
draw_space(x, y, a, targets[i*COLUMNS + j]);
elseif maze[i][j] == WALL then
draw_wall(x, y, a);
elseif maze[i][j] == PLAYER then
draw_player(x, y, a, targets[i*COLUMNS + j]);
end
end
end
for i = 1, 5, 1 do
display.rect(x0-i, y0-i, total_w+2*i, total_h+2*i, COLOR.WHITE);
end
end
function victory()
beep();
draw_maze();
display.print("YOU WIN :)", 300, 200, FONT.LARGE );
local level_menu = sokoban_menu.submenu["Level"]
if level_menu.value < level_menu.max then
level_menu.value = level_menu.value + 1
end
end
function split_target()
for i = 1, LINES, 1 do
for j = 1, COLUMNS, 1 do
if (maze[i][j] == TARGET) then
targets[i*COLUMNS + j] = true;
maze[i][j] = SPACE;
elseif (maze[i][j] == BOX_ON_TARGET) then
targets[i*COLUMNS + j] = true;
maze[i][j] = BOX;
elseif (maze[i][j] == PLAYER_ON_TARGET) then
targets[i*COLUMNS + j] = true;
maze[i][j] = PLAYER;
else targets[i*COLUMNS + j] = false; end
end
end
end
function get_current_pos()
for i = 1, LINES, 1 do
for j = 1, COLUMNS, 1 do
if (maze[i][j] == PLAYER) then
return i,j;
end
end
end
-- error
return -1,-1
end
function move(dl, dc)
local l,c
l,c = get_current_pos();
if (maze[l][c] ~= PLAYER) then printf("whoops"); return; end
if (maze[l+dl][c+dc] == SPACE) then-- what's in the destination box?
-- simple, just move
maze[l][c] = SPACE;
maze[l+dl][c+dc] = PLAYER;
elseif (maze[l+dl][c+dc] == BOX) then -- move only if we can push the box in a free space
if (maze[l+dl*2][c+dc*2] == SPACE) then
maze[l+dl*2][c+dc*2] = BOX;
maze[l+dl][c+dc] = PLAYER;
maze[l][c] = SPACE;
end
end
end
function check_solution()
for i = 1, LINES, 1 do
for j = 1, COLUMNS, 1 do
if ((targets[i*COLUMNS + j]) and (maze[i][j] ~= BOX)) then
return 0;-- at least one box not on target position
end
end
end
return 1; -- looks alright
end
function main()
setup(sokoban_menu.submenu["Level"].value)
menu.block(true);
local status,error = xpcall(function()
split_target();
keys:start()
draw_maze();
while true do
if menu.visible == false then return end
-- print_maze();
local key = keys:getkey();
if key == KEY.LEFT or key == KEY.WHEEL_LEFT then
move(0, -1);
elseif key == KEY.RIGHT or key == KEY.WHEEL_RIGHT then
move(0, 1);
elseif key == KEY.UP or key == KEY.WHEEL_UP then
move(-1, 0);
elseif key == KEY.DOWN or key == KEY.WHEEL_DOWN then
move(1, 0);
elseif key == KEY.SET or key == KEY.UNPRESS_SET then
elseif key == KEY.Q or key == KEY.TRASH or key == KEY.MENU then
printf("Exiting...\n");
menu.block(false);
keys:stop()
return;
end
display.draw(draw_maze)
if check_solution() ~= 0 then break end
task.yield(100)
end
printf("You win!\n");
victory();
task.yield(5000)
end,debug.traceback)
if status == false then
print(error)
end
menu.block(false);
keys:stop()
end
sokoban_menu = menu.new
{
parent = "Games",
name = "Sokoban",
help = "A simple game in Lua",
submenu =
{
{
name = "Play",
select = function(this) task.create(main) end,
},
{
name = "Level",
value = 1,
min = 1,
max = 6
}
}
}
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...