Autor Beitrag
erlo68
Hält's aus hier
Beiträge: 3



BeitragVerfasst: Fr 20.09.13 23:22 
Moin erstmal...

bin was C# angeht noch recht Grün hinter den Ohren aber ich habe mir vorgenommen ein Tic-Tac-Toe Spiel zu programmieren, und bin dabei auf einige Hürden gestoßen die ich selbst nicht gelöst bekomme.

Es hat noch viele fehlende stellen wie den Bot oder die Gewinn-Abfragen aber man soll nachher entweder zu PvP oder PvE spielen können. Dafür Benutze ich PictureBoxen und überprüfe über ein 2-Dimensionales-Array die Gewinn-Abfragen.

Mein Hauptproblem liegt darin das ich das Hauptfenster quasi auf Eis legen will, solange ein anderes Fenster aktiv ist, da ich vor dem Start via 2 anderer Fenster die Namen der Spieler einlese. Ich wollte das so machen da ich das so schöner finde. nun möchte ich den Namen "Player1" im Form1 in den im Form2 eingegebenen Namen umwandeln aber statt dessen verschwindet sogar das "Player1" im Form1.
Ich habe ihn zwar Standardmäßig als "" deklariert aber diesen hab ich durch die Eingabe in Form2 überschrieben (meiner Meinung nach).

Zum Zweiten brauch ich eine Idee wie ich es hin bekomme, dass das Programm wartet bis ich ein Feld angeklickt habe und erst dann die Abfrage ob jemand gewonnen hat starte, noch nicht fertig aber der Testfall hat nicht gegriffen, da ich kein Plan davon habe in welcher Reihenfolge das Programm arbeitet. Währe schön wenn ich das irgendwie vorgeben könnte.

Hier meine Forms:

PS: Kann auf Wunsch auch das gesamte Projekt per PN schicken zur besseren Übersicht. Und schon mal Danke im Voraus :D

Form1:
ausblenden volle Höhe 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:
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public static int count = 0// Wenn gerade dann wird ein X, im 2-D-Array eine 1, gesetzt und bei Ungerade ein O; im 2-D-Array eine 2.
        public static bool player1 = false// Prüfung ob Spieler 1 ein Spieler oder ein Bot ist.
        public static string name1 = "";    //Namensgebung (Teilproblem)
        public static string name2 = "";    // "" ""
        public static bool player2 = false// Spielerprüfung für Spieler 2
        public static bool wins = false;    //Gewinnprüfung
        public static int[,] grid = { { 000 }, { 000 },{000} }; //Spielfeld als 2-D-Array
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) //Start des Spiels mit Namensabfrage
        {
            button1.Enabled = false;
            button2.Enabled = true;
            button3.Enabled = true;
            pictureBox1.Enabled = true;
            pictureBox2.Enabled = true;
            pictureBox3.Enabled = true;
            pictureBox4.Enabled = true;
            pictureBox5.Enabled = true;
            pictureBox6.Enabled = true;
            pictureBox7.Enabled = true;
            pictureBox8.Enabled = true;
            pictureBox9.Enabled = true;
            if (player1 == true && player2 == true)
            {
               
                Form2 Player1 = new Form2();    // Initialisierung der Namensgebung durch aufrufen eines Fensters
                Player1.Show();
                label1.Text = name1;            //eigentliche Namensänderung nach Eingabe der Namen im oberem Schritt (Teilproblem)
                label2.Text = name2;
            }
            
        }

        private void button2_Click(object sender, EventArgs e) // Dient zur Zurücksetzung des Spielfeldes
        {
            for (int i = 0; i < 3; i ++)
            {
                for (int j = 0; j < 3; j++)
                {
                    grid[i, j] = 0;
                }
            }
            pictureBox1.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox2.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox3.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox4.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox5.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox6.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox7.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox8.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox9.Image = WindowsFormsApplication1.Properties.Resources.clear;
            pictureBox1.Enabled = true;
            pictureBox2.Enabled = true;
            pictureBox3.Enabled = true;
            pictureBox4.Enabled = true;
            pictureBox5.Enabled = true;
            pictureBox6.Enabled = true;
            pictureBox7.Enabled = true;
            pictureBox8.Enabled = true;
            pictureBox9.Enabled = true;
            count = 0;
            do                                            //Gewinnabfrage (teilproblem... falscher Ansatz???)
            {
                wins = win(grid);
            } while (wins == false);
            MessageBox.Show(name1 + " hat gewonnen!");
        }

        private static bool win(int[,] grid)              //Gewinnabfrage vie 2-D-Array
        {
            bool yes = false;
            if (grid[00] == 1 && grid[10] == 1 && grid[20] == 1)
            {
                yes = true;
            }
            return yes;
        }
        private void button3_Click(object sender, EventArgs e)   /Dient zum zurücksetzen der gesamten Form1
        {
            while (Controls.Count > 0)
            {
                Controls[0].Dispose();
            }
            count = 0;
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    grid[i, j] = 0;
                }
            }
            InitializeComponent();
        }


Form2:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
namespace WindowsFormsApplication1
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) //Zeigt das Zugewiesene Symbol (X oder O) und Fragt den Namen ab.
        {
            Form1.name1 = textBox1.Text;                       //Sollte den Namen "Player1" nachher ersetzen
            Form3 Player2 = new Form3();                       //Abfrage für Spieler 2 öffnen.
            Player2.Show();
            this.Close();
        }
    }
}


Form3:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
namespace WindowsFormsApplication1
{
    public partial class Form3 : Form
    {
        public Form3()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Form1.name2 = textBox1.Text;
            this.Close();
        }

        private void Form3_Load(object sender, EventArgs e)          //Erst hiernach soll die Form1 weiter "arbeiten"
        {

        }
    }
}
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4795
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Sa 21.09.13 10:04 
Hallo und :welcome:

daß die Namen nicht übernommen werden liegt daran, daß du die 2. Form zwar aufrufst, aber der Programmablauf danach sofort fortgesetzt wird (und dann sind die beiden Namensvariablen ja noch nicht gefüllt).
Verwende anstatt Show() die Methode ShowDialog() - diese wartet solange, bis das Fenster wieder geschlossen wird (dies ist dann ein sogenannter "modaler Dialog").

Einige Tipps kann ich dir auch noch geben (damit dein Programm besser wartbar bleibt):
- Packe (wie das interne grid) die PictureBoxen in ein Array:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
private PictureBox[] pictureBoxes;

public Form1()
{
   InitializeComponent();

   pictureBoxes = new PictureBox[] { pictureBox1, pictureBox2, /*...*/ pictureBox9 };
}

Nun kannst du elegant mithilfe einer Schleife die einzelnen PictureBoxen ansprechen, z.B.
ausblenden C#-Quelltext
1:
2:
3:
4:
foreach (PictureBox pb in pictureBoxes)
{
  pb.Enabled = true;
}

- verwende möglichst keine öffentlichen (public) Felder, sondern nimm dafür Eigenschaften
- und static sollten deine meisten Variablen auch nicht sein
- nur eine Kleinigkeit:
den Namensbereich WindowsFormsApplication1 brauchst du nicht zu schreiben, da die Klasse sich ja im selben Namensbereich befindet.
- die do..while-Schleife bei der Gewinnabfrage blockiert deine GUI (sofern die Bedingung nciht erfüllt ist). In WinForms mußt du ereignisorientiert programmieren!
- InitializeComponent() sollte nur innerhalb des Konstruktors aufgerufen werden (d.h. entferne diesen Aufruf sowie die Controls-Schleife). Schreibe besser eine Methode, welche explizit wieder einzelne Controls initialisiert.
- die beiden Forms (für die Spielereingabe) sollten beide von der Hauptform aus aufgerufen werden (und dann reicht es auch, wenn du nur genau eine Spieler-Form erstellst und diese dann aber für jeden Spieler einmal aufrufst - anstatt 2 (fast) identische Form-Klassen zu erstellen). Wenn du für den Namen dann eine Eigenschaft verwendest, so benötigst du dann auch nicht mehr die public static Namensvariablen in Form1).

Wenn du mal etwas Zeit (und Muße) hast, kannst du dir mal meinen Artikel Kommunikation von 2 Forms durchlesen (dieser geht auf einige der oben beschriebenen Tipps ein - besonders Eigenschaften und Ereignisse - und am Ende befindet sich auch ein Beispielprojekt).

P.S: Ich hoffe, ich habe dich jetzt nicht mit der Menge an Informationen abgeschreckt!?
erlo68 Threadstarter
Hält's aus hier
Beiträge: 3



BeitragVerfasst: Sa 21.09.13 22:40 
Danke erstmal für die schnelle Antwort.

Ich habe einige deiner Vorschläge befolgt und das meiste funktioniert nun.
Zudem da ich noch Anfänger bin weiß ich nicht wie ich die Controls einzeln initialisieren soll, da mir die jetzige Methode nicht mehr komplett weiterhilft wie ich feststellen musste.
Nur weiß ich nicht was du mit "Eigenschaften" meintest, aber nun habe ich das Problem das ich den Computer Gegner nicht implementiert bekomme.

Am liebsten würde ich ihn durch 2-3 eigene Methoden erstelle wozu ich aber eine Möglichkeit bräuchte die meinem Programm sagt das ich und der Bot abwechselnd spielen, der bot soll also warten bis ich meinen Zug gemacht habe. Im Moment versuche ich ihn durch eine Komplette Erweiterung der meisten meiner Methoden welsche die PictureBoxen betrifft einzufügen, insofern das er immer dann an der Reihe ist sobald der "Enabled"-Status geändert wurde welches eintrifft sobald ein Feld angeklickt und somit nicht mehr anklickbar sein soll. Dies führte aber zu weiteren Fehlern die mich zwingen würden meine ganze Struktur auf den Bot auszulegen worauf ich aber nicht hinaus möchte.

Irgendwelche Ideen oder Anregungen? :?!?:
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4795
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 23.09.13 17:55 
Hallo,

bei Eigenschaften meine ich selbige der Programmiersprache C#: Eigenschaften.

Und mit deinem "Bot" (andere sagen auch "KI" dazu) bist du schon auf dem richtigen Weg. Immer wenn der Spieler einen Zug ausgeführt hat, läßt du den "Bot" einen Zug berechnen und durchführen. Wenn du diese Funktionalität in eine Methode packst, so reduziert sich dadurch der Code.
Ich lese aus deiner Beschreibung, daß du für jede einzelne PictureBox eine eigene Ereignismethode hinterlegt hast. Dies ist aber nicht erforderlich - besser ist es, nur eine Ereignismethode für alle PictureBox-Click zu verwenden und dann anhand des übergebenen Objekts sender die PcitrueBox zu ermitteln:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
void PictureBox_Click(object sender, EventArgs e)
{
  PictureBox pb = sender as PictureBox;
  if (pb != null// überprüfen, ob wirklich eine PictureBox angeklickt wurde
  {
    // nun kann auf 'pb' zugegriffen werden, z.B.
    pb.Enabled = false;
 
    CalculateBot(); // hier den Bot rechnen lassen (und Anzeige aktualisieren)
  }
}
erlo68 Threadstarter
Hält's aus hier
Beiträge: 3



BeitragVerfasst: Mo 23.09.13 19:35 
Mit dem Wort KI halte mich da etwas zurück da dieser ja nur vorbestimmte Abfragen abgeht ^^

Aber jetzt fällt es mir wie Schuppen von den Augen! O-O

Ich bin natürlich so schlau un benutze die Enabled.Changed-Events anstatt der Click-Events *facepalm* das würde einiges vereinfachen. Nungut erstmal danke für den Rat und ich meld mich dann wieder obs geklappt hat.

EDIT: Naja kurz nach der "Erleuchtung" ist mir eingefallen,da ich auch Bot gegen Bot spielen lassen will, da komm ich mit Click-Events nicht weit :/