Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Simple Sokoban
#1
 SIMPLE SOKOBAN

 How to Play:
 -Use arrow keys to move the player
 -Push crates (blue boxes) onto targets (yellow boxes)
 -All crates must be on targets to win
 -Press R to restart the level
 -Press ESC to quit

 Reference :
 move one tile at a time by Marcus
https://www.naalaa.com/forum/thread-355-...ml#pid2359


Code:
'===============================================================
' SIMPLE SOKOBAN
'
' How to Play:
' -Use arrow keys to move the player
' -Push crates (blue boxes) onto targets (yellow boxes)
' -All crates must be on targets to win
' -Press R to restart the level
' -Press ESC to quit
'
' Reference :
' move one tile at a time by Marcus
' https://www.naalaa.com/forum/thread-355-post-2359.html#pid2359
'================================================================


set window "Simple Sokoban", 256, 240, false, 3
set redraw off

' Map legend:
' 0 = empty floor
' 1 = wall
' 2 = player start
' 3 = box/crate
' 4 = target/goal
' 5 = box on target
' 6 = player on target

map = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
    [1, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 1, 1, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
    [1, 0, 1, 0, 3, 0, 0, 0, 0, 4, 1, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
    [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1],
    [1, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 1, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]

' Backup for restart functionality - using simple copy
mapBackup = []
for y = 0 to sizeof(map) - 1
    mapBackup[y] = copy(map[y])
next

' Width and height of map
mapHeight = sizeof(map)
mapWidth = sizeof(map[0])

' Find player starting position
playerX = unset
playerY = unset
for y = 0 to mapHeight - 1
    for x = 0 to mapWidth - 1
        if map[y][x] = 2
            playerY = y
            playerX = x
            map[y][x] = 0
            break
        endif
    next
next

' Count targets for win condition
targetCount = 0
for y = 0 to mapHeight - 1
    for x = 0 to mapWidth - 1
        if map[y][x] = 4 or map[y][x] = 5
            targetCount = targetCount + 1
        endif
    next
next

' Game state
gameWon = false
showWinMessage = 0

' Movement animation state
playerScreenX = playerX * 16
playerScreenY = playerY * 16
playerDX = 0
playerDY = 0
playerSteps = 0
boxMoveX = 0
boxMoveY = 0
boxSteps = 0
boxStartX = 0
boxStartY = 0
boxDestX = 0
boxDestY = 0

' Main game loop
while not keydown(KEY_ESCAPE, true)
    ' Handle restart
    if keydown(KEY_R, true)
        ' Restore map from backup using simple copy
        for y = 0 to mapHeight - 1
            map[y] = copy(mapBackup[y])
        next
        gameWon = false
        showWinMessage = 0
       
        ' Reset player position
        for y = 0 to mapHeight - 1
            for x = 0 to mapWidth - 1
                if map[y][x] = 2
                    playerY = y
                    playerX = x
                    map[y][x] = 0
                    break
                endif
            next
        next
        playerScreenX = playerX * 16
        playerScreenY = playerY * 16
        playerSteps = 0
        boxSteps = 0
    endif
   
    ' Only accept input when no animation is playing
    if playerSteps = 0 and boxSteps = 0 and not gameWon
        dx = 0
        dy = 0
       
        ' Check for arrow keys
        if keydown(KEY_LEFT)  then dx = -1
        elseif keydown(KEY_RIGHT) then dx = 1
        elseif keydown(KEY_UP)    then dy = -1
        elseif keydown(KEY_DOWN)  then dy = 1
       
        if dx or dy
            ' Calculate next positions
            nextX = playerX + dx
            nextY = playerY + dy
            beyondX = playerX + dx * 2
            beyondY = playerY + dy * 2
           
            ' Check bounds
            if nextX >= 0 and nextX < mapWidth and nextY >= 0 and nextY < mapHeight
                ' What's in the next cell?
                nextCell = map[nextY][nextX]
               
                ' Can walk onto empty floor or target
                if nextCell = 0 or nextCell = 4
                    playerDX = dx * 16
                    playerDY = dy * 16
                    playerSteps = 16
                   
                ' Can push a box if space beyond is empty/target
                elseif nextCell = 3 or nextCell = 5
                    if beyondX >= 0 and beyondX < mapWidth and beyondY >= 0 and beyondY < mapHeight
                        beyondCell = map[beyondY][beyondX]
                       
                        if beyondCell = 0 or beyondCell = 4
                            ' Start player movement
                            playerDX = dx * 16
                            playerDY = dy * 16
                            playerSteps = 16
                           
                            ' Start box movement
                            boxMoveX = nextX * 16
                            boxMoveY = nextY * 16
                            boxStartX = nextX
                            boxStartY = nextY
                            boxDestX = beyondX
                            boxDestY = beyondY
                            boxSteps = 16
                           
                            ' Update map: remove box from current position
                            if nextCell = 3 then map[nextY][nextX] = 0
                            elseif nextCell = 5 then map[nextY][nextX] = 4
                        endif
                    endif
                endif
            endif
        endif
    endif
   
    ' Animate player
    if playerSteps > 0
        playerSteps = playerSteps - 1
        playerScreenX = playerScreenX + playerDX / 16
        playerScreenY = playerScreenY + playerDY / 16
       
        if playerSteps = 0
            ' Snap to grid and update logical position
            playerX = playerScreenX / 16
            playerY = playerScreenY / 16
           
            ' Handle player standing on target
            if map[playerY][playerX] = 4
                map[playerY][playerX] = 6  ' Player on target
            endif
        endif
    endif
   
    ' Animate box
    if boxSteps > 0
        boxSteps = boxSteps - 1
        boxMoveX = boxMoveX + playerDX / 16
        boxMoveY = boxMoveY + playerDY / 16
       
        if boxSteps = 0
            ' Place box in new position
            if map[boxDestY][boxDestX] = 4
                map[boxDestY][boxDestX] = 5  ' Box on target
            else
                map[boxDestY][boxDestX] = 3  ' Box on floor
            endif
           
            ' Check win condition
            boxesOnTarget = 0
            for y = 0 to mapHeight - 1
                for x = 0 to mapWidth - 1
                    if map[y][x] = 5 then boxesOnTarget = boxesOnTarget + 1
                next
            next
           
            if boxesOnTarget = targetCount
                gameWon = true
                showWinMessage = 120  ' Show for 2 seconds (120 frames at 60fps)
            endif
        endif
    endif
   
    ' Drawing
    set color 20, 20, 30      ' Dark background
    cls
   
    ' Draw targets (empty goals)
    set color 255, 220, 0
    for y = 0 to mapHeight - 1
        for x = 0 to mapWidth - 1
            cell = map[y][x]
            if cell = 4 or cell = 5 or cell = 6
                draw rect x*16, y*16, 16, 16
            endif
        next
    next
   
    ' Draw walls
    set color 96, 64, 32
    for y = 0 to mapHeight - 1
        for x = 0 to mapWidth - 1
            if map[y][x] = 1
                draw rect x*16, y*16, 16, 16, true
            endif
        next
    next
   
    ' Draw boxes
    set color 0, 0, 255
    for y = 0 to mapHeight - 1
        for x = 0 to mapWidth - 1
            if map[y][x] = 3 or map[y][x] = 5
                draw rect x*16 + 2, y*16 + 2, 12, 12, true
            endif
        next
    next
   
    ' Draw moving box during animation
    if boxSteps > 0
        set color 160, 100, 60
        draw rect boxMoveX + 2, boxMoveY + 2, 12, 12, true
        set color 130, 70, 40
        draw rect boxMoveX + 4, boxMoveY + 4, 8, 8, false
    endif
   
    ' Draw player
    set color 32, 200, 64
    draw rect playerScreenX + 2, playerScreenY + 2, 12, 12, true
   
    ' Draw win message
    if gameWon
        set color 0, 0, 0, 180
        draw rect 40, 90, 176, 60, true
       
        set color 255, 255, 100
        set caret 128, 100
        center "LEVEL COMPLETE!"
       
        set color 200, 200, 200
        set caret 128, 120
        center "Press R to restart"
       
        if showWinMessage > 0
            showWinMessage = showWinMessage - 1
        endif
    endif
   
    redraw
    fwait 60
wend
Reply
#2
Very nice!

But:

Code:
' Backup for restart functionality - using simple copy
for y = 0 to sizeof(map) - 1
    mapBackup[y] = copy(map[y])
next

...

' Restore map from backup using simple copy
for y = 0 to mapHeight - 1
    map[y] = copy(mapBackup[y])
next

, can be replaced with:

Code:
mapBackup = copy(map)

...

map = copy(mapBackup)

The 'copy' function always makes "deep" copies Smile
Reply
#3
Thank you Marcus, it's much more efficient now.
'deep copy' makes life easier 
Big Grin
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)