PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Minesweeper


Disaster2k
05.01.2010, 12:26
Hi,

ich probier gerade ein Minesweeper zu programmieren. Soweit funktioniert es auch also das Spielbrett und die Eingabe. Allerdings schaff ich es nicht rekursiv zu prüfen ob eines der Nachbarfelder eine Mine ist.

Kann mir dabei vielleicht jemand helfen?

THX


Firefall
05.01.2010, 13:10
Hats du Probleme mit der Rekursionslogik oder mit der konkreten Umsetzung?

Xpyder
05.01.2010, 15:35
Also, ich könnte da helfen. Ich schreibe mal, wie ich das machen würde.

Erstmal: Lege Dir ein zweidimensionales Array an. Nehmen wir an, Du willst ein 9x9 Felder Array haben wie beim Minesweeper von Microsoft. Dann lege so ein Array an:

var Spielfeld:array[0..10,0..10] of byte;

Dabei sind 1 bis 9 das Spielfeld und 0 und 10 der "Rand". Glaube mir, das macht es einfacher.

Zu dann legst Du noch 2 andere Arrays an, diese aber als "Konstanten", also mit vordefinierten Inhalten:

const Xlauf:array[0..7]of shortint=(-1,0,1,-1,1,-1,0,1);
const Ylauf:array[0..7]of shortint=(-1,-1,-1,0,0,1,1,1);
Ich erkläre später, wozu die gut sind - aber vielleicht kommst Du auch jetzt schon selbst drauf.

Achja, benutze mal, einmal am Programmanfang:

randomize;
Das setzt den Zufallsgenerator auf einen "willkürlichen" Wert. Wenn Du das nicht machst, werden die Minen bei jedem Start des Programms immer an derselben Stelle liegen.

Nun startet das Spiel. Zuerst die Vorbereitung:

Du löschst das Spielfeld. Es gibt zwei möglichkeiten.
Die erste (dazu brauchst Du zwei Variablen, ich nehme i und j) :
for i:=0 to 10 do for j:=0 to 10 do Spielfeld[i,j]:=0;

Die zweite (bessere) :
fillchar(Spielfeld,sizeof(Spielfeld),0);

Wir wollen 10 Minen:
(achja deklariere mal X und Y als Byte)


for i:=1 to 10 do
begin
repeat
X:=succ(random(9));Y:=succ(random(9)); {succ erhöht den Wert in Klammern um 1}
{Damit enthalten jetzt X und Y jeweils Werte von 1 bis 9}
until Spielfeld[X,Y]=0;
{Diese komische repeat-Schleife verhindert, daß nicht eine Mine irgendwo hingesetzt wird,
wo schon eine liegt}
Spielfeld[X,Y]:=1; {Ich nehme als "Mine" mal 1}
end;


Das Spielfeld ist "virtuell" und hat nichts mit der Anzeige zu tun. Die Anzeige ist nochmal ein extra Feld. Die kannst Du direkt auf dem Bildschirm darstellen.

So und nun nehmen wir mal an, der Spieler klickt/wählt ein Feld im Spielfeld.
Die Koordinaten übergeben wir an X und Y. (Liegen dann jeweils zwischen 1 und 9.)

Und dann:
Wenn Spielfeld[X,Y]=1, dann ist der Depp auf eine Mine getreten, ansonsten:

Summe:=0;
for i:=0 to 7 do inc(Summe,Spielfeld[X+Xlauf[i],Y+Ylauf[i]]);


Und Summe enthält dann die Anzahl der Minen, die um das betreffende Feld herumliegen. Also wird Summe (kann 0 bis 8 sein) in das Feld (NICHT DAS SPIELFELD, SONDERN DIE ANZEIGE!) eingetragen.

Und das kann man so lange wiederholen, bis alle Felder aufgedeckt sind, oder der Spieler vorzeitig eine Mine gefunden hat.

Ich hoffe, das hilft ein wenig. Wenn nicht, kann ich das gern noch genauer erklären.

Xpyder
05.01.2010, 16:29
Achja. Das rekursive Finden der Grenzen, wenn man auf ein Feld kommt, wo keine Mine ist.
Auch das kann man mit den oberen Xlauf und YLauf Arrays machen. Es funktioniert wie das Füllen von Flächen...

Dazu kann man das entweder mit dem Stack machen oder man legt sich selbst einen "Stack" an, der quasi ein Array ist. Dieses Array braucht eigentlich nur so groß sein wie das Spielfeld, also 9x9 = 81.

Und dann geht man von der Anzeige aus. Nur Felder, die noch nicht aufgedeckt sind, lassen das Ganze Weiterlaufen. Das Stack-Array enthält nur Werte von 0 bis 7.... Ich erkläre das mal kurz. Man braucht noch ein Hilfsfeld, dieses entspricht dem Ausgabefeld. Dabei sind zu Anfang alle Felder mit 255 zu füllen. (Also irgendeinem Wert, der nicht 0 bis 8 ist.)
Ein Feld 255 (also größer 8), entspricht einem "nicht aufgedeckten" Feld. Ein aufgedecktes Feld hat dann Werte 0 bis 8 (für die umgebenden Minen).

Das deklarieren:

var HilfsFeld:array[0..10,0..10]of byte;{Passend zum Spielfeld, das muss mit 255 gefuellt werden}
var Stack:array[0..81]of byte;
var P:integer;
var Xr,Yr,Xt,Yt:byte;


Wenn auf so ein leeres Feld gestoßen, dann rennt diese Füllfunktion los:

P:=0;Stack[P]:=0;Xr:=X;Yr:=Y;HilfsFeld[Xr,Yr]:=0;
repeat
Xt:=Xr+Xrun[Stack[P]];Yt:=Yr+Yrun[Stack[P]];{Temporaer pruefen}
{Hier pruefen, ob Spielfeldgrenze erreicht oder ein Feld, das schon bearbeitet wurde}
if (Xt<1) or (Xt>9) or (Yt<1) or (Yt>9) or (HilfsFeld[Xt,Yt]<>255) then
begin {Hier ist ein rekursiver "Pfad" zu Ende}
if Stack[P]=7 then dec(P) else inc(Stack[P]);
end
else
begin {Hier erfolgt die Pruefung auf Minen um das Feld herum}
Summe:=0;
for i:=0 to 7 do inc(Summe,Spielfeld[Xt+Xlauf[i],Yt+Ylauf[i]));
HilfsFeld[Xt,Yt]:=Summe; {Kann dann auch wieder grafisch ausgegeben werden}
if Summe=0 then
begin
Xr:=Yt;Yr:=Yt;{Werte uebernehmen}
inc(P);{Neues Feld}
Stack[P]:=0;{Wieder auf erste Richtung initialisieren}
end
else
begin
if Stack[P]=7 then dec(P) else inc(Stack[P]);
end;
end;
until P<0;


Danach entspricht der Inhalt des Hilfsfelds eigentlich der zu machenden Grafikausgabe.
Ich hoffe, ich hab das jetzt alles richtig gemacht. Ich hab es nicht getestet.

Disaster2k
06.01.2010, 11:25
Oh man das ist aber super. Ich werde das am Abend in Ruhe probieren. Das Spielfeld und so hab ich schon fertig mir ging es wirklich um das Aufdecken bis zu den Grenzen und die eben rekursiv. Aber da wird mir dein Lösung schon weiter helfen.

Ich meld mich noch mal, Vielen Dank.

Xpyder
07.01.2010, 07:01
Das mit dem "Hilfsstack" hab ich wohl irgendwie halb müde gebastelt.
Aber bei diesen kleinen Feldern geht auch der normale Stack.
Ich habe jetzt mal eben (in 11 Stunden) ein eigenes MineSweeper gebaut.
Ich verwende zwei meiner Units (für die Tastenabfrage und für das Speichern/Laden der Siegerliste).
Ich baue vielleicht noch eine Version, wo ich das gleich einbaue (ohne Units) und poste dann den Sourcecode. (Der sieht leider schon wieder aus.... Oh mann...)

Disaster2k
07.01.2010, 10:38
Irgendwie komm ich nicht ganz klar :).

Hier ist mal mein code. Ich bräuchte auch eine schönere Lösung für die Eingabe-Abfrage.

(* ---- *)
(* Stellt das Spielbrett von Minensuche mit einer Größe von *)
(* 9x9 dar und plaziert 9 Mienen auf diesem. Die angrezenden *)
(* Felder erhalten einen Counter, welcher zeigt wie viele *)
(* Mienen sich in der unmittelbaren Umgebung befinden. *)
(*============================================================*)

Program Minensuche;

USES
WinCrt;

TYPE

Field = RECORD
mine: Boolean;
neighbors: 0..8;
visible: BOOLEAN;
END;
MineBoard = ARRAY['a'..'i',1..9] OF Field;

(* Liefert eine Zufallszahl zwischen 1 -9 *)
FUNCTION getRandomNumber: INTEGER;

BEGIN
getRandomNumber:= Random(9) + 1;
END;

(* Setzt alle Felder auf 0 *)
PROCEDURE ClearBoard(VAR board: MineBoard);

VAR
i: CHAR;
j: INTEGER;

BEGIN
FOR i:= 'a' TO 'i' DO BEGIN
FOR j:= 1 TO 9 DO BEGIN
board[i,j].mine:= False;
board[i,j].neighbors:= 0;
board[i,j].visible:= False;
END;
END;
END;

(* Stellt das fertige Minenfeld dar *)
PROCEDURE DisplayBoard(VAR board: MineBoard);

CONST
trennlinie = ' +---+---+---+---+---+---+---+---+---+';

VAR
i: CHAR;
j: INTEGER;

BEGIN
WriteLn(' ---- Wilkommen bei der Minensuche ---- ');
WriteLn;
WriteLn(' 1 2 3 4 5 6 7 8 9');

FOR i:= 'a' TO 'i' DO BEGIN
WriteLn(trennlinie);
Write(' ', i, ' ');
FOR j:= 1 TO 9 DO BEGIN

If board[i,j].mine THEN BEGIN
If board[i,j].visible THEN BEGIN
Write('| M ');
END ELSE BEGIN
Write('| ');
END;
END
ELSE BEGIN
If board[i,j].visible THEN BEGIN
Write('| ',board[i,j].neighbors,' ');
END ELSE BEGIN
Write('| ');
END;
END;
IF j = 9 THEN BEGIN
Write('|');
END;
END;
WriteLn;
END;
WriteLn(trennlinie);

END;

Function StrToInt(j: CHAR): INTEGER;

BEGIN

CASE j OF

'1': StrToInt:= 1;
'2': StrToInt:= 2;
'3': StrToInt:= 3;
'4': StrToInt:= 4;
'5': StrToInt:= 5;
'6': StrToInt:= 6;
'7': StrToInt:= 7;
'8': StrToInt:= 8;
'9': StrToInt:= 9;

END;

END;

FUNCTION GetCharValue(i: INTEGER): CHAR;

BEGIN

CASE i OF

1: GetCharValue:= 'a';
2: GetCharValue:= 'b';
3: GetCharValue:= 'c';
4: GetCharValue:= 'd';
5: GetCharValue:= 'e';
6: GetCharValue:= 'f';
7: GetCharValue:= 'g';
8: GetCharValue:= 'h';
9: GetCharValue:= 'i';

END;

END;

(* Generiert Zufallszahlen und plaziert 10 Minen auf dem Spielbrett *)
PROCEDURE PlaceMines(VAR board: MineBoard);

VAR
i: INTEGER;
x: CHAR;
y: INTEGER;

BEGIN

i:= 0;

WHILE i <= 9 DO BEGIN
x:= GetCharValue(getRandomNumber);
y:= getRandomNumber;

IF NOT board[x,y].mine THEN BEGIN
board[x,y].mine:= True;
i:= i + 1;
END;

END;

END;

(* Prüft ob das Nachbarfeld ein gültiges Feld und kein Minenfeld ist. *)
(* JA: Der Count wird um eins erhöht: *)
(* NEIN: Der Count bleibt gleich. *)
PROCEDURE setCount(VAR board: MineBoard; i: CHAR; j: INTEGER);

BEGIN

IF (i >= 'a') and (j > 0) and (i <= 'i') and (j <= 9) THEN BEGIN
IF NOT (board[i,j].mine) THEN BEGIN
board[i,j].neighbors:= board[i,j].neighbors + 1
END;
END;

END;

(* Sucht nach Minenfelder und erhöht den Count der umliegenden Felder *)
PROCEDURE UpdateNeighborCounters(VAR board: MineBoard);

VAR
i: CHAR;
j: INTEGER;
k: CHAR;

BEGIN

FOR i:= 'a' TO 'i' DO BEGIN
FOR j:= 1 TO 9 DO BEGIN
IF (board[i,j].mine) THEN BEGIN
k:= i;
Inc(k);
setCount(board, k,j-1);
setCount(board, k,j);
setCount(board, k, j+1);
setCount(board, i,j-1);
setCount(board, i, j+1);
k:=i;
Dec(k);
setCount(board, k,j-1);
setCount(board, k,j);
setCount(board, k,j+1);
END;
END
END;

END;


PROCEDURE UncoverField(board: MineBoard; i: CHAR; j: INTEGER; VAR isMine: BOOLEAN);

VAR
k: CHAR;

BEGIN

IF (i >= 'a') and (j > 0) and (i <= 'i') and (j <= 9) THEN BEGIN
IF board[i,j].mine THEN BEGIN
isMine:= TRUE;
WriteLn(i, j, ' ist eine Mine'); ReadLn;
END ELSE BEGIN
board[i,j].visible:= True;
k:= i;
Inc(k);
WriteLn(k);
IF NOT isMine THEN UncoverField(board, k,j-1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, k,j, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, k, j+1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, i,j-1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, i, j+1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
k:=i;
Dec(k);
IF NOT isMine THEN UncoverField(board, k,j-1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, k,j, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN UncoverField(board, k,j+1, isMine);
WriteLn(k, j, ' ist eine Mine'); ReadLn;
IF NOT isMine THEN BEGIN
k:= i;
Inc(k);
board[k,j-1].visible:= True;
board[k,j].visible:= True;
board[k, j].visible:= True;
board[i,j-1].visible:= True;
board[i, j+1].visible:= True;
k:=i;
Dec(k);
board[k,j-1].visible:= True;
board[k,j].visible:= True;
board[k,j+1].visible:= True;
END;
END;
END;

END;

VAR
board: MineBoard;
svalue: STRING;
isMine: BOOLEAN;
i: CHAR;
j: INTEGER;

BEGIN

(*Einmalige Initialisierung der Zufallszahl*)
Randomize;
isMine:= False;

ClearBoard(board);
PlaceMines(board);
UpdateNeighborCounters(board);

WHILE NOT isMine DO BEGIN

DisplayBoard(board);
WriteLn;
Write('Welches Feld soll aufgedeckt werden: ');
Readln(svalue);

i:=svalue[1];
j:=StrToInt(svalue[2]);
WriteLn(i, j, ' wurde eingegeben');
UncoverField(board,i,j,isMine);
clrscr;

END;


END.

DANKE

Xpyder
07.01.2010, 15:48
(Anmerkung: Hab für meine Version nur so lange (11 Stunden) gebraucht, weil ich's ja noch "schick" gemacht hab - mit Rähmchengrafik und Farben und so Kram... - nicht, daß sowas nötig wäre, aber mir war grad danach.)

Weia. Du scheibst wahnsinnig ordentlichen Code. Formatiert und so. Ich muß immer hoch und runter scrollen, um die Zusammenhänge zu sehen...
Und von diesen ganzen CASE- und IF-Kaskaden krieg ich 'ne Krise. Ganz früher hab ich das auch so gemacht... Ich mach das immer mit Entscheidungstabellen (so Arrays, die ich als Konstante festlege)... Aber OK - jeder, wie er will.

Einiges ist ein wenig umständlich gemacht (man kann Zahlen und Zeichen auch anders ineinander wandeln). Ich geb' bloß mal ein paar Tips, um den Code etwas einzukürzen:

Zeichen "a" bis "i" zu 1 bis 9:
Zahl:=ord(Zeichen)-96;

Zeichen "1" bis "9" zu 1 bis 9:
Zahl:=ord(Zeichen)-48; oder val(Zeichen,Zahl,Fehler); {Fehler muß Integervariable sein}

Zahl 1 bis 9 zu Zeichen "a" bis "i":
Zeichen:=chr(Zahl)+96;

Zahl 1 bis 9 zu Zeichen "1" bis "9":
Zeichen:=chr(Zahl)+48; oder str(Zahl,Zeichen);

Und die CASE-Anweisung erlaubt auch mehrere Zeichen oder Zahlen hintereinander, zum Beispiel so:

CASE Zeichen OF
"a".."i":BEGIN
Zahl:=ord(Zeichen)-96;
END;
END;

Man kann auch mehrere Werte oder "Bereiche" (also die mit ..) durch Kommata voneinander getrennt benutzen, das muß aber natürlich alles den Typ des Wertes hinter CASE haben...

So, und jetzt noch das mit dem "Nachbarn finden". Ich verwende dazu 2 Arrays, die 8 Shortints lang sind, eins für die X- und eins für die Y-Richtung. Außerdem ist Spielfeld nicht von 1 bis 9, sondern von 0 bis 10. Die 0 und die 10 sind ein "Rand", der zum Spielen nicht benutzt wird und auf dem nie Minen landen. Aber er macht es einfacher, die Nachbarn zu finden - ohne daß man auf die Ränder aufpassen muß.

Ich schreibe es mal mit + vor den positiven Addierern (obwohl das + nicht nötig ist), damit klarer wird, was ich mache:

var AddX:array[0..7]of shortint=( 0, 0,-1,+1,-1,+1,+1,-1);
var AddY:array[0..7]of shortint=(-1,+1, 0, 0,-1,+1,-1,+1);

Hier mal eine Veranschaulichung: P ist die zu testende Position, (also X;Y)
die Zahlen die Indizes der Positionen, die geprüft werden:

+---+---+---+
| 4 | 0 | 6 |
+---+---+---+
| 2 | P | 3 |
+---+---+---+
| 7 | 1 | 5 |
+---+---+---+

D.h. eine Position X,Y läßt sich eben z.B. so testen:

Summe:=0;
for i:=0 to 7 do
if Feld[X+AddX[i],Y+AddY[i]].Mine then inc(Summe);

Daher finde ich es auch besser, immer mit Zahlen zu arbeiten, wo ich kann. Wenn man das Ganze mit Buchstaben und Ziffern machen will, würde ich persönlich vorschlagen, das nur bei der Eingabe zu benutzen und für den ganzen Berechnungsprozeß alles in Zahlen zu wandeln.

ICh teste erst die Positionen der "geraden" Himmelsrichtungen und dann die schrägen. Grund: Die ersten 4 Indizes (0 bis 3) kann man gleich für das "Füllen" benutzen. Denn dieses "Finden und Aufdecken" ist im Prinzip eine rekursive Füllfunktion. Vorgestern habe ich da was mit 'nem künstlichen Stack versucht, aber das war wohl nicht so, wie ich das sonst mache. Aber für so kleine Bereiche kann man auch den echten Stack verwenden. Bei einem 9x9 Feld mit 10 Minen kann er nur maximal 71 Schritte "in die Tiefe" gehen, im Worst Case.

Dazu benutzt man eine Funktion, die selbstaufrufend ist. Ich benutze bei mir 3 Felder, Spielfeld (da sind die Minen), Hilfsfeld (das ist zur Anzeige) und Zaehlfeld (da sind vorher die gezählten "Nachbarn" der Minen drin. Du hast es mit 'nem Record gemacht - das finde ich sogar besser als bei mir.
Ich schreibe mal meine Funktion hier so um, daß es mit Deinem Record geht. Außerdem hat mein Feld eine variable Größe (da wird dann eben nur die linke obere Ecke des MaximalFelds benutzt, wenn ich ein kleines Feld benutze), aber ich schreibe das mal für ein Feld um, das von 1 bos 9 geht:

{---------------------------------------------------------------------------}
var Xt:char;
Yt:byte; {Die deklariere ich global, damit sie nicht Stack verbrauchen bei der Rekursion.}

{das hier benutzt den Stack}
procedure fuelle(X:char;Y:byte);
var R:byte;
begin
R:=0;
repeat
Xt:=chr(ord(x)+AddX[R]); {Hier sieht man den Nachteil, wenn man für Koordinaten chars benutzt.}
Yt:=Y+AddY[R]; {Hier geht das Addieren}
with MineBoard do
if (Xt>='a') and (Xt<='i') and (Yt>=1) and (Yt<=9) and (not visible) and (not mine) then
begin visible:=true;
if neighbors=[] then fuelle(Xt,Yt);
end;
inc(R);
until R>3;
end;
{---------------------------------------------------------------------------}
{wird true, wenn auf eine Mine getreten}
function entdecke(X:char;Y:byte):boolean;
var i:byte;
begin
with MineBoard[X,Y] do
begin entdecke:=mine;
if mine then
visible:=true {Mine aufdecken...}
else
begin
if neighbors<>[] then
visible:=true
else
if not visible then
begin
visible:=true; fuelle(X,Y);
end;
end;
end;
end;
{---------------------------------------------------------------------------}


Das Ganze funktioniert jetzt so:
Wenn der Spieler das Feld X/Y angewählt hat, führt man einfach die Funktion entdecke mit den Koordinaten aus. Diese macht dann das alles - d.h. sie entdeckt alle Felder bis zum "Rand" oder bis die erste "Nachbarn" Zahl kommt (die sie dann mit aufdeckt).
Außerdem liefert die Funktion ein TRUE zurück, wenn das Feld eine Mine war (und macht dann nichts weiter) oder eben ein FALSE, wenn es ein normales Feld war.

Das sollte jetzt funktionieren. Probier es aus.
Achja, natürlich mußt Du dann nachher (nach Aufruf der entdecke-Funktion) über das Feld gehen und alles, was "visible=true" ist, entsprechend "aufdecken", also anzeigen.

Ich hoffe, das hilft.

Disaster2k
07.01.2010, 16:50
@Xpyder

Danke für das du dir so viel Zeit nimmst um mir zu helfen.

Welches Pascal benutzt du? Ich hab hier Boarland Pascal 7. Weil deine Syntax irgendwie anders ist und beim mir auch nicht funktioniert.

z.B.:

var AddX:array[0..7]of shortint=( 0, 0,-1,+1,-1,+1,+1,-1);

Ich hab für das blöde Spiel jetzt schon soo viel Zeit verbraten das ich an mir selbst zweifle...

Xpyder
07.01.2010, 17:46
@Xpyder

Danke für das du dir so viel Zeit nimmst um mir zu helfen.

Welches Pascal benutzt du? Ich hab hier Boarland Pascal 7. Weil deine Syntax irgendwie anders ist und beim mir auch nicht funktioniert.

z.B.:

var AddX:array[0..7]of shortint=( 0, 0,-1,+1,-1,+1,+1,-1);

Ich hab für das blöde Spiel jetzt schon soo viel Zeit verbraten das ich an mir selbst zweifle...

Ich benutze auch Borland Pascal 7.
Ich hab n Fehler gemacht. Vor AddX und AddY gehört nicht VAR, sondern CONST. (weil es ja eine Zuweisung enthält).

Diogenes
07.01.2010, 20:24
Ich benutze auch Borland Pascal 7.
Ich hab n Fehler gemacht. Vor AddX und AddY gehört nicht VAR, sondern CONST. (weil es ja eine Zuweisung enthält).

In FreePascal würd's gehen, und hätte lokal auch eine andere Wirkung, nämlich die, die Variable bei jedem Einsprung neu zu initialisieren. Macht im globalen keinen Unterschied, lokal aber schon.

Disaster2k
09.01.2010, 09:26
@Xpyder

Wirst du deine Version von Minesweeper auch hier zeigen?

Danke

Xpyder
09.01.2010, 15:04
@Xpyder

Wirst du deine Version von Minesweeper auch hier zeigen?

Danke
Naja, so toll ist die auch nicht. Habe den Sourcecode ziemlich einfach gehalten und auch den Aufbau der "Grafik".
Müßte den Source nur etwas modifizieren, weil ich da zwei meiner Units benutzt habe - die eine zur Tastaturabfrage und die andere für das Laden und Speichern der Siegerlisten. Ich würds dann so machen, daß ich das Ganze als einen einzigen Source hier poste.
Aber ich könnte ja die EXE, so wie sie jetzt ist, hier anhängen - falls Interesse.

Xpyder
11.01.2010, 17:31
OK, hier ist mein Minesweeper. Nicht schön, aber dafür selten.
Mit Source. Ich habe den diesmal sehr einfach gehalten. Und wie immer kann ich mich im Source nie zwischen deutsch und englisch entscheiden.

Und JA!, ich weiß, daß meine Sourcen total "unordentlich" sind und nicht so schön formatiert, wie es so'n Informatiklehrer den Schülern wahrscheinlich beibringen würde... So programmiere ich eben - wem es nicht paßt, braucht es nicht benutzen.

Weil das ganze Ding so furchtbar billig ist, bin ich auch sehr sehr sparsam mit der Kommentarfunktion umgegangen. Falls noch irgend etwas unklar ist, einfach mal fragen.

Anmerkung 1: Ich habe nicht wie Disaster2k so ein schönes Array of Record für das Spielfeld benutzt, sondern 3 Arrays, eins für die Minen, eins für die Anzeige und eins für die "Zähler". Ist etwas ätzend gecodet, aber es funktioniert trotzdem. Bei größeren Projekten programmiere ich etwas anders - schon aus Speicher- und Geschwindigkeitsgründen...

Anmerkung 2: Diese "Advancedkey" Function habe ich aus einer meiner selbstgebastelten Units hier reinkopiert.

Anmerkung 3: Die File-Unit habe ich ebenfalls weggelassen und habe eine eigene kleine Bastelei hier eingebaut, die die Siegerlisten lädt und speichert. Und ja, die sind leicht verschlüsselt, damit jemand, der nur die IGMINES.EXE hat, nicht in der Siegerliste herumgurken kann, um sich selbst an die Spitze zu setzen.

Das angehangene ZIP enthält den Pascal-Source IGMINES.PAS (für Borland Turbo Pascal 7) und die compilierte IGMINES.EXE, für Leute, die kein Borland Pascal zur Hand haben oder keine Lust, extra zu compilieren.

Disaster2k
12.01.2010, 14:00
Ich mach mir ins Hemd, das ist ja mal sehr geil :)

Vielen Dank, also soweit bin ich dann noch lange nicht. Ich muss gestehen ich hab den blöden rekursiven Aufruf noch immer nicht geschafft.

Der Code mag zwar nicht so übersichtlich sein, aber das Resultat ist dafür gigantisch finde ich. Du hast mir jetzt einen Grund gegeben mich gleich wieder damit zu befassen und annähernd an dein Endresultat zu kommen.

Ich dank dir und hoff du hilfst wenn ich noch fragen habe.

THX

Xpyder
12.01.2010, 16:38
Ich mach mir ins Hemd, das ist ja mal sehr geil :)
Achnaja, Textmode und so... Ist ja auch alles nur Rähmchengrafik und so Kram. Außerdem programmiere ich schon seit... 23 Jahren oder so - und über 16 Jahre davon auf dem PC. Und wenn man es mal unter diesem Gesichtspunkt betrachtet, ist es eher armselig, was ich da bisher geleistet habe.

Vielen Dank, also soweit bin ich dann noch lange nicht. Ich muss gestehen ich hab den blöden rekursiven Aufruf noch immer nicht geschafft.
Also eigentlich hab ich Dir ja hier im Thread sogar Code dazu gepostet, der sogar Deine Variablen und Dein Record-Spielfeld verwendet.

Der Code mag zwar nicht so übersichtlich sein,
Übersichtlichkeit ist halt relativ. Der Eine findet es übersichtlich, alles zu formatieren, am besten jeden Befehl auf eine extra Zeile, damit es ordentlicher aussieht und man alles gut abgrenzt.
Der Andere (so wie ich) schreibt es etwas kompakter, damit eine Function oder Procedure möglichst insgesamt auf den Bildschirm paßt, so daß man gleichzeitig das ganze Ding im Blick hat und ohne hoch- und runterscrollen gleich nachvollziehen kann, was das Ding macht (und wo Fehler sind, falls vorhanden).
Übrigens gibt es meines Wissens so Programme, die Sourcen nachträglich formatieren.

aber das Resultat ist dafür gigantisch finde ich. Du hast mir jetzt einen Grund gegeben mich gleich wieder damit zu befassen und annähernd an dein Endresultat zu kommen.
Naja - zuviel des Lobs für so ein Teil. Das war eine Arbeit von vielleicht insgesamt 12 Stunden oder so. (OK, ich tippe auch nicht gerade langsam.)
(Falls du mal etwas besseres - und grafisches - von mir sehen willst, auf meiner Seite gibt es einiges Zeug.)

Ich dank dir und hoff du hilfst wenn ich noch fragen habe.
Ja, ich helfe immer gern. Und ich freue mich, wenn sich noch jemand für Programmieren interessiert - und eben auch, wenn er dabei meine Interessen berührt. (Es passiert sowieso äußerst selten mal, daß jemand hier im Forum etwas in den Pascal-Bereich schreibt.)
Ich selbst bin z.B. kein so Skripter oder Benutzer von VMs, sondern mag halt mehr die "konventionellen" Programmiersprachen, die in nativen Maschinencode übersetzt werden und direkt auf der Maschine laufen.
Aber was ich mache, ist im Prinzip auch nichts besonderes - und ich bin mir auch sicher, daß mich 95% der User dieses Forums dafür insgeheim auslachen (auch wenn es nicht alle so direkt schreiben). Aber mir geht es ja auch sowieso nicht darum, daß mich irgend jemand "cool" findet - weil ich erwachsen bin und so etwas nicht brauche...