Delphi-PRAXiS
Seite 2 von 4     12 34      

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 10:16

AW: Minesweeper
 
Zitat:

Zitat von DeddyH (Beitrag 1294730)
Es geht doch noch einfacher.

Viele Wege führen nach Rom. Da ich die Koordinate schon in Left/Top habe... Kann ich das Tag Feld für andere Sachen benutzen.

Taktzykle technisch ist "Deine" Methode etwas schneller als Mit Div und Mod.
Aber es erspart leider den Hilfesuchenden auch das nachdenken... :stupid:

Mavarik

DeddyH 25. Mär 2015 10:25

AW: Minesweeper
 
Um "schneller" ging es mir gar nicht. Aber ich mag nicht einsehen, dass ich Werte neu berechnen soll, wenn ich sie vorher bereits verfügbar hatte.

Mavarik 25. Mär 2015 10:30

AW: Minesweeper
 
Zitat:

Zitat von DeddyH (Beitrag 1294730)
Delphi-Quellcode:
const
  BITS_PER_BYTE = 8;

btw. Seit wann hab sich die Bits zu mehr oder weniger als acht in einem Byte zusammengerottet...?

DeddyH 25. Mär 2015 10:39

AW: Minesweeper
 
Man kann auch Konstanten definieren, um Magic Values zu vermeiden, das erhöht die Lesbarkeit des Codes.

[edit] Nachtrag: http://de.wikipedia.org/wiki/Magisch...8Informatik%29
Zitat:

Ein im Sourcecode eines Programms auftauchender Zahlenwert (auch engl. „hard coded value“ genannt), dessen Bedeutung sich nicht unmittelbar erkennen lässt – seine Bedeutung ist somit „magisch“. Derartige Magische Zahlen sind zu vermeiden und durch gut benannte Konstantendefinitionen zu ersetzen, deren Name Bedeutung und Herkunft klar angeben.
[/edit]

Popov 25. Mär 2015 10:46

AW: Minesweeper
 
Ohne das jetzt einiges zu kritisieren (es sind alles Top Ideen und Vorschläge), aber der TE ist meiner Meinung nach ein Anfänger. Das ist auch der Grund wieso er den für den Profi aufwendigen Weg mit Palels geht, statt es anders (besser) zu machen. Womit sich die Frage stellt ob er mit den ganzen Vorschlägen etwas anfangen kann (oder überhaupt zu der Zeit will).

Mavarik 25. Mär 2015 10:53

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294748)
Das ist auch der Grund wieso er den für den Profi aufwendigen Weg mit Panels geht, statt es anders (besser) zu machen.

Ich finde das mit den Panels gar nicht schlecht... Besser als Buttons... :stupid:

Mavarik

[OT]

Zitat:

Zitat von DeddyH (Beitrag 1294745)
Man kann auch Konstanten definieren, um Magic Values zu vermeiden, das erhöht die Lesbarkeit des Codes.

für GIF87a (0x474946383761) lass ich mir das ja noch gefallen...

Aber für ein Shl 8 oder Shl 16 genauso wenig wir für Byte := Byte XOR $FF;

Man kann es auch übertreiben...

[/OT]

DeddyH 25. Mär 2015 10:55

AW: Minesweeper
 
Wenn ich schon Konstanten einführe, dann konsequent. Aber jeder so, wie er meint.

Mavarik 25. Mär 2015 11:01

AW: Minesweeper
 
Zitat:

Zitat von DeddyH (Beitrag 1294754)
Wenn ich schon Konstanten einführe, dann konsequent. Aber jeder so, wie er meint.

Ist ja alles richtig... Ich hatte nur "Angst" das Du sagt... Wenn ich das mit XY-Pascal für XY CPU übersetze ist ein Byte 9 bit...:stupid:

Popov 25. Mär 2015 11:05

AW: Minesweeper
 
Zitat:

Zitat von DeddyH (Beitrag 1294745)
Man kann auch Konstanten definieren, um Magic Values zu vermeiden, das erhöht die Lesbarkeit des Codes.

Ich weiß nicht ob ich dir widersprechen oder zustimmen soll. Der Widerspruch wäre: guck, auch in den Delphi Units gibt es Magic Values. Warum soll man es besser als Delphi machen? Die Zustimmung wäre: ich hab mich mal etwas genauer mit RTF und Fonts auseinandergesetzt weil ist es brauchte. Ich benötigte nicht alle Details, aber mehr als die Hilfe verriet. Dazu habe ich etwas im Delphi-Code gestöbert und bin stets auf Zahlen gestoßen mit denen ich nichts anzufangen wußte. Als ich nach Tagen alle Magic Values entschlüsselt habe, wußte ich für kurze Zeit fast alles über RTF und Fonts ;)

Nur eine Auswahl auf die Schnelle:
Delphi-Quellcode:
procedure TCustomRichEdit.Print(const Caption: string);
...
begin
...
    if IsRectEmpty(PageRect) then
    begin
      rc.right := PageWidth * 1440 div LogX;
      rc.bottom := PageHeight * 1440 div LogY;
    end

function TTextAttributes.GetSize: Integer;
...
begin
...
  Result := Format.yHeight div 20;
end;

function TTextAttributes.GetHeight: Integer;
begin
  Result := MulDiv(Size, RichEdit.FScreenLogPixels, 72);
end;

DeddyH 25. Mär 2015 11:09

AW: Minesweeper
 
Und das soll was genau aussagen? "Wenn andere etwas schlecht machen, darf ich das auch" oder "Mein Code war schwer zu schreiben, dann soll er auch schwer zu lesen sein"?

BadenPower 25. Mär 2015 11:30

AW: Minesweeper
 
Zitat:

Zitat von Mavarik (Beitrag 1294752)
Ich finde das mit den Panels gar nicht schlecht... Besser als Buttons... :stupid:

Das mit den Panels finde ich persönlich auch praktisch, denn mann kann sie einerseits als "Schalfläche" darstellen und ohne großen Aufwand nach dem Linksklick als reine Anzeige für Freifläche oder Zahlendarstellung benutzen.

Einzig das Darstellen von Bildchen als Symbole (z.B Fähnchen) auf dem Panel ist umständlicher als bei der Verwendung von TSpeedButton oder TBitBtn.


Zitat:

Zitat von Popov (Beitrag 1294748)
Das ist auch der Grund wieso er den für den Profi aufwendigen Weg mit Panels geht, statt es anders (besser) zu machen.

Was wäre denn besser geeignet?

Mavarik 25. Mär 2015 11:32

AW: Minesweeper
 
Zitat:

Zitat von BadenPower (Beitrag 1294762)
Was wäre denn besser geeignet?

Ein fettes Image wo ich kleine Bildchen draufposte...

Da habe ich alle Freiheiten...

Mavarik

BadenPower 25. Mär 2015 11:48

AW: Minesweeper
 
Zitat:

Zitat von Mavarik (Beitrag 1294763)
Ein fettes Image wo ich kleine Bildchen draufposte...

Auch für jede Zahl und das Freifeld ein Image?

Gut, wären dann 13 Bildchen.
Zahlen 1-8, Leerfeld, Markierung, Bombe eplodiert, Bombe, Fragezeichen.

Popov 25. Mär 2015 11:49

AW: Minesweeper
 
Zitat:

Zitat von BadenPower (Beitrag 1294762)
Was wäre denn besser geeignet?

Eine Bitmap, bzw. eine Image oder Paint oder Formular. Einfach ein normales Feld in das man zeichnen kann.

Mal auf die Schnelle. Du benötigst eine Image Komponente. In OnMouseDown fragst du ab welches Feld geklickt wurde.

Delphi-Quellcode:
const
  MaxX = 20;
  MaxY = 20;
  CellWidth = 15;
  CellHeigth = 15;
  LineWidth = 1;

procedure TForm1.FormCreate(Sender: TObject);
var
  x, y: Integer;
begin
  with Image1.Picture do
  begin
    Bitmap.Width := (MaxX * (CellWidth + LineWidth)) + LineWidth;
    Bitmap.Height := (MaxY * (CellHeigth + LineWidth)) + LineWidth;

    for x := 0 to MaxX do
    begin
      Bitmap.Canvas.MoveTo(x * (CellWidth + LineWidth ), 0);
      Bitmap.Canvas.LineTo(x * (CellWidth + LineWidth ), Bitmap.Height);
    end;
    for y := 0 to MaxY do
    begin
      Bitmap.Canvas.MoveTo(0, y * (CellHeigth + LineWidth ));
      Bitmap.Canvas.LineTo(Bitmap.Width , y * (CellHeigth + LineWidth ));
    end;
  end;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  c, r: Integer;
begin
  c := X div (CellWidth + LineWidth );
  r := Y div (CellHeigth + LineWidth );

  //Falls über das MaxX oder MaxY geklickt: -1
  if c > (MaxX - 1) then
    c := -1;
  if r > (MaxY - 1) then
    r := -1;

  Caption := Format('Feld geklickt: x = %d; y = %d', [c, r]);
end;

BadenPower 25. Mär 2015 12:04

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294766)
Eine Bitmap, bzw. eine Image oder Paint oder Formular. Einfach ein normales Feld in das man zeichnen kann.

Schön, jetzt hast Du ein schönes Karofeld, welches Du anklicken kannst und Dir die gewünschten Werte gibt.

Aber es ist nun keine Funktionalität sichtbar. Schaltflächendesign / Anzeigedesign usw.
Das müsstest Du nun ja wiederum selbst implementieren um den Minesweeper-Look herzustellen.

Popov 25. Mär 2015 12:41

AW: Minesweeper
 
@BadenPower

Und wo ist das Problem?

Ich wollte dir mit dem Beispiel nur zeigen, dass man innerhalb von fünf bis zehn Minuten ein Feld generieren kann und auch die Koordinaten abfragen kann. Du willst eine 3D-Zelle?

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  BtnBmp: TBitmap;
begin
  BtnBmp := TBitmap.Create;
  with BtnBmp do
  try
    Canvas.Brush.Color := clBtnFace;
    Width := 20;
    Height := 20;

    Canvas.MoveTo(0, Height-1);
    Canvas.Pen.Color := clWhite;
    Canvas.LineTo(0, 0);
    Canvas.LineTo(Width-1, 0);
    Canvas.Pen.Color := clGray;
    Canvas.LineTo(Width-1, Height-1);
    Canvas.LineTo(0, Height-1);

    Self.Canvas.Draw(10, 10, BtnBmp);
  finally
    BtnBmp.Free;
  end;
end;
Auch fünf Minuten.

DeddyH 25. Mär 2015 13:01

AW: Minesweeper
 
Es gibt doch mehrere Möglichkeiten, so etwas umzusetzen: entweder man schreibt etwas "Großes", das sich um die Darstellung sowie die komplette Logik kümmert oder man verwaltet mehrere "kleine" Komponenten, die jede für sich autark mindestens die Darstellung übernehmen. Was nun "besser" oder "schlechter" ist, muss jeder für sich selbst entscheiden.

puderle 25. Mär 2015 13:11

AW: Minesweeper
 
FILL-Algorithmus

"FLOODFILL" z.b. - so habe ich mein Minesweeper gemacht.

genau das passiert wenn du auf ein Feld klickst das keine Mine ist und andere umliegende Felder auch aufgedeckt werden müssen.

lass dich von den spaghetticode noobs hier nicht verwirren.. habe mir mal erlaubt deine frage zu beantworten xD

http://de.wikipedia.org/wiki/Floodfill

rekursiv(kürzer):
http://www.tutego.de/blog/javainsel/...llalgorithmen/

Daniel 25. Mär 2015 13:39

AW: Minesweeper
 
Zitat:

Zitat von puderle (Beitrag 1294780)
habe mir mal erlaubt deine frage zu beantworten xD

Nein. Du hast lediglich die Anwesenden beleidigt:

Zitat:

Zitat von puderle (Beitrag 1294780)
lass dich von den spaghetticode noobs hier nicht verwirren

Herzlichen Dank auch.

BadenPower 25. Mär 2015 16:28

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294776)
@BadenPower
Ich wollte dir mit dem Beispiel nur zeigen, dass man innerhalb von fünf bis zehn Minuten ein Feld generieren kann und auch die Koordinaten abfragen kann.

Ja klar, man kann auch die Symbole, welche man anzeigen lassen will immer selbst zeichnen anstatt ein Bild davon in eine ImageList zu laden um es wieder zu verwenden.

Es ist doch wohl eher eine subjektive Frage ob man etwas bestehendes mit Funktionalität wie zum Beipiel ein Panel verwendet und nur Properties ändern braucht, als alles selbst zu Zeichnen und die gesamte Funktionalität und das optische Erscheinungsbild für alle ausführbaren Aktionen auch noch zusätzlich hinzufügen muss.

saii 25. Mär 2015 18:26

AW: Minesweeper
 
Wie Popov schon gesagt hat, bin ich Anfänger und folglich sagen mir die meisten Methoden und Befehle, die ihr hier auflistet garnichts.
Im Moment bin ich auch nicht bedacht, das Programm so perfekt wie möglich zu machen, sondern bis Freitag das ganze zum Laufen zu bringen.

Vielleicht perfektionier ich das danach.
Aber jetzt fehlt mir erstmal noch, dass das Anklicken eines Panels ohne benachbarte Minen, alle benachbarten Panels aufdeckt.
Dazu reicht mir auch, wenn alle Panels ohne benachbarte Minen aufgedeckt werden, nicht niur die benachbarten Panels (Stichwort Floodfill. Mal sehen ob ich das noch mache)

Und dann fehlt nur noch die Nachricht "Du hast gewonnen", für die ich mir auch noch etwas ausdenken muss.

Womit ich Hilfe brauche:
Zunächst funktioniert das Aufdecken ALLER Panels, die den Wert m=0 (=keine benachbarten Minen) haben nicht.
Könnte da jemand bitte mal reingucken?

Relevanter Teil: (gesamtes Programm auf Seite 4)
Delphi-Quellcode:
procedure TForm1.CannonFire(var A,B,m:integer; Sender: TObject);
var h,j:integer;
begin
        Panel:=TPanel(Sender);
        A:=StrToInt(Copy(Panel.Name, 3,2));
        B:=StrToInt(Copy(Panel.Name, 6,2));

        if IntA[A,B]<>9 then
        begin
                m:=IntA[A,B];
                if IntA[A,B]=0 then
                begin
                        for h:= 0 to 14 do
                        begin
                                for j:=0 to 14 do
                                begin
                                        if IntA[h,j]=0 then PanelA[h,j].Caption:='_';

                                        if (h+1>=0) and (h+1<=14) and (j>=0)  and (j<=14)  then      PanelA[h+1,j].Caption:='_';
                                        if (h+1>=0) and (h+1<=14) and (j+1>=0) and (j+1<=14) then      PanelA[h+1,j+1].Caption:='_';
                                        if (h>=0)  and (h<=14)  and (j+1>=0) and (j+1<=14) then      PanelA[h,j+1].Caption:='_';
                                        if (h-1>=0) and (h-1<=14) and (j+1>=0) and (j+1<=14) then      PanelA[h-1,j+1].Caption:='_';
                                        if (h-1>=0) and (h-1<=14) and (j>=0)  and (j<=14)  then      PanelA[h-1,j].Caption:='_';
                                        if (h-1>=0) and (h-1<=14) and (j-1>=0) and (j-1<=14) then      PanelA[h-1,j-1].Caption:='_';
                                        if (h>=0)  and (h<=14)  and (j-1>=0) and (j-1<=14) then      PanelA[h,j-1].Caption:='_';
                                        if (h+1>=0) and (h+1<=14) and (j-1>=0) and (j-1<=14) then      PanelA[h+1,j-1].Caption:='_';
                                end;
                        end;
                end;
        end;
       
        if IntA[A,B]=9 then
        begin
                ShowMessage('Du hast Verloren!');
                close;
        end;
end;

DeddyH 25. Mär 2015 18:41

AW: Minesweeper
 
Wenn Deine beiden Schleifen von 0 bis 14 laufen, dann nenne mir doch mal einen Fall, in dem
Delphi-Quellcode:
h+1>=0 
j>=0
j<=14
nicht zutreffen.

saii 25. Mär 2015 18:44

AW: Minesweeper
 
:? Stimmt allerdings. Ich wollte halt sichergehen. Aber du hast natürlich Recht :)

*Edit:
Aber wenn h=14 dann wird h+1>14
-> Dann brauch ich das, oder?

BUG 25. Mär 2015 18:59

AW: Minesweeper
 
Zitat:

Zitat von saii (Beitrag 1294833)
Ich wollte halt sichergehen.

Das ist im Allgemeinen keine schlechte Idee.
Für solche Aussagen (sollte immer wahr sein, wenn das Programm korrekt ist) bieten sich Assertions an.
Aber bitte nur einmal in der Schleife :mrgreen:

Popov 25. Mär 2015 19:48

AW: Minesweeper
 
Zitat:

Zitat von saii (Beitrag 1294829)
WDazu reicht mir auch, wenn alle Panels ohne benachbarte Minen aufgedeckt werden, nicht niur die benachbarten Panels (Stichwort Floodfill. Mal sehen ob ich das noch mache)

Die Floodfill Methode ist geeignet Flächen zu füllen, für das Aufdecken einer Fläche wie es in MineSweeper gemacht wird, ist es der falsch Algorithmus. Oder man nimmt es nicht so genau und macht es zu 95%.

Man kann es vielleicht als Grundlage nehmen, mehr aber auch nicht.

saii 25. Mär 2015 19:54

AW: Minesweeper
 
Alles klar.
Und weiß jemand wie ich bei meinem Problem weiterkomme?

Bjoerk 25. Mär 2015 21:21

AW: Minesweeper
 
Muß man die IntA[h + 1, j + 1] ect. evtl. auf Null prüfen (keine Ahnung?) bevor Caption auf '_' gesetzt wird?

BadenPower 25. Mär 2015 21:36

AW: Minesweeper
 
Zitat:

Zitat von Popov (Beitrag 1294840)
Die Floodfill Methode ist geeignet Flächen zu füllen, für das Aufdecken einer Fläche wie es in MineSweeper gemacht wird, ist es der falsch Algorithmus.

Der FloodFill-Algorithmus in der 4-Neighbour-Variante ist doch genau der Ansatz den er braucht.

Wenn in ein Feld geklickt wird, dass keine Zahl und keine Bombe enthält, dann alles Aufdecken, was leer ist und was angrenzend eine Zahl hat.

Also, wenn leer, dann aufdecken und rekursiv oben unten links und rechts weiter prüfen. Wenn eine Zahl, dann nur aufdecken und diese Stelle nicht mehr weiter untersuchen.

Popov 25. Mär 2015 21:43

AW: Minesweeper
 
Mein Hinweis auf falschen Algorithmus in Post #65 bezog sich auf Floofill. Dein Code in Post #61 ist aber... sagen wir es mal psychologisch nett: noch weniger richtig.

Du kannst die Matrix nicht mit For-Schleifen durchgehen. Damit bekommst du irgendwas hin, vielleicht sogar freie Flächen inkl. Zahlenwerte um die Minen, aber eben alle Flächen in der Matrix. Das ist aber nicht unbedingt das wie MineSweeper funktioniert. Du musst nur die Fläche um den Klick herum frei machen.

Das bedeutet, dass du von deiner geklickten Zelle heraus arbeiten musst. Das bekommt man nicht ohne Hilfe eines Stapelspeichers (Stack) hin. Das ist nicht leicht, aber auch nicht schwer.

Ich versuche das ganz Simpel zu erklären, also nicht optimiert. Nehmen wir an du hast ein Feld von 10x10 Feldern. Feld x/y 5/6 ist frei und alle Felder um das Feld sind auch frei. Du klickst in das Feld 5/6 rein. Dein Problem ist nun, dass du mitten drin bist und nun die freie Fläche aufdecken mußt. Wie kannst du nun jedes Freie benachbarte Feld ansprechen?

Gehen wir von dem Feld 5/6 aus. Das ist ein Feld, das kannst du bei der Gelegenheit überprüfen und aufdecken. Nun hast du 8 benachbarte Felder um das Feld 5/6 (4/5, 5/5, 6/5, 4/6, 6/6, 4/7, 5/7, 6/7). Die Felder legst du auf Stapel. Mit eine While-Schleife prüfst du den Count des Stapels. Ist er größer Null, sind Koordinaten drin. Also nimmst du den obersten Wert vom Stapel, z. B. 6/7 und überprüfst das Feld und dann alle benachbarte Felder aufnehmen (bereits aufgedeckte Felder wie z. B. 5/6 nicht auf Stapel legen, denn das ist bereits abgearbeitet). Im Grunde war es das schon. Du legst neue Koordinate auf Stack und arbeitest den obersten immer ab. Auf diese Weise wird jede Feld abgearbeitet.

Hier eine kleine Demo (ohne Code) wie so ein Stack funktioniert: http://www.delphipraxis.net/1246627-post16.html. Die roten Punkte sind die Koordinate auf Stack.

Popov 25. Mär 2015 21:58

AW: Minesweeper
 
Zitat:

Zitat von BadenPower (Beitrag 1294846)
Zitat:

Zitat von Popov (Beitrag 1294840)
Die Floodfill Methode ist geeignet Flächen zu füllen, für das Aufdecken einer Fläche wie es in MineSweeper gemacht wird, ist es der falsch Algorithmus.

Der FloodFill-Algorithmus in der 4-Neighbour-Variante ist doch genau der Ansatz den er braucht.

Wenn in ein Feld geklickt wird, dass keine Zahl und keine Bombe enthält, dann alles Aufdecken, was leer ist und was angrenzend eine Zahl hat.

Also, wenn leer, dann aufdecken und rekursiv oben unten links und rechts weiter prüfen. Wenn eine Zahl, dann nur aufdecken und diese Stelle nicht mehr weiter untersuchen.

Code:
2|1|3
-+-+-
1|x|1
-+-+-
1|1|1
Das Problem ist (siehe Beispiel oben), dass wenn du auf X klickst, es nicht ausreicht nur die Felder zu den vier Seiten aufzudecken, sondern auch die in der Diagonalen (z. B. auch das Feld mit der 2). So funktioniert der MineSweeper von MS. Aber so einfach ist es dann doch nicht, denn so ohne weiteres wird keine 8-Neighbour-Variante draus. 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.

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 00:06 Uhr.
Seite 2 von 4     12 34      

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