TicTacToe - Bitte um Kritik und Verbesserungsvorschläge



  • Hallo.
    Ich habe ein kleines TicTacToe gebastelt und möchte wissen ob mein OOP denken in Ordnung ist oder was ich dadran verbessern sollte. Desweiterem frage Ich mich ob ich die Prüfung auf den Sieger hinbekommen kann, ohne 1000 ifs zu machen.

    // Main (1)

    import java.util.Scanner;
    
    public class Main
    {
        private Scanner sc;
    
        public Main()
        {
            sc = new Scanner(System.in);
            main();
        }
    
        public void main()
        {
            Field Arena = new Field(3,3);
    
            Player Chuck_Norris = new Player('X'); 
            Player Michael_Jackson = new Player('O');
    
            GameManager gm = new GameManager(Chuck_Norris,Michael_Jackson,Arena);
            ConditionManager cm = new ConditionManager();
            PrintManager pm = new PrintManager();
    
            int x,y;
    
            while(gm.End() == false)
            {
                System.out.print("Zeile: -- ");
                y = sc.nextInt();
    
                System.out.print("Spalte: - ");
                x = sc.nextInt();
    
                if ( gm.Turn(y-1,x-1,cm) == false)
                {
                    System.out.println("Ungültiger Zug, nochmal!");
                    System.out.print("Zum weiterspielen 9 eingeben!");
                    y = sc.nextInt();
                }
    
                System.out.print("\f");
                pm.PrintField(Arena);
                System.out.print("\n");
            }
        }
    }
    

    // Field (2)

    public class Field
    {
        private int x,y;
        private char [][] field;
    
        public Field(int rows, int columns)
        {
            x = columns;
            y = rows;
            field = new char[y][x];
            Init();
        }
    
        public void SetCell(Player p, int y, int x) { field[y][x] = p.GetSymbol(); }
        public char GetCell(int y, int x) { return field[y][x]; }
    
        public int GetX() { return x; }
        public int GetY() { return y; }
    
        private void Init()
        {
            for (int i=0 ; i<y ; i++)
            {
                for (int j=0 ; j<x ; j++)
                {
                    field[i][j] = ' ';
                }
            }
        }
    }
    

    // Player (3)

    public class Player
    {
        private int points;
        private Symbol s;
    
        public Player(char c)
        {
            s = new Symbol(c);
        }
    
        public char GetSymbol()
        {
            return s.GetSymbol();
        }
    }
    

    // Symbol (4)

    public class Symbol
    {
        private char symbol;
    
        public Symbol(char c)
        {
            symbol = c;
        }
    
        public char GetSymbol()
        {
            return symbol;
        }
    }
    

    // GameManager (5)

    public class GameManager
    {
        // Referenzen
        private Field f;
        private Player p1;
        private Player p2;
        private Player current;
    
        private boolean end;
    
        public GameManager(Player a, Player b, Field c)
        {
            f = c;
            p1 = a;
            p2 = b;
            current = a;
            end = false;
        }
    
        public boolean Turn(int y, int x, ConditionManager cm)
        {
            if(f.GetCell(y,x) == ' ')
            {
                f.SetCell(current,y,x);
            }
            else
            {
                return false;
            }
    
            char winner = cm.Check(f);
            if(winner != ' ')
            {
                System.out.println(winner+" hat gewonnen!");
                end=true;
            }
    
            if(p1 == current) { current = p2; }
            else { current = p1; }
    
            return true;
        }
    
        public boolean End()
        {
            if(end==true) { return true; }
    
            int count = 0;
            for (int i=0 ; i<f.GetY() ; i++)
            {
                for (int j=0 ; j<f.GetX() ; j++)
                {
                    if( f.GetCell(i,j) == ' ' )
                    {
                        count += 1;
                    }
                }
            }
    
            if(count > 0) { return false; }
            else { return true; }
        }
    }
    

    // ConditionManager (6)

    public class ConditionManager
    {
        public ConditionManager()
        {
        }
    
        public char Check(Field f) // Hier soll auf einen sieg geprüft werden
        {
            return ' ';
        }
    }
    

    // PrintManager (7)

    public class PrintManager
    {
        public PrintManager()
        {
        }
    
        public void PrintField(Field f)
        {
            for (int i=0 ; i<f.GetY() ; i++)
            {
                System.out.print("|");
                for (int j=0 ; j<f.GetX() ; j++)
                {
                    System.out.print(f.GetCell(i,j));
                    System.out.print("|");
                }
                System.out.print("\n");
            }
        }
    }
    

    Mfg Progger33 😃





  • Sieht ganz gut aus. Drei Vorschläge:
    - Methodennamen werden in der Regel klein geschrieben
    - im GameManager sollte keine Ausgabe erfolgen ("xy hat gewonnen"), stattdessen sollte es eine methode geben mit der überprüft wird, ob/wer gewonnen hat
    - Die Symbol Klasse scheint mir im Moment nicht sinnvoll zu sein.

    gruß



  • gastantwort schrieb:

    - Methodennamen werden in der Regel klein geschrieben

    Das ist doch garnicht wahr lieber gast.



  • So hab das design komplett überarbeitet und neu angefangen. Ich bin endlich damit zufrieden und es funktioniert alles nur gibs noch keine KI.

    Ich hab die verschmelzung von den ganzen managern zu einer klasse für sinvoll gehalten, da ich damit nur einen prozess,ein spiel beschreiben will und damit nicht mehrere spiele,prozesse vllt au von ganz anderen typen verwalten will.

    Irgentwelche einwände gegen das klassendesign,algorithmen oder das steuerungssystem des spiels und verbesserungsvorschläge?

    Wenn ich das nochmal in C++ mach hab ich sicher mehr leser xD

    Version 2:
    Symbol

    public class Symbol
    {
        private char symbol;
    
        public Symbol(char s) { symbol = s; }
        public char GetSymbol() { return symbol; }
    }
    

    Player

    public abstract class Player
    {
        private int points;
        private Symbol symbol;
    
        public Player(char s)
        {
            points = 0;
            symbol = new Symbol(s);
        }
    
        // Wäre es vllt. sinnvoller das Symbol-Obj zurückzugeben?
        public char GetSymbol() { return symbol.GetSymbol(); }
    
        public void CountUp() { points++; }
    
        public abstract void Turn(TicTacToe g);
    }
    

    Field

    public class Field
    {
        private boolean visible;
        private int rows, columns;
        private char [][] table;
    
        public Field(int r, int c)
        {
            rows = r;
            columns = c;
            table = new char[rows][columns];
            visible = true;
            Init();
        }
    
        public void Init()
        {
            for(int i=0 ; i<rows ; i++)
            {
                for(int j=0 ; j<columns ; j++)
                {
                    table[i][j] = ' ';
                }
            }
        }
    
        // Statt einem char array könnte man ebenfalls auch ein symbol array nutzen doch wäre dies sinnvoll?
        public void SetCell(Player p, int r, int c) { table[r][c] = p.GetSymbol(); }
        public char GetCell(int r, int c) { return table[r][c]; }
    
        public int GetRows() { return rows; }
        public int GetColumns() { return columns; }
    
        public int GetEmptyCells()
        {
            int CountOfEmptyCells = 0;
            for(int i=0 ; i<rows ; i++)
            {
                for(int j=0 ; j<columns ; j++)
                {
                    if(GetCell(i,j) == ' ') { CountOfEmptyCells += 1; }
                }
            }
            return CountOfEmptyCells;
        }
    
        // Wäre es besseres OOP Design, wenn ich überall klassenintern Set und GetCell benutze statt direkt aufs array zuzugreifen?
        public void Show()
        {
            if(visible == true)
            {
                for(int i=0 ; i<rows ; i++)
                {
                    System.out.print("|");
                    for(int j=0 ; j<columns ; j++)
                    {
                        System.out.print(table[i][j]);
                        System.out.print("|");
                    }
                    System.out.print("\n");
                }
            }
        }
    
        public void SetVisibility(boolean status) { visible = status; }
        public boolean IsVisible() { return visible; }
    }
    

    HumanPlayer

    import java.util.Scanner;
    
    public class HumanPlayer extends Player
    {
        private Scanner sc;
    
        public HumanPlayer(char s)
        {
            super(s);
            sc = new Scanner(System.in);
        }
    
        @Override
        public void Turn(TicTacToe g)
        {
            int r,c;
    
            System.out.print("Zeile: -- ");
            r = sc.nextInt();
            System.out.print("Spalte: - ");
            c = sc.nextInt();
    
            if( g.Turn(r-1,c-1) == false )
            {
                System.out.println("Ungültige Eingabe, nochmal der selbe Spieler! Etwas zum weiterspielen eingeben...");
                sc.next(); // kann ich das noch anders verzögern damit es in Run() nicht sofort wegemacht wird durch \f ?
            }
        }
    }
    

    ComputerPlayer

    public class ComputerPlayer extends Player
    {
        public ComputerPlayer(char s)
        {
            super(s);
        }
    
        @Override
        public void Turn(TicTacToe g)
        {
        }
    }
    

    Main

    public class Main
    {
        public Main()
        {
            main();
        }
    
        public void main()
        {
            HumanPlayer A = new HumanPlayer('X');
            HumanPlayer B = new HumanPlayer('O');
            Field Board = new Field(3,3);
    
            TicTacToe game = new TicTacToe(A,B,Board);
            if( game.Run() == false ) { System.out.println("Error!"); }
        }
    }
    

    TicTacToe

    public class TicTacToe
    {
        private Field f;
        private Player p1, p2, current;
    
        public TicTacToe(Player a, Player b, Field c)
        {
            p1 = a;
            p2 = b;
            current = p1;
            f = c;
        }
    
        public boolean Turn(int r, int c)
        {
            if(f.GetCell(r,c) == ' ') { f.SetCell(current,r,c); }
            else { return false; }
    
            if(p1 == current) { current = p2; }
            else { current = p1; }
    
            return true;
        }
    
        public boolean IsEnd()
        {
            int CountOfEmptyCells = f.GetEmptyCells();
    
            if( CheckSeries( p1.GetSymbol() ) == true )
            {
                System.out.println("Spieler "+p1.GetSymbol()+" hat gewonnen!");
                p1.CountUp();
                return true;
            }
            else if( CheckSeries( p2.GetSymbol() ) == true )
            {
                System.out.println("Spieler "+p2.GetSymbol()+" hat gewonnen!");
                p2.CountUp();
                return true;
            }
    
            if(CountOfEmptyCells > 0) { return false; }
            else { return true; }
        }
    
        public boolean CheckSeries(char s)
        {
            /* Eigentlich wollte ich GetRows und GetColumns vermeiden, da die verwaltung die aufgabe von field ist, doch sah ich keine andere lösung wie seht ihr das? */
    
            int needed = 3; // Zahl der benötigten Symbole in einer reihe zum sieg
    
            // Von links nach rechts
            for(int i=0 ; i<f.GetRows() ; i++)
            {
                for(int j=0 ; j<f.GetColumns() ; j++)
                {
                    int count = 0;
                    char [] Cells = new char[needed];
                    for(int e=0 ; e<needed ; e++)
                    {
                        if(i >= 0 && i < f.GetRows())
                        {
                            if(j >= 0 && j+e < f.GetColumns())
                            {
                                Cells[e] = f.GetCell(i,j+e);
                                if(Cells[e] == s) { count++; }
                            }
                        }
                    }
                    if( count == needed ) { return true; }
                }
            }
    
            // von oben nach unten
            for(int i=0 ; i<f.GetRows() ; i++)
            {
                for(int j=0 ; j<f.GetColumns() ; j++)
                {
                    int count = 0;
                    char [] Cells = new char[needed];
                    for(int e=0 ; e<needed ; e++)
                    {
                        if(i >= 0 && i+e < f.GetRows())
                        {
                            if(j >= 0 && j < f.GetColumns())
                            {
                                Cells[e] = f.GetCell(i+e,j);
                                if(Cells[e] == s) { count++; }
                            }
                        }
                    }
                    if( count == needed ) { return true; }
                }
            }
    
            // diagonal
            for(int i=0 ; i<f.GetRows() ; i++)
            {
                for(int j=0 ; j<f.GetColumns() ; j++)
                {
                    int count = 0;
                    char [] Cells = new char[needed];
                    for(int e=0 ; e<needed ; e++)
                    {
                        if(i >= 0 && i+e < f.GetRows())
                        {
                            if(j >= 0 && j+e < f.GetColumns())
                            {
                                Cells[e] = f.GetCell(i+e,j+e);
                                if(Cells[e] == s) { count++; }
                            }
                        }
                    }
                    if( count == needed ) { return true; }
                }
            }
    
            return false;
        }
    
        public boolean Run()
        {
            while(IsEnd() == false)
            {
                if(current == p1) { p1.Turn(this); }
                else if(current == p2) { p2.Turn(this); }
                else { return false; }
                System.out.print("\f");
                f.Show();
                System.out.print("\n");
            }
    
            return true;
        }
    }
    

    Mfg Progger33


Anmelden zum Antworten