3 Gewinnt Spiel - Problem mit zweidimensionalen char Array
-
Hi Leute,
ich versuche ein 3 gewinnt Spiel zu machen. Dies ist erst eine arbeitsversion.
Ich habe keinen Plan wo mein Fehler liegt:(. Wär echt cool wenn jemand mir da Tipps geben könnte woran es vielleicht liegt.Der Compiler meldet zurück, dass field nicht initialisiert wurde. Und außerdem sei dadurch field "static". Ich kann mit dem ganzen Kram ehrlich gesagt nicht viel anfangen:(.
Ich wäre echt dankbar wenn ihr mir helfen könntet,
VG DaNewGuy.Hier die Datei ---- main.cpp ----
//Hier binde ich den Header an #include "map.h" //Ich definiere hier ROW für Zeilen und COLUMN für Spalten #define ROW 6 #define COLUMN 7 int main() { //Ich deklariere hier ein Objekt der Klasse map map GAME; //Hier greife ich auf die Methode showmap des Objektes GAME GAME.showmap(ROW ,COLUMN); //Ab hier steht eigentlich nur noch der Wartealgorithmus //Da die anwendung sonst gleich schließt char wait; std::cin >> wait; return 0; }
Hier die Headerdatei ---- map.h ----
#include <iostream> class map { //field[row][column] //Hier wird das Feld definiert und initialisiert //Ich habe keine Ahnung, ob die Initialisierung richtig ist private: char field[][5]= {"1|2|3","-----","4|5|6", "-----","7|8|9"}; public: //showmap gibt das Feld per for-Schleife zurück //darum muessen Row und Column übergeben werden void showmap(int Row, int Column); //changemap ändert ein Feld abhängig von der player nummer //player==1 ändert er ein Zeichen in X //player==2 ändert er ein Zeichen in O void changemap(int player, int postion); }; void map::showmap(int Row, int Column){ for(int i = 0; i < Row; i++) for(int j = 0; j < Column;j++) std::cout << field[i][j]; std::cout << std::endl; } void map::changemap(int player, int postion) { //Variable wird dann bei switch das entsprechende Feld //duch sign erstzen char sign; if (player ==1) sign='X'; else sign='O'; switch (postion) { case 1: field[0][0] = sign;break; case 2: field[0][2] = sign;break; case 3: field[0][4] = sign;break; case 4: field[2][0] = sign;break; case 5: field[2][2] = sign;break; case 6: field[2][4] = sign;break; case 7: field[4][0] = sign;break; case 8: field[4][2] = sign;break; case 9: field[4][4] = sign;break; } }
-
Dieser Thread wurde von Moderator/in akari aus dem Forum VCL (C++ Builder) in das Forum C++ (auch C++0x) verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
std::disclaimerstd:string useThat;
Uralt, aber für den Angfang nett: EVA, d.h. Trenne Eingabe, Verarbeitung und Ausgabe
Ich bin mir nicht sicher ob er "1|2|3" implizit in einen char-array konvertieren kann, wen dann warscheinlich in sowas: {"123"} --> {'1','2','3','\0'}. Ich würde vermutlich nur 3x3 Felder (die die sich ändern) speichern:
char felder[9] = { '1','2','3','4','5','6','7','8','9' }; void ausgabe() { for (int row=0; row < 3; ++row) for (int col=0; col < 3; ++col) { std::cout << felder[row*3+col]; // gib feldwer aus // falls in dieser ROW noch eine Columns kommt, mach nen | hin if (col != 2) std::cout << '|'; // falls dies die letzte COL dieser ROW war UND falls danach noch was kommt, mach nen horzizontalen Trenner hin if (col == 2 && row != 2) std::cout << "\n-+-+-\n"; // sonst mach noch 2 Leerzeilen drunter und gibts sicher aus else if (col == 2) std::cout << "\n" << std::endl; } }
Die Eingabe und Co hast du ja schon - ich hoffe X kann sich nicht auf ein O legen
Abgesehen davon bist du so schon eine Dimension des Arrays losgeworden - das hilft beim Denken.
-
#include <iostream> #include <algorithm> const int zeilen = 6; const int spalten = 7; struct spielzustand { typedef int zeile_t[spalten]; zeile_t raster[zeilen]; spielzustand() {reset();} void reset(); zeile_t & operator[](int z) {return raster[z];} zeile_t const& operator[](int z) const {return raster[z];} }; void spielzustand::reset() { for (int z=0; z<zeilen; ++z) std::fill(raster[z]+0,raster[z]+spalten,0); } void darstellen(std::ostream& os, spielzustand const& sz) { for (int z=0; z<zeilen; ++z) { os << '|'; for (int s=0; s<spalten; ++s) { int f = sz[z][s]; if (f<0 || f>2) f=0; os << (" |\0X|\0O|"+f*3); } os << '\n'; } os.flush(); } int main() { spielzustand sz; sz[2][3]=1; sz[3][2]=2; darstellen(std::cout,sz); }
-
@vor_schlag: Denkst du ernsthaft, dass ein Anfänger mit deinem Beispiel etwas anfangen kann? Bezweifel ich stark.
Ich persönlich finde vorallem die Zeilen 28-30 extrem hässlich.back to topic:
DaNewGuy schrieb:
Der Compiler meldet zurück, dass field nicht initialisiert wurde. Und außerdem sei dadurch field "static". Ich kann mit dem ganzen Kram ehrlich gesagt nicht viel anfangen:(.
Du solltest damit unbedingt etwas anfangen können. Mach dich mit den Meldungen vertraut, versuche sie zu interpretieren.
Du versuchst das member field zu initialisieren, so wie du es vermutlich von lokalen Variablen gewohnt bist. So funktioniert das allerdings nicht bei Klassenmembern. Dafür ist der Konstruktor zuständig.
Versuch mal folgendes Beispiel zu compilieren und zu verstehen, was der compiler meckert:
class Test { char a = 'x'; }; int main() { Test t; return 0; }
-
oh stimmt, jetzt kapiere ich es teilweise:). Also muss ich bei klassen die initialisierung immer im Konstruktor machen?Das macht sinn. Ich versuche es gleich und melde mich zurück;-).
EDIT: MUHAHAHA! Es hat geklappt:D.Jetzt kann ich endlich weiterbasteln.
-
Aber hast du als Attribut nur char* bla; geschrieben und dann im ctor bla = "Halelilula"; geschrieben? Weil das wär illegal.
-
So jetzt ist mein erstes game auch schon fertig:D.
@Eisflamme: Was wäre denn illegal? Wenn ich einen Pointer auf einen char machen würde?Hier der Source code:).
main.cpp
//Hier binde ich den Header an #include "map.h" int main() { //Ich deklariere hier ein Objekt der Klasse map map GAME; do{ GAME.turn(1); if (GAME.plyWin(1)==true){ std::cout << "Player 1 is the winner.\n\n"; GAME.showmap(); break; } GAME.turn(2); if (GAME.plyWin(2)==true){ std::cout << "Player 2 is the winner.\n\n"; GAME.showmap(); break; } } while (1); //Ab hier steht eigentlich nur noch der Wartealgorithmus //Da die anwendung sonst gleich schließt char wait; std::cin >> wait; return 0; }
Und hier die map.h
#include <iostream> #define LENGTH 9 class map { //field[row][column] //Hier wird das Feld definiert und initialisiert //Ich habe keine Ahnung, ob die Initialisierung richtig ist private: char field[9]; public: //showmap gibt das Feld per for-Schleife zurück //darum muessen Row und Column übergeben werden map(); void showmap(); //changemap ändert ein Feld abhängig von der player nummer //player==1 ändert er ein Zeichen in X //player==2 ändert er ein Zeichen in O void changemap(int player,int position); void plyTurn(int player); bool plyWin(int ply); bool checkValid(int position); void turn(int player); }; //konstruktor für Initialisierung der Felder map::map(){ field[0]='1'; field[1]='2'; field[2]='3'; field[3]='4'; field[4]='5'; field[5]='6'; field[6]='7'; field[7]='8'; field[8]='9'; } //Methode um karte per for-Loop anzuzeigen //Wie mir hier geraten wurde bin ich auf ein eindimensionales Feld umgestiegen //Ueber die for-Schleife wird alles ausgespuckt //Die if anweisung mit dem Modulo drin sorgt für zeilenumbruch void map::showmap(){ for(int i = 0; i < LENGTH; i++){ std::cout << field[i]; //(i+1)%3==0 dient dazu alle drei schritte eine neue zeile zu beginnen if((i+1)%3==0) std::cout << std::endl; } //gibt zwei Leerzeilen nach beendigung der for-Schleife aus std::cout << "\n\n"; } //Abhängig von player wird ein anderes Zeichen gesetzt //ich habe hier einfach X für Player 1 festgesetzt void map::changemap(int player, int postion){ //Variable wird dann bei switch das entsprechende Feld //durch sign erstzen char sign; if (player ==1)sign='X'; else sign='O'; switch (postion) { case 1: field[0]= sign;break; case 2: field[1]= sign;break; case 3: field[2]= sign;break; case 4: field[3]= sign;break; case 5: field[4]= sign;break; case 6: field[5]= sign;break; case 7: field[6]= sign;break; case 8: field[7]= sign;break; case 9: field[8]= sign;break; } } //Das ist der Text der angezeigt wird beim Spieler wechsel void map::plyTurn(int player){ if (player==1){ std::cout << "It's Player 1's turn.\n"; std::cout << "Where do you want to place your X?\n"; std::cout << "Please enter a number between 1 and 9"; } else { std::cout << "It's Player 2's turn.\n"; std::cout << "Where do you want to place your O?\n"; std::cout << "Please enter a number between 1 and 9"; } } //Hier wird die Sieges-Regel definiert bool map::plyWin(int ply){ char sign; if (ply==1) sign='X'; else sign='O'; //horizontale siege if (field[0]==sign && field[1]==sign && field[2]==sign) return true; else if (field[3]==sign && field[4]==sign && field[5]==sign) return true; else if (field[6]==sign && field[7]==sign && field[8]==sign) return true; //vertikale siege else if (field[0]==sign && field[3]==sign && field[6]==sign) return true; else if (field[1]==sign && field[4]==sign && field[7]==sign) return true; else if (field[2]==sign && field[5]==sign && field[8]==sign) return true; //diagonale siege else if (field[0]==sign && field[4]==sign && field[8]==sign) return true; else if (field[2]==sign && field[4]==sign && field[6]==sign) return true; else return false; } //Überprüft ob der Zug legal ist sprich ob die eingabe nicht schon vergeben ist bool map::checkValid(int position){ if (field[position-1]=='X' || field[position-1]=='O'){ std::cout << "You cannot place your sign here.\n\n"; showmap(); std::cout << "Please enter a number between 1 and 9"; return false; } else return true; } //Ein Zug wird hier mit den einzelnen schritten durchgeführt void map::turn(int player){ int pos; showmap(); plyTurn(player); std::cout << std::endl; do{ std::cin >> pos; }while(checkValid(pos)==false); std::cout << std::endl; changemap(player,pos); }
-
Hätte mal noch eine Frage wie kann ich denn die Siegesregel(siehe Code unten) kompakter formulieren? Das sieht ja voll hässlich aus.
BTW Falls ihr noch andere verbesserungsvorschläge habt ruhig her damit:D. Ich will ja was lernen.
EDIT 1: Hmm ... ich glaube alle Sieges-kombinationen (wenn einzelne Zelle mit 1 bis 9 nummeriert ist) lassen sich durch 3 teilen. Aber ich habe keinen plan wie ich das benutzen kann um eine bessere Regel zu definieren???[quote="DaNewGuy"]
//Hier wird die Sieges-Regel definiert bool map::plyWin(int ply){ char sign; if (ply==1) sign='X'; else sign='O'; //horizontale siege if (field[0]==sign && field[1]==sign && field[2]==sign) return true; else if (field[3]==sign && field[4]==sign && field[5]==sign) return true; else if (field[6]==sign && field[7]==sign && field[8]==sign) return true; //vertikale siege else if (field[0]==sign && field[3]==sign && field[6]==sign) return true; else if (field[1]==sign && field[4]==sign && field[7]==sign) return true; else if (field[2]==sign && field[5]==sign && field[8]==sign) return true; //diagonale siege else if (field[0]==sign && field[4]==sign && field[8]==sign) return true; else if (field[2]==sign && field[4]==sign && field[6]==sign) return true; else return false; }
-
Also spontan würd ich ab Zeile 6 schreiben:
return // horizontale Siege field[0]==sign && field[1]==sign && field[2]==sign || field[3]==sign && field[4]==sign && field[5]==sign || field[6]==sign && field[7]==sign && field[8]==sign || //vertikale siege field[0]==sign && field[3]==sign && field[6]==sign || field[1]==sign && field[4]==sign && field[7]==sign || field[2]==sign && field[5]==sign && field[8]==sign || //diagonale siege field[0]==sign && field[4]==sign && field[8]==sign || field[2]==sign && field[4]==sign && field[6]==sign;
-
hmm ... das sieht echt besser aus:). Das müsste auch noch schneller sein als meine Version oder? Da keine if-, else if-, oder else-Anweisung verwendet wird.
-
Keine Ahnung, wird vielleicht eh wegoptimiert. Aber langsamer ist die if-lose Variante auf keinen Fall.
-
Ich finde schon, dass man Anfängern die richtige Datenstruktur nahebringen sollte, wenn man detailliert erklärt, was warum wie ist geht das in Ordnung.
Gerade um C-style Arrays zu vermeiden sollte man std::vector oder std::array als bessere Alternativen aufzählen.
Ich würde das Spielfeld folgendermassen modellieren:#include <cassert> // für assert() #include <vector> // für std::vector #include <algorithm> // für std::fill() class Board { unsigned int Rows_; // Anzahl der Zeilen unsigned int Cols_; // Anzahl der Spalten std::vector<char> Squares; // Spielfelder public: Board( unsigned int Rows, unsigned int Cols ) : Rows_( Rows ), // Anzahl der Zeilen übernehmen Cols_( Cols ), // Anzahl der Spalten übernehmen Squares_( Rows * Cols ) // Spielfeld initialisieren { } void clear() { // alle Spielfelder auf o zurücksetzen std::fill( Squares_.begin(), Squares_.end(), 0 ); } unsigned int rows() const { // Anzahl der Zeilen zurückgeben return Rows_; } unsigned int cols() const { // Anzahl der Spalten zurückgeben return Cols_; } char& operator()( unsigned int Row, unsigned int Col ) { // Zugriff auf Spielfeld assert_bounds( Row, Col ); return Squares_[Row * cols() + Col]; } const char& operator()( unsigned int Row, unsigned int Col ) const { // Zugriff auf Spielfeld assert_bounds( Row, Col ); return Squares_[Row * cols() + Col]; } private: void assert_valid_bounds( unsigned int Row, unsigned int Col ) const { // Zeilenindex und Spaltenindex müssen gültig sein, // ansonsten das Programm abbrechen std::assert( Row < rows() ); std::assert( Col < cols() ); } }; int main() { // Spielfeld der Größe 3x3 erzeugen Board TicTacToeBoard( 3,3 ); // setze ein 'X' in die Mitte TicTacToeBoard( 1,1 ) = 'X'; // setze ein 'O' in die linke obere Ecke TicTacToeBoard( 0,0 ) = 'O'; }
Man kann jedes n-dimensionale Array als eindimensionales Arrays darstellen, man muss sich nur die Größen der einzelnen Dimensionen merken. In diesem Fall ist das Spielfeld zweidimensional, jede Dimension hat die Größe 3. Damit hat das lineare Array (hier durch einen std::vector repräsentiert) die Größe 9 und die Indizes sind folgendermassen verteilt:
`
0 | 1 | 2
---+---+---
3 | 4 | 5
---+---+---
6 | 7 | 8
`
Man kann den linearen Index aus dem 2D Index berechnen, indem man den Zeilenindex mit der Anzahl der Spalten multipliziert und dann den Spaltenindex addiert.
Der Zugriff erfolg dann über den ()-operator, der als Argumente den Zeilen- und Spaltenindex erwartet. Die Besonderheit bei den ()-Operatoren ist, dass sie Methodenaufrufe sind, aber keinen Methodennamen haben. Das lässt dann diese Notation zu:
char Mark = TicTacToeBoard( 0,0 );
Da der Operator eine Referenz auf das Spielfeldelement zuückgibt kann das Spielfeldelement durch den Zugriff verändert werden:
TicTacToeBoard( 2,1 ) = 'X';
Das Spielbrett Beispiel eignet sich hervorragend zur Einführung in die STL Container und Algorithmen, weil relativ wenig neue Funktionen/Methoden benötigt werden, aber das Spielbrett Raum für eigene Erweiterungen lässt, bis man eine eigene Klasse für n-dimensionale Arrays hat.