Wie es gemacht wird: Wir schreiben "Minesweeper" in 4 Minuten

Bild

Von einem Übersetzer: Dieser Beitrag ist eine Übersetzung des Originalartikels von Maki Chiz, einem erfahrenen Programmierer, der nicht nur großartige Programme schreibt, sondern auch seinen Kollegen, sowohl Anfängern als auch Profis, die Fähigkeiten verschiedener Sprachen demonstriert.

Minesweeper ist ein lustiges Spiel, viele von uns spielen es. Vielleicht möchten Sie Minesweeper selbst machen?

Skillbox empfiehlt: Der Online- Beruf „Web Developer Profession“ .

Wir erinnern Sie daran: Für alle Leser von „Habr“ - ein Rabatt von 10.000 Rubel bei der Anmeldung für einen Skillbox-Kurs mit dem Aktionscode „Habr“.
Die Einzigartigkeit des Spiels ist, dass es sehr einfach und gleichzeitig sehr aufregend ist. In Minesweeper gibt es keine listige Spielmechanik. Klicken Sie einfach auf die Felder und hoffen Sie, dass sich keine Minen darunter befinden.

Ich schlage vor, "Minesweeper" über die Verarbeitung zu schreiben. Dies ist ein großartiges Tool, mit dem Sie grafische Anwendungen in Java erstellen können. Link dazu hier .

Bevor ich anfange, möchte ich sagen, dass dieses Tutorial für diejenigen gedacht ist, die Java bereits kennen. Erfahrung mit der Verarbeitung ist optional.

Also los geht's. Der erste Schritt besteht darin, den Status des Spiels zu bestimmen. Ich habe beschlossen, es so umzusetzen.

int gridW; // grid width int gridH; // grid height int numMines; // number of mines on the board int[][] mines; // entry is 1 for having a mine and 0 for not boolean[][] flags; // entry is true if you have flagged that spot boolean[][] revealed; // entry is true if that spot is revealed 

Bis auf diesen Abschnitt scheint hier alles Sinn zu machen: int [] [] mines . Warum ist das Minengitter eine ganze Zahl und kein Boolescher Wert? Tatsache ist, dass es einfach ist zu berechnen, wie viele Minen sich in der Nähe einer bestimmten Position befinden.

 int calcNear(int x, int y) { int i=0; for (int offsetX=-1; offsetX<=1; offsetX++) { for (int offsetY=-1; offsetY<=1; offsetY++) { i+=mines[offsetX+x][offsetY+y]; } } return i; } 

Dieser Code bestimmt, wie viele Minen sich in der Nähe eines bestimmten Standorts befinden. Nachdem wir die Ausnahmen entfernt haben, erhalten wir etwas Ähnliches:

 boolean outBounds(int x,int y){ return x<0||y<0||x>=gridW||y>=gridH; } int calcNear(int x, int y) { if(outBounds(x,y))return 0; int i=0; for (int offsetX=-1; offsetX<=1; offsetX++) { for (int offsetY=-1; offsetY<=1; offsetY++) { if (outBounds(offsetX+x, offsetY+y))continue; i+=mines[offsetX+x][offsetY+y]; } } return i; } 

Die Hauptaufgabe des Spiels selbst besteht darin, die Quadrate ab Punkt x, y freizulegen.

 void reveal(int x, int y){ if(outBounds(x,y))return; if(revealed[x][y])return; revealed[x][y]=true; if(calcNear(x,y)!=0)return; reveal(x-1,y-1); reveal(x-1,y+1); reveal(x+1,y-1); reveal(x+1,y+1); reveal(x-1,y); reveal(x+1,y); reveal(x,y-1); reveal(x,y+1); } 

Als Ergebnis haben wir den folgenden Algorithmus:

  • Wenn sich die Position außerhalb des Feldes befindet, beenden Sie;
  • Wenn die Position bekannt gegeben wird, beenden Sie;
  • Wir eröffnen die aktuelle Position;
  • Wenn wir eine Bombe in der Nähe der aktuellen Position haben, beenden Sie;
  • Wenn wir diesen Punkt während der Ausführung der Funktion erreicht haben, wurde die aktuelle Position innerhalb des Spielfelds gefunden und es befinden sich keine Bomben daneben. Öffnen Sie benachbarte Felder.

Grundsätzlich ist der Rest des Spiels der gleiche. Den vollständigen Code finden Sie, indem Sie auf diesen Link klicken.

Wenn Sie mehr über Visualisierung erfahren möchten, lesen Sie weiter.

Visualisierung

Hier sollte alles klar sein, aber ich werde die schwierigen Teile erklären.

Wir haben also eine Variable cellSize, die die Anzahl der Pixel in jedem Quadrat bestimmt.

 void settings(){ size(gridW*cellSize, gridH*cellSize); } 

Daher erstellen wir ein Feld mit den Seiten gridW x gridH, in dem die Größe jedes Quadrats dem Wert von cellSize entspricht.

Dann bringen wir die Variablen in ihren ursprünglichen Zustand zurück.

 void setup(){ //initialize and clear the arrays mines=new int[gridW][gridH]; flags=new boolean[gridW][gridH]; revealed=new boolean[gridW][gridH]; for(int x=0;x<gridW;x++){ for(int y=0;y<gridH;y++){ mines[x][y]=0; flags[x][y]=false; revealed[x][y]=false; } } } 

So initialisieren Sie ein Feld:
 //Place numMines mines on the grid void placeMines(){ int i=0; while(i<numMines){//We don't want mines to overlap, so while loop int x=int(random(gridW)); int y=int(random(gridH)); if(mines[x][y]==1)continue; mines[x][y]=1; i++; } } //Clear the mines void clearMines() { for (int x=0; x<gridW; x++) { for (int y=0; y<gridH; y++) { mines[x][y]=0; } } } 

Schalten Sie dann die Antwort auf Mausklicks ein.

 //We don't want the first click to be a mine boolean firstClick=true; void mousePressed() { int x=int(mouseX/cellSize); int y=int(mouseY/cellSize); //Right-click is flagging or de-flagging a square if (mouseButton==RIGHT) { flags[x][y]=!flags[x][y]; return; } else {//left-click //Avoid making the first click a mine if (firstClick) { firstClick=false; do { clearMines(); placeMines(); } while (calcNear(x,y)!=0); } //Check for game loss if (mines[x][y]!=0) { println("Dang!"); exit(); } else {//If game not lost, reveal starting from that square reveal(x, y); } } } 

Und die Anzeigefunktion nennen wir einmal für jeden Frame.

 void draw() { background(0); //For each cell for (int x=0; x<gridW; x++) { for (int y=0; y<gridH; y++) { //The colors alternate for texture color col1=color(2*255/5); color col2=color(3*255/5); color txtColor=color(0); int near=calcNear(x, y); if (flags[x][y]) { col1=color(255, 0, 0); col2=color(4*255/5, 255/5, 255/5); } else if (revealed[x][y]) { col1=color(255/2); col2=color(255/2); } if (near==1)txtColor=color(255*1/4, 255*1/4, 255*3/4); if (near==2)txtColor=color(255*1/4, 255*3/4, 155*1/4); if (near==3)txtColor=color(255, 0, 0); if (near==4)txtColor=color(0, 0, 255); if (near==5)txtColor=color(255, 0, 0); boolean alternate=(x+y)%2==0; if (alternate) { fill(col2); stroke(col2); } else { fill(col1); stroke(col1); } //if(mines[x][y]>0){ //fill(0,255,0); //stroke(0,255,0); //} rect(x*cellSize, y*cellSize, cellSize, cellSize); //If there is a mine near this spot and it is revealed if (near>0&&revealed[x][y]) { fill(txtColor); noStroke(); textAlign(LEFT, TOP); textSize(cellSize); text(""+near, x*cellSize, y*cellSize); } } } } 

Und das ist alles, hier ist unser Minensuchboot.

Das Spiel sieht einfach aus, ist aber insgesamt voll funktionsfähig. Und denken Sie daran: Minesweeper macht süchtig!

Skillbox empfiehlt:

Source: https://habr.com/ru/post/de425317/


All Articles