Banner
{ Deutsch | English }
Minesweeper

Minesweeper − Misel



Source Code


General idea:
  1. Randomize mine positions
  2. update the indicators
  3. let player click on a field
  4. if field is
    • a mine - end game
    • a "number-field" - open it
    • a free field - open it and its surroundings
  5. Do that until all fields are uncovered

The first function checks the right mouse click to set the flags.
It is necessary to distinguish between Mozilla/Netscape and Internet Explorer because they capture the right mouse click in a different way (one of the differences between MS's and Netscape's JavaScript).
<SCRIPT type="text/javascript"> function rechts(e) { if (document.all) { Klick = "links"; if ((event.button == 2) || (event.button == 3)) { if (event.srcElement.tagName == "IMG") Klick = "rechts"; } } else { Klick = "links"; if ((e.which == 3) && (e.target.tagName == "IMG")) { Klick = "rechts"; return false; } else return false; } }
The program gets its settings by its window.name. Changes to the name are made in the form below the game.
var variable = window.name; if (variable!='') { var temp1=variable.lastIndexOf("c"); var temp2=variable.lastIndexOf("m"); num_rows=variable.slice(1,temp1); num_columns=variable.slice(temp1+1,temp2); num_mines=variable.slice(temp2+1,variable.length); }
If there's no window name then use the standard settings for rookies.
else { var num_rows=8; var num_columns=8; var num_mines=10; }
General variables
The number of mines that are already set during the initialization.
var current_mines_set=0;
The actual number of all fields
var field_number= num_columns * num_rows;
the string that is displayed when the player has won the game
var sieg="Congratulations, you won the game!";
The status of the game:
  • 0 - game not started yet
  • 1 - game in progress
  • 2 - game lost
  • 3 - game won
var game_status=0;
the beginning-time
var start;
Defines the graphics used in the game. The names are pretty self-explanatory. smiley_o is the :-O smiley button when you click on a field.
var covered = new Image; covered.src ="covered.gif"; var no_mine = new Image; no_mine.src = "0.gif"; var mine1 = new Image; mine1.src = "1.gif"; var mine2 = new Image; mine2.src = "2.gif"; var mine3 = new Image; mine3.src = "3.gif"; var mine4 = new Image; mine4.src = "4.gif"; var mine5 = new Image; mine5.src = "5.gif"; var mine6 = new Image; mine6.src = "6.gif"; var mine7 = new Image; mine7.src = "7.gif"; var mine8 = new Image; mine8.src = "8.gif"; var mine = new Image; mine.src = "9.gif"; var exploded = new Image; exploded.src = "exploded.gif"; var flag = new Image; flag.src="flag.gif"; var falsemine = new Image; falsemine.src="falsemine.gif"; var smileystandard = new Image; smileystandard.src="smiley1.gif"; var smileypressed = new Image; smileypressed.src="smiley2.gif"; var smileywon = new Image; smileywon.src="smiley3.gif"; var smileyloose = new Image; smileyloose.src="smiley4.gif"; var smiley_o = new Image; smiley_o.src="smiley5.gif";
Before initializing the gamefield let me explain the values that each field can have:
  • 0 - covered empty field
  • 1 - covered field with 1 mine near
  • .
  • .
  • 8 - covered field with 8 mines near
  • 9 - covered mine
  • 10 - opened empty field
  • 11 - opened field with 1 mine near
  • .
  • .
  • 18 - opened field with 8 mines near
  • 19 - exploded mine - game over
  • 20 - field with flag but no mine
  • 21 - field with flag but no mine but one mine surrounding
  • .
  • .
  • 29 - field with flag and mine

Initializing the playing field. The virtual playing field is larger than what you actually see when you play it. It's done so to better get along with the show_neighbours() function.
var field=new Array(); for (var a=-1; a < num_rows+1; a++) { field[a]=new Array(); for (var b=-1; b < num_columns+1; b++) { field[a][b]=0; } }
Simple function that changes images in the document.
function Bildwechsel(Bildnr,Bildobjekt) { window.document.images[Bildnr].src = Bildobjekt.src; }
Resets all values and fields to start a new game.
function reload() { for (var a=-1; a < num_rows+1; a++) { for (var b=-1; b < num_columns+1; b++) { field[a][b]=0; } } for (var i=0; i < num_rows; i++) { for (var j=0; j < num_columns; j++) { window.document.images[('Z'+i+'_'+j)].src=covered.src; } } current_mines_set=0; game_status=0; field_number= num_columns * num_rows; document.anzeige.mines.value=num_mines; document.anzeige.time.value=0; }
When the game is won the game status will be changed accordingly, the smiley is changed and each remaining field will be marked with a flag. Afterwards the victory is announced.
function win_game() { game_status=3; window.document.images['smiley'].src = smileywon.src; for (var zeilen=0; zeilen < num_rows; zeilen++) { for (var spalten=0; spalten < num_columns; spalten++) { if (field[zeilen][spalten]==9) { var image_number='Z'+zeilen+'_'+spalten; field[zeilen][spalten]+=11; window.document.images[image_number].src=flag.src; document.anzeige.mines.value=0; } } } alert(sieg); }
This function will be called once the player has clicked on a mine, where x and y are the coordinates of the exploded mine to put the "red bomb" there. The smiley will be changed as well and every other mine (field[x][y]==9) will be revealed and each wrong flag ((field[x][y]>19) && (field[x][y]<29)) will be shown as well.
function lose_game(y,x) { game_status=2; window.document.images['smiley'].src = smileyloose.src; for (var zeilen=0; zeilen < num_rows; zeilen++) { for (var spalten=0; spalten < num_columns; spalten++) { var image_number='Z'+zeilen+'_'+spalten; switch (field[zeilen][spalten]) { case 9: if ((zeilen==y) && (spalten==x)) window.document.images[image_number].src=exploded.src; else window.document.images[image_number].src=mine.src; break; case 29: window.document.images[image_number].src=flag.src; break; default: if ((field[zeilen][spalten]>19) && (field[zeilen][spalten]<29)) window.document.images[image_number].src=falsemine.src; break; } } } }
When the game is started first this function will be called to ensure that the first click will never hit a mine. That's why it has these two parameters.
function initialisierung(no_mine_y,no_mine_x) {
Since the game is started now this must be set here ;-)
game_status=1;
The random factor is calculated as a mine per fields ratio. On a big field with many mines it doesn't take so long to put the mines on random positions.
var random_factor=eval(0.1 /( num_rows / num_mines * num_columns));
As long as not all mines are set this loop will continue to set them.
while (current_mines_set < num_mines) { for (var zeilen=0; zeilen < num_rows; zeilen++) { if (current_mines_set == num_mines) break; for (var spalten=0; spalten < num_columns; spalten++) {
This is quite a long condition but necessary to set the mines properly. It checks whether all mines are already set, whether there's already a mine on the field, uses the random() function to randomize the positions and checks if that's the position of the first click or not.
if ((current_mines_set < num_mines) && (field[zeilen][spalten]!=9) && (Math.random() < random_factor) && ((spalten!=no_mine_x) || (zeilen!=no_mine_y))) {
sets the mines
field[zeilen][spalten]=9; current_mines_set++; } } } }
Each time a mine is set the value of each surrounding field is increased by one (as long as there's no mine on the field). This method is more effective than an extra function that looks for the mines and its surrounding fields afterwards.
for (zeilen=0; zeilen < num_rows; zeilen++) { for (spalten=0; spalten < num_columns; spalten++) { if (field[zeilen][spalten]==9) { if (field[zeilen-1][spalten-1]!=9) field[zeilen-1][spalten-1]++; if (field[zeilen-1][spalten]!=9) field[zeilen-1][spalten]++; if (field[zeilen-1][spalten+1]!=9) field[zeilen-1][spalten+1]++; if (field[zeilen][spalten-1]!=9) field[zeilen][spalten-1]++; if (field[zeilen][spalten+1]!=9) field[zeilen][spalten+1]++; if (field[zeilen+1][spalten-1]!=9) field[zeilen+1][spalten-1]++; if (field[zeilen+1][spalten]!=9) field[zeilen+1][spalten]++; if (field[zeilen+1][spalten+1]!=9) field[zeilen+1][spalten+1]++; } } }
At the end of the init function the start time is saved.
start = new Date(); }
This function shows the neighbours of a field. This is necessary when the player clicks on an empty field or when the player clicks on an already opened field when the amount of flags on the surrounding fields equals this field's number (then the user can open every adjacent field with one click). The difference is that the first part can't hit a mine but the other one can. That's the reason for the remote variable. It's 1 when it's started for the second purpose so that every covered field will be opened and 0 for the first purpose and opens every surrounding and covered field but a mine.
function show_neighbours(y,x,remote) { if (field[y-1][x-1] < (9+remote)) show_field(y-1,x-1); if (field[y-1][x] < (9+remote)) show_field(y-1,x); if (field[y-1][x+1] < (9+remote)) show_field(y-1,x+1); if (field[y][x-1] < (9+remote)) show_field(y,x-1); if (field[y][x+1] < (9+remote)) show_field(y,x+1); if (field[y+1][x-1] < (9+remote)) show_field(y+1,x-1); if (field[y+1][x] < (9+remote)) show_field(y+1,x); if (field[y+1][x+1] < (9+remote)) show_field(y+1,x+1); }
As the name says this is the function that actually opens the fields.
function show_field(y,x) {
It only does something when the game hasn't ended yet.
if (game_status<2) {
And if the game hasn't started yet it also starts setting the mines.
if (game_status==0) initialisierung(y,x);
The image_number is used for the image-changing later on.
var image_number='Z'+y+'_'+x;
If the clicked field is already uncovered the program checks if the surrounding flags equal the number of the field. If yes it opens the rest of the surrounding fields.
if ((field[y][x] > 10) && (field[y][x] < 19)) { var surrounding_flags=0; if (field[y-1][x-1] > 19) surrounding_flags++; if (field[y-1][x] > 19) surrounding_flags++; if (field[y-1][x+1] > 19) surrounding_flags++; if (field[y][x-1] > 19) surrounding_flags++; if (field[y][x+1] > 19) surrounding_flags++; if (field[y+1][x-1] > 19) surrounding_flags++; if (field[y+1][x] > 19) surrounding_flags++; if (field[y+1][x+1] > 19) surrounding_flags++; if ((surrounding_flags+10)==field[y][x]) show_neighbours(y,x,1); }
This actually uncovers a field. The "(y >= 0) && (y < num_rows) && (x >= 0) && (x < num_columns)" part is there because of the difference in size between the virtual and actual play field (as explained earlier).
if ((field[y][x] < 10) && (y >= 0) && (y < num_rows) && (x >= 0) && (x < num_columns)) { switch (field[y][x]) { case 0: window.document.images[image_number].src=no_mine.src; break; case 1: window.document.images[image_number].src=mine1.src; break; case 2: window.document.images[image_number].src=mine2.src; break; case 3: window.document.images[image_number].src=mine3.src; break; case 4: window.document.images[image_number].src=mine4.src; break; case 5: window.document.images[image_number].src=mine5.src; break; case 6: window.document.images[image_number].src=mine6.src; break; case 7: window.document.images[image_number].src=mine7.src; break; case 8: window.document.images[image_number].src=mine8.src; break; case 9: lose_game(y,x); break; }
>Decrements the number of fields that are covered. When this number and the number of mines are equal then the game is won. The field status will be changed to "uncovered" and if the field is empty the surrounding fields will be opened as well.
field_number--; if ((field_number==num_mines) && (field[y][x]!=9)) win_game(); field[y][x]+=10; if (field[y][x]==10) show_neighbours(y,x,0); } } }
This function toggles the flag. It changes the field's value and the image. It also counts the number of the set flags and displays them.
function toggle_flag(y,x) { var image_number='Z'+y+'_'+x; if (field[y][x]<10) { field[y][x]+=20; window.document.images[image_number].src=flag.src; document.anzeige.mines.value--; } else if (field[y][x]>19) { field[y][x]-=20; window.document.images[image_number].src=covered.src; document.anzeige.mines.value++; } }
The look() function is the one that decides what to do after a mouse click - toggle a flag or open a field. It also calculates and displays the elapsed time. It does that only when the user clicks on a field for performance reasons.
function look(y,x) { if (game_status<2) window.document.images['smiley'].src = smileystandard.src; if (game_status>1) { } else { if (game_status > 0) { time = new Date(); usertime=Math.floor(eval( ((time.getTime() - start.getTime())) / 1000)); document.anzeige.time.value=usertime; } if (Klick == "rechts") toggle_flag(y,x); else show_field(y,x); } } </SCRIPT>
The rest is almost always pure HTML. The game field is one table and background images of the cells build the interface.
<FORM name="anzeige" action="" onsubmit="loadwinmine()"> <TABLE cellpadding="0" cellspacing="0"> <TR> <TD height="12" width="12" style="background-image:URL('topleft.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="12" style="background-image:URL('top.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="12" width="12" style="background-image:URL('topright.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> </TR> <TR> <TD height="33" width="12" style="background-image:URL('left1.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="33" bgcolor="#C0C0C0" align="left"> <IMG width="1" height="1" src="spacer.gif" alt=""> <TABLE width="100%"> <TR> <TD align="center"> <INPUT style="font-weight:bold; font-family:'System'; color:#FF0000; background-color:#000000; border-top-color:#808080; border-left-color:#808080; border-bottom-color:#C0C0C0; border-right-color:#C0C0C0; " type="Text" name="mines" value="num_mines" maxlength="3" size="3" readonly> </TD> <TD align="center" width="75%"> <IMG src="smiley1.gif" name="smiley" onmousedown="Bildwechsel(5,smileypressed); " onclick="reload(); " onmouseup="Bildwechsel(5,smileystandard)" ALT="Smiley"> </TD> <TD align="center"> <INPUT style="text-align:right; font-weight:bold; font-family:'System'; color:#FF0000; background-color:#000000; border-top-color:#808080; border-left-color:#808080; border-bottom-color:#C0C0C0; border-right-color:#C0C0C0; " type="Text" name="time" value="num_mines" maxlength="3" size="3" readonly> </TD> </TR> </TABLE> </TD> <TD height="33" width="12" style="background-image:URL('right1.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> </TR> <TR> <TD height="10" width="12" style="background-image:URL('middleleft.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="10" style="background-image:URL('middle.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="10" width="12" style="background-image:URL('middleright.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> </TR> <TR> <TD width="12" style="background-image:URL('left2.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD bgcolor="#C0C0C0"> <SCRIPT type="text/javascript">
This last script is for building the actual game field and puts the "covered-mine"-pictures in the right place.
The look() function is called via the onMouseUp-Eventhandler. OnMouseDown just changes the smiley. Before that are three functions that are necessary for capturing the right mouse click.
document.onmousedown=rechts; document.onclick=rechts; document.body.oncontextmenu = Function("rechts(); return false; "); document.anzeige.mines.value=num_mines; document.anzeige.time.value=0; for (var i=0; i < num_rows; i++) { for (var j=0; j < num_columns; j++) { document.write('<IMG src="covered.gif" onMouseDown="rechts; if (game_status<2) { window.document.images[\'smiley\'].src = smiley_o.src; }" onMouseup="look('+i+','+j+')" alt="field" name="Z'+i+'_'+j+'">'); } document.write('<BR>'); } </SCRIPT> </TD> <TD width="12" style="background-image:URL('right2.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> </TR> <TR> <TD height="12" width="12" style="background-image:URL('bottomleft.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="12" style="background-image:URL('bottom.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> <TD height="12" width="12" style="background-image:URL('bottomright.gif')" bgcolor="#C0C0C0"> <IMG width="1" height="1" src="spacer.gif" alt=""> </TD> </TR> </TABLE> </FORM>