Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Sokoban game, and Game development.

Sometimes the art of coding takes you to unexpected shores. This is why I wrote a Sokoban game for the Commodore 64.

How did it come to my mind? Back in the eighties, there was a magazine called Zzap!64 covering the best games for 8-bit computers like C64, Spectrum ZX and more. There also was an italian version of the magazine, which recently came back to life, and I have the honor of being one of the editors.

The 8-bit scene is incredibly exciting, with a lot of new games being released every week, and there are also many new tools for modern computers to develop 8-bit games.

One of them is ugBasic, a cross-platform language similar to good old BASIC. I tried it, and ended up building a Sokoban game.

I already built a lot of Sokoban games for various platforms in various languages, so I said to myself: why not? Although I can code a Sokoban game with my eyes closed, this time I ran into a series of problems, like low RAM, no tweening, and just a few colors.

But eventually, as usual, I managed to build a Sokoban game with 64 levels. How can you play, if you don’t have an actual C64 hardware? With emulator like VICE.

Look at the source code:

BITMAP ENABLE (2)
COLOR BORDER BLACK

DIM levels AS BYTE (2304) = #{_
0,0,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,1,4,2,1,3,0,0,0,0,1,0,0,0,0,0,1,1,1,_
1,0,0,0,0,1,1,3,0,2,0,1,0,0,4,0,0,1,0,3,1,2,0,1,0,0,1,1,1,1,1,1,1,1,1,1,_
2,0,1,1,1,1,2,0,1,1,1,1,4,3,0,0,1,1,0,3,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,_
2,0,4,0,0,1,0,0,3,3,0,1,1,1,0,2,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,0,0,5,4,1,1,0,0,3,2,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,1,1,1,_
0,0,1,1,1,1,0,4,1,1,1,1,2,3,1,1,1,1,0,0,0,0,1,1,2,3,1,0,1,1,1,0,0,0,1,1,_
1,0,0,0,0,1,0,0,1,1,0,1,0,3,2,3,2,0,1,0,1,0,0,0,1,0,4,0,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,1,1,3,2,4,0,0,0,0,1,0,0,0,0,3,2,0,1,0,0,1,0,0,1,1,1,1,0,0,1,_
1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,2,1,0,0,3,3,2,1,1,0,0,0,4,1,1,1,1,1,1,1,_
1,0,0,0,1,1,1,3,1,2,1,1,0,0,0,0,1,1,0,3,1,2,0,1,1,0,4,0,0,1,1,1,1,0,0,1,_
0,0,0,1,0,0,0,0,2,0,0,0,1,3,1,1,0,0,0,4,2,1,0,0,0,3,0,0,0,0,1,1,1,1,0,0,_
2,2,6,1,1,1,0,3,3,1,1,1,1,3,0,0,1,1,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,1,1,1,_
1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,0,1,1,0,3,3,3,0,1,0,2,6,2,0,1,1,1,1,1,1,1,_
0,0,1,1,1,1,0,0,0,0,1,1,0,3,2,0,1,1,1,0,5,0,1,1,1,3,2,0,1,1,1,0,4,1,1,1,_
1,0,0,0,0,1,1,0,1,0,0,1,2,5,1,0,0,1,2,0,1,0,1,1,4,3,3,0,0,0,0,0,1,0,0,0,_
1,0,0,0,0,1,1,0,2,2,2,1,0,3,3,3,0,1,0,4,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,_
0,2,2,0,0,1,0,2,4,3,0,1,1,0,3,3,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,_
0,4,1,1,1,1,0,0,1,1,1,1,3,3,3,0,0,1,2,2,2,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,_
1,1,0,6,0,1,1,1,0,5,0,1,0,0,3,5,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,_
0,0,1,1,1,1,0,0,1,1,1,1,0,0,3,0,0,1,1,3,5,2,0,1,0,0,2,0,1,1,0,0,4,0,1,1,_
0,0,0,0,0,1,2,1,0,1,0,1,0,3,3,2,0,1,2,0,0,1,1,1,0,1,3,1,1,1,0,0,4,1,1,1,_
1,0,0,0,0,1,1,0,2,2,2,1,0,3,3,0,3,1,4,0,1,0,0,1,0,0,1,0,0,1,1,1,1,1,1,1,_
1,1,0,0,1,1,2,0,3,0,3,0,4,1,0,0,1,0,0,1,3,2,2,0,0,0,0,1,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,1,2,3,0,1,1,0,3,2,4,1,1,0,2,3,0,1,1,0,0,1,1,1,1,0,0,1,1,1,1,_
0,0,0,1,1,1,0,0,0,1,1,1,1,3,3,0,3,0,1,2,2,0,2,0,1,0,0,1,1,1,1,4,0,1,1,1,_
1,0,0,1,1,1,0,0,0,1,1,1,0,5,2,4,0,1,1,3,5,0,0,1,0,0,0,1,1,1,0,0,0,1,1,1,_
1,1,0,0,1,1,1,1,3,0,1,1,0,6,5,2,0,1,0,0,3,0,0,1,0,0,0,1,1,1,0,0,0,1,1,1,_
1,1,0,0,0,1,1,0,0,1,0,1,1,6,3,5,5,1,0,0,0,1,0,1,0,0,0,0,0,1,1,1,1,1,1,1,_
0,0,1,0,0,0,0,2,0,0,2,2,0,0,0,1,1,1,0,3,3,3,0,1,1,1,4,0,0,1,1,1,1,1,1,1,_
1,1,0,0,1,1,0,0,0,0,1,1,0,3,0,3,0,1,0,0,1,3,4,1,2,2,2,0,1,1,1,1,1,1,1,1,_
1,0,0,1,1,1,1,0,0,0,0,0,1,3,2,2,0,2,0,0,1,1,4,1,0,3,3,0,0,1,0,0,0,0,0,1,_
0,2,1,1,1,1,0,2,1,1,1,1,3,0,0,3,4,0,0,0,0,3,0,0,2,0,1,1,1,1,1,1,1,1,1,1,_
1,1,1,1,4,0,1,1,0,0,0,0,0,3,3,1,2,0,2,0,0,0,0,1,0,3,0,1,0,1,1,1,0,0,2,1,_
0,0,0,1,1,1,0,1,0,1,1,1,0,0,0,1,1,1,5,6,3,5,0,1,0,0,0,0,0,1,1,1,1,0,0,1,_
1,1,1,0,0,1,0,0,2,3,0,1,0,0,2,3,4,1,1,1,2,3,1,1,1,1,0,0,1,1,1,1,0,0,1,1,_
0,0,1,1,1,1,0,3,2,0,1,1,0,0,3,2,1,1,1,3,2,0,1,1,1,4,0,1,1,1,1,0,0,1,1,1,_
1,1,1,0,0,1,0,0,1,0,0,1,0,0,3,3,3,0,4,2,2,0,2,0,1,1,1,0,0,1,1,1,1,1,1,1,_
0,0,1,1,1,1,0,2,2,0,4,0,0,3,0,1,1,0,1,0,0,1,1,0,1,3,2,0,3,0,1,0,0,1,1,1,_
1,1,0,0,1,1,1,1,0,3,1,1,0,0,3,2,3,0,0,0,2,4,2,0,1,0,0,0,1,1,1,1,1,1,1,1,_
0,0,0,0,1,1,0,0,2,0,0,1,1,3,5,3,0,1,1,0,2,4,1,1,1,1,0,0,1,1,1,1,1,1,1,1,_
1,1,0,2,0,1,1,1,3,2,0,1,0,0,0,2,0,1,0,3,3,0,0,1,1,0,4,1,1,1,1,1,1,1,1,1,_
0,0,0,0,1,1,4,3,3,0,1,1,1,3,2,2,1,1,1,0,2,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,_
0,0,0,0,0,1,0,1,3,2,0,1,0,0,3,2,1,1,1,1,3,2,1,1,1,1,4,0,1,1,1,1,0,0,1,1,_
1,1,0,4,1,1,0,0,5,0,1,1,0,0,5,2,0,1,0,0,5,3,0,1,0,0,0,1,1,1,1,1,1,1,1,1,_
1,1,1,0,0,0,1,1,1,4,1,0,0,0,0,0,3,0,0,1,2,2,2,1,0,3,0,1,3,1,1,1,0,0,0,1,_
1,0,0,1,1,1,0,0,0,1,1,1,0,0,5,3,4,0,0,0,2,5,0,0,1,1,0,0,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,1,1,4,0,1,1,1,1,3,5,1,1,0,0,5,2,0,0,0,0,0,0,0,0,0,0,1,1,1,1,_
0,0,1,1,1,1,0,2,3,2,1,1,0,0,3,0,0,0,1,1,0,0,4,2,1,1,3,0,1,1,1,1,0,0,1,1,_
1,2,0,0,2,1,0,2,0,1,0,1,0,0,0,3,0,1,0,3,1,3,1,1,1,0,4,0,1,1,1,1,1,1,1,1,_
0,0,2,0,0,1,0,1,5,1,0,1,5,0,3,0,0,1,0,0,4,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,_
2,0,0,1,1,1,2,1,0,1,1,1,0,0,0,3,0,1,0,3,3,2,0,1,0,0,4,1,1,1,1,1,1,1,1,1,_
0,0,0,1,1,1,0,1,0,1,1,1,0,1,6,0,1,1,0,3,5,3,0,1,0,0,2,0,0,1,1,1,1,0,0,1,_
1,1,0,0,1,1,1,1,0,2,1,1,1,1,3,0,3,0,0,4,0,3,1,0,0,1,2,2,0,0,0,0,0,1,1,1,_
1,1,0,0,1,1,0,0,0,5,1,1,0,1,0,2,1,1,0,0,4,3,1,1,1,1,0,5,0,1,1,1,0,0,0,1,_
1,1,1,0,0,1,0,0,1,3,2,1,0,0,0,3,2,1,0,0,1,3,2,1,1,0,1,0,0,1,1,0,0,4,0,1,_
1,0,0,1,1,1,1,3,0,0,0,1,1,4,3,2,0,1,1,3,1,2,1,1,0,0,0,2,1,1,0,0,0,0,1,1,_
1,1,0,0,1,1,1,0,3,0,1,1,0,2,3,2,1,1,0,0,3,2,0,1,1,1,4,0,0,1,1,1,1,0,0,1,_
1,1,1,1,0,0,1,0,4,0,0,0,0,5,5,5,2,0,0,0,3,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,1,1,0,0,0,0,0,3,3,5,0,0,0,2,4,2,0,1,0,0,1,1,1,1,1,1,1,1,1,1,_
1,1,0,0,1,1,1,1,2,4,2,0,0,0,3,2,1,0,0,0,0,3,0,0,1,1,1,0,3,1,1,1,1,0,0,1,_
1,0,2,0,1,1,1,4,0,3,1,1,0,0,5,0,0,0,0,5,0,5,0,0,1,0,0,0,1,1,1,1,1,1,1,1,_
1,0,0,0,1,1,1,3,1,0,1,1,0,4,0,0,1,1,0,1,0,2,1,1,2,3,3,2,0,1,1,1,0,0,0,1,_
0,0,0,0,0,1,0,1,0,1,0,1,0,2,0,3,2,0,1,1,3,5,3,0,1,1,0,2,0,1,1,1,1,4,0,1,_
1,0,0,0,0,1,1,3,1,1,2,1,1,0,1,1,2,1,0,0,0,3,2,0,0,3,0,0,4,0,1,1,0,0,1,1_
}

wallImage := LOAD IMAGE("images/wall.png")
playerImage := LOAD IMAGE("images/player.png")
crateImage := LOAD IMAGE("images/crate.png")
goalImage := LOAD IMAGE("images/goal.png")
floorImage := LOAD IMAGE("images/floor.png")
crateOnGoalImage := LOAD IMAGE("images/crate_in_place.png")
arrowImage := LOAD IMAGE("images/arrow.png")
lockedImage := LOAD IMAGE("images/locked.png")
unlockedImage := LOAD IMAGE("images/unlocked.png")

DIM levelArray AS BYTE (8, 8)
DIM solvedLevels AS BYTE WITH 0 (64) 
CONST floor = 0
CONST wall = 1
CONST goal = 2
CONST crate = 3
CONST player = 4

VAR level AS BYTE = 0 
VAR posX AS BYTE
VAR posY AS BYTE
VAR number
VAR playerX AS BYTE
VAR playerY AS BYTE
VAR destinationX AS BYTE
VAR destinationY AS BYTE 
VAR crateDestinationX AS BYTE
VAR crateDestinationY AS BYTE
VAR start AS BYTE

CLS
LOCATE 17, 6 : PRINT "SOKO64"
LOCATE 15, 10 : PRINT "PRESS FIRE"
LOCATE 9, 23 : PRINT "2022 Emanuele Feronato"
PUT IMAGE playerImage AT 120, 110
PUT IMAGE crateImage AT 152, 110
PUT IMAGE goalImage AT 184, 110
DO
	IF JFIRE(1) THEN
		EXIT
	ENDIF 
LOOP	
PUT IMAGE floorImage AT 120, 110
PUT IMAGE playerImage AT 136, 110
WAIT 100 MILLISECONDS
PUT IMAGE floorImage AT 136, 110
PUT IMAGE playerImage AT 152, 110
PUT IMAGE crateImage AT 168, 110
WAIT 100 MILLISECONDS
PUT IMAGE floorImage AT 152, 110
PUT IMAGE playerImage AT 168, 110
PUT IMAGE crateOnGoalImage AT 184, 110
WAIT 1000 MILLISECONDS

playLevel : 
	CLS
	IF level < 6 THEN
		start = 1
	ELSE
		start = level - 5
		IF start > 52 THEN
			start = 52
		ENDIF
	ENDIF		
	FOR i = 0 TO 12 
		IF start + i < 10 THEN
			LOCATE i * 3 + 1, 22 : PRINT "0"
			LOCATE i * 3 + 2, 22 : PRINT (start + i)	
		ELSE
			LOCATE i * 3 + 1, 22 : PRINT (start + i)
		ENDIF
		IF solvedLevels(start + i - 1) = 1 THEN
			PUT IMAGE unlockedImage AT 8 + 24 * i, 172
		ELSE
			PUT IMAGE lockedImage AT 8 + 24 * i, 172
		ENDIF		
	NEXT
	LOCATE 0, 0 : PRINT "(P)rev"	
	LOCATE 17, 0 : PRINT "(R)etry"	
	LOCATE 34, 0 : PRINT "(N)ext" 
	PUT IMAGE arrowImage AT 8 + 24 * (level - start + 1), 184
	posY = 48
	number = 36 * level
	posX = 96
	FOR i = 0 TO 7
		levelArray(0, i) = wall
		PUT IMAGE wallImage AT posX, 32
		levelArray(7, i) = wall
		PUT IMAGE wallImage AT posX, 144 
		ADD posX, 16
	NEXT
	FOR i = 1 TO 6	
		levelArray(i, 0) = wall
		PUT IMAGE wallImage AT 96, posY 
		levelArray(i, 7) = wall
		PUT IMAGE wallImage AT 208, posY
		posX = 112
		FOR j = 1 TO 6
			value = levels(number)
			levelArray(i, j) = floor
			IF value = wall THEN
				levelArray(i, j) = wall
				PUT IMAGE wallImage AT posX, posY
			ENDIF
			IF value = player THEN
				levelArray(i, j) = player
				PUT IMAGE playerImage AT posX, posY
				playerX = j
				playerY = i
			ENDIF
			IF value = player + goal THEN
				levelArray(i, j) = player + goal
				PUT IMAGE playerImage AT posX, posY
				playerX = j
				playerY = i
			ENDIF
			IF value = crate THEN
				levelArray(i, j) = crate
				PUT IMAGE crateImage AT posX, posY
			ENDIF
			IF value = goal THEN
				levelArray(i, j) = goal
				PUT IMAGE goalImage AT posX, posY
			ENDIF	
			IF value = crate + goal THEN
				levelArray(i, j) = crate + goal
				PUT IMAGE crateOnGoalImage AT posX, posY
			ENDIF	
			ADD posX, 16
			INC number
		NEXT
		ADD posY, 16
	NEXT
	
playerInput:  
	DO
		IF JLEFT(1) THEN
			destinationX = playerX - 1
			destinationY = playerY
			crateDestinationX = destinationX - 1
			crateDestinationY = destinationY
	  		GOTO movePlayer
		ENDIF
		IF JRIGHT(1) THEN
			destinationX = playerX + 1
			destinationY = playerY
			crateDestinationX = destinationX + 1
			crateDestinationY = destinationY
			GOTO movePlayer
		ENDIF
		IF JUP(1) THEN
			destinationX = playerX 
			destinationY = playerY - 1
			crateDestinationX = destinationX 
			crateDestinationY = destinationY - 1
			GOTO movePlayer
		ENDIF
		IF JDOWN(1) THEN
			destinationX = playerX 
			destinationY = playerY + 1
			crateDestinationX = destinationX
			crateDestinationY = destinationY + 1
			GOTO movePlayer
		ENDIF
		IF KEY STATE(KEY R) THEN
			GOTO playLevel
		ENDIF
		IF (KEY STATE(KEY P)) AND (level > 0) THEN	
			DEC level
			GOTO playLevel
		ENDIF
		IF (KEY STATE(KEY N)) AND (level < 63) THEN	
			INC level
			GOTO playLevel
		ENDIF
	LOOP
	
movePlayer :
	IF (levelArray(destinationY, destinationX) = goal) OR (levelArray(destinationY, destinationX) = floor) THEN
		IF levelArray(playerY, playerX) = player THEN
			PUT IMAGE floorImage AT playerX * 16 + 96 , playerY * 16 + 32
		ELSE
			PUT IMAGE goalImage AT playerX * 16 + 96 , playerY * 16 + 32
		ENDIF		
		ADD levelArray(playerY, playerX), - player
		playerX = destinationX
		playerY = destinationY
		ADD levelArray(destinationY, destinationX), player
    	PUT IMAGE playerImage AT playerX * 16 + 96, playerY * 16 + 32
    	WAIT 100 MILLISECONDS
    	GOTO playerInput
    ENDIF
    IF (levelArray(destinationY, destinationX) = crate) OR (levelArray(destinationY, destinationX) = crate + goal) THEN
		IF (levelArray(crateDestinationY, crateDestinationX) = goal) OR (levelArray(crateDestinationY, crateDestinationX) = floor) THEN
			IF levelArray(playerY, playerX) = player THEN
				PUT IMAGE floorImage AT playerX * 16 + 96 , playerY * 16 + 32
			ELSE
				PUT IMAGE goalImage AT playerX * 16 + 96 , playerY * 16 + 32
			ENDIF
			ADD levelArray(playerY, playerX), - player
			playerX = destinationX
			playerY = destinationY
			ADD levelArray(destinationY, destinationX), player
			ADD levelArray(destinationY, destinationX), -crate
			ADD levelArray(crateDestinationY, crateDestinationX), crate
	    	PUT IMAGE playerImage AT playerX * 16 + 96, playerY * 16 + 32
	    	IF levelArray(crateDestinationY, crateDestinationX) = crate + goal THEN
	    		PUT IMAGE crateOnGoalImage AT crateDestinationX * 16 + 96, crateDestinationY * 16 + 32
	    	ELSE
	    		PUT IMAGE crateImage AT crateDestinationX * 16 + 96, crateDestinationY * 16 + 32
	    	ENDIF
	    	FOR i = 0 TO 7
    	 		FOR j = 0 TO 7
    	 			IF (levelArray(i, j) = goal) OR (levelArray(i, j) = player + goal) THEN
    	 				WAIT 100 MILLISECONDS
	    				GOTO playerInput		
    	 			ENDIF
    	 		NEXT
    	 	NEXT
    	 	GOTO levelCompleted	
		ENDIF
	ENDIF
	GOTO playerInput
    
levelCompleted :
	LOCATE 17, 3 : PRINT "SOLVED!" 
	solvedLevels(level) = 1
	DO
		IF KEY STATE(KEY R) THEN
			GOTO playLevel
		ENDIF
		IF (KEY STATE(KEY P)) AND (level > 0) THEN	
			DEC level
			GOTO playLevel
		ENDIF
		IF (KEY STATE(KEY N)) AND (level < 63) THEN	
			INC level
			GOTO playLevel
		ENDIF
	LOOP

Tomorrow I will comment the source and upload the game to my itch page, meanwhile you can download the source code of the whole project and play with it.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.