Seite 1 von 1

Warum werden in diesem Code Klassen verwendet?

Verfasst: Samstag 13. Mai 2023, 16:03
von Pythonfan3000
Warum werden in diesem Code Klassen verwendet?

Also was für einen Vorteil hat das?:

Der Code ist für ein Tic-Tac-Toe-Spiel

Code: Alles auswählen

class Board():
    def __init__(self):
        self.state = [0, 0, 0, 0, 0, 0, 0, 0, 0]

    def make_turn(self, cell, player):
        if self.is_valid_turn(cell):
            self.state[cell] = player.symbol
            return True
        return False

    def is_valid_turn(self, cell):
        if self.state[cell] == 0:
            return True
        else:
            return False

    def check_win(self, player):
        s = player.symbol
        if self.state[0] == s and self.state[1] == s and self.state[2] == s:
            return True
        elif self.state[3] == s and self.state[4] == s and self.state[5] == s:
            return True
        elif self.state[6] == s and self.state[7] == s and self.state[8] == s:
            return True

        elif self.state[0] == s and self.state[3] == s and self.state[6] == s:
            return True
        elif self.state[1] == s and self.state[4] == s and self.state[7] == s:
            return True
        elif self.state[2] == s and self.state[5] == s and self.state[8] == s:
            return True

        elif self.state[0] == s and self.state[4] == s and self.state[8] == s:
            return True
        elif self.state[2] == s and self.state[4] == s and self.state[6] == s:
            return True

    def is_full(self):
        for i in self.state:
            if i == 0:
                return False
        return True

    def sign_to_printable(self, sign):
        if sign == 0:
            return " "
        elif sign == 1:
            return "X"
        else:
            return "O"

    def print_board(self):
        print(" " + self.sign_to_printable(self.state[0]) + " | " + self.sign_to_printable(self.state[1]) + " | " + self.sign_to_printable(self.state[2]) + " \n" +
              " " + self.sign_to_printable(self.state[3]) + " | " + self.sign_to_printable(self.state[4]) + " | " + self.sign_to_printable(self.state[5]) + " \n" +
              " " + self.sign_to_printable(self.state[6]) + " | " + self.sign_to_printable(self.state[7]) + " | " + self.sign_to_printable(self.state[8]) + " \n")


class Player:
    def __init__(self, symbol):
        self.symbol = symbol


if __name__ == '__main__':
    player_a = Player(1)
    player_b = Player(-1)
    board = Board()
    active_player = player_a
    while not board.is_full():
        board.print_board()
        try:
            cell = int(input("Where do you want to place your sign? [1-9]"))
        except ValueError:
            continue
        cell = cell - 1
        if cell < 0 or cell > 8:
            print("Please enter a number between 1 and 9")
            continue
        if not board.make_turn(cell, active_player):
            print("Invalid Move")
            continue

        if board.check_win(active_player):
            print("You wonnered! GW.")
            break

        if …

Re: Warum werden in diesem Code Klassen verwendet?

Verfasst: Samstag 13. Mai 2023, 16:36
von grubenfox
Mein erster Gedanke beim Lesen der Frage: "Warum nicht, so ist alles zusammen was zusammen gehört..." aber wenn das obige die kompletten Klassen sind, dann könnte man da wohl auch wirklich drauf verzichten. Die Klasse 'Player' ist ja irgendwie dermaßen über(flüssig) und wenn die raus ist dann bleibt nur noch 'Board' und auf die könnte man beim obigen Code auf verzichten und aus den Methoden einfach Funktionen machen.

Also da frage ich mich ja auch: was soll das, warum sind da Klassen. Gegen ´Board' habe ich ja nichts, aber 'Player´?

Re: Warum werden in diesem Code Klassen verwendet?

Verfasst: Samstag 13. Mai 2023, 16:43
von grubenfox
Man müsste die Funktionen natürlich im Vergleich zu den obigen Methoden noch so anpassen, dass die Funktionen irgendwie Zugriff auf das Spielbrett mit den Spielfeldern bekommt (also oben im Code das 'state´)

Re: Warum werden in diesem Code Klassen verwendet?

Verfasst: Samstag 13. Mai 2023, 16:45
von Sirius3
Klassen benutzt man, um komplexe Zustände mit den Methoden zu verbinden, die mit den Zuständen arbeiten.
Nun ist hier der Zustand nicht sehr komplex, so dass man sich tatsächlich fragen kann, ob die Klasse die Lesbarkeit erhöht. Bei der Player-Klassei st das sogar sehr fraglich.

Das Brett enthält die Symbole X O und _, die Indirektion mit den Zahlen macht den Code nur umständlich, sollte man also nicht machen.
Code zu kopieren und leicht zu verändern ist nicht sehr wartbar. Wenn man zum Beispiel die unglückliche Abkürzung `s` in symbol umbenennen möchte, muß man das an sehr vielen Stellen machen.
Dass an mehreren Stellen geprüft wird, ob der Zug gültig ist, und das auch noch innerhalb der Hauptschleife macht den Code schwer lesbar.
Es sollte eine main-Funktion geben.

Code: Alles auswählen

PLAYER_A = "X"
PLAYER_B = "O"
EMPTY = " "

class Board():
    LINES = [
        (0, 1, 2),
        (3, 4, 5),
        (6, 7, 8),
        (0, 3, 6),
        (1, 4, 7),
        (2, 5, 8),
        (0, 4, 8),
        (2, 4, 6),
    ]

    def __init__(self):
        self.state = [EMPTY] * 9

    def make_turn(self, cell, symbol):
        self.state[cell - 1] = symbol

    def is_empty_cell(self, cell):
        return self.state[cell - 1] == 0

    def check_win(self, symbol):
        for line in self.LINES:
            if all(self.start[i] == symbol for i in line):
                return True
        return False

    def is_full(self):
        return not any(c == EMPTY for c in self.state)

    def print_board(self):
        for i in [0, 3, 6]:
            print('|'.join(f" {c} " for c in self.state[i: i+3]))


def input_cell(board):
    while True:
        try:
            cell = int(input("Where do you want to place your sign? [1-9]"))
        except ValueError:
            pass
        else:
            if 1 < cell < 9:
                print("Please enter a number between 1 and 9")
            elif not board.is_empty_cell(cell):
                print("Invalid Move")
            else:
                return cell


def main():
    board = Board()
    active_player = PLAYER_A
    while not board.is_full():
        board.print_board()
        cell = input_cell(board)
        board.make_turn(cell, active_player):
        if board.check_win(active_player):
            print("You won! GW.")
            break
        if active_player == PLAYER_A:
            active_player = PLAYER_B
        else:
            active_player = PLAYER_A

if __name__ == '__main__':
    main()

Re: Warum werden in diesem Code Klassen verwendet?

Verfasst: Samstag 13. Mai 2023, 17:14
von __blackjack__
In `check_win()` könnte man noch `any()` verwenden, statt der Schleife und den ``return True``/``return False``-Anweisungen.

`is_full()` lässt sich einfacher ausdrücken: ``EMPTY not in self.state``.

Ich wiederhole ungern den Typnamen in Methodennamen, denn ``board.print_board()`` ist enthält nicht wirklich mehr sinnvolle Information als ``board.print()``. Noch eher würde ich ein ``print(board)`` bevorzugen. Dazu müsste man die `__str__()`-Methode entsprechend implementieren.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
PLAYER_A = "X"
PLAYER_B = "O"
EMPTY = " "


class Board:
    LINES = [
        (0, 1, 2),
        (3, 4, 5),
        (6, 7, 8),
        (0, 3, 6),
        (1, 4, 7),
        (2, 5, 8),
        (0, 4, 8),
        (2, 4, 6),
    ]

    def __init__(self):
        self.state = [EMPTY] * 9

    def __str__(self):
        return "\n".join(
            "|".join(f" {c} " for c in self.state[i : i + 3])
            for i in [0, 3, 6]
        )

    def make_turn(self, cell, symbol):
        self.state[cell - 1] = symbol

    def is_empty_cell(self, cell):
        return self.state[cell - 1] == EMPTY

    def check_win(self, symbol):
        return any(
            all(self.state[i] == symbol for i in line) for line in self.LINES
        )

    def is_full(self):
        return EMPTY not in self.state


def input_cell(board):
    while True:
        try:
            cell = int(input("Where do you want to place your sign? [1-9]"))
        except ValueError:
            pass
        else:
            if 1 < cell < 9:
                print("Please enter a number between 1 and 9")
            elif not board.is_empty_cell(cell):
                print("Invalid Move")
            else:
                return cell


def main():
    board = Board()
    active_player = PLAYER_A
    while not board.is_full():
        print(board)
        board.make_turn(input_cell(board), active_player)
        if board.check_win(active_player):
            print("You won! GW.")
            break

        active_player = PLAYER_B if active_player == PLAYER_A else PLAYER_A


if __name__ == "__main__":
    main()

Re: Warum werden in diesem Code Klassen verwendet?

Verfasst: Montag 15. Mai 2023, 16:39
von grubenfox
Pythonfan3000 hat geschrieben: Samstag 13. Mai 2023, 16:03 Warum werden in diesem Code Klassen verwendet?

Also was für einen Vorteil hat das?:
Wenn man mal dieses Code-Beispiel beiseite lässt und ganz allgemein fragt... vielleicht hilft dies etwas: https://docs.python-guide.org/writing/s ... rogramming