Banner
{ Deutsch | English }
Sokoban

Sokoban − Misel



Source Code


General idea:
  1. Load level
  2. check where player could move
  3. if he can't move then don't move
  4. If there's a box
    • check if box can be move - if yes then move box
  5. move player
  6. Do that until all boxes are in a target

This script loads the actual level. It's stored in the window name. The data is stored externally in maze files for a better flexibility.
You can use the level editor to build a maze or just use any ASCII editor to build the maze manually after you understood the structure of such a level (see end of this document).
<SCRIPT type="text/javascript"> var level; if (window.name=="") level=1; else level=window.name; var level_string='<SCRIPT type="text/javascript" src="' + level + '.js"><\/SCRIPT>'; document.write(level_string); </SCRIPT>
Now comes the real deal ;)
<SCRIPT type="text/javascript">
The level properties. The variables must be declared here because they're global. The values are assigned by the functions in the maze file.
var box_image=new Image; box_image.src="box.gif"; var player_image=new Image; player_image.src="player.gif"; var wall_image=new Image; wall_image.src="wall.gif"; var free_image=new Image; free_image.src="free.gif"; var target_image=new Image; target_image.src="target.gif"; var box_in_target_image=new Image; box_in_target_image.src="box_in_target.gif"; var player_x; var player_y; var num_targets; var copyright=""; var num_rows; var num_columns;
load_level_size() is defined in the maze file. It's a function that sets the size of the maze. This must happen before the field is initialized. Each field is set to 0 and afterwards changed to its correct setting that is stored in the maze file under the load_level() function.
load_level_size(); var field=new Array(); for (var i=0; i < num_rows; i++) { field[i]=new Array(); for (var j=0; j < num_rows; j++) { field[i][j]=0; } } load_level();
Variables that are needed in every game.
how many boxes are already on a target
var box_in_target=0;
0-not finished, 1-finished
var game_status=0;
number of moves
var turns=0;
the image changing function
function Bildwechsel(Bildnr,Bildobjekt) { window.document.images[Bildnr].src = Bildobjekt.src; }
This checks if a key is pressed and starts the move_player() function with the according parameters.
function Tastendruck(Druck) { if (document.all) k = window.event.keyCode; else k = Druck.which; if (k == 38 || k == 87) move_player(0,-1); if (k == 37 || k == 65) move_player(-1,0); if (k == 39 || k == 68) move_player(1,0); if (k == 40 || k == 83) move_player(0,1); }
This function changes the image of a field.
function change_image(y,x) { var image_number='f'+y+'_'+x; switch (field[y][x]) { case 0: Bildwechsel(image_number,free_image); break; case 10: Bildwechsel(image_number,target_image); break; case 20: Bildwechsel(image_number,box_image); break; case 30: Bildwechsel(image_number,box_in_target_image); break; default: if (field[y][x]>=100) Bildwechsel(image_number,player_image); break; } }
This function moves the player and does everything that happens afterwards and everything else that deals with it.
if the game is still running...
if (game_status==0) {
increase the move counter
turns++;
check whether the new player position will be within the maze
if ((player_y+y>-1)&&(player_x+x>-1)&&(player_y+y<num_rows)&&(player_x+x<num_columns)) {
check the value of the new field and acts accordingly
switch(field[player_y+y][player_x+x]) {
∗ empty field, just move the player
case 0: field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=100; change_image(player_y,player_x); break;
∗ target field, just move the player
case 10: field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=100; change_image(player_y,player_x); break;
∗ field with a box and
∗ field with a box in a target
check if the box can be moved (i.e. if there's an empty field behind the target) and move the box and the player (or not)
If a box is moved on a target box_in_target will be increased − if it's moved out of a target it will be decreased.
case 20: if ((field[player_y+2*y][player_x+2*x] != 1) && (field[player_y+2*y][player_x+2*x] != 20) && (field[player_y+2*y][player_x+2*x] != 30) && (player_y+2*y > -1) && (player_x+2*x > -1) && (player_y+2*y < num_rows) && (player_x+2*x < num_columns)) { field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=80; change_image(player_y,player_x); field[player_y+y][player_x+x]+=20; change_image(player_y+y,player_x+x); if (field[player_y+y][player_x+x]==30) box_in_target++; } break; case 30: if ((field[player_y+2*y][player_x+2*x] != 1) && (field[player_y+2*y][player_x+2*x] != 20) && (field[player_y+2*y][player_x+2*x] != 30) && (player_y+2*y > -1) && (player_x+2*x > -1) && (player_y+2*y < num_rows) && (player_x+2*x < num_columns)) { field[player_y][player_x]-=100; change_image(player_y,player_x); player_x += x; player_y += y; field[player_y][player_x]+=80; change_image(player_y,player_x); field[player_y+y][player_x+x]+=20; change_image(player_y+y,player_x+x); if (field[player_y+y][player_x+x]!=30) box_in_target--; } break; }
When all boxes are in a target the game is over.
if (box_in_target == num_targets)game_status=1; } }
If the game is won, the level number is increased and a message pops up.
if (game_status==1) { alert('You won the game in '+turns+' moves!'); level++; window.name=level; }} </SCRIPT>
The following part belongs in the BODY.
<SCRIPT type="text/javascript">
for capturing the keys
document.onkeydown = Tastendruck;
builds the maze
for (var i=0; i < num_rows; i++) { for (var j=0; j < num_columns; j++) { var temp = field[i][j]; switch (temp) { case 0: document.write('<IMG alt="f" src="'+free_image.src+'" name="f'+i+'_'+j+'">'); break; case 1: document.write('<IMG alt="w" src="'+wall_image.src+'" name="f'+i+'_'+j+'">'); break; case 10: document.write('<IMG alt="f" src="'+target_image.src+'" name="f'+i+'_'+j+'">'); break; case 20: document.write('<IMG alt="w" src="'+box_image.src+'" name="f'+i+'_'+j+'">'); break; case 30: document.write('<IMG alt="w" src="'+box_in_target_image.src+'" name="f'+i+'_'+j+'">'); box_in_target++; break; default: document.write('<IMG alt="f" src="'+free_image.src+'" name="f'+i+'_'+j+'">'); break; } } document.write('<BR>'); }
puts the player into the maze
Bildwechsel('f'+player_y+'_'+player_x,player_image); </SCRIPT>
writes the copyright notice
<SCRIPT type="text/javascript"> document.write("<SMALL>"+copyright+"<\/SMALL>"); </SCRIPT>
These buttons are the "mouse control" of the game (for Opera users especially because Opera doesn't support keyboard input).
<TABLE> <TR> <TD> </TD> <TD> <INPUT type="button" value="&uArr; " onclick="move_player(0,-1); "> </TD> <TD> </TD> </TR> <TR> <TD> <INPUT type="button" value="&lArr; " onclick="move_player(-1,0); "> </TD> <TD> <INPUT type="button" value="&dArr; " onclick="move_player(0,1); "> </TD> <TD> <INPUT type="button" value="&rArr; " onclick="move_player(1,0); "> </TD> </TR> </TABLE>
Here you can select the level. When the selection has changed it renames the window and refreshes it. After reloading the script gets the new name and loads the appropriate level.
<FORM name="test" action="">Choose your Maze: <SELECT name="level" size="1" onChange="window.name=document.test.level.options[document.test.level.selectedIndex].text; location.reload(); "> <OPTION>1</OPTION> <OPTION>2</OPTION> . . . <OPTION>19</OPTION> <OPTION>20</OPTION> </SELECT> <BR> <BR> <INPUT type="button" name="text" value="New Game" onClick="location.reload(); "> </FORM>

This is an example of a maze file.
It consists of two functions that change globally defined variables of the program. The first function is a smaller one because it only contains the size of the game field. It must be in an extra function because the actual field data can only be changed after the field is initialized. But it needs the size for doing that.
function load_level_size() { num_rows=9; num_columns=9; }
The second function contains much more data.
function load_level() {
The player position
player_x=4; player_y=4;
The field data
  • 0 - empty field
  • 1 - wall
  • 10 - target
  • 20 - box
  • 30 - box in target
  • 100 - Spieler
  • 110 - player in target
field[0][0]=0; field[0][1]=0; field[0][2]=1; field[0][3]=1; field[0][4]=1; . . . field[8][5]=1; field[8][6]=1; field[8][7]=0; field[8][8]=0;
The number of targets is important to say the program when the game is over.
num_targets=5;
Under the game field the following message can be displayed.
copyright="This is an example level"; }