For some reason I thought that Gemini would be good at generating levels of this kind, perhaps because it actually TOLD me it was super good at such tasks ...
Code:
' Code by me, levels by Gemini 3.1 Pro with Extended Thinking. I told Gemini to generate levels of
' increasing difficulty - number 16 should be super hard.
' Move with arrow keys, restart with esc key, skip level (number 15 is impossible) with S key.
#win32
visible vLevel
visible vTilesImage, vPlayerImage
set window "Sokoban", 256, 224, false, 3
set redraw off
CreateAssets()
levels = [
" " +
" " +
" " +
" ##### " +
" # # " +
" # . # " +
" # $ # " +
" # @ # " +
" # # " +
" ##### " +
" " +
" " +
" ",
" " +
" " +
" " +
" ####### " +
" # # " +
" # . . # " +
" # $ $ # " +
" # @ # " +
" # # " +
" ####### " +
" " +
" " +
" ",
" " +
" " +
" " +
" ######## " +
" # # " +
" # . ## # " +
" # . $ # " +
" # $@ # " +
" # # " +
" ######## " +
" " +
" " +
" ",
" " +
" " +
" " +
" ######### " +
" # . . # " +
" # $ # " +
" # $ # " +
" # @ # " +
" ######### " +
" " +
" " +
" " +
" ",
" " +
" " +
" ####### " +
" # # " +
" # . # " +
" ## $ # " +
" ## $ ## " +
" # @ # " +
" # . # " +
" ####### " +
" " +
" " +
" ",
" " +
" " +
" " +
" ####### " +
" # # " +
" ### $ ## " +
" # . $ # " +
" # . @ # " +
" ########## " +
" " +
" " +
" " +
" ",
" " +
" " +
" " +
" ###### " +
" # # " +
" # $. ### " +
" # $ . # " +
" ## @ # " +
" ####### " +
" " +
" " +
" " +
" ",
" " +
" " +
" ###### " +
" # # " +
" ### ## ### " +
" # . .. .# " +
" # $ $ ## " +
" ## $ $ # " +
" # @ # " +
" ######## " +
" " +
" " +
" ",
" " +
" " +
" ####### " +
" # # " +
" # .$. # " +
" ## $ ## " +
" ## @ ## " +
" # $ # " +
" # . # " +
" ####### " +
" " +
" " +
" ",
" " +
" " +
" ##### " +
" # # " +
" ### $ ### " +
" # . # " +
" # $ $ # " +
" ### . ### " +
" # . # " +
" #@ # " +
" ##### " +
" " +
" ",
" " +
" " +
" ######## " +
" # . # " +
" # $ # " +
" # ## # " +
" # . # " +
" #### $ # " +
" # # " +
" #@ # " +
" ##### " +
" " +
" ",
" " +
" " +
" ###### " +
" # # " +
" ### $ ### " +
" # ... # " +
" # $$ # " +
" ### @ ### " +
" # # " +
" ###### " +
" " +
" " +
" ",
" " +
" " +
" " +
" ####### " +
" # . # " +
" # $ # " +
" # $..# " +
" ## $ ## " +
" # @# " +
" ##### " +
" " +
" " +
" ",
" " +
" " +
" ###### " +
" # # " +
" # $ # " +
" ### # ### " +
" # . . . # " +
" # $ $ ## " +
" ## @ # " +
" ######### " +
" " +
" " +
" ",
" " +
" ####### " +
" # . # " +
" # $ # " +
" ### ### ### " +
" # . . . # " +
" # $$$ # " +
" # @ ### # " +
" ########### " +
" " +
" " +
" " +
" ",
" " +
" " +
" ########## " +
" # # . # " +
" # $ # # " +
" ## $ # # " +
" # . . # " +
" ## $$ ## " +
" # @ # " +
" # . # " +
" ####### " +
" " +
" "]
levelIndex = 0
do
vLevel = LevelFromString(levels[levelIndex])
do
levelComplete = true
for y = 0 to 12 for x = 0 to 15
if vLevel.fg[x][y] levelComplete = vLevel.fg[x][y].Update() and levelComplete
next
if keydown(KEY_S, true) levelComplete = true
set color 0, 0, 0
cls
set color 255, 255, 255
for y = 0 to 12 for x = 0 to 15 draw image vTilesImage, x*16, y*16, vLevel.bg[x][y]
for y = 0 to 12 for x = 0 to 15 if vLevel.fg[x][y] vLevel.fg[x][y].Draw()
set color 255, 255, 255
set caret width(primary)/2, height(primary) - 14
center "LEVEL " + (levelIndex + 1)
redraw
fwait 60
until levelComplete or keydown(KEY_ESCAPE, true)
if levelComplete levelIndex = levelIndex + 1
until levelIndex = sizeof(levels)
set color 0, 0, 0
cls
set caret width(primary)/2, (height(primary) - fheight())/2
set color 255, 255, 255
center "GAME COMPLETED!"
redraw
wait 3000
end
function CreateAssets()
vTilesImage = createimage(64, 16)
set image grid vTilesImage, 4, 1
set image vTilesImage
set color 32, 32, 32
draw rect 0, 0, 16, 16, true
set color 48, 48, 48
for i = 0 to 63 draw pixel rnd(16), rnd(16)
set color 128, 128, 128
draw rect 16, 0, 16, 16, true
set color 208, 208, 208
for y = 0 to 3
draw line 16, y*4, 32, y*4
for x = 0 to 1 draw line 16 + x*8 + (y%2)*4, y*4, 16 + x*8 + (y%2)*4, y*4 + 4
next
set color 255, 255, 255
draw image vTilesImage, 32, 0, 0
set color 128, 64, 32
draw rect 32, 0, 16, 16, false
set color 192, 128, 96
draw rect 48, 0, 16, 16, true
set color 128, 96, 64
draw rect 48, 0, 16, 16, false
draw rect 51, 3, 10, 10, false
draw line 55, 4, 59, 8
draw line 51, 7, 55, 11
set image primary
vPlayerImage = createimage(16, 16)
set image vPlayerImage
set color 0, 0, 0, 0
cls true
set color 255, 208, 0
for y = 0 to 1 for x = 0 to 1 draw ellipse 7 + x, 7 + y, 7, 7, true
set color 64, 32, 0
draw line 4, 4, 4, 8
draw line 11, 4, 11, 8
draw line 5, 12, 10, 12
draw pixel 4, 11; draw pixel 11, 11
set image primary
endfunc
function LevelFromString(s)
level = [bg: fill(0, 16, 13), fg: fill(unset, 16, 13)]
for i = 0 to 207
y = floor(i/16); x = i%16
select mid(s, i)
case "#" level.bg[x][y] = 1
case "." level.bg[x][y] = 2
case "$" level.fg[x][y] = Block(vTilesImage, 3, x, y)
case "@" level.fg[x][y] = Player(x, y)
endsel
next
return level
endfunc
function Block(img, cel, x, y)
b = [img: img, cel: cel, x: x, y: y, dx: 0, dy: 0, spd: 1]
b.Move = function()
if this.dx > 0 this.dx = max(this.dx - this.spd, 0)
elseif this.dx < 0 this.dx = min(this.dx + this.spd, 0)
if this.dy > 0 this.dy = max(this.dy - this.spd, 0)
elseif this.dy < 0 this.dy = min(this.dy + this.spd, 0)
return this.dx or this.dy
endfunc
b.BeginMove = function(dx, dy)
' If you can figure out the purpose of "- (dx > 0)*this.spd" and "- (dy > 0)*this.spd", you
' may reward yourself with a cookie :)
this.dx = -dx*16 - (dx > 0)*this.spd
this.dy = -dy*16 - (dy > 0)*this.spd
vLevel.fg[this.x][this.y] = unset
this.x = this.x + dx; this.y = this.y + dy
vLevel.fg[this.x][this.y] = this
endfunc
b.Update = function()
return not this.Move() and vLevel.bg[this.x][this.y] = 2
endfunc
b.Draw = function()
set color 255, 255, 255
draw image this.img, this.x*16 + this.dx, this.y*16 + this.dy, this.cel
endfunc
return b
endfunc
function Player(x, y)
p = Block(vPlayerImage, 0, x, y)
p.Update = function()
if not this.Move()
dx = 0; dy = 0
if keydown(KEY_LEFT) dx = -1
elseif keydown(KEY_RIGHT) dx = 1
elseif keydown(KEY_UP) dy = -1
elseif keydown(KEY_DOWN) dy = 1
if dx or dy
newX = this.x + dx; newY = this.y + dy
if vLevel.fg[newX][newY]
if vLevel.bg[newX + dx][newY + dy] <> 1 and not vLevel.fg[newX + dx][newY + dy]
vLevel.fg[newX][newY].BeginMove(dx, dy)
this.BeginMove(dx, dy)
endif
elseif vLevel.bg[newX][newY] <> 1
this.BeginMove(dx, dy)
endif
endif
endif
return true
endfunc
return p
endfunc
