Hilfe bei 4 gewinnt
-
Hallo,
ich bin hier neu und auch ein Noob in Sachen programmieren, wie der Name schon erschließen lässt.Dennoch würde ich gerne eine Frage stellen, sollte sich jemand erbarmen.
Ich programmiere derzeit ein 4 gewinnt (zweispielervariante) für meinen Programmierkurs.
Nun bin ich soweit, dass mein Programm immerhin schonmal auf EIngabe hin mein Spielfeld füllt.
Nunja, ich will jetzt die Gewinnbedingungen festlegen und komme gerade einfach nicht drauf, wie ich folgendes richtig formulieren soll.Ich will, dass mein Programm am Ende einer groß gefüllten while-Schleife erstmal prüft, ob alle Felder gefüllt sind. Wenn ja, ist die Bedingung erfüllt, die Schleife wird verlassen und das Programm endet vorerst (das funktioniert).
Nur der Zähler funktioniert nicht wirklich.
//für unentschieden int a,b; int vollodernicht = 0; for (a = 0; a < 6; a++) { for (b = 0; b < 7; b++) { if (spielfeld[a][b] == '+' || spielfeld[a][b] == '-') { vollodernicht = vollodernicht +1; } } } if (vollodernicht = 42) { ende = 2;
Also ich will, dass er jedes Element meines Spielfeldes prüft und immer, wenn ein + oder - drin ist (mein 4 gewinnt wird halt mit + und - gefüllt) soll der Zähler um eins erhöht werden. Wenn alle Felder voll sind soll die int "vollodernicht" 42 sein und wenn das so ist soll meine variable ende auf 2 gesetzt werden. Wenn die whileschleife dann nochmal durchlaufen wird soll er sie eben hier verlassen.
Problem ist, dass egal was bei der if-bedingung in den for-schleifen steht, vollodernicht, wird immer auf 42 gesetzt, direkt nach dem ersten Spielzug.
Sollte jemand wirklich helfen wollen, entschuldige ich mich schonmal für ne dumme Frage, unvollständige Angaben und alles andere, was falsch ist.
Bei Bedarf kann ich auch den ganzen Code mit den bisherigen Kommentaren reinstellen. Er ist nur...schlecht^^
-
== statt = im if
-
Danke für die rasche Antwort. Ja, das hat geholfen. Es funktioniert zwar noch immer nicht wie gewollt, da das programm doch nicht endet, wenn alle Felder voll sind^^ aber darüber muss ich nach dem Essen erst nochmal selbst nachdenken, bevor ich nochmal nachfragen sollte.
-
Ist das denn überhaupt nötig?
Du kannst ja schon beim Eingeben mitzählen, der wievielte Zug das ist. Bei 42 ist Schluss.
Oder du überprüfst nur die oberste Reihe auf Vollständigkeit. Und da reicht es aus zu schauen ob das Feld nicht leer ist.
int spiel_ist_voll = 1; //Besser gewählter Name for (b = 0; b < 7; b++) { if (spielfeld[5][b] == ' ') { spiel_ist_voll = 0; break; // schon bei einer leeren Stelle ist das Feld nicht voll } } if (spiel_ist_voll == 1) ende = 2;
-
die idee mit der ersten reihe finde ich gut. in der form gibt er mir aber immer ein unentschieden an, sobald die erste spalte voll ist, unabhängig vom rest. vllt ist aber was anderes vorher auch schon schuld daran^^ danke auch hier für den vorschlag.
ich stelle einfach mal den ganzen code bisher rein. dabei ist es nicht so wichtig, ob er gut ist, umständlich oder aus programmierersicht lächerlich^^ er soll nur funktionieren und ich wollte nicht blind ne lösung aus dem internet nehmen, sondern mein erstes größeres machwerk auch irgendwie selbst hinbekommen, soweit möglich. das heißt nicht, dass ich nicht schonmal nachgeschaut habe. das spielfeld selbst habe ich abgeschaut.
sobald es vollends funktioniert wollte ich es vllt nochmal besser gliedern und evtl irgendwie in funktionen packen. das könnte aber auch wunschdenken sein^^ es reicht schon, wenn es am ende funktioniert^^#include <stdio.h> int main(void) { printf("Willkommen zu Vier gewinnt\n"); printf("Enter druecken um Spiel zu starten\n"); char beliebig; scanf("%c",&beliebig); char spielfeld [6][7]={' '}; //Startbrett leer int i; //vor der for-Schleife definiert, da ich in Codeblocks nicht den richtigen Modus einstellen konnte printf(" 1 2 3 4 5 6 7\n"); printf(" +---+---+---+---+---+---+---+\n"); for( i = 0 ; i < 6; i++) { printf(" | %c | ", spielfeld[i][0]); printf("%c | ", spielfeld[i][1]); printf("%c | ", spielfeld[i][2]); printf("%c | ", spielfeld[i][3]); printf("%c | ", spielfeld[i][4]); printf("%c | ", spielfeld[i][5]); printf("%c | \n", spielfeld[i][6]); printf(" +---+---+---+---+---+---+---+\n"); } int ende; //soll später bei der Gewinnabfrage auf 1 gesetzt werden, wenn z.b. ein Sieger feststeht, um die bald folgende Schleife zu verlassen int zeile = 5; int spielzug = 0; int spalte; spaltevoll: //Programm soll hier her springen und die Spaltenabfrage erneut starten, wenn eine Spalte voll ist. Ich wusste nicht, //wie ich das hier mit einer Schleife hätte machen sollen while(ende !=1 && ende != 2) //die große fast alles umschließende Schleife, die dann verlassen wird, wenn das Spiel ein Ende gefunden hat { spielzug = spielzug +1; // soll folgend für das Programm anzeigen, welcher Spieler dran ist. Ungerade Spielzüge bedeutet Spieler 1, gerade Spieler 2 if (spielzug %2 != 0) { printf("Spieler 1 ist am Zug - Zugeingabe 1 - 7\n"); } if (spielzug %2 ==0) { printf("Spieler 2 ist am Zug - Zugeingabe 1 - 7\n"); } scanf("%i",&spalte); //das erste if soll prüfen, ob die Eingabe überhaupt korrekt war. Gleichzeitig wird hier, wie auch später die int Spielzug immer dann um 1 zurückgesetzt, //so dass das Programm den wieder den richtigen Spieler am Zug sein lässt if (spalte != 1 && spalte != 2 && spalte != 3 && spalte != 4 && spalte != 5 && spalte != 6 && spalte != 7) { printf("Ungueltige Spaltenangabe - erneut Spalte angeben\n"); spielzug = spielzug - 1; } // Die ganzen Einzelfälle wurden getrennt mit if und else aufgezählt, da mir keine günstige Lösung mit einer Schleife einfiel. // Letztlich soll das Programm prüfen, ob ein + oder - in einer zelle steht. Wenn ja wird bei der Spaltenwahl die darüberliegende Zelle gefüllt. // Ist die oberste Zelle voll springt goto zurück zur Spaltenwahl if (spalte == 1 || spalte == 2 || spalte == 3 || spalte == 4 || spalte == 5 || spalte == 6 || spalte == 7) { if(spielzug %2 != 0) //spieler 1 am Zug, wenn die Eingabe korrekt war { if (spielfeld[zeile-5][spalte-1] == '+' || spielfeld[zeile-5][spalte-1] == '-') { printf("Spalte %i voll - neue Spalte waehlen\n", spalte); spielzug = spielzug - 1; goto spaltevoll; } if(spielfeld[zeile][spalte-1] != '+' && spielfeld[zeile][spalte-1] != '-' ) { spielfeld[zeile][spalte-1] = '+'; } else { if(spielfeld[zeile-1][spalte-1] != '+' && spielfeld[zeile-1][spalte-1] != '-' ) { spielfeld[zeile-1][spalte-1] = '+'; } else { if(spielfeld[zeile-2][spalte-1] != '+' && spielfeld[zeile-2][spalte-1] != '-') { spielfeld[zeile-2][spalte-1] = '+'; } else { if(spielfeld[zeile-3][spalte-1] != '+' && spielfeld[zeile-3][spalte-1] != '-' ) { spielfeld[zeile-3][spalte-1] = '+'; } else { if(spielfeld[zeile-4][spalte-1] != '+' && spielfeld[zeile-4][spalte-1] != '-') { spielfeld[zeile-4][spalte-1] = '+'; } else { if(spielfeld[zeile-5][spalte-1] != '+' && spielfeld[zeile-5][spalte-1] != '-') { spielfeld[zeile-5][spalte-1] = '+'; } } } } } } } } //quasi analog für Spieler 2 if (spielzug %2 ==0) { if (spielfeld[zeile-5][spalte-1] == '+' || spielfeld[zeile-5][spalte-1] == '-') { printf("Spalte %i voll - neue Spalte waehlen\n", spalte); spielzug = spielzug - 1; goto spaltevoll; } if(spielfeld[zeile][spalte-1] != '+' && spielfeld[zeile][spalte-1] != '-' ) { spielfeld[zeile][spalte-1] = '-'; } else { if(spielfeld[zeile-1][spalte-1] != '+' && spielfeld[zeile-1][spalte-1] != '-' ) { spielfeld[zeile-1][spalte-1] = '-'; } else { if(spielfeld[zeile-2][spalte-1] != '+' && spielfeld[zeile-2][spalte-1] != '-') { spielfeld[zeile-2][spalte-1] = '-'; } else { if(spielfeld[zeile-3][spalte-1]!= '+' && spielfeld[zeile-3][spalte-1] != '-' ) { spielfeld[zeile-3][spalte-1] = '-'; } else { if(spielfeld[zeile-4][spalte-1] != '+' && spielfeld[zeile-4][spalte-1] != '-') { spielfeld[zeile-4][spalte-1] = '-'; } else { if(spielfeld[zeile-5][spalte-1] != '+' && spielfeld[zeile-5][spalte-1] != '-') { spielfeld[zeile-5][spalte-1] = '-'; } } } } } } } // Ausgabe des Brettes, jetzt mit den geänderten Inhalten für die jeweiligen Elemente des Arrays printf(" 1 2 3 4 5 6 7\n"); printf(" +---+---+---+---+---+---+---+\n"); for( i = 0; i < 6; i++) { printf(" | %c | ", spielfeld[i][0]); printf("%c | ", spielfeld[i][1]); printf("%c | ", spielfeld[i][2]); printf("%c | ", spielfeld[i][3]); printf("%c | ", spielfeld[i][4]); printf("%c | ", spielfeld[i][5]); printf("%c | \n", spielfeld[i][6]); printf(" +---+---+---+---+---+---+---+\n"); } //für unentschieden int b; int spiel_ist_voll = 1; for (b = 0; b < 7; b++) { if (spielfeld[0][b] == ' ') { spiel_ist_voll = 0; break; } } if (spiel_ist_voll == 1) ende = 2; } //Klammer zu für while if (ende = 2) { printf("Unentschieden\n"); } } //void klammer zu
-
programmiernoob schrieb:
sobald es vollends funktioniert wollte ich es vllt nochmal besser gliedern und evtl irgendwie in funktionen packen.
Mach das sofort.
Dann bekommst du es einfacher zum funktionieren.Du brauchst dann nur einmal die Ausgabe.
Du brauchst dann nur einmal die Überprüfung für den Zug. Die Funktion bekommt als Paramter die Spielernummer oder das Symbol.while(ende !=1 && ende != 2)
wird zu
while( ende== 0)
if (spielzug %2 != 0) { printf("Spieler 1 ist am Zug - Zugeingabe 1 - 7\n");
Wird zu
printf("Spieler %d ist am Zug - Zugeingabe 1 - 7\n", 2 - (spielzug % 2));
if (spalte != 1 && spalte != 2 && spalte != 3 && spalte != 4 && spalte != 5 && spalte != 6 && spalte != 7)
Wird zu
if (spalte < 1 || spalte > 7)
-
das glaube ich gerne. es ist nur schwer für mich zu realisieren, da ich überhaupt keine ahnung habe, auf was ich hinaus will und wie es auszusehen hat.
ich muss nachmittags mal schauen, ob ich das hinbekomme. ich kämpfe ja nicht nur mit der gestaltung und logik, sondern auch mit der funktionsweise der einzelnen werkzeuge, die c mir bereit stellt^^
danke auch für die verbesserungsvorschläge. das wirkt deutlich besser formuliert.
-
Und statt der if-else Kaskaden solltest du eine einfache for-Schleife benutzen (und diese dann in eine eigene Funktion auslagern).
Funktionen helfen dir dabei Codeduplizierungen und Fehler zu vermeiden.
Am besten schreib ersteinmal deine Hauptfunktion main so, daß sie nur aus Funktionsaufrufen besteht, im Sinne von:int main() { menu(); while (!is_gameover()) { do_move_for_playerX(); print_board(); } print_winner(); }
Dann kannst du nach und nach die Funktionen implementieren und die richtigen Funktionsparameter einsetzen.
-
die kaskaden habe ich dann genommen, als ich mit for schleifen nicht auf das richtige gekommen bin. mir tut es selbst schon weh, wenn ich es sehe, da ich weiß, dass es einfacher und besser geht, ich aber momentan keine bessere lösung hinklotzen kann.
was für mich persönlich wichtiger ist, wäre die unentschieden auswertung. ich komme auf kein gutes ergebnis. ich hatte ja meinen ansatz mit den spielfeldern auszählen, die gefüllt sind, welcher entweder gar nicht oder sofort beim ersten zug zum unentschieden geführt hat.
zudem die variante vom dirk.
das führt, bei mir zumindest, dazu, dass das spiel immer dann endet, wenn das oberste feld der ersten spalte gefüllt ist. also spielfeld [0][0].ich wollte es jetzt noch umformulieren, in der hoffnung, dass es vllt anders funktioniert und ich es danach schöner formulieren könnte:
if(spielfeld[0][0] != ' ' && spielfeld[0][1] != ' ' &&spielfeld[0][2] != ' ' &&spielfeld[0][3] != ' ' &&spielfeld[0][4] != ' ' &&spielfeld[0][5] != ' ' &&spielfeld[0][6] != ' ') { ende = 2; } }
also wenn jedes feld der obersten zeile ungleich dem ursprünglich eingefügtem leerzeichen ist. führt aber auch nur dazu, dass es ein unentschieden genau dann ausgibt, wenn die erste spalte voll ist.
woran könnte das liegen?
-
Hast du denn den Fehler bei
[code"=c"]
if (ende = 2)
[/code]
korrigiert (s. Antwort von volkard)? Am besten die Warnstufe deines Compilers auf Maximum stellen - und die Warnungen auch beachten und korrigierenUnd weil ich gerne helfe guten Code zu erzeugen:
for (i = 0; i < 6; i++) if(spielfeld[zeile - i][spalte-1] != '+' && spielfeld[zeile - i][spalte-1] != '-' ) { spielfeld[zeile - i][spalte-1] = '+'; break; // Schleife beenden }
(kann man noch weiter verschönern, aber so solltest du es wohl verstehen!)
-
ja, = wurde durch == im if ersetzt.
11|warning: missing braces around initializer [-Wmissing-braces]|
11|warning: (near initialization for 'spielfeld[0]') [-Wmissing-braces]|
201|warning: control reaches end of non-void function [-Wreturn-type]|
||=== Build finished: 0 error(s), 3 warning(s) (0 minute(s), 0 second(s)) ===|das ist zeile 11
char spielfeld [6][7]={' '};
die for-schleife verstehe ich, ja. abermals danke dafür. ich werde sie aber nicht einbauen. solange ich in irgendeiner form eine eigene lösung erarbeitet habe, will ich die auch nehmen, solange sie funktioniert. ich fühl mich nicht wohl dabei fremde sachen einfach einzubauen und abzugeben. ich werde mir das aber auch alles speichern und für mich selbst ansehen und durchdenken, wie man es besser hätte machen können.
nur dieses unentschieden macht mich fertig
bevor ich dann zum letzten teil des programms komme, wenn es darum einen sieg festzustellen.
-
hmm...den fehler beim unentschieden finde ich immer noch nicht.
dafür funktionert scheinbar die restliche gewinnauswertung mit horizontalen, vertikalen und diagonalen (zwar umständlich, weil er bei jedem durchlauf jedes feld kontrolliert, aber immerhin selbst gebastelt).dafür gibt es zum abschluss noch ein kleines problem
char auswahl; if (ende != 0) { while (auswahl != 'q' && auswahl != 'n') { printf("Neues Spiel (n) - Spiel verlassen (q)\n"); scanf("%c", &auswahl); if (auswahl == 'n') { goto anfang; } if (auswahl == 'q') { } } }
n und q nimmt er auch an und es passiert, was passieren soll, aber der text vom printf wird zweimal ausgegeben. woran liegt das? soll ja nur ein kleiner abschlusstext sein, wenn ein ende erreich wurde so dass das programm nicht gleich schließt.
kann abschließend noch jemand dazu was sagen? dann will ich euch nicht länger belästigen^^ ich habe viele gute und hilfreiche antworten und ansätze bekommen. das mit dem unentschieden werde ich dann schon noch selbst rausbekommen^^
-
Schreib mal ein Leerzeichen vor den Formatbefehl bei scanf:
scanf(" %c", &auswahl);
Dadurch werden sog. Whitespaces (Linefeed, Carriage Return, Tabs und Leerzeichen) überlesen (da wahrscheinlich noch bei Druck auf Return/Enter dieses Zeichen im Tastaturpuffer drin ist - welches du sonst zuerst einliest und dann erst im zweiten Durchgang deiner Schleife auf deine weitere Eingabe gewartet wird).
Es freut mich, daß du selber die Probleme angehst. Falls du doch nicht weiter mit dem Unentschieden kommst (und bevor es zum Elfmeterschießen kommt ;-), kannst du ja deinen aktuellen Code dazu posten.
-
stimmt. das hatten wir auch mal in einer übung gelernt. glaube ich. es kommt mir zumindest bekannt vor. außerhalb einer schleifen hatten wir mal, in einer der ersten übungen, auch ein zweites scanf darunter geschrieben, da das erste von einem "return" gefressen wurde, welches noch in der eingabe stand. ich habe nur so viele sachen, die wir gelernt haben auch schon wieder vergessen^^ (der kurs ist für studienbegleitende IT-ausbildung. ich lerne das also mehr aus eigenintesse und nicht, weil ich das dauerhaft brauchen werde.)
freitag ist abgabe *hust
also werde ich heut abend und morgen nochmal schauen, ob ich was rausfinde bzgl. unentschieden und im zweifelsfall frage ich nachmittags/abends nochmal. wenn jemand da ist, ist es gut...wenn nicht dann halt nicht^^
-
programmiernoob schrieb:
stimmt. das hatten wir auch mal in einer übung gelernt.
Das wäre schon sensationell.
programmiernoob schrieb:
außerhalb einer schleifen hatten wir mal, in einer der ersten übungen, auch ein zweites scanf darunter geschrieben, da das erste von einem "return" gefressen wurde, welches noch in der eingabe stand
Das relativiert die Sache schon wieder.
Denn die wenigsten wissen, was ein Leerzeichen im Formatstring vonscanf
bedeutet.Aber nochmal zu deinem Problem mit der auswahl.
Das wäre etwas für eine do-while-Schleife (über fast alles).int main() { do // (nochmal) { menu(); while (!is_gameover()) { do_move_for_playerX(); print_board(); } print_winner(); nochmal abfragen } while (nochmal); }
und schon brauchst du auch kein
goto
mehr.
Und du brauchst auch nur auf 'n' prüfen.
-
also das mit dem leerzeichen im scanf hat er auch gezeigt, war aber nicht direkt im skript. also ich hätte es kennen können. mein dozent kennt das sehr sicher^^ das zweite scanf war nur die anfangsvariante, um uns nicht gleich zu überfordern^^
ja, die gotos sind nicht schön. morgen mache ich mich daran das ganze in funktionen zu packen. da das programm jetzt in der form funktioniert, werde ich mich darum kümmern dinge zusammen zufassen, wo möglich. beim nächsten mal habe ich dann vllt genug ahnung es von vornherein so zu machen
ich schau mir das morgen nochmal genau an. ein freund von mir hatte einen sehr guten einfall wegen dem unentschiedeneinfach ende = 3 setzen, wenn der spielzugzähler bei 42 ist. das ganze mit if. das spart alle rätselerei und ist relativ narrensicher.
an do while dachte ich auch mal. ich hab in erinnerung, dass bei do while und while die art der abfrage der bedingung anders ist. also beim einen wird am anfang geprüft, beim anderen am ende. vllt meine ich gerade spontan aber auch was anderes. ich muss mir das morgen nochmal anschauen.
danke für deine mühe.
-
oje..mir fällt gerade auf, dass das e schon ein vorschlag vom dirk war. das habe ich damals fehlinterpretiert. das tut mir leid
(ich kann jetzt nur nicht editieren - darum der spam)