AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Minesweeper

Ein Thema von saii · begonnen am 21. Mär 2015 · letzter Beitrag vom 29. Mär 2015
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.154 Beiträge
 
Delphi 10.3 Rio
 
#1

AW: Minesweeper

  Alt 25. Mär 2015, 22:05
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
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#2

AW: Minesweeper

  Alt 25. Mär 2015, 22:30
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.

Geändert von Popov (25. Mär 2015 um 22:32 Uhr)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Minesweeper

  Alt 26. Mär 2015, 02:37
Simpler Maussucht Käse:
Weil das ja so simple ist würde ich ggf. vorschlagen, daß du das kurz in der Play ergänzt.

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.
  Mit Zitat antworten Zitat
BadenPower

Registriert seit: 17. Jun 2009
616 Beiträge
 
#4

AW: Minesweeper

  Alt 26. Mär 2015, 09:15
Weil das ja so simple ist würde ich ggf. vorschlagen, daß du das kurz in der Play ergänzt.
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.
Programmieren ist die Kunst aus Nullen und Einsen etwas sinnvollen zu gestalten.
Der bessere Künstler ist allerdings der Anwender, denn dieser findet Fehler, welche sich der Programmierer nicht vorstellen konnte.

Geändert von BadenPower (26. Mär 2015 um 09:24 Uhr)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Minesweeper

  Alt 26. Mär 2015, 11:28
So könnte dann die Play aussehen: [..]
Du musst aber noch "TMinesweeperForm.PanelsMouseUp" ändern, da dort ein Fehler ist. [..]
Stimmt. Badenser vor. 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:
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.
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.154 Beiträge
 
Delphi 10.3 Rio
 
#6

AW: Minesweeper

  Alt 26. Mär 2015, 11:42
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.
Es sollte generell hier unser bestreben sein, helfen aber nicht "vorsagen".

Mavarik
Angehängte Grafiken
Dateityp: jpg bart-simpson-generator.jpg (33,7 KB, 18x aufgerufen)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#7

AW: Minesweeper

  Alt 26. Mär 2015, 11:55
Es sollte generell hier unser bestreben sein, helfen aber nicht "vorsagen".Mavarik
Klar, bin ja nicht erst seit gestern hier? Anyway, keine Regel ohne Ausnahme..
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#8

AW: Minesweeper

  Alt 26. Mär 2015, 11:58
So, ich hab auf die Schnelle den MineSweeper programmiert, wobei ich mich streng an den von saii vorgegebenen Stil gehalten habe. Ich hab aber nicht nur das was saii bereits vorgegeben hat so weiter gemacht, sondern auch Neues in dem Stil programmiert. So habe ich aus TStringList einen Stack gebastelt, statt das zu nehmen was Delphi bietet. Ich meine wenn man schon das Feld aus Panelen bastelt und über Koordinaten im Namen "P_07_10' anspricht, dann muss man TStringList für den Stack nehmen

Trotzdem, auch wenn das Spiel nun funktioniert, komplizierter als mit Panelen kann man es nicht machen. Sicher, wenn man sich die Mühe macht, kann man es optiomieren, aber trotzdem.

Wie gesagt, das Spiel funktioniert, ist aber noch nicht ganz fertig.
Angehängte Dateien
Dateityp: zip MineSweeper.zip (3,1 KB, 7x aufgerufen)
  Mit Zitat antworten Zitat
Bjoerk

Registriert seit: 28. Feb 2011
Ort: Mannheim
1.384 Beiträge
 
Delphi 10.4 Sydney
 
#9

AW: Minesweeper

  Alt 26. Mär 2015, 12:49
Was soll das hier werden? Postet jetzt jeder seinen Minensucher? Mavarik hatte doch schon mich (und wie ich im nachhinein finde völlig zurecht und was mir nur aus Begeisterung passierte) darauf aufmerksam gemacht, daß es in den allermeisten Fällen nicht hilfreich ist, ganze units zu posten? Sorry, ist nicht persönlich gemeint.
  Mit Zitat antworten Zitat
Popov
(Gast)

n/a Beiträge
 
#10

AW: Minesweeper

  Alt 26. Mär 2015, 13:35
@Bjoerk

Ich könnte das nun politisch machen und das Grundgesetz zitieren, aber das wäre dann doch ein Overkill. Aber lass mich dann doch klein wenig den Geist unserer Verfassung hier einbringen, in dem da wäre: kein Mensch sollte sich über den anderen erheben.

Das hier ist ein Forum. Das hier ist keine Moodle- oder andere Lern-Platform, die den Sinn hat den Leuten etwas beizubringen. Wer hier postet hat ein Problem und möchte gerne eine Lösung. Wer hier ein Problem postet, der macht es nicht um zu diskutieren, sondern um etwas zu erfahren. Er will eine Lösung.

Wenn ein Anfänger die Aufgabe vom Lehrer bekommt ein Programm zu schreiben, und er kommt mit der Aufgabe nicht klar, so dass er hier postet, dann liegt es an jedem User hier ob er ihm hilft oder nicht. Aber eines werde ich garantiert nicht machen - ich werde mich nicht über den Anfänger erheben und versuchen einen Delphi-Lehrer zu spielen. Ich werde mir nicht das Recht nehmen zu sagen: wenn ich ihm den ganzen Code gebe, dann lernt er nichts. Ich bin nicht seine Mama, ich bin nicht sein Papa, ich bin auch nicht sein Lehrer. Er ist ein freier Mensch der selbst entscheiden muss wie er glücklich wird. Und wenn ich aus Langeweile die Lösung programmiere und hier poste, dann liegt es an dem Fragersteller ob er das beachtet oder nicht. Oder ob er vielleicht dann doch lieber die kleinen Hinweise hat und selbst an das Ziel zu kommen. Dieses Recht werde nicht ich für ihn ausüben, denn wer bin ich denn, dass ich mir das Recht nehme über jemand anders zu entscheiden?

Also, entweder ist das hier ein Lehrer-Forum wo Programmierer die Probleme haben etwas lernen können. Dann sollten sich aber bitte alle dann auch so verhalten und didaktisch korrekt lehren. Oder es ist ein Problem Forum, d. h. jeder der Probleme hat darf sie posten und drauf hoffen, dass einer antwortet. Und wie einer drauf antwortet, das sollte jedem selbst überlassen werden.

Aber wo wir schon dabei sind - es gibt im Grunde zwei Arten wie einer etwas lernen kann: man lernt an einem Tag das A, am zweiten das B, am dritten das C usw. Irgendwann nach Wochen hat man so viel gelernt, dass man daraus ein Wort bilden kann. Die zweite Methode ist gleich mit dem Wort anzufangen, z. B. "Apfel". Danach nimmt man sich die einzelnen Buchstaben durch.

Wie gesagt, das hier ist kein Lehrer-Forum, sondern ein Hilfe-Forum. Deshalb ist es eigentlich unhöflich einem der Hilfe braucht nur ein Schlagwort zu liefern mit dem Gedanken - so lernt er es besser. Wer es so machen will, kann es so machen. Da ich aber hier kein Lehrer bin, entscheide ich gelegentlich einfach den ganzen Code zu geben. Weil es mir einfach egal ist ob er was lernt. Es ist seine Entscheidung. Es ist anmaßend diese Entscheidung für ihn zu übernehmen. Wenn einer am ertrinken ist, dann retten man ihn, man ruft ihm vom Strand nicht zu wie man schwimmen lernen kann.

Geändert von Popov (26. Mär 2015 um 13:37 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:11 Uhr.
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