Frage an die Programmierer unter euch

sterum

Well-Known Member
Ich hab mich jetzt mal ein wenig mit mehrdimensionalen Arrays beschäftigt und bin dadurch auf Minesweeper gekommen.
Jetzt frage ich mich wie ich nun jedes Element mit der Anzahl der Umliegenden Minen setzte.

Meine Lösung sieht zur Zeit so aus (Perl), jedoch scheint mir das ganze etwas zu "aufgebläht".

@feld = Spielfeld
X bezeichnet ein mit einer Mine belegtes Element
$max = Maximale Spielfeldgröße

Code:
# Ecke Links oben
my $zahl = 0;
$zahl+=1 if $feld[0][1] eq 'X';
$zahl+=1 if $feld[1][0] eq 'X';
$zahl+=1 if $feld[1][1] eq 'X';
$feld[0][0] = $zahl if $zahl;

# Ecke Rechts oben
$zahl = 0;
$zahl+=1 if $feld[0][$size] eq 'X';
$zahl+=1 if $feld[1][$size] eq 'X';
$zahl+=1 if $feld[1][$size-1] eq 'X';
$feld[0][$size] = $zahl if $zahl;

# Ecke Links unten
$zahl = 0;
$zahl+=1 if $feld[$size-1][0] eq 'X';
$zahl+=1 if $feld[$size][1] eq 'X';
$zahl+=1 if $feld[$size-1][1] eq 'X';
$feld[$size][0] = $zahl if $zahl;

# Ecke Rechts unten
$zahl = 0;
$zahl+=1 if $feld[$size][$size-1] eq 'X';
$zahl+=1 if $feld[$size-1][$size-1] eq 'X';
$zahl+=1 if $feld[$size-1][$size] eq 'X';
$feld[$size][$size] = $zahl if $zahl;

# Linke Kante
$zahl = 0;
for ($i=1;$i<$size;$i++) {
  $zahl+=1 if $feld[0][$i-1] eq 'X';
  $zahl+=1 if $feld[0][$i+1] eq 'X';
  $zahl+=1 if $feld[1][$i-1] eq 'X';
  $zahl+=1 if $feld[1][$i] eq 'X';
  $zahl+=1 if $feld[1][$i+1] eq 'X';
  $feld[0][$i] = $zahl if $zahl;
  }

# Rechte Kante
$zahl = 0;
for ($i=1;$i<$size;$i++) {
  $zahl+=1 if $feld[$size][$i-1] eq 'X';
  $zahl+=1 if $feld[$size][$i+1] eq 'X';
  $zahl+=1 if $feld[$size-1][$i-1] eq 'X';
  $zahl+=1 if $feld[$size-1][$i] eq 'X';
  $zahl+=1 if $feld[$size-1][$i+1] eq 'X';
  $feld[$size][$i] = $zahl if $zahl;
  }

# Obere Kante
$zahl = 0;
for ($i=1;$i<$size;$i++) {
  $zahl+=1 if $feld[$i-1][0] eq 'X';
  $zahl+=1 if $feld[$i+1][0] eq 'X';
  $zahl+=1 if $feld[$i-1][1] eq 'X';
  $zahl+=1 if $feld[$i][1] eq 'X';
  $zahl+=1 if $feld[$i+1][1] eq 'X';
  $feld[$i][0] = $zahl if $zahl;
  }

# Untere Kante
$zahl = 0;
for ($i=1;$i<$size;$i++) {
  $zahl+=1 if $feld[$i-1][$size] eq 'X';
  $zahl+=1 if $feld[$i+1][$size] eq 'X';
  $zahl+=1 if $feld[$i-1][$size-1] eq 'X';
  $zahl+=1 if $feld[$i][$size-1] eq 'X';
  $zahl+=1 if $feld[$i+1][$size-1] eq 'X';
  $feld[$i][$size] = $zahl if $zahl;
  }

# Innenfeld
for ($x=1;$x<$size;$x++) {
  for ($y=1;$y<$size;$y++) {
    $zahl = 0;
    if ($feld[$x][$y] ne 'X') {
      $zahl+=1 if $feld[$x-1][$y-1] eq 'X';
      $zahl+=1 if $feld[$x][$y-1] eq 'X';
      $zahl+=1 if $feld[$x+1][$y-1] eq 'X';
      $zahl+=1 if $feld[$x-1][$y] eq 'X';
      $zahl+=1 if $feld[$x+1][$y] eq 'X';
      $zahl+=1 if $feld[$x-1][$y+1] eq 'X';
      $zahl+=1 if $feld[$x][$y+1] eq 'X';
      $zahl+=1 if $feld[$x+1][$y+1] eq 'X';
      $feld[$x][$y] = $zahl if $zahl;
      }
    }
  }

Vielleicht gibt es ja eine einfachere (kürzere) Lösung.
Wie würdet ihr das machen?
 
Erstens, Du kannst Dir das mit dem Rand als Sonderfall sparen. Nimm doch einfach einen blanken Rand an. Im naivsten Falle kann das Feld an (1,1) anfangen anstatt (0,0). Sonst kann man das auch mit Modulo-Operator noch kürzer machen.

Dann, anstatt die langen Kollonnen von einzelnen Zuweisungen zu machen. Mach eine draus. Du weißt doch alle Offsets, die Du brauchst, rundherum um ein Feld. Mach ein Array für alle 8 Möglichkeiten. In etwa so:

Code:
(-1,-1),(0,-1),(1,-1)
(-1,0),(1,0),
(-1,1),(0,1),(1,1)

Das sind die 8 Offsets rundherum um eine Bombe, die Du dann mit einer for-Schleife erledigen kannst.
 
Das mit dem leeren Rand ist mir auch schon in den Sinn gekommen. Scheint wirklich die einfachse Lösung zu sein.
Die 8 Offsets um ein Feld sind auch klar. Aber das mit einer Zuweisung durch ein Array versteh ich jetzt nicht ganz.
 
Wenn Du das mit den 8 Offsets verstanden hast, dann verstehst Du auch, dass man die 8-Array-Elemente pro Feld, das Du gerade berechnest, aufaddieren kann. Von anderen Sachen habe ich nicht gesprochen.

Es geht übrigens noch besser. Du kannst schon beim Bombensetzen, die Score-Werte drumherum inkrementieren. Das "Rand-Prinzip" musst Du aber immer noch beachten, weil das Setzen an den Rand oder Ecke ein Spezialfall sein würde.
 
Hmm..
Ich komm einfach nicht dahinter wie ich mit EINEM Array Element die X und die Y Koordinaten unabhängig voneinander bearbeiten kann außer vielleicht mit Stringoperationen.

Code:
array[0] = (-1, -1)
feld[x][y] = "Bombe"
feld[NordWestlich][von x und y] = feld[x - 1][y - 1]  ->  klar, aber wie mit nur einem Array Element manipulieren.
 
Schau mal wie Arrays unter Perl definiert werden und wie mehrere Dimensionen gehandhabt werden. Das was ich geschrieben habe ist nur symbolisch gemeint. Das sollst Du nicht einfach ohne nachzudenken übernehmen.
 
Dann werd ich am Abend mal das Kamelbuch um Rat fragen :)

Übrigens, die Werte um eine Bombe schon beim setzen aufzuaddieren ist eine hervorragende Idee, da dann nicht jedes Element nochmal bearbeitet werden muß.
Das könnte bei einem 10000x10000 großen Feld ein Weilchen dauern. :ugly:
 
Ich denke, ich erstelle einfach ein Array mit 16 Elementen und frage immer zwei Elemente in einem Durchlauf der Schleife ab...

[EDIT]
Jetzt hab ich es verstanden!
DIe Offsets müssen auch in ein mehrdimensionales Array.
Das kann ich dann mit einer Schleife abarbeiten.

Für alle interessierten, so sieht mein Code zum erzeugen des Spielfelds jetzt aus:
Code:
#!/usr/bin/perl -w
#
# mine.pl
#

my @mines;
my @offset = ([-1,-1], [0,-1], [1,-1],
	      [-1, 0], [1, 0],
	      [-1, 1], [0, 1], [1, 1]);
my $size = (shift @ARGV);
my $x;
my $y;

# Spielfeld initialisieren
for ($x=0;$x<($size+2);$x++) {
  for ($y=0;$y<($size+2);$y++) {
    $mines[$x][$y] = '0';
    }
  }

# Minen auf dem Spielfeld verteilen
my $i=0;
while ($i < $size) {
  $x = int(rand($size))+1;
  $y = int(rand($size))+1;
  if ($mines[$x][$y] ne 'M') {
    $mines[$x][$y] = 'M';

    # Minenanzahl auf die Angrenzenden Elemente setzten
    for (my $j=0;$j<8;$j++) {
      $mines[$x+$offset[$j][0]][$y+$offset[$j][1]] += 1 if ($mines[$x+$offset[$j][0]][$y+$offset[$j][1]]) ne 'M';
      }
    $i++;
    }
  }

out(@mines);

# Spielfeld auf Bildschirm ausgeben
sub out {
my $size = @_ - 2;
print("  ");
for (my $i=0;$i<$size;$i++) {
  print($i+1 . " ");
  }
  print("\n");

for (my $x=1;$x<($size+1);$x++) {
  print($x . " ");
  for (my $y=1;$y<($size+1);$y++) {
    if ($_[$x][$y]) {
      print("$_[$x][$y] ");
      }
    else {print("  ");}
    }
  print("\n");
  }
}

[/EDIT]
 
Zuletzt bearbeitet:
Der Vollständigkeit halber hier eine "spielbare" Version.

Achtung, es gibt keinerlei Komfort wie Marker für die Minen oder einer Überprüfung der Eingabe auf Richtigkeit und dergleichen.

Code:
#!/usr/bin/perl -w
#
# mine.pl
#

my @offset = ([-1,-1], [0,-1], [1,-1],
	      [-1, 0], [1, 0],
	      [-1, 1], [0, 1], [1, 1]);
my $size = (shift @ARGV);
my $x;
my $y;

# Spielfeld initialisieren
initialize(\@mines, 0, $size);
initialize(\@feld, 'X', $size);

# Minen auf dem Spielfeld verteilen
my $i=0;
while ($i < $size) {
  $x = int(rand($size))+1;
  $y = int(rand($size))+1;
  if ($mines[$x][$y] ne 'M') {
    $mines[$x][$y] = 'M';

    # Minenanzahl auf die Angrenzenden Elemente setzten
    for (my $j=0;$j<8;$j++) {
      $mines[$x+$offset[$j][0]][$y+$offset[$j][1]] += 1 if ($mines[$x+$offset[$j][0]][$y+$offset[$j][1]]) ne 'M';
      }
    $i++;
    }
  }

out(@feld);
while (! $game_over) {
  print("\nPosition X: ");
  chomp(my $x = <STDIN>);
  print("Position Y: ");
  chomp(my $y = <STDIN>);

  $feld[$x][$y] = $mines[$x][$y];
  out(@feld);

  # Liegt hier eine Mine?
  if ($mines[$x][$y] eq 'M') {
    $game_over = 1;
    print("\nGame Over\n");
    }
  }

# Spielfeld auf Bildschirm ausgeben
sub out {
my $size = @_ - 2;

system("clear");
print("  ");

for (my $i=0;$i<$size;$i++) {
  print($i+1 . " ");
  }
  print("\n");

for (my $x=1;$x<($size+1);$x++) {
  print($x . " ");
  for (my $y=1;$y<($size+1);$y++) {
    if ($_[$x][$y]) {
      print("$_[$x][$y] ");
      }
    else {print("  ");}
    }
  print("\n");
  }
}

# Zweidimensionales Array mit einem bestimmten Wert initialisieren
# initialize(Arrayreferenz, Initialwert, Feldgröße);
sub initialize {
  my $ref = shift(@_);
  my $item = shift(@_);
  my $size = shift(@_);
  my $x;
  my $y;

  for ($x=0;$x<$size+2;$x++) {
    for ($y=0;$y<$size+2;$y++) {
      $ref->[$x][$y] = $item;
      }
    }
  }
 
Zurück
Oben