Verschachtelte bedingte Ausdrücke sind nicht so leicht zu lesen. Da hätte ich wahrscheinlich ``if``/``elif``/``else`` gewählt. Und das `b` als Abkürzung für `board` hätte ich mir auch gespart. Das bringt dem Leser IMHO nichts.
`newboard()` ist zwar hübsch funktional umgesetzt, aber es macht keinen wirklichen Sinn hier die Datenstruktur zu kopieren. Das könnte man durch eine `put_symbol()` ersetzen. Die dann allerdings so simpel ist, dass man sich fragen muss, ob man sie braucht.
Den `counter` in der `display()`-Funktion kann man mit `enumerate()` erzeugen. Das `x` auch für `O` stehen kann ist ein bisschen verwirrend. Man könnte es zum Beispiel `symbol` nennen. Die `print()`-Argumente wiederholen sich fast identisch. Das könnte man heraus ziehen und in den Zweigen nur den Wert bestimmen der sich verändert.
In der `main()` ist `turn` IMHO ein falscher Name. Daran ist ja kein Spielzug gebunden sondern ein Symbol, beziehungsweise der numerische Wert, der das Symbol repräsentiert.
Da `i` in der Schleife nicht verwendet wird, ist es nicht nötig, dass diese Schleife rückwärts „zählt”. Ein einfaches ``range(9)`` dürfte für die meisten Leser leichter zu verstehen sein. Selbst wenn man rückwärts von 9 bis 1 zählen will, fände ich ``reversed(range(1, 10))`` leichter zu lesen als ``range(9, 0, -1)``, wo zumindest ich immer erst überlegen muss von wo bis wo das nun genau geht. Noch besser wäre es die 9 gar nicht hart zu kodieren, sondern die Länge vom `board` zu verwenden. Damit hat man eine magische Zahl weniger im Quelltext.
Von ``continue`` bin ich kein grosser Fan, weil das ein wenig wie GOTO ist. Es wird von irgendwo mitten in der Schleife an den Anfang gesprungen und dieser Programmfluss spiegelt sich nicht in der Quelltextstruktur wieder. Man kann auch schlechter nachträglich Code am Ende des Schleifenkörpers einfügen, der grundsätzlich vor dem nächsten Durchlauf ausgeführt werden soll, wenn weiter oben ein ``continue`` sozusagen die Abkürzung zum Schleifenanfang nimmt. In konkreten Fall könnte man zum Beispiel einen ``else``-Zweig zur Ausnahmebehandlung hinzufügen.
Statt an mehreren Stellen im Quelltext eins von der Eingabe der Feldnummer abzuziehen, sollte man das gleich bei der Eingabe erledigen. Wenn man interene Werte nicht gleich nach der Benutzereingabe und kurz vor der Ausgabe umrechnet, bekommt man bei komplexeren Programmen schnell das Problem, dass man nicht mehr so genau weiss was wann verwendet wird und man sich intern in Umrechnungen verheddert.
*Vor* dem Zug das Spielersymbol zu wechseln ist verwirrend, denn das entspricht nicht der Vorgehensreihenfolge bei einem „echten” Spiel. Da legt man am Anfang fest wer anfängt, der macht einen Zug, und *dann* wechselt der Spieler.
`who_wins()` wird zweimal mit dem gleichen `board` als Argument aufgerufen. Das ergibt beide male das gleiche Ergebnis, ist also unnötiger Aufwand. Die beiden Zweige sind auch nahezu identisch. Die ganzen leeren `input()`-Aufrufe sollten aus dem Programm verschwinden. So ein unnötiges Bestätigen müssen nervt spätestens nach einer Weile ziemlich.
`sys.exit()` und damit auch das `sys`-Modul kann man sich mit ``return``-Anweisungen sparen.
Die Schreibweise von `Nothing` und das (nicht) setzen von Leerzeichen folgen nicht dem
Style Guide for Python Code.
Ungetestet:
Code: Alles auswählen
NOTHING = 0
X = 1
O = 4
def who_wins(board):
winningpos = list(
map(
sum,
[
board[:3], board[3:6], board[6:9], board[:7:3],
board[1:8:3], board[2:9:3], board[:9:4], board[2:7:2]
]
)
)
if 3 * O in winningpos:
return O
elif 3 * X in winningpos:
return X
else:
return NOTHING
def whos_next(player_symbol):
return O if player_symbol == X else X
def put_symbol(board, position, symbol):
board[position] = symbol
def display(board):
for counter, symbol in enumerate(board, 1):
if symbol == X:
value = ' X'
elif symbol == O:
value = ' O'
else:
value = ' ' + str(counter)
print(value, '|' if counter % 3 else '\n', end='')
def main():
board = [NOTHING] * 9
symbol = X
for _ in range(len(board)):
display(board)
while True:
try:
field = int(input('Welches Feld wollen Sie besetzen? ')) - 1
except ValueError:
print('Zahl eingeben')
else:
if board[field] == NOTHING:
break
else:
print('Dieses Feld ist schon besetzt')
put_symbol(board, field, symbol)
winner = who_wins(board)
if winner == O:
print("Spieler 'O' gewinnt")
return
elif winner == X:
print("Spieler 'X' gewinnt")
return
symbol = whos_next(symbol)
print('Unentschieden')
if __name__ == '__main__':
main()