Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

27.03.2012, 21:03

Problem beim Programmieren eines Tic Tac Toe - Spiels

Hallo zusammen,

ich programmiere zurzeit ein kleines Tic Tac To - Spiel mit Anwendung von Klassen.
Das Programm macht das was es soll. Lediglich bei der Ermittlung des Gewinners gibt es einige Probleme.
Ich denke, dass es nicht an der Funktion selbst liegt, denn bei der habe ich mir viel Mühe gegeben :D .

Hoffe ihr könnte mir weiterhelfen

--------------------------------

Der Quellcode:

main.cpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// TicTacToe_mit_Klassen
// main.cpp
//
#include <iostream>
#include "Game.hpp"
#include "Player.hpp"

using namespace std;

int CPlayer::m_PlayerTurn;

// Hauptprogramm
//
int main ()
{
    // Instanzen
    //
    CGame Game;         
    CPlayer Player;
    CPlayer Player1;    
    CPlayer Player2;    

    // Variablen
    //
    int SelectedField = 0;
    CPlayer::m_PlayerTurn = 1;

    Game.DrawFields();

    do
    {   
        do
        {
            // Wert vom Spieler entgegennehmen 
            //
            cout << "Spieler " << CPlayer::m_PlayerTurn << " ist an der Reihe" << endl;
            cout << "Welches Feld soll besetzt werden: ";
            cin >> SelectedField;

        } while(Game.bCheckForBusyFields(SelectedField) == true);   // Prüfen, ob das Feld schon besetzt ist; true heißt: Ja, das Feld ist besetzt

        // Spielfeld aktualisieren und zeichnen
        //
        Game.InitFields(SelectedField, CPlayer::m_PlayerTurn);
        Game.DrawFields();

        // Prüfen, ob jemand gewonnen hat
        //
        Game.CheckForWinner(CPlayer::m_PlayerTurn);

        // Prüfen, welcher Spieler am Zug ist und Ergebnis speichern
        //
        CPlayer::m_PlayerTurn = Game.CheckForPlayerTurn(CPlayer::m_PlayerTurn);

    } while(Game.bGameOver() == false);

    cout << "Ende" << endl;

    system("pause>nul");

    return 0;
}


Player.hpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
// TicTacToe_mit_Klassen
// Player.hpp
//
class CPlayer
{

public:

    static int m_PlayerTurn;
};


Game.hpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// TicTacToe_mit_Klassen
// Game.hpp
//
class CGame
{

private:

    char m_chField[9];
    int m_Winner;
    bool m_bGameOver;

public:

    CGame();
    void InitFields(int SelectedField, int PlayerTurn); // Initialisiert die einzelnen Spielfelder mit den übergebenen Werten
    void DrawFields();                                  // Zeichnet das Spielfeld
    bool bCheckForBusyFields(int SelectedField);        // Prüft, ob ein übergebenes Feld bereits besetzt ist
    int CheckForPlayerTurn(int PlayerTurn);             // Prüft, welcher Spieler dran ist
    bool bGameOver();                                   // Prüft, ob das Spiel zuende ist
    void CheckForWinner(static int PlayerTurn);         // Ermittlet den Gewinner
    void ShowWinner();                                  // Gibt den Gewinner anschließend auf dem Bildschirm aus

};


Game.cpp:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// TicTacToe_mit_Klassen
// Game.cpp
//
#include <iostream>
#include "Game.hpp"
#include "Player.hpp"

using namespace std;

CGame::CGame()
{
    m_chField[0] = '1';
    m_chField[1] = '2';
    m_chField[2] = '3';
    m_chField[3] = '4';
    m_chField[4] = '5';
    m_chField[5] = '6';
    m_chField[6] = '7';
    m_chField[7] = '8';
    m_chField[8] = '9';

} // CGame

// DrawField
//
// Aufgabe: Spielfeld zeichnen
//
void CGame::DrawFields()
{
    system("cls");

    for (int i=0; i<9; i++)
    {
        cout << m_chField[i];
        cout << " ";

        if (i==2 || i==5)
        {
            cout << endl;
        }

        if (i==2)
        {
            cout << " " << endl;
        }

        if (i==5)
        {
            cout << " " << endl;
        }
    }

    cout << endl << endl;

} // DrawField

void CGame::InitFields(int SelectedField, int PlayerTurn)
{
    for (int i=0; i<9; i++)
    {
        if (i == SelectedField-1)
        {
            if (PlayerTurn == 1)
            {
                m_chField[i] = 'X';
            }
            else if (PlayerTurn == 2)
            {
                m_chField[i] = 'O';
            }

        }
    }

}

bool CGame::bCheckForBusyFields(int SelectedField)
{
    if(m_chField[SelectedField-1] == 'X' || m_chField[SelectedField-1] == 'O')
    {
        cout << "-------------------------------------------------------" << endl;
        cout << "Feld ist bereits besetzt, bitte anderes Feld aussuchen!" << endl;
        cout << "-------------------------------------------------------" << endl;

        // Ausgewähltes Feld ist besetzt, also "true" zurücksenden
        //
        return true;
    }

} // CheckForBusyFields

int CGame::CheckForPlayerTurn(int PlayerTurn)
{
    if (PlayerTurn == 1)
    {
        PlayerTurn++;
        return PlayerTurn;
    }
    else if(PlayerTurn == 2)
    {
        PlayerTurn--;
        return PlayerTurn;
    }

} // CheckForPlayerTurn

bool CGame::bGameOver()
{
    if (m_bGameOver == true)
    {
        return true;
    }
}

// CheckForWinner()
//
// Aufgabe: Sieger ermitteln
//
void CGame::CheckForWinner(static int PlayerTurn)
{
    // Waagerechte Siege abfragen
    //
    if ((m_chField[0] == 'X' && m_chField[1] == 'X' && m_chField[2] == 'X') ||
        (m_chField[0] == 'O' && m_chField[1] == 'O' && m_chField[2] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }
    else if ((m_chField[3] == 'X' && m_chField[4] == 'X' && m_chField[5] == 'X') ||
             (m_chField[3] == 'O' && m_chField[4] == 'O' && m_chField[5] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }
    else if ((m_chField[6] == 'X' && m_chField[7] == 'X' && m_chField[8] == 'X') ||
             (m_chField[6] == 'O' && m_chField[7] == 'O' && m_chField[8] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }

    // Senkrechte Siege abfragen
    //
    else if ((m_chField[0] == 'X' && m_chField[3] == 'X' && m_chField[6] == 'X') ||
             (m_chField[0] == 'O' && m_chField[3] == 'O' && m_chField[6] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }
    else if ((m_chField[1] == 'X' && m_chField[4] == 'X' && m_chField[7] == 'X') ||
             (m_chField[1] == 'O' && m_chField[4] == 'O' && m_chField[7] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }
    else if ((m_chField[2] == 'X' && m_chField[5] == 'X' && m_chField[8] == 'X') ||
             (m_chField[2] == 'O' && m_chField[5] == 'O' && m_chField[8] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }

    // Diagonal
    //
    else if ((m_chField[0] == 'X' && m_chField[4] == 'X' && m_chField[8] == 'X') ||
             (m_chField[0] == 'O' && m_chField[4] == 'O' && m_chField[8] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }
    else if ((m_chField[2] == 'X' && m_chField[4] == 'X' && m_chField[6] == 'X') ||
             (m_chField[2] == 'O' && m_chField[4] == 'O' && m_chField[6] == 'O'))
    {
        m_Winner = PlayerTurn;
        m_bGameOver = true;
    }

} // CheckForWinner


// ShowWinner
//
// Aufgabe: Gibt den Gewinner der Runde aus
//
void CGame::ShowWinner()
{
    system("cls");

    cout << "Gewonnen hat Spieler " << m_Winner << "!" << endl;
    cout << "Glueckwunsch!" << endl;

    system("pause>nul");

}

Dreat

Frischling

Beiträge: 86

Wohnort: Heilbronn

  • Private Nachricht senden

2

27.03.2012, 21:31

Du hast das Problem etwas sehr bedürftig ausgedrückt :D

Vielleicht könntest du erläutern was in der Konsole falsch ausgegeben wird?

3

27.03.2012, 21:39

Nunja, das Programm springt in der main-Funktion nach der ersten Do-While-Schleife einfach raus.
somit steht dort dann, nachdem man ein Feld ausgewählt hat, "Ende" ^^

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

4

27.03.2012, 21:44

C-/C++-Quelltext

1
2
3
4
5
6
7
bool CGame::bGameOver()
{
    if (m_bGameOver == true)
    {
        return true;
    }
}

hmm... Was passiert wenn m_bGameOver nicht true ist? -> Kompilerwarnungen verstehen lernen und ernst nehmen!
Den Fehler machst du nicht nur einmal. Versuch TicTacToe erstmal ohne OOP. ;)

Du könntest es übrigens direkt so schreiben:

C-/C++-Quelltext

1
2
3
4
bool CGame::bGameOver()
{
   return m_bGameOver;
}
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

5

27.03.2012, 22:35

Vielen Dank, es hat endlich geklappt. :)

Das solche dummen Fehler auch immer nur mir passieren... :dash:
Beim nächsten Mal werde ich die Variablen gleich initialisieren. Ehrenwort ;)

Was das OOP anbelangt:
Ja ich weiß, eine Klasse für das ganze Spiel anzulegen zeugt wohl nicht von OOP.
Ich hatte anfangs auch geplant, eine weiter Klasse namens "CSpieler" anulegen, doch wusste ich nicht,
welche Funktionen ich in welche Klasse tun soll. Außerdem gab es Probleme seitens der Zugriffe auf die Membervariablen
der unterschiedlichen Klassen.

Aber wie würde man es denn in OOP schreiben, bzw. in welche Klassen sollte man das Programm aufteilen?

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

6

28.03.2012, 11:04

[...] doch wusste ich nicht, welche Funktionen ich in welche Klasse tun soll.

viel wichtiger sollte doch wohl sein, welche Informationen die Klasse kapseln soll
die Methoden, die mit diesen hantieren müssn, ergeben sich erst daraus

Aufteilung der Klassen:
ich habe nicht wirklich Lust, mir den Quelltext genauer anzusehen und da keine textuelle Beschreibung vorhanden ist, kann ich dir keine Antwort liefern
du könntest dir aber Bücher/Tutorials zu den Themen objektorientiertes Design und objektorientierte Analyse anschauen
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Sp3iky

Treue Seele

Beiträge: 232

Beruf: Entwicklungsingenieur

  • Private Nachricht senden

7

28.03.2012, 14:49

Da die anderen keine Lust haben, sich den Quellcode anzuschauen, hab ich es mal gemacht.

Grundsätzlich ist deine CPlayer-Klasse totaler murks.

Zum einen gibt es da nur eine Membervariable und zum anderen ist diese auch noch static. Was es bedeutet, eine Membervariable static zu machen, kannst du dir ja mal anlesen. Grundsätzlich heißt das aber, dass es eigentlich keine Instanzvariable ist, sondern eine Klassenvariable. Es gibt sie nur einmal und auf sie kann auch zugegriffen werden, wenn du keine Instanz der Klasse erstellst.

Du hast diesen Fehler aber gar nicht gemerkt, weil du zwar Instanzen dieser Klasse anlegst, sie dann aber gar nicht benutzt. Stattdessen benutzt du die globale Variable "int CPlayer::m_PlayerTurn;".

Und diese Variable hat gar nichts mit deiner Klasse oder deren statischer Membervariable zu tun. Was du da angelegt hast, ist eine globale Variable im Namespace CPlayer. Der Namespace ist das gleiche, wie das "std::" vor cout und so, wenn du nicht using namespace verwendest.

Zusammenfassend kann man also sagen, dass du deine Player-Klasse weder nutzt, noch wirklich gebrauchen kannst für dieses Spiel. Die Game-Klasse ist zumindest schon halbwegs ordentlich definiert. Auf die Implementierung gehe ich nicht ein, da hier auch nicht Effizienz im Vordergrund steht, sondern Funktionalität. Es fehlen allerdings auch noch viele Sachen wie zum Beispiel const-Korrektheit. Du solltest auf jeden Fall noch einige Grundlagentutorials zu C++ durcharbeiten.

PS: Es gibt auch Debugger, mit denen man zeilenweise durch das Programm gehen kann und sich den aktuellen Zustand der Variablen anzeigen lassen kann. Dann hättest du dein initiales Problem auch sehr schnell selbst gefunden. Debuggen ist ein elementarer Bestandteil der Programmierung und sollte von Anfang an genutzt werden.

Werbeanzeige