Delphi-PRAXiS
Seite 8 von 14   « Erste     678 910     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Minesweeper (https://www.delphipraxis.net/184385-minesweeper.html)

Mavarik 25. Mär 2015 22:05

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294847)
Das bekommt man nicht ohne Hilfe eines Stapelspeichers (Stack) hin. Das ist nicht leicht, aber auch nicht schwer.

Ok Sagen wir es einfachen rekursives aufrufen einer Procedure

Simpler Maussucht Käse:

Delphi-Quellcode:
Procedure FindeLeer(X,Y);
begin
  if (X = 0) or (Y=0) or (X > Max) or (Y > Max) then
    Exit;
 
  if (Feld[X,Y] <> Mine) and (Feld[X,Y] <> Zahl) then
    begin
      Aufdecken(X,Y);
      FindeLeer(X+1,Y);
      FindeLeer(X-1,Y);
      FindeLeer(X,Y+1);
      FindeLeer(X,Y-1);
    end;
end;
Fertig

Mavarik

Popov 25. Mär 2015 22:30

AW: Minesweeper
 
Ob man es Stapel oder rekursiven Prozeduraufruf (was im Hintergrund das Gleiche ist), leere Felder aufdecken nur ein kleiner Teil des Ganzen. Aber schon mal der Anfang.

Bjoerk 26. Mär 2015 02:37

AW: Minesweeper
 
Zitat:

Zitat von Mavarik (Beitrag 1294849)
Simpler Maussucht Käse:

Weil das ja so simple ist würde ich ggf. vorschlagen, daß du das kurz in der Play ergänzt. :-D

Delphi-Quellcode:
unit MinesweeperUnit;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;

const
  cGridCount = 9;
  cMinesCount = 10;
  cFlagSign = '?';
  cMineSign = 'X';

type
  TMinesweeperPanel = class(TPanel)
  private
    function GetMine: boolean;
  public
    X, Y: integer;
    FlankingMinesCount: integer;
    property Mine: boolean read GetMine;
  end;

  TMinesweeperForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure PanelsMouseUp(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  private
    FPanels: array[0..cGridCount - 1, 0..cGridCount - 1] of TMinesweeperPanel;
    function InPanelGrid(X, Y: integer): boolean;
    function GetFlankingMinesCount(X, Y: integer): integer;
    procedure NewGame;
    procedure ShowMines;
    procedure CreatePanels;
    procedure Play(X, Y: integer);
  end;

var
  MinesweeperForm: TMinesweeperForm;

implementation

{$R *.dfm}

{ TMinesweeperPanel }

function TMinesweeperPanel.GetMine: boolean;
begin
  Result := FlankingMinesCount < 0; // ***  -1 als Kennzeichnung für Mine ***
end;

{ TMinesweeperForm }

procedure TMinesweeperForm.CreatePanels;
var
  I, X, Y: integer;
begin
  I := 0;
  for X := 0 to cGridCount - 1 do
    for Y := 0 to cGridCount - 1 do
    begin
      Inc(I);
      FPanels[X, Y] := TMinesweeperPanel.Create(Self);
      FPanels[X, Y].Parent := Self;
      FPanels[X, Y].Name := 'Panel' + IntToStr(I);
      FPanels[X, Y].Width := 30;
      FPanels[X, Y].Height := 30;
      FPanels[X, Y].Caption := '';
      FPanels[X, Y].Left := 20 + (X * 30);
      FPanels[X, Y].Top := 20 + (Y * 30);
      FPanels[X, Y].BorderStyle := bsSingle;
      FPanels[X, Y].OnMouseUp := PanelsMouseUp;
      FPanels[X, Y].X := X;
      FPanels[X, Y].Y := Y;
    end;
end;

function TMinesweeperForm.InPanelGrid(X, Y: integer): boolean;
begin
  Result := (X >= 0) and (X <= cGridCount - 1) and (Y >= 0) and (Y <= cGridCount - 1);
end;

function TMinesweeperForm.GetFlankingMinesCount(X, Y: integer): integer;
begin
  Result := 0;
  if InPanelGrid(X + 1, Y) and FPanels[X + 1, Y].Mine then
    Inc(Result);
  if InPanelGrid(X + 1, Y + 1) and FPanels[X + 1, Y + 1].Mine then
    Inc(Result);
  if InPanelGrid(X, Y + 1) and FPanels[X, Y + 1].Mine then
    Inc(Result);
  if InPanelGrid(X - 1, Y + 1) and FPanels[X - 1, Y + 1].Mine then
    Inc(Result);
  if InPanelGrid(X - 1, Y) and FPanels[X - 1, Y].Mine then
    Inc(Result);
  if InPanelGrid(X - 1, Y - 1) and FPanels[X - 1, Y - 1].Mine then
    Inc(Result);
  if InPanelGrid(X, Y - 1) and FPanels[X, Y - 1].Mine then
    Inc(Result);
  if InPanelGrid(X + 1, Y - 1) and FPanels[X + 1, Y - 1].Mine then
    Inc(Result);
end;

procedure TMinesweeperForm.NewGame;
var
  I, X, Y: integer;
begin
  ClientWidth := cGridCount * 30 + 40;
  ClientHeight := ClientWidth;
  for X := 0 to cGridCount - 1 do
    for Y := 0 to cGridCount - 1 do
    begin
      FPanels[X, Y].Caption := '';
      FPanels[X, Y].Color := clBtnFace;
      FPanels[X, Y].Enabled := true;
      FPanels[X, Y].FlankingMinesCount := 0;
    end;
  for I := 1 to cMinesCount do
  begin
    X := Random(cGridCount);
    Y := Random(cGridCount);
    // FPanels[X, Y].Caption := cMineSign;
    FPanels[X, Y].FlankingMinesCount := -1;
  end;
  for X := 0 to cGridCount - 1 do
    for Y := 0 to cGridCount - 1 do
      if not FPanels[X, Y].Mine then
        FPanels[X, Y].FlankingMinesCount := GetFlankingMinesCount(X, Y);
end;

procedure TMinesweeperForm.FormCreate(Sender: TObject);
begin
  Randomize;
  CreatePanels;
  NewGame;
end;

procedure TMinesweeperForm.Play(X, Y: integer);
var
  I, J: integer;
begin
  if InPanelGrid(X, Y) and FPanels[X, Y].Enabled then
  begin
    if FPanels[X, Y].FlankingMinesCount > 0 then
      FPanels[X, Y].Caption := IntToStr(FPanels[X, Y].FlankingMinesCount)
    else
      FPanels[X, Y].Caption := '';
    FPanels[X, Y].Color := clWindow;
    FPanels[X, Y].Enabled := false;
(*
Mit der linken Maustaste freigelegte Felder, die keine Mine enthalten, enthüllen die Anzahl der Minen,
die sich in den benachbarten acht Feldern befinden.
Ein aufgedecktes Feld, das an allen Seiten von Minen umgeben ist, wird eine 8 zeigen.
Durch die Zahlen der aufgedeckten Felder ist es meist möglich, den Aufenthaltsort der Minen herauszufinden.

Wenn man einen Doppelklick mit der linken Maustaste auf ein aufgedecktes Feld macht,
in dessen Nachbarschaft bereits alle Minen markiert sind oder darauf mit der linken
und der rechten Maustaste gleichzeitig klickt, werden die restlichen Felder mit einem Mal aufgedeckt.
Eine Sonderrolle spielen Felder, die keine Minen in ihrer Nachbarschaft aufweisen:
Solche zeigen zum einen nicht etwa eine 0, sondern sie werden farblich anders dargestellt.
Zum anderen werden alle noch verdeckten Felder in ihrer Nachbarschaft automatisch aufgedeckt.
Ist ein so neu aufdecktes Feld ebenfalls ein Null-Feld, so wird dieser Prozess rekursiv weitergeführt.
*)
      for I := -1 to 1 do
        for J := -1 to 1 do
          if ((I <> 0) or (J <> 0)) and InPanelGrid(X + I, Y + J) then
            if .. and .. and .. and .. and .. then Play(X + I, Y + J);
  end;
end;

procedure TMinesweeperForm.ShowMines; // GameOver;
var
  X, Y: integer;
begin
  for X := 0 to cGridCount - 1 do
    for Y := 0 to cGridCount - 1 do
      if FPanels[X, Y].Mine then
        FPanels[X, Y].Caption := cMineSign;
  Application.ProcessMessages;
end;

procedure TMinesweeperForm.PanelsMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Panel: TMinesweeperPanel;
begin
  Panel := TMinesweeperPanel(Sender);
  if Panel.Mine then
  begin
    Panel.Color := clHighlight;
    ShowMines;
    Showmessage('Du hast leider verloren.');
    NewGame;
  end
  else
    if Button = mbLeft then
      Play(Panel.X, Panel.Y)
    else
      if Button = mbRight then
      begin
        if Panel.Caption = cFlagSign then
          Panel.Caption := ''
        else
          Panel.Caption := cFlagSign;
      end;
end;

end.

BadenPower 26. Mär 2015 08:28

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294848)
Denn nehmen wir das Beispiel oben und machen das Feld mit der 3 leer
Code:
2|1|_
-+-+-
1|x|1
-+-+-
1|1|1
darf das nicht aufgedeckt werden.

Das 3er Feld wird auch aufgedeckt, da es auf keinen Fall eine Bombe sein kann. Und auch die dann weiter angrenzenden Felder werden wiederum geprüft.

Die Diagonale ist keine Sperre.

Und so macht es der MS-Minesweeper auch.

BadenPower 26. Mär 2015 09:15

AW: Minesweeper
 
Zitat:

Zitat von Bjoerk (Beitrag 1294855)
Weil das ja so simple ist würde ich ggf. vorschlagen, daß du das kurz in der Play ergänzt. :-D

So könnte dann die Play aussehen:

Delphi-Quellcode:
procedure TMinesweeperForm.Play(X, Y: Integer);
begin
  if ((InPanelGrid(X, Y)) and (FPanels[X, Y].Enabled)) then
   begin
    FPanels[X, Y].Color := clWindow;
    FPanels[X, Y].Enabled := false;
    if (FPanels[X, Y].FlankingMinesCount > 0) then
     begin
      FPanels[X, Y].Caption := IntToStr(FPanels[X, Y].FlankingMinesCount)
     end
    else
     begin
      FPanels[X, Y].Caption := '_';
      Play(X, Y + 1);       //unten
      Play(X, Y - 1);       //oben
      Play(X + 1, Y);       //rechts
      Play(X - 1, Y);       //links
      Play(X + 1, Y + 1);   //unten-rechts
      Play(X + 1, Y - 1);   //oben-rechts
      Play(X - 1, Y + 1);   //unten-links
      Play(X - 1, Y - 1);   //oben-links
     end;
  end;
end;

EDIT:

Du musst aber noch "TMinesweeperForm.PanelsMouseUp" ändern, da dort ein Fehler ist.

Du solltest zuerst abfragen, ob die rechte Maustaste betätigt wurde und in der ELSE überprüfen ob eine Mine unter dem Feld liegt und nicht umgekehrt, denn sonst hast Du immer verloren, auch wenn Du nur ein Marker setzen möchtest.

Mavarik 26. Mär 2015 10:40

AW: Minesweeper
 
[QUOTE=BadenPower;1294864]
Zitat:

Zitat von Popov (Beitrag 1294848)
Die Diagonale ist keine Sperre.

Und so macht es der MS-Minesweeper auch.

Ahh ok wusste ich nicht... Konnte das Ding noch NIE leiden...
Aber Du hast es ja schon richtig ergänzt...

Mavarik

Popov 26. Mär 2015 10:42

AW: Minesweeper
 
Zitat:

Zitat von BadenPower (Beitrag 1294864)
Das 3er Feld wird auch aufgedeckt, da es auf keinen Fall eine Bombe sein kann. Und auch die dann weiter angrenzenden Felder werden wiederum geprüft.

Wie ich schon sagte, die Drei darf nicht aufgedeckt werden wenn das Feld leer ist. Das sind die Regeln.

BadenPower 26. Mär 2015 11:21

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294884)
Zitat:

Zitat von BadenPower (Beitrag 1294864)
Das 3er Feld wird auch aufgedeckt, da es auf keinen Fall eine Bombe sein kann. Und auch die dann weiter angrenzenden Felder werden wiederum geprüft.

Wie ich schon sagte, die Drei darf nicht aufgedeckt werden wenn das Feld leer ist. Das sind die Regeln.

DOCH, auch dieses Feld wird aufgedeckt.

So ist es beim MS-MineSweeper auf alle Fälle. Habe diese Konstellation mehrfach überprüft und immer wenn das Diagonalfeld auch leer war hat die MS-Version auch alles weitere aufgedeckt, was an das Diagonalfeld angrenzte.

NOCHMALS:
Die Diagonale ist KEINE Sperre im MS-MineSweeper.

Mavarik 26. Mär 2015 11:27

AW: Minesweeper
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von BadenPower (Beitrag 1294898)
NOCHMALS:
Die Diagonale ist KEINE Sperre im MS-MineSweeper.

Sicher? Hab das noch nicht gesehen...
Online zu testen wer Minesweeper nicht hat.

Oder meinst Du das? (Anhang)

Bjoerk 26. Mär 2015 11:28

AW: Minesweeper
 
Zitat:

Zitat von BadenPower (Beitrag 1294867)
So könnte dann die Play aussehen: [..]
Du musst aber noch "TMinesweeperForm.PanelsMouseUp" ändern, da dort ein Fehler ist. [..]

Stimmt. Badenser vor. :thumb: Ich kannte das Spiel nur vom Namen her und muß sagen ich finds ganz nett (Jedenfalls besser als das langweilige Fußballspiel gestern abend). :)
Delphi-Quellcode:
procedure TMinesweeperForm.Play(X, Y: integer); // PanelsMouseUp, Button mbLeft;
var
  I, J: integer;
begin
  if FPanels[X, Y].FlankingMinesCount > 0 then
    FPanels[X, Y].Caption := IntToStr(FPanels[X, Y].FlankingMinesCount)
  else
    FPanels[X, Y].Caption := '';
  FPanels[X, Y].Color := clWindow;
  FPanels[X, Y].Enabled := false;
  for I := -1 to 1 do
    for J := -1 to 1 do
      if ((I <> 0) or (J <> 0)) // Alle außer 0 / 0;
        and InPanelGrid(X + I, Y + J)
          and FPanels[X + I, Y + J].Enabled
            and (FPanels[X + I, Y + J].FlankingMinesCount = 0)
              and (FPanels[X + I, Y + J].Caption <> cFlagSign) then Play(X + I, Y + J);
end;

procedure TMinesweeperForm.PanelsMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  Panel: TMinesweeperPanel;
begin
  Panel := TMinesweeperPanel(Sender);
  if Button = mbLeft then
  begin
    if Panel.Mine then
    begin
      Panel.Color := clHighlight;
      ShowMines;
      Showmessage('Du hast leider verloren.');
      NewGame;
    end
    else
      Play(Panel.X, Panel.Y)
  end
  else
    if Button = mbRight then
    begin
      if Panel.Caption = cFlagSign then
        Panel.Caption := ''
      else
        Panel.Caption := cFlagSign;
    end;
end;
Damit "er" bei Themes auch die Color annimmt muß man in der CreatePanels noch ergänzen:
Delphi-Quellcode:
FPanels[X, Y].ParentBackground := false;
Bleibt abzuwarten ob TE den Code versteht. Ich denke aber mal ja, hat ja schon viel Vorarbeit geleistet und ich hab ja versucht mich so eng an seinen Code anzulehnen wie möglich.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:08 Uhr.
Seite 8 von 14   « Erste     678 910     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz