4-Gewinnt Problem



  • grüßt euch,

    ich bin gerade dabei das spiel 4-gewinnt zu programmieren.

    Mein Spieler soll nur die Spalte auswählen können in die der Spielchip geworfen wird. Wenn mein Spieler nun die Zahl 2 eingibt, dass soll GANZ UNTEN(zeile5) ein Zeichen X angezeigt wird.

    Aber wie verwirkliche ich das?

    int main()
    {
    	char feld[5][7] = { { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 } };
    
    	int i = 0;
    	int s = 0;
    
    	for (int i = 0; i < 5; i++){
    
    		for (int k = 0; k < 7; k++){
    			printf("%c", feld[i][k]);
    		}
    		printf("\n");
    	}
    
    	printf("Spieler1: In welche Spalte wollen Sie ihren Spielchip setzen?\n");
    	scanf("%d", &s);
    
    	for (int i = 0; i < 5; i++){
    
    		for (int k = 0; k < 7; k++){
    
    			printf("%c", feld[i][k]);
    		}
    		printf("\n");
    	}
    
    	system("pause");
    	return 0;
    }
    

    Die nächste Schwierigkeit ist dann, dass der Spielchip nicht ganz durchfällt, wenn z.b. in spalte 2 ganz unten schon ein chip liegt.

    Mfg

    Dulfried



  • Tja, wo fängt man da an?

    Erstens: Das Kompilieren.

    test.c: In Funktion »main«:
    test.c:14:11: Fehler: Redefinition von »i«
    test.c:11:6: Anmerkung: Vorherige Definition von »i« war hier
    test.c:14:2: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    test.c:14:2: Anmerkung: -std=c99 oder -std=gnu99 verwenden, um den Code zu übersetzen
    test.c:16:3: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    test.c:18:4: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »printf« [standardmäßig aktiviert]
    test.c:20:3: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »printf« [standardmäßig aktiviert]
    test.c:23:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »printf« [standardmäßig aktiviert]
    test.c:24:2: Warnung: Unverträgliche implizite Deklaration der eingebauten Funktion »scanf« [standardmäßig aktiviert]
    test.c:26:11: Fehler: Redefinition von »i«
    test.c:14:11: Anmerkung: Vorherige Definition von »i« war hier
    test.c:26:2: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    test.c:28:3: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    

    Binde mal stdio.h ein und werd die Deklarationen in den Schleifenköpfen los.

    Zweitens: der Code.

    Ich mein, wo SETZT du denn den Wert? Das tust du da gar nicht. Im Moment gibst du nur zweimal aus und dazwischen liest du eine Benutzereingabe.
    Danach durchgehen ist schon mal richtig - aber von unten, und dann jedes Mal prüfen, ob das Feld nicht schon besetzt ist. Und wenn alle Felder in der Spalte besetzt sind, musst du das auch kommunizieren.

    Drittens: system
    OK: ich will jetzt wissen, wer diesen Unsinn immer noch angibt. Wo ist die Furcht vor getchar begründet?



  • Dulfried schrieb:

    Aber wie verwirkliche ich das?

    Du suchst in der entsprechenden Spalte von oben nach unten nach dem letzten unbesetzen Feld.

    Vor allem benutzt du Funktionen dafür.

    Ebenso für die Ausgabe des Feldes.
    Du trennst die interne Information von der Darstellung. Ein unbesetzes Feld ist dann z.B 0
    Der eine Spieler kann durch +1, der andere durch -1 oder auch 1 und 2 gekennzeichnet sein.

    Bei der Ausgabe (die wird dann etwas aufwendiger) erzeugst du dann die Spielsteine.

    Du machst viele Funktionen, die nur spezielle Aufgaben haben. Z.B.
    Eingabe mit Bereichsüberprüfung
    Stein setzen mit Fehlerüberprüfung
    prüfen auf Sieg/Unentschieden
    Feld zeichnen.
    ....



  • dachschaden schrieb:

    Drittens: system
    OK: ich will jetzt wissen, wer diesen Unsinn immer noch angibt. Wo ist die Furcht vor getchar begründet?

    Das es ( getchar ) das '\n' von der letzten scanf-Aktion noch einliest und dann das Programm sofort beendet.
    So wie in diesem Programm.

    Warum startet man das Programm nicht in der Konsole oder benutzt die IDE richtig 😕



  • printf("Spieler1: In welche Spalte wollen Sie ihren Spielchip setzen?\n");
    	scanf("%d", &s);
    
    	//system("cls");
    
    	for (int i = 0; i < 5; i++){
    
    		for (int k = 0; k < 7; k++){
    			if (k == s && i==4){
    				printf("%c", 'O');
    
    			}
    			else {
    				printf("%c", feld[i][k]);
    			}
    		}
    		printf("\n");
    	}
    

    @dachschaden Habe das mit dem falsch von unten ausgeben irgendwie nicht hinbekommen.

    Jetzt gibt es mir wenigstens mal das Zeichen richtig aus.

    Jetzt muss ich halt nur noch hinbekommen, dass ich nicht nur in der untersten Zeile zeichen ausgebe.



  • dachschaden schrieb:

    Tja, wo fängt man da an?

    Erstens: Das Kompilieren.

    test.c: In Funktion »main«:
    test.c:14:11: Fehler: Redefinition von »i«
    test.c:11:6: Anmerkung: Vorherige Definition von »i« war hier
    test.c:14:2: Fehler: Anfangsdeklarationen in »for«-Schleifen sind nur im C99-Modus erlaubt
    [...]
    

    [...] werd die Deklarationen in den Schleifenköpfen los.

    Der Name dieses Unterforums ist "C (alle ISO-Standards)"... 🙄



  • Dulfried schrieb:

    Jetzt gibt es mir wenigstens mal das Zeichen richtig aus. .

    Das nützt dir aber nichts, da du es in deinem Spielfeld nicht vermerkst.

    Dulfried schrieb:

    Jetzt muss ich halt nur noch hinbekommen, dass ich nicht nur in der untersten Zeile zeichen ausgebe.

    Das sollte dein letztes Problem sein.

    Trenne Berechnung von der Darstellung und der Interaktion.

    Wenn dein Programm richtig rechnet, kannst du immer noch die tollste Ausgabe machen die geht.
    Wenn dein Programm nicht richtig rechnet, ist die Ausgabe völlig egal.

    Mit der C-Standardbibliothek geht es nicht. Du kannst aber mal nach der improved console suchen. Ist sogar aus dem Forum.

    ~Vier gewinnt über WhatsApp geht auch. Alles Handarbeit, aber macht auch mal Spaß.~



  • @MatzeHHC: Hast recht. Ich bin blöd. 😞

    DirkB schrieb:

    Das es ( getchar ) das '\n' von der letzten scanf-Aktion noch einliest und dann das Programm sofort beendet.
    So wie in diesem Programm.

    Als ob es in diesem Forum nicht bereits genug Ansätze geben würde (die auch noch portabel sind). Oder man macht halt zweimal getchar . Aber doch nicht immer system . 😡

    DirkB schrieb:

    Warum startet man das Programm nicht in der Konsole oder benutzt die IDE richtig 😕

    Kennen die meisten wahrscheinlich nicht einmal.
    Ich gebe allerdings auch zu, bevor ich auf Linux gewechselt bin, habe ich die Konsole auch eher misstrauisch beäugt.

    @Dulfried

    #define ROWS    (5)
    #define COLUMNS (7)
    #define EMPTY   ' '
    #define SET_1   'x'     /*Spieler 1*/
    #define SET_2   'o'     /*Spieler 2*/
    
    int drop_chip
    (
            char tiles[ROWS][COLUMNS],
            const size_t column
    )
    {
            size_t xrow=ROWS;
            while(((xrow--)+1))
            {
                    if(tiles[xrow][column]!=EMPTY)
                            continue;
    
                    tiles[xrow][column]=SET_1;
    
                    /*Konnte setzen.*/
                    return 1;
            }
    
            /*Konnte nicht setzen.*/
            return 0;
    }
    

    Relativ einfach. Von unten anfangen, wenn schon gesetzt, dann weitermachen. Wenn's nicht geht, 0 zurückgeben. Wenn's doch geklappt hat, 1 zurückgeben. Damit weiß der Aufrufer, ob's auch geklappt hat.



  • ok und wie ich definiere ich dann ein einzelnes feld meines arrays?

    feld[5][s] = 'X' ;
    

    Das funktioniert nämlich auch nicht, kann mir das array danach nicht mehr ausgeben lassen.



  • [quote="Dulfried"..., kann mir das array danach nicht mehr ausgeben lassen.[/quote]Dann wird da (bei der Ausgabe) etwas falsch sein oder s liegt außerhalb vom erlaubten Bereich.



  • ja klar, da k von 0 hochzählt darf ich maximal eine 4 nehmen



  • Neues Problem 😡

    Habe versucht, dass er erkennt ob die unterste zeile schon belegt ist und gegebenenfalls eine zeile weiter nach oben springt (f--).

    Funktioniert aber nicht, wenn ich mit dem cursor über das feld[f][s] gehe, dann zeigt er mit nach der while-schleife eine speicheradresse an. Ich glaube hier liegt das Problem

    for (i = 0; i < 5; i++){
    		printf("Spieler2: In welche Spalte wollen Sie ihren Spielchip setzen?\n");
    		scanf("%d", &s);
    
    		system("cls");
    
    		int f = 4;
    
    		while (feld[f][s] != 32){
    			f--;
    		}
    		feld[f][s] == 'X';
    
    		for (int i = 0; i < 5; i++){
    
    			for (int k = 0; k < 7; k++){
    				printf("%c", feld[i][k]);
    			}
    			printf("\n");
    		}
    	}
    


  • Das Problem ist, was du in Zeile 32 geschrieben hast.

    Was soll das passieren?
    Was steht da und passiert wirklich.



  • meinst du weil ich da feld mit [i] ausgebe und vorher mit [f] angegeben hab?

    ich weiß dass ich selber drauf kommen soll, aber stehe echt auf dem Schlauch.
    Wäre dir dankbar wenn du meinen letzten code-abschnitt verbessern könntest.



  • Sorry, ich meinte Zeile 12 :

    feld[f][s] == 'X';
    

    Da ist der noch Fehler drin.

    ~(vielleicht hat mich die 32 in Zeile 9 verwirrt)~



  • Dulfried schrieb:

    Wäre dir dankbar wenn du meinen letzten code-abschnitt verbessern könntest.

    Nee, Jung. DirkB hat dir bereits geschrieben, dass du für so was kleine Funktionen schreiben sollst, die Teilaufgaben übernehmen. Eine Funktion, um den gegenwärtigen Status anzuzeigen, eine Funktion, um einen Punkt in eine Spalte zu legen (den Code hast du sogar großzügigerweise von mir bekommen). Eine Möglichkeit, um dir das Spielfeld - sogar mit Rahmen! - anzeigen zu lassen bekommst du locker in 26 Zeilen Code hin. Vielleicht ein bisschen mehr, wenn du direkt ein eindimensionales Array verwendest und mehr mit Parametern arbeitest, die Länge und Breite des gegenwärtigen Feldes mit übergeben.

    Waurm verwendest du 32 statt einfach das Leerzeichen? Warum verwendest du nicht direkt memset ?

    #define EMPTY ' '
    const size_t rows=5;
    const size_t columns=7;
    char tiles[rows*columns];
    memset(&tiles,EMPTY,sizeof(tiles));
    

    Die Speicheradresse, die du vermutlich im Debugger gesehen hast, war ein Wert, auf den du gar nicht zugreifen solltest. Das ist so der erste Schubser in Richtung "prüft mal auf Zugriffe, die über den reservierten Speicher hinausgehen."

    Prüfst du, ob der Wert in s in den erlaubten Grenzen liegt? Erste Sache, die man macht. Und == ist falsch, du möchtest zuweisen, nicht vergleichen.

    Noch eine Sache: Unter *nix-Systemen kann man einfach:

    puts("\ec");
    

    machen, und dann wird der Bildschirm geleert. Ich glaube nicht, dass das unter Windows möglich ist, aber ausprobieren geht über studieren.

    Auch, warum man system vermeiden sollte.



  • dachschaden schrieb:

    ... und dann wird der Bildschirm gelehrt. Ich glaube nicht, dass das unter Windows möglich ist, aber ausprobieren geht über studieren.

    Nee, unter Windows muss man den Bildschirm schon selber lernen.
    Es gibt unter Windows aber jede Menge Funktionen für die Konsole, mit deren Hilfe man sich kleine Hilfsfunktionen schreiben kann, die zB. die Konsole leeren, die Konsole in der Größe verändern, einzelne Zeichen direkt an der gewünschten Position ausgeben - so dass für so ein Spiel zB. gar nicht bei jeder Ausgabe die gesamte Konsole geleert werden muss, Zeichenpositionen eine andere Farbe geben, und und und ...
    Einfach mal im etwas weiter oben angesiedelten Konsoleforum stöbern. Das ist zwar für neue Beiträge geschlossen, aber für das Hantieren mit der Konsole findet man da jede Menge nützliche Codefragmente.

    Oder, hier studieren und dann selbst machen:
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms682073(v=vs.85).aspx



  • @DirkB

    danke für deinen hinweis, jetzt funktioniert wenigstens mal mein grundprinzip, welches ich noch erweitern werde.



  • nochmal eine kurze Frage:

    Also mein programm lässt den spieler nun eine spalte auswählen, in welche der
    chip bis zum letzten freien Platz durchrutsch.
    Das Problem ist jetzt, dass die Gewinnabfrage horizontal nicht funktionier, ich den Fehler aber einfach nicht finde. (Die Abfrage vertikal funktioniert).

    Mein Programm geht in die schleifen einfach nicht rein, ich hoffe ihr seht den
    Fehler

    char feld[5][7] = { { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 }, { 32, 32, 32, 32, 32, 32, 32 } };
    
    	int z=0;
    	int s = 0;
    	int gewonnen1 = 0, gewonnen2=0;
    	int reihe = 1;
    	int k = 0;
    	int i = 0;
    
    	for (int i = 0; i < 5; i++){
    
    		for (int k = 0; k < 7; k++){
    			printf("%c", feld[i][k]);
    		}
    		printf("\n");
    	}
    
    	while (gewonnen1 != 1 && gewonnen2 !=1){
    
    		if (reihe == 1){
    			printf("Spieler1: In welche Spalte wollen Sie ihren Spielchip setzen?\n");
    			scanf("%d", &s);
    
    			system("cls");
    
    			int f = 4;
    
    			while (feld[f][s] != 32){
    				f--;
    			}
    			feld[f][s] = 'X';
    
    			for (int i = 0; i < 5; i++){
    
    				for (int k = 0; k < 7; k++){
    					printf("%c", feld[i][k]);
    				}
    				printf("\n");
    				reihe = 2;
    			}
    
    			//Gewinnabfrage vertikal
    			if (i<2 && i >=0 && k < 7 && k>=0){
    				if (feld[i][k] == 'X'  && feld[i + 1][k] == 'X' && feld[i + 2][k] == 'X' && feld[i + 3][k] == 'X'){
    					gewonnen1 = 1;
    				}
    				if (feld[i+1][k] == 'X'  && feld[i + 2][k] == 'X' && feld[i + 3][k] == 'X' && feld[i + 4][k] == 'X'){
    					gewonnen1 = 1;
    				}
    			}
    
    			//Gewinnabfrage horizontal
    
    			if (i<=4 && i >= 0 && k <= 3 && k >= 0){
    				if (feld[i][k] == 'X'  && feld[i][k + 1] == 'X' && feld[i][k + 2] == 'X' && feld[i][k + 3] == 'X'){
    					gewonnen1 = 1;
    				}
    				if (feld[i][k + 1] == 'X'  && feld[i][k + 2] == 'X' && feld[i][k + 3] == 'X' && feld[i][k + 4] == 'X'){
    					gewonnen1 = 1;
    				}
    				if (feld[i][k + 2] == 'X'  && feld[i][k + 3] == 'X' && feld[i][k + 4] == 'X' && feld[i][k + 5] == 'X'){
    					gewonnen1 = 1;
    				}
    				if (feld[i][k + 3] == 'X'  && feld[i][k + 4] == 'X' && feld[i][k + 5] == 'X' && feld[i][k + 6] == 'X'){
    					gewonnen1 = 1;
    				}
    			}
    


  • Dann überlege mal welchen Wert i und k nach der for-Schleife haben? Du möchtest wohl eher f und s abfragen.

    PS: Ich kenne "4 gewinnt" eher mit einem 6*7 Feld. Du solltest statt der Literale daher besser Konstanten (d.h. Defines) verwenden, um das flexibler zu gestalten.


Anmelden zum Antworten