C# Vier Gewinnt

Ich habe momentan die Aufgabe (leider relativ eilig, Abgabe ist morgen 23:59) in C# ein „Vier Gewinnt“ zu programmieren.
An sich läuft auch alles rund, nur die Kontrolle, ob jemand gewonnen hat will nicht so recht. funktionieren. Das Spiel läuft weiter obwohl schon längst jemand gewonnen hat.
Das Main Programm ist relativ kurz:

              bool check = false;
              int spieler = 1;
              while (check == false)
              {
                  spiel.Set_stone(spieler);
                  check = spiel.Check();
                  spieler++;
              }```

Die Klasse "Vier_gewinnt" enthält also unter anderem die Methode "Check", die relativ lang ist. im wesentlichen läuft eine Doppelschleife, die das 7x7 Array durchläuft welches das Spielfeld repräsentiert und darin überprüft ob vier Steine nebeneinander liegen. Zunächst (ich hoffe ich vertue mich jetzt nicht) entlang der Zeilen, dann entlang der Spalten, dann die aufsteigenden Diagonalen, dann die Absteigenden Diagonalen.

     ``` public bool Check()
      {
          for (int i = 0; i < 3; i++)
          {
              for (int u = 0; u < 3; u++)
              {
                  if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i, u + 1] && Spielfeld[i, u] == Spielfeld[i, u + 2] && Spielfeld[i, u] == Spielfeld[i, u + 3])
                  {
                      if (Spielfeld[i, u] == 1)
                      {
                          Console.WriteLine("SPIELER 1 hat gewonnen!");
                          return true;
                      }
                      if (Spielfeld[i, u] == 9)
                      {
                          Console.WriteLine("SPIELER 2 hat gewonnen!");
                          return true;
                      }
                  }
                  if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i, u + 1] && Spielfeld[i, u] == Spielfeld[i, u + 2] && Spielfeld[i, u] == Spielfeld[i, u + 3])
                  {
                      if (Spielfeld[i, u] == 1)
                      {
                          Console.WriteLine("SPIELER 1 hat gewonnen!");
                          return true;
                      }
                      if (Spielfeld[i, u] == 9)
                      {
                          Console.WriteLine("SPIELER 2 hat gewonnen!");
                          return true;
                      }
                  }
                  if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i + 1, u + 1] && Spielfeld[i, u] == Spielfeld[i + 2, u + 2] && Spielfeld[i, u] == Spielfeld[i + 3, u + 3])
                  {
                      if (Spielfeld[i, u] == 1)
                      {
                          Console.WriteLine("SPIELER 1 hat gewonnen!");
                          return true;
                      }
                      if (Spielfeld[i, u] == 9)
                      {
                          Console.WriteLine("SPIELER 2 hat gewonnen!");
                          return true;
                      }
                  }
                  if (Spielfeld[i,u+4]!=0 && Spielfeld[i,u+4] == Spielfeld[i+1, u+3] && Spielfeld[i+1,u+3]==Spielfeld[i+2,u+2] && Spielfeld[i + 2, u + 2]== Spielfeld[i + 3, u + 1] && Spielfeld[i + 3, u + 1]== Spielfeld[i + 4, u])
                 {
                      if (Spielfeld[i, u] == 1)
                      { Console.WriteLine("SPIELER 1 hat gewonnen!"); }
                      if (Spielfeld[i, u] == 9)
                      { Console.WriteLine("SPIELER 2 hat gewonnen!"); }
                      return true;
                  }
                  else continue;
              }
          }
          return false;
      }```

Aber irgendwie überprüft die Methode nicht sondern gibt immer "false" zurück. 
Findet hier vielleicht jemand den Fehler?

Mit bestem Dank im Voraus

Sebastian

PS.: wenn mir jemand erklären kann wie ich *.txt oder gar *.cs Dateien hier hoch laden kann, kann ich gerne auch den ganzen Code des Programms zeigen.

hi,

den nicht, aber die ersten beiden Abfragen sind identisch, und die 4. ermittelt nicht den passenden Gewinner.

mit den Schleifen von 0 bis 2 deckt man auch nicht alle Möglichkeiten ab.

bei einem kurzen online Test arbeitet das aber soweit wie erwartet.

grüße
lipi

Oh, danke erst mal für die Antwort.
ja, hoppla, da ist mir ein kleiner Fehler unterlaufen beim zusammen kopieren. hatte zuerst alles extra in einer Doppelschleife und hab dann alle in eine Schleife zusammen kopiert… das muss ich noch ausbessern. danke für den Hinweis.
wieso ermittelt die 4. nicht den richtigen Gewinner?

Die Schleifen gehen ja auch von 0 bis 3 weil dann hoch addiert wird und wenn ich wissen will ob nach einem Element vier identische kommen brauche ich nur bis zum 3. gehen, weil wenn ich von dem aus noch drei hoch gehe komme ich genau bis zu 7. Den Denkfehler hatte ich zu beginn drinnen, dass ich die schleifen von 0 bis 6 laufen habe lassen und hab dann immer einen Error bekommen, weil er damit natürlich über die Array-Grenzen geht.

„Wie erwartet“ bedeutet hier, dass er was gemacht hat? Also hat er die Überprüfung durchgeführt oder hat „wie erwartet“ nichts gemacht, weil ja ein Fehler drinnen ist?

Reicht es nicht, wenn du das Symbol

</>

benutzt?

Für kurze Abschnitte schon, aber das komplette Programm hat 140 Zeilen… das ist glaube ich angenehmer wenn man das als eigene Datei hat.

hi,

weil

if (Spielfeld[i,u+4]!=0 && Spielfeld[i,u+4] == Spielfeld[i+1, u+3] && Spielfeld[i+1,u+3]==Spielfeld[i+2,u+2] && Spielfeld[i + 2, u + 2]== Spielfeld[i + 3, u + 1] && Spielfeld[i + 3, u + 1]== Spielfeld[i + 4, u])

das Feld Spielfeld[i, u] nicht prüft, nach dem aber der Gewinner ermittelt wird.

aber nicht in jeder Abfrage.

 { 
               { 0,0,0,0,0,0,0 },
               { 1,1,1,1,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 } }

Ergebnis: (w, s, d1 und d2 ergänzt. ohne Debugmeldungen nerft sowas)

 w
SPIELER 1 hat gewonnen!
ende

aber bei

{ 
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 0,0,0,0,0,0,0 },
               { 1,1,1,1,0,0,0 } }

kann es logischer weise nicht gehen, da da unten (bei waagerecht) und rechten (bei senkrecht) gar nicht geprüft wird.
Das ergibt nur:

ende

Das Startfeld der Prüfung kann nur in Spielfeld [0bis2,0bis2] liegen. Darüber hinaus geht die Schleife nicht.

Vermutlich sind mehrere Schleifen sinnig oder eine Einschränkung der if-Abfrage, damit man den Bereich nicht verlässt (das dürfte nen Fehler geben, oder?)

Korrekt wäre imo auch i < 4, da du die 3 noch brauchst. Warum du die letzte Diagonale Abfrage +4 nimmst, weiß ich nicht genau. korrekt fände ich auch +3 Da i und u nie -1 werden, fehlt dir sonst eine Diagonale die du niemals prüfst.

aber das ist ja alles ein Fehler in der Schleife selbst. Dir ging es ja um die Funktion der Schleife an sich.

Definiere Spielfeld[] mal unmittelbar davor, oder gib es in Check() aus. Da muss ein Fehler im Restlichen sein.

grüße
lipi

1 Like

super, da arbeite ich mich mal langsam durch, ich glaube ich finde die Hunde im Code langsam. also die Spalten funktionieren schon, keine Ahnung warum, ich hab nichts daran geändert, die Zeilen nicht, verstehe ich auch nicht warum weil der Code ja im Grunde genau so ausschaut…
dass ich die Schleifen für jede Überprüfung anders begrenzen muss ist mir auch gerade aufgefallen, da werde ich wohl tatsächlich für alle Überprüfungen wieder eine eigene Schleife machen.
oh, ja, stimmt, beim weiter lesen merke ich, dass neue Schleifen vermutlich tatsächlich all meine Probleme lösen könnten.
Danke, das war mir mal eine große Hilfe, ich werde das jetzt mal durch arbeiten und dann bescheid sagen ob es funktioniert hat.

3 Like

Ja schon, aber wenn es anders nicht geht … :blush:

So, alles geschafft, es läuft. Danke für die Mithilfe.
Jetzt noch eine Frage zur Kosmetik: wie Schütze ich das Programm gegen Falscheingaben? Meine Idee war in der Methode "Set_stone()" jeweils vor dem Einlesen die variable „spalte“ auf 0 zu setzen und dann
while (spalte < 1 && spalte >7) vor das eigentliche Einlesen zu Setzen, klappt aber nicht, da sprengt das ganze plötzlich die Grenzen des Arrays.
ist nicht zwingend notwendig, steht nicht in der Angabe, aber ich fände es schön wenn das funktionieren würde ohne im Code grob was umzustellen.
ich Schicke jetzt hier noch den gesamten Code:

using System;

namespace vier_gewinnt

{

    class MainClass

    {

        public static void Main(string[] args)

        {

            Vier_gewinnt spiel = new Vier_gewinnt();

            bool check = false;

            int spieler = 1;


            while (check == false)

            {

                Console.Clear();

                spiel.Set_stone(spieler);

                check = spiel.Check();

                spieler++;

            }

        }

    }



    class Vier_gewinnt

    {

        private int[,] Spielfeld = new int[7, 7];

        public Vier_gewinnt() //Konstruktor
        {
        }
    
        public void Set_stone(int spieler)

        {

            int spalte;



            if (spieler % 2 == 1) //überprüfen welcher Spieler an der Reihe ist

            {

                Ausgabe(); //spielfeld anzeigen

                Console.WriteLine("[SPIELER 1] - In welche Spalte (1-7) möchten Sie ihren Stein platzieren?");

                    spalte = Convert.ToInt16(Console.ReadLine()); //spalte einlesen

                for (int i = 6; i >= 0; i--) //an das unterst mögliche Feld einen Stein setzen

                {

                    if (Spielfeld[i, spalte-1] == 0)

                    {

                        Spielfeld[i, spalte-1] = 1;

                        break;

                    }

                }

            }



            if (spieler % 2 == 0) //überprüfen weilcher Spieler an der Reihe ist

            {

                Ausgabe();//Spielfeld anzeigen

                Console.WriteLine("[SPIELER 2] - In welche Spalte (1-7) möchten Sie ihren Stein platzieren?");

                    spalte = Convert.ToInt16(Console.ReadLine()); //Spalte einlesen

                for (int i = 6; i >= 0; i--) //an das unterst mögliche Feld einen Stein setzen

                {

                    if (Spielfeld[i, spalte-1] == 0)

                    {

                        Spielfeld[i, spalte-1] = 9;

                        break;

                    }

                }

            }

        }





        public bool Check() //überprüft ob ein Spieler gewonnen hat

        {

            //überprüfen ob vier steine eines Spielers in einer Reihe liegen

            for (int u = 0; u < 3; u++)

            {

                for (int i = 0; i < 7; i++)

                {

                    if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i, u + 1] && Spielfeld[i, u] == Spielfeld[i, u + 2] && Spielfeld[i, u] == Spielfeld[i, u + 3])

                    {

                        if (Spielfeld[i, u] == 1)

                        {

                            Console.WriteLine("SPIELER 1 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        if (Spielfeld[i, u] == 9)

                        {

                            Console.WriteLine("SPIELER 2 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        else continue;

                    }

                }

            }



            //überprüfen ob vier Steine eines Spielers in einer Spalte liegen

            for (int i = 3; i >= 0; i--)

            {

                for (int u = 0; u < 7; u++)

                {

                    if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i + 1, u] && Spielfeld[i, u] == Spielfeld[i + 2, u] && Spielfeld[i, u] == Spielfeld[i + 3, u])

                    {

                        if (Spielfeld[i, u] == 1)

                        {

                            Console.WriteLine("SPIELER 1 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        if (Spielfeld[i, u] == 9)

                        {

                            Console.WriteLine("SPIELER 2 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        else continue;

                    }

                }

            }



            //überprüfen ob vier Steine eines Spielers diagonal nach unten liegen

            for (int u = 0; u < 3; u++)

            {

                for (int i = 0; i < 3;i++)

                {

                    if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i + 1, u + 1] && Spielfeld[i, u] == Spielfeld[i + 2, u + 2] && Spielfeld[i, u] == Spielfeld[i + 3, u + 3])

                    {

                        if (Spielfeld[i, u] == 1)

                        {

                            Console.WriteLine("SPIELER 1 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        if (Spielfeld[i, u] == 9)

                        {

                            Console.WriteLine("SPIELER 2 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        else continue;

                    }



                }

            }



            //überprüfen ob vier Steine eines Spielers diagonal nach oben liegen

            for (int u = 0; u < 3;u++)

            {

                for (int i = 6; i > 3; i--)

                {

                    if (Spielfeld[i, u] != 0 && Spielfeld[i, u] == Spielfeld[i - 1, u + 1] && Spielfeld[i, u] == Spielfeld[i - 2, u + 2] && Spielfeld[i, u] == Spielfeld[i - 3, u + 3])

                    {

                        if (Spielfeld[i, u] == 1)

                        {

                            Console.WriteLine("SPIELER 1 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        if (Spielfeld[i, u] == 9)

                        {

                            Console.WriteLine("SPIELER 2 hat gewonnen!");

                            Ausgabe();

                            return true;

                        }

                        else continue;

                    }



                }

            }

            return false; //falls nichts davon der Fall ist "false" zurück geben, damit das Spiel weiter geht

        }





        public void Ausgabe() //das Spielfeld anzeigen

        {

            Console.WriteLine("[ 1 2 3 4 5 6 7 ]"); // zur besseren Orientierung die Namen der Spalten in der ersten Zeile anzeigen

            for (int i = 0; i < 7;i++) //das aktuelle Spielfeld ausgeben

            {

                Console.WriteLine("[ {0} {1} {2} {3} {4} {5} {6} ]", Spielfeld[i,0], Spielfeld[i, 1], Spielfeld[i, 2], Spielfeld[i, 3], Spielfeld[i, 4], Spielfeld[i, 5], Spielfeld[i, 6]);

            }

        }

    }

}

Hallo!

Die benutzereingabe wird so lange wiederholt, wie sie kleiner als 1 und (gleichzeitig!) größer als 7 ist.

Wann passiert denn sowas?

Guten Morgen,

hatte vorher keine Zeit zu schreiben, wollte aber auch vorschlagen, && durch || zu ersetzen. :wink:

Gruß
Christa

oh ja, klar… war dann gestern wohl doch schon zu spät für „Mal eben schnell den Code adaptierten“ :smile:
danke.

die Seite sagt ich solle die hilfreichste Antwort wählen, das wäre deine hier, aber da es eine Antwort auf eine Antwort ist kann ich die nicht wählen. fühle dich jedenfalls als hilfreichster Kommentar.
großes DANKESCHÖN jedenfalls nochmal.

1 Like

hi,

danke dir,

schade nur, dass du die Fehler dennoch drin gelassen hast :wink:

image

Aus Sicht des u:
kleiner als 3 ist eben weiterhin 2. und 2+3 ist 5 und nicht 6.

grüße
lipi

oh verdammt, da habe ich mir die Testläufe wohl etwas zu leicht gemacht…
allerdings verstehe ich es nicht, um eben diesen Fehler zu vermeiden habe ich es ja mal probiert mit 4 zu kompilieren und da ist ein Overflow Error gekommen, also war mein Schluss, dass er mit 3 bis ans Ende des Arrays kommt…
naja, jetzt ist es abgegeben, jetzt kann ich es eh nicht mehr ändern, in 45 Minuten ist auch die Frist um und morgen läutet um 6:30 der Wecker…
da muss ich hoffen, dass der Tutor etwas schleißig drüber schaut bei über 20 Programmen die er zum kontrollieren bekommt :smile:

hi,

na jetz können wir ja ausführlicher werden, ist ja abgegeben.

Ich bleibt mal bei der ersten Prüfung, die anderen sind letztlich vom Sinn her identisch.

Deine Schleife prüft

for (int u = 0; u < 3; u++)

also die 0, 1 und 2 - was Spalte 1, 2 und 3 entspricht, Dazu

for (int i = 0; i < 7; i++)

also 0 bis 6 bzw. Reihe 1 bis 7.
image

nun macht deine Prüfung aber folgendes: zu jedem Feld wird geprüft, ob die 3 nachfolgenden die gleichen sind.
image

du erkennst: deine Prüfung kommt nicht da hin. Die Schleife ist zu knapp.

ob nun <= 3 oder <4 kommt aufs gleiche raus. Bei mir gibt’s keinen Fehler dabei. Bei 5 gibts dann den erwarteten Fehler, wenn die Prüfung das Feld außerhalb erreichen würde.

vielleicht hast du beim Spaltentest int i = 3 mit verändert.
Rein vom Aussehen her wäre i >= 3 ohnehin etwas schöner.
Und kleiner gleich bzw. größer gleich 3 geht faktisch immer, sonst ist es mal 4, mal 2… erzeugt nur Fehlerqellen.

grüße
lipi

Hallo,
Abgabe war ja schon, kann ich jetzt auch meinen Senf dazu geben:
Ich weiß nicht, was Du studierst, aber als Programmierer sollte man immer die Darstellung wählen, die ein Problem (oder im besten Fall einige Probleme) mit der kleinsten Komplexität löst. Dann macht man auch keine Fehler.

Eine solche Darstellung ist nicht unbedingt gleichbedeutend mit der, wie Menschen in ihrem Kopf die Sache darstellen würden. Im konkreten Fall gilt die Bitboard-Darstellung von John Tromp als die derzeit effizienteste. Das gesamte Brett kann mit 2 mal 49 bits (je eins für Rot und Weiß) kodiert werden.

Das Brett sieht so aus:

  6 13 20 27 34 41 48
+---------------------+ 
| 5 12 19 26 33 40 47 | oben
| 4 11 18 25 32 39 46 |
| 3 10 17 24 31 38 45 |
| 2  9 16 23 30 37 44 |
| 1  8 15 22 29 36 43 | 
| 0  7 14 21 28 35 42 | unten
+---------------------+

Die Prüfung, sieht dann so aus:

final boolean haswon(long board)
{
        long y = board & (board >> 6);
        if ((y & (y >> 2*6))) // check diagonal \
                return true;
        y = board & (board >> 7);
        if ((y & (y >> 2*7))) // check horizontal -
                return true;
        y = board & (board >> 8); // check diagonal /
        if ((y & (y >> 2*8)))
                return true;
        y = board & (board >> 1); // check vertical |
        return (y & (y >> 2*1)) != 0;
}

Dabei werden alle Positionen auf dem Brett gleichzeitig geprüft, ist also eine O(1) Operation. Außerdem werden nur die Grundrechenoperationen & und >> (and und shift) benutzt, das sollte also insgesamt sehr schnell gehen.

Übrigens ist das Spiel seit 1988 gelöst, es gewinnt immer der Spieler, der zuerst setzen kann.