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

Anonymous

unregistriert

1

02.09.2006, 10:51

TicTacToe für die Konsole [Teil 2]

So, nun geht es weiter mit dem 2. Teil dieses Tutorial's. Diesmal werden
wir die Tastaturabfrage überarbeiten. Das heißt, wenn z.B. "2" eingegeben
wird, dann wird auf dem 2. Feld ein X ausgegeben.

Zuerst müssen wir die draw_field Funktion überarbeiten. Diese habe
ich natürlich wieder ausreichend kommentiert:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void draw_field (char *field) // Parameter, damit wir das field Array nicht global deklarieren müssen

{
    system ("cls");

    // Hier werden jetzt die einzelnen Feld Variablen des Arrays in dem Spiel Feld ausgegeben

    // Da dieses Feld bei jedem Durchlauf aktualisiert wird, reicht es, wenn wir den Inhalt

    // der einzelnen Werte des Arrays ändern

    std::cout << std::endl << std::endl << std::endl
              << "     " << field[1] << " | " << field[2] << " | " << field[3] << std::endl
              << "    ---|---|---" << std::endl
              << "     " << field[4] << " | " << field[5] << " | " << field[6] << std::endl
              << "    ---|---|---" << std::endl
              << "     " << field[7] << " | " << field[8] << " | " << field[9] << std::endl;
}


Anschließend müssen wir uns der Tastaturabfrage widmen, ansonsten
bringt das alles ja nichts. ;) Es wird auch gleich noch geprüft, ob schonmal
eine auf einem bestimmten Feld ein "X" gezeichnet wurde. Hier der Code
und wieder gut und hoffentlich verständlich kommentiert:

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
char key_input (char *field) // Wie bei draw_field ;)

{
    char input = ' ';

    std::cin >> input;

    switch (input)
    {
        case '1':
            if (field[1] == 'X') // Wenn der erste Wert des field Array's ein "X" beinhaltet,...

            {
                std::cin.sync (); // Eingabe Buffer leeren

                std::cout << std::endl << "Dieses Feld ist bereits belegt!"; // Info ausgeben

                std::cin.get (); // Auf Enter vom Benutzer warten

            }
            else // Ach, ist das Feld doch noch frei? =)

            {
                field[1] = 'X'; // Dann weise dem Feld doch gleich mal ein "X" zu ;)

            }
            break;

            // Genau das gleiche für die 8 anderen Felder

        case '2':
            if (field[2] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[2] = 'X';
            }
            break;

        case '3':
            if (field[3] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[3] = 'X';
            }
            break;

        case '4':
            if (field[4] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[4] = 'X';
            }
            break;

        case '5':
            if (field[5] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[5] = 'X';
            }
            break;

        case '6':
            if (field[6] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[6] = 'X';
            }
            break;

        case '7':
            if (field[7] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[7] = 'X';
            }
            break;

        case '8':
            if (field[8] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[8] = 'X';
            }
            break;

        case '9':
            if (field[9] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[9] = 'X';
            }
            break;

        case 'q': break;

        default:
        {
            std::cin.sync ();
            std::cout << std::endl << "Falsche Eingabe!";
            std::cin.get ();
            break;
        }
    }

    return input;
}


Sieht recht lang aus, ist aber im Grunde nicht mal eine 10 Minuten
arbeit. ;) Könnte man sicherlich noch verbessern, werde ich dann im
nächsten Teil überarbeiten, wenn möglich.

Jetzt nochmal an die Main und schon sind wir fertig:

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
int main (void)
{
    SetConsoleTitleW (L"Tic Tac Toe");

    char input = ' ';

    char field[10]; // Array für die einzelnen Felder von der Größe 10, da ein String immer mit \0 endet

                    // Und da wir 9 Felder haben

    for (int i = 0; i < 10; ++i) // Initialisieren des String, hier ist natürlich eine for Schleife

                                 // angebracht, würden wir es nicht initialisieren, hätte das Array

                                 // komische Werte, Ihr könntet ja mal testen diese Schleife wegzulassen.

                                 // Und hier werden keine Klammern benutzt, da diese Schleife nur einen

                                 // Befehl ausfürhen soll.

        field[i] = ' ';

    while (input != 'q')
    {
        draw_field (field); // Das Feld Array der "draw_field" Funktion übergeben, damit sie darin verwendet

                            // werden können.

        std::cout << std::endl << std::endl
                  << "Geben Sie eine Zahl zwischen 1 und 9 ein, um ein Feld auszuwählen" << std::endl
                  << "oder geben Sie \"q\" ein, um das Programm zu beenden: ";

        input = key_input (field); // Das gleiche wie bei draw_field

        std::cin.sync ();
    }

    return 0;
}


Mh okay, für einen Anfänger ist das vielleicht noch viel Code, aber ihr
werdet merken, wenn ihr erstmal öfter was geschrieben habt, dass das
eigentlich ein super kleiner Source ist und das wichtigste ist ja sowieso
der Spaß dabei. ;) Ich zeig euch dann jetzt noch einmal den gesamten
Code und dann kommen auch schon meine Abschluß Worte. =)

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
#include <iostream>
#include <windows.h> 

void draw_field (char *field)
{
    system ("cls");

    std::cout << std::endl << std::endl << std::endl
              << "     " << field[1] << " | " << field[2] << " | " << field[3] << std::endl
              << "    ---|---|---" << std::endl
              << "     " << field[4] << " | " << field[5] << " | " << field[6] << std::endl
              << "    ---|---|---" << std::endl
              << "     " << field[7] << " | " << field[8] << " | " << field[9] << std::endl;
}

char key_input (char *field)
{
    char input = ' ';

    std::cin >> input;

    switch (input)
    {
        case '1':
            if (field[1] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[1] = 'X';
            }
            break;

        case '2':
            if (field[2] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[2] = 'X';
            }
            break;

        case '3':
            if (field[3] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[3] = 'X';
            }
            break;

        case '4':
            if (field[4] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[4] = 'X';
            }
            break;

        case '5':
            if (field[5] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[5] = 'X';
            }
            break;

        case '6':
            if (field[6] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[6] = 'X';
            }
            break;

        case '7':
            if (field[7] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[7] = 'X';
            }
            break;

        case '8':
            if (field[8] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[8] = 'X';
            }
            break;

        case '9':
            if (field[9] == 'X')
            {
                std::cin.sync ();
                std::cout << std::endl << "Dieses Feld ist bereits belegt!";
                std::cin.get ();
            }
            else
            {
                field[9] = 'X';
            }
            break;

        case 'q': break;

        default:
        {
            std::cin.sync ();
            std::cout << std::endl << "Falsche Eingabe!";
            std::cin.get ();
            break;
        }
    }

    return input;
}

int main (void)
{
    SetConsoleTitleW (L"Tic Tac Toe");

    char input = ' ';

    char field[10]; 
    for (int i = 0; i < 10; ++i) 
        field[i] = ' ';

    while (input != 'q')
    {
        draw_field (field);
        std::cout << std::endl << std::endl
                  << "Geben Sie eine Zahl zwischen 1 und 9 ein, um ein Feld auszuwählen" << std::endl
                  << "oder geben Sie \"q\" ein, um das Programm zu beenden: ";

        input = key_input (field);
        std::cin.sync ();
    }

    return 0;
}


So, wie gesagt ich glaube ich muss mir nochmal etwas ordentliches für
die Tastaturabfrage einfallen lassen, aber ansonsten bin ich recht zufrieden.
Ich hoffe Ihr auch. ;) Im nächsten Teil werden wir dann einen 2. Spieler
einfügen, paar kleinere Optimierungen vornehmen und eine Abfrage
einbauen, wer denn nun gewonnen hat. Damit wäre das Spielchen auch
schon fertig. Ich werde mir dann noch überlegen, ob ich nochmal eine
Erweiterung mit Farben, usw. schreiben werde, man wird sehen. =)
Feedback ist natürlich erwünscht.

Weiterführende Links:

Teil 1
Teil 3

2

02.09.2006, 11:58

Was spricht gegen SetConsoleTitleA ? Wenn du sonnst kein Unicode nimmst warum da? :D
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

babelfish

Alter Hase

Beiträge: 1 222

Wohnort: Schweiz

Beruf: Informatiker

  • Private Nachricht senden

3

02.09.2006, 12:39

Fehler:
bei case '6': hast du das Feld 7 genommen.

Sonst klappt alles :)

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

4

02.09.2006, 12:49

Zitat von »"Devil"«

Was spricht gegen SetConsoleTitleA ? Wenn du sonnst kein Unicode nimmst warum da? :D


Vielleicht möchte wer das Spiel ja mal TicTäcTöe nennen *g* Und bei den Feldern wäre es zwar schöner gewesen Unicode zu nutzen, aber wirklich notwendig ..? Nein, eher nicht. Von daher geht das schon in Ordnung.

5

02.09.2006, 17:13

Tjo wer hindert dich daran mit SetWindowA nen ä oder ö zu benutzen?
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

Das Gurke

Community-Fossil

Beiträge: 1 996

Wohnort: Pinneberg

Beruf: Schüler

  • Private Nachricht senden

6

02.09.2006, 17:38

Dann geh von nem Chinesen aus der seine Kanjis dareinhaben will xD Es ist imho immer noch besser wenig Unicode zu unterstützen als garnicht.

7

02.09.2006, 17:57

Nein ein wiederlicher MischMasch ist nicht besser ...
Devil Entertainment :: Your education is our inspiration
Der Spieleprogrammierer :: Community Magazin
Merlin - A Legend awakes :: You are a dedicated C++ (DirectX) programmer and you have ability to work in a team? Contact us!
Siedler II.5 RttR :: The old settlers-style is comming back!

Also known as (D)Evil

8

02.09.2006, 22:59

nich schlecht, aber ich finde, man sollte die großen switch-dinger wegmachen..
was spricht gegen:

C-/C++-Quelltext

1
2
3
4
input -= 48;
if ((input > 0) && (input <10))
    if (field[input] == 'X')
            blabla;


NATÜRLICH gehts mit switch, aber da schreibt man sich ja blöde---
ich finds uneffizient...

Anonymous

unregistriert

9

03.09.2006, 09:37

Zitat von »"WarMuuh!!"«

nich schlecht, aber ich finde, man sollte die großen switch-dinger wegmachen..
was spricht gegen:

C-/C++-Quelltext

1
2
3
4
input -= 48;
if ((input > 0) || (input <10))
    if (field[input] == 'X')
            blabla;


NATÜRLICH gehts mit switch, aber da schreibt man sich ja blöde---
ich finds uneffizient...
Soll angeblich schneller sein.

babelfish

Alter Hase

Beiträge: 1 222

Wohnort: Schweiz

Beruf: Informatiker

  • Private Nachricht senden

10

03.09.2006, 13:08

Zitat von »"nix da"«

Zitat von »"WarMuuh!!"«

nich schlecht, aber ich finde, man sollte die großen switch-dinger wegmachen..
was spricht gegen:

C-/C++-Quelltext

1
2
3
4
input -= 48;
if ((input > 0) || (input <10))
    if (field[input] == 'X')
            blabla;


NATÜRLICH gehts mit switch, aber da schreibt man sich ja blöde---
ich finds uneffizient...
Soll angeblich schneller sein.

Naja, bei so einem kleinen Spiel würde das nicht allzu viel bewirken.

Ich bevorzuge die Variante von WarMuuh!!, na gut, bei 9 Felder würde die case Variante noch passen,
aber müsste ich das für etwa 100 Felder machen dann sc****se ich auf die Rechenzeit ;)

Werbeanzeige