Banner
{ Deutsch | English }
Minesweeper

Minesweeper − Misel



Quellcode


Vorbetrachtung:
  1. zufällige Minenpositionen bestimmen
  2. Indikatoren aktualisieren
  3. Spieler auf ein Feld klicken lassen
  4. Feld
    • enthält eine Mine - Spiel beenden
    • ist ein Zahlenfeld - Feld öffnen
    • ist ein leeres Feld - dieses Feld uns alle umgebenden öffnen
  5. wiederholen bis alle Felder aufgedeckt sind

Die erste Funktion prüft die Klicks mit der rechten Maustaste. Es ist notwendig, hier zwischen Mozilla/Netscape und Internet Explorer zu unterscheiden, da diese Mausklicks unterschiedlich handhaben (einer der Unterschiede zwischen dem JavaScript von MS und Netscape).
<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; } }
Das Spiel erhält seine Einstellungen durch den window.name. Änderungen daran werden durch das Formular unter dem Spiel getätigt.
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); }
Wenn kein Fenstername gesetzt ist, werden die Standardeinstellungen für Anfänger verwendet.
else { var num_rows=8; var num_columns=8; var num_mines=10; }
Allgemeine Variablen
Die Anzahl der Minen, die bereits bei der Initialisierung gesetzt werden.
var current_mines_set=0;
die Gesamtanzahl der Felder
var field_number= num_columns * num_rows;
die Zeichenkette, die angezeigt wird, wenn das Spiel gewonnen wurde
var sieg="Congratulations, you won the game!";
Der Status des Spiels:
  • 0 - Spiel hat noch nicht begonnen
  • 1 - Spiel läuft
  • 2 - Spiel verloren
  • 3 - Spiel gewonnen
var game_status=0;
die Startzeit
var start;
Definition der Grafiken des Spiels. Die Namen sind selbsterklärend. smiley_o ist der :-O Smileybutton während man auf ein Feld klickt.
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";
Bevor das Spielfeld initialisiert wird, hier einmal die Werte, die ein Feld annehmen kann:
  • 0 - verdecktes leeres Feld
  • 1 - verdecktes Feld mit 1 Mine in einem Nachbarfeld
  • .
  • .
  • 8 - verdecktes Feld mit 8 Minen in den Nachbarfeldern
  • 9 - verdeckte Mine
  • 10 - aufgedecktes leeres Feld
  • 11 - aufgedecktes Feld mit 1 Mine in einem Nachbarfeld
  • .
  • .
  • 18 - aufgedecktes Feld mit 8 Minen in den Nachbarfeldern
  • 19 - explodierte Mine, Spiel verloren
  • 20 - Feld mit einer Flagge aber ohne Mine darunter
  • 21 - Feld mit einer Flagge aber keiner Mine darunter, aber 1 Mine in einem Nachbarfeld
  • .
  • .
  • 29 - Feld mit einer Flagge und einer Mine darunter

Initialisierung des Spielfeldes. Das virtuelle Spielfeld ist größer als das, was man sieht, wenn das Spiel gespielt wird, da dies mit der show_neighbours()-Funktion besser funktioniert.
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; } }
Einfache Funktion, die Bilder im Dokument austauscht.
function Bildwechsel(Bildnr,Bildobjekt) { window.document.images[Bildnr].src = Bildobjekt.src; }
Setzt alle Werte und Felder zurücl um ein neues Spiel zu starten.
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; }
Wenn ein Spiel gewonnen wurde, wird der Spielstatus entsprechend angepasst, der Smiley ausgetauscht und jedes nicht aufgedeckte Feld mit einer Flagge markiert. Anschließend wird der Sieg angekündigt.
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); }
Diese Funktion wird aufgerufen, sobald der Spieler auf eine Mine geklickt hat. x und y sind die Koordinaten der explodierten Mine, um das entsprechende Bild zu setzen. Der Smiley wird ebenfalls ausgetauscht und jede andere Mine (field[x][y]==9) wird aufgedeckt und jede falsche Flagge ((field[x][y]>19) && (field[x][y]<29)) wird ebenfalls gezeigt.
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; } } } }
Wenn das Spiel gestartet wird, wird diese Funktion aufgerufen, um sicherzustellen, dass der erste Klick keine Mine trifft. Daher werden auch die beiden Parameter übermittelt.
function initialisierung(no_mine_y,no_mine_x) {
Da das Spiel nun gestartet wurde, muss der Spielstatus angepasst werden. ;-)
game_status=1;
Der Zufallsfaktor wird auf Basis der "Minen-pro-Feld"-Quote berechnet. Auf einem großen Spielfeld dauert es nicht lange um alle Minen auf zufällige Positionen zu setzen.
var random_factor=eval(0.1 /( num_rows / num_mines * num_columns));
So lange nicht alle Minen gesetzt wurden, wird diese Schleife wiederholt.
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++) {
Das ist eine recht lange Bedingung, aber sie ist notwendig, um die Minen korrekt zu setzen. Sie prüft, ob alle Minen bereits gesetzt wurden, ob sich bereits eine Mine auf dem aktuellen Feld befindet, benutzt die random()-Funktion um die Positionen zufällig zu wählen und prüft, ob dies die Stelle des ersten Klicks ist oder nicht.
if ((current_mines_set < num_mines) && (field[zeilen][spalten]!=9) && (Math.random() < random_factor) && ((spalten!=no_mine_x) || (zeilen!=no_mine_y))) {
setzt die Minen
field[zeilen][spalten]=9; current_mines_set++; } } } }
Jedes Mal, wenn eine Mine gesetzt wird, wird der Wert jedes angrenzenden Feldes um 1 erhöht (solange sich dort nicht auch eine Mine befindet). Diese Methode ist effektiver als eine Extrafunktion, die nach dem Setzen der Minen die umliegenden Felder prüft.
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]++; } } }
Am Ende der Initialisierungsfunktion wird die Startzeit gespeichert.
start = new Date(); }
Diese Funktion zeigt die Nachbarfelder. Sie wird benötigt, wenn der Spieler auf ein leeres Feld klickt, oder auf ein Feld, dessen angrenzende Minen- und Flaggenanzahl identisch ist (dann kann der Spieler alle angrenzenden Felder mit einem Klick aufdecken). Der Unterschied ist, der erste Teil kann keine Mine treffen, der zweite Teil schon. Dies ist der Grund für die remote-Variable. Sie ist 1 wenn die Funktion für den zweiten Zweck aufgerufen wird und jedes verdeckte Feld aufgedeckt wird. Ist sie 0 werden nur die Felder ohne Mine aufgedeckt.
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); }
Wie der Name schon sagt, ist dies die Funktion, die die Felder letztendlich öffnet.
function show_field(y,x) {
Sie wird nur ausgeführt, wenn das Spiel noch nicht zuende ist.
if (game_status<2) {
Wenn das Spiel noch nicht begonnen hat, werden die Minen gesetzt.
if (game_status==0) initialisierung(y,x);
Die image_number-Variable wird für den Bildwechsel weiter unten benötigt.
var image_number='Z'+y+'_'+x;
Wenn das angeklickte Feld bereits aufgedeckt ist, prüft das Programm, ob die angrenzenden Flaggen der Nummer auf dem Feld entsprechen. Ist dies der Fall, werden die übrigen angrenzenden Felder aufgedeckt.
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); }
Dies deckt ein Feld auf. Der "(y >= 0) && (y < num_rows) && (x >= 0) && (x < num_columns)"-Teil berücksichtigt den Größenunterschied zwischen dem virtuellen und dem eigentlichen Spielfeld (wie weiter oben bereits erklärt).
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; }
Verringert die Anzahl der verdeckten Felder. Wenn diese Zahl und die Anzahl der übrigen Minen identisch ist, ist das Spiel gewonnen. Der Status des Feldes wird zu "aufgedeckt" geändert und wenn das Feld leer ist, werden die angrenzenden Felder ebenfalls aufgedeckt.
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); } } }
Diese Funktion setzt die Flagge. Sie passt den Wert eines Feldes und dessen Grafik an. Außerdem zählt sie die Anzahl der gesetzten Flaggen und zeigt sie an.
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++; } }
Die look()-Funktion bestimmt, was nach einem Mausklick passiert - eine Flagge setzen oder ein Feld aufdecken. Außerdem berechnet sie die verstrichene Zeit und zeigt sie an. Dies passiert aus Performance-Gründen nur, wenn ein Mausklick erfolgt.
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>
Der Rest ist fast immer reines HTML. Das Spielfeld ist eine Tabelle und Hintergrundbilder der Zellen bilden das 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">
Dieses letzte Script baut das Spielfeld auf und setzt die "verdeckte Mine"-Bilder an die richtigen Stellen.
Die look()-Funktion wird via onMouseUp-Eventhandler aufgerufen. OnMouseDown ändert nur den Smiley. Davor stehen 3 Funktionen, die Klicks mit der rechten Maustaste registrieren.
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>