NEA Computing Task 2 Würfelspiel
Ich habe vor kurzem Aufgabe 2 wie unten gezeigt eingestellt und mir ist klar, dass jemand die Frage auf dieser Seite hier beantwortet , aber ich wollte eine neue Meinung
AUFGABE 2:
- Ermöglicht zwei Spielern die Eingabe ihrer Daten, die dann authentifiziert werden, um sicherzustellen, dass sie autorisierte Spieler sind.
- Ermöglicht jedem Spieler, zwei 6-seitige Würfel zu würfeln.
- Berechnet und gibt die Punkte für jede Runde und die Gesamtpunktzahl jedes Spielers aus.
- Ermöglicht den Spielern, 5 Runden zu spielen.
- Wenn beide Spieler nach 5 Runden die gleiche Punktzahl haben, kann jeder Spieler jeweils 1 Würfel werfen, bis jemand gewinnt.
- Outputs, die am Ende der 5 Runden gewonnen haben.
- Speichert die Punktzahl des Gewinners und seinen Namen in einer externen Datei.
- Zeigt die Punktzahl und den Spielernamen der 5 besten Gewinnpunkte aus der externen Datei an.
try: File = open("Users.txt","r")
except FileNotFoundError:
raise SystemExit("User file not found")
File = open("Users.txt", "r")
def Login(Username, Player):
File.seek(0)
for Line in File:
ValidUsername = Line.split(",")[0]
ValidPassword = Line.split(",")[1].replace("\n", "")
if Username == ValidUsername:
Password = input("Password: ")
if Password == ValidPassword:
print("Player",Player,"logged in")
print("")
return True
else: print("Invalid Details")
return False
try:
while True:
print("Player 1 Login")
Username1 = input("Username: ")
if Login(Username1, 1): break
print("")
while True:
print("Player 2 Login")
Username2 = input("Username: ")
if Username1 == Username2:
print("Double login detected")
elif Login(Username2, 2): break
print("")
except KeyboardInterrupt:
raise SystemExit("Exiting...")
finally:
File.close()
import random
Player1Score = 0
Player2Score = 0
def Roll():
Dice1 = random.randint(1, 6)
Dice2 = random.randint(1, 6)
print("You rolled a",Dice1,"and a",Dice2)
Change = Dice1 + Dice2
Change += 10 if (Dice1 + Dice2) % 2 == 0 else -5
if Change < 0: Change = 0
if Dice1 == Dice2:
Dice3 = random.randint(1, 6)
print("Your third roll is a",Dice3)
Change += Dice3
print("")
return Change
for X in range(5):
print("Play:",X + 1,"starting")
input("Player 1, press enter to roll: ")
Player1Score += Roll()
input("Player 2, press enter to roll: ")
Player2Score += Roll()
print("Player 1 now has a score of",Player1Score)
print("Player 2 now has a score of",Player2Score)
print("")
if Player1Score > Player2Score: Winner = 1
if Player1Score < Player2Score: Winner = 2
if Player1Score == Player2Score:
print("You both got the same score")
def SameScore():
input("Press enter to roll dice: ")
print("")
Dice1 = random.randint(1, 6)
Dice2 = random.randint(1, 6)
print("Player 1 rolled:",Dice1)
print("Player 2 rolled:",Dice2)
if Dice1 == Dice2: return False
if Dice1 > Dice2: return 1
if Dice1 < Dice2: return 2
Winner = False
while not Winner:
Winner = SameScore()
if Winner == 1:
Winner = Username1 + ": " + str(Player1Score)
print(Username1,"won with",Player1Score,"points")
print(Username2,"lost with",Player2Score,"points")
if Winner == 2:
Winner = Username2 + ": " + str(Player2Score)
print(Username2,"won with",Player2Score,"points")
print(Username1,"lost with",Player1Score,"points")
WinnerScore = int(Winner.split(": ")[1])
FileWritten = False
try:
File = open("Scores.txt", "r")
Data = File.readlines();File.close()
for X in range(len(Data)):
if WinnerScore > int(Data[X].split(": ")[1]):
Data.insert(X, Winner + "\n")
if len(Data) > 5: Data.pop(5)
FileWritten = True; break
if len(Data) < 5:
if not FileWritten: Data.append(Winner + "\n")
File = open("Scores.txt","w")
for X in Data:
File.write(X.replace("\n","") + "\n")
except FileNotFoundError:
File = open("Scores.txt","w")
File.write(Winner + "\n")
File.close()
print("")
File = open("Scores.txt","r")
print("Highscores:")
for Line in File:
if Line != "": print(Line.replace("\n", ""))
File.close()
Alle Gedanken / Optimierungen wäre sehr dankbar
Antworten
Willkommen bei Code Review!
PEP-8
In Python ist es üblich (und empfohlen), den PEP-8-Styleguide zu befolgen, um sauberen, wartbaren und konsistenten Code zu schreiben.
Funktionen und Variablen sollten in a lower_snake_case, Klassen als UpperCamelCaseund Konstanten als benannt werden UPPER_SNAKE_CASE.
F-Saiten
Neu in Python 3 eingeführt ist der F-String; Anstatt print("string", variable, "string")Sie zu haben, können Sie Folgendes tun:
print(f"string {variable} string")
für den gleichen Effekt.
Funktionen
Teilen Sie Ihren Code in einzelne kleinere Funktionen auf und erledigen Sie einzelne Aufgaben. Einige Beispiele wären das Abrufen von Benutzer / Kennwort von users.txt, das Überprüfen von Benutzerdetails, das Lesen von Benutzeranmeldeinformationen usw.
if __name__ Block
Bei Skripten empfiehlt es sich, Ihre ausführbare Funktion in die if __name__ == "__main__"Klausel aufzunehmen.
Kontrollfluss
Sie versuchen, die Benutzerdatei zweimal zu öffnen (dies gilt auch für die Punktedatei). Darauf folgt eine Funktionsdefinition, gefolgt von Code, mit dem Benutzer sich anmelden können. Dann haben Sie Ihre erste Importanweisung, gefolgt von einer weiteren Funktionsdefinition und späterem Code.
Versuchen Sie, Blöcke zu gruppieren. In Python (und fast allen Programmiersprachen) sind Importe das allererste (nach Shebang). Dann globale Konstanten, Funktionen / Klassendefinitionen und dann der Skriptcode selbst.
with Aussagen
Anstatt geöffnete Dateideskriptoren zu verwalten und später deren Schließen zu steuern, verfügt Python über eine withAnweisung, die dies für Sie umschließt:
with open(your_file, mode) as f:
something = f.read()
# f.write(something)
Dadurch wird der Dateideskriptor automatisch geschlossen (und bleibt im Gültigkeitsbereich).
Zugehörige Attribute
Sie haben ein Spielerelement, dem Eigenschaften wie Benutzername, Spieler-ID und Punktzahl zugeordnet sind. Dies kann in eine Klasse eingeteilt werden, anstatt 10 verschiedene Variablen für jeden Spieler zu verwalten.
Eingebaute Methoden
An vielen Stellen in Ihrem Code machen Sie eine .replace("\n", ""). Dies ist nicht wirklich erforderlich, da String-Objekte in Python über eine .strip()Methode verfügen , mit der alle Leerzeichen (und Zeilenumbrüche) bereinigt werden.
In ähnlicher Weise versuchen Sie, einen zu erfassen FileNotFoundError, um aus demselben Grund einen weiteren Fehler auszulösen. Lassen Sie den speziell für den Job definierten Fehler dies tun. Sie müssen nicht erfassen, wenn das Programm bei fehlender Datei trotzdem fehlschlagen soll.
Umschreiben
from operator import itemgetter
import random
USERS_FILE: str = "Users.txt"
SCORES_FILE: str = "Scores.txt"
NUM_PLAYERS: int = 2
NUM_ROUNDS: int = 5
def roll_die() -> int:
return random.randint(1, 6)
class Player:
def __init__(self, _id: int, name: str):
self._id = _id
self.name = name
self.score = 0
def add_score(self, value: int):
self.score += value
if self.score < 0:
self.score = 0
def turn(self):
input(f"{self.name}'s turn. Press enter to roll.")
dice_1 = roll_die()
dice_2 = roll_die()
print(f"{self.name} rolled {dice_1} and {dice_2}.")
change = dice_1 + dice_2
change += 10 if change % 2 == 0 else -5
if change < 0:
change = 0
if dice_1 == dice_2:
dice_3 = roll_die()
print(f"Third roll is {dice_3}")
change += dice_3
self.add_score(change)
def __str__(self) -> str:
return f"Player({self.name}): {self.score}"
def fetch_users() -> dict:
users = {}
with open(USERS_FILE, "r") as f:
users = dict([line.strip().split(",") for line in f])
return users
def authenticate(users: dict, name: str, password: str) -> bool:
return users.get(name) == password
def show_highscores() -> None:
with open(SCORES_FILE, "r") as f:
print(f.read())
def fetch_highscores() -> list:
scores = []
with open(SCORES_FILE, "r") as f:
for line in f:
name, score = line.strip().split(": ")
score = int(score)
scores.append((name, score))
return scores
def write_score(player: Player, limit: int = 5):
current_highscores = fetch_highscores()
current_highscores.append((player.name, player.score))
sorted_scores = sorted(current_highscores, key=itemgetter(1), reverse=True)
with open(SCORES_FILE, "w") as f:
for name, score in sorted_scores[:limit]:
f.write(f"{name}: {score}\n")
def get_player(_id: int, users: dict) -> Player:
print(f"Players {_id} login")
while True:
name = input("username: ")
password = input("password: ")
if authenticate(users, name, password):
return Player(_id, name)
print("Invalid details. Try again!")
def get_winning_player(players: list) -> Player:
def filter_winning_players(player_dices: list, value: int):
filtered = []
for player, dice in player_dices:
print(f"{player.name} rolled {dice}.")
if dice == value:
filtered.append(player)
return filtered
max_score = max([player.score for player in players])
winners = [player for player in players if player.score == max_score]
if len(winners) == 1:
return winners[0]
print(f"{len(winners)} players have the same score. Trying to determine single winning player.")
while True:
input("Press enter to roll dice: ")
dices = [roll_die() for _ in range(len(winners))]
max_dice = max(dices)
winners = filter_winning_players(zip(winners, dices), max_dice)
if len(winners) == 1:
return winners[0]
def game():
users = fetch_users()
players = [get_player(count + 1, users) for count in range(NUM_PLAYERS)]
for round in range(1, NUM_ROUNDS + 1):
for player in players:
player.turn()
print(f"Player scores at end of round {round}:")
for player in players:
print(str(player))
winner = get_winning_player(players)
print(f"Winner is {winner}.")
write_score(winner)
print("Highscores: ")
show_highscores()
if __name__ == "__main__":
game()
HINWEIS
Das Umschreiben ermöglicht mehrere Spieler sowie die Option, mehrere Runden ( NUM_PLAYERSund NUM_ROUNDS) festzulegen .
Es gibt eine Menge in Ihrem Code, die überarbeitet werden kann, aber beginnen wir von vorne mit einigen Python-Styleguides (auch PEP8 genannt ).
Importe
Es wird empfohlen, alle Importe oben in Ihre Datei zu schreiben.
Benennung
In Python sollte der Name der Funktionen und Variablen snake_case sein. D. Das heißt, anstatt def Roll()Sie sollten haben def roll(), anstatt Player1ScoreSie sollten haben player1_scoreund so weiter. Du hast die Idee. Lesen Sie hier mehr darüber
Sag NEIN zu `;` in Python!
Nicht ;in Python verwenden. Es wird nicht benötigt und erinnert mich an die schweren Zeiten, in denen ich C / C ++ verwendet habe. Du willst doch nicht, dass ich traurig bin, oder? :(
Allgemeines
Es wird normalerweise empfohlen, Inline-Codeblöcke zu vermeiden. Es ist schwer zu folgen und es hat keine Vorteile. Diese:
if Player1Score > Player2Score: Winner = 1
Sollte geschrieben werden als:
if Player1Score > Player2Score:
Winner = 1
Das heißt, Ihr bisheriger Code würde unter Berücksichtigung der oben genannten Ratschläge folgendermaßen aussehen:
import random
try:
file = open("Users.txt", "r")
except FileNotFoundError:
raise SystemExit("User file not found")
file = open("Users.txt", "r")
def same_score():
input("Press enter to roll dice: ")
print("")
dice_1 = random.randint(1, 6)
dice_2 = random.randint(1, 6)
print("player 1 rolled:", dice_1)
print("player 2 rolled:", dice_2)
if dice_1 == dice_2:
return False
if dice_1 > dice_2:
return 1
if dice_1 < dice_2:
return 2
def login(username, player):
file.seek(0)
for line in file:
valid_username = line.split(",")[0]
valid_password = line.split(",")[1].replace("\n", "")
if username == valid_username:
password = input("password: ")
if password == valid_password:
print("player", player, "logged in")
print("")
return True
else:
print("Invalid Details")
return False
def roll():
dice1 = random.randint(1, 6)
dice2 = random.randint(1, 6)
print("You rolled a", dice1, "and a", dice2)
change = dice1 + dice2
change += 10 if (dice1 + dice2) % 2 == 0 else -5
if change < 0:
change = 0
if dice1 == dice2:
dice3 = random.randint(1, 6)
print("Your third roll is a",dice3)
change += dice3
print("")
return change
try:
while True:
print("player 1 login")
username1 = input("username: ")
if login(username1, 1):
break
print("")
while True:
print("player 2 login")
username2 = input("username: ")
if username1 == username2:
print("Double login detected")
elif login(username2, 2):
break
print("")
except KeyboardInterrupt:
raise SystemExit("Exiting...")
finally:
file.close()
player1_score = 0
player2_score = 0
for x in range(5):
print("Play:", x + 1, "starting")
input("player 1, press enter to roll: ")
player1_score += roll()
input("player 2, press enter to roll: ")
player2_score += roll()
print("player 1 now has a score of", player1_score)
print("player 2 now has a score of", player2_score)
print("")
if player1_score > player2_score:
winner = 1
if player1_score < player2_score:
winner = 2
if player1_score == player2_score:
print("You both got the same score")
winner = False
while not winner:
winner = same_score()
if winner == 1:
winner = username1 + ": " + str(player1_score)
print(username1, "won with", player1_score, "points")
print(username2, "lost with", player2_score, "points")
if winner == 2:
winner = username2 + ": " + str(player2_score)
print(username2, "won with", player2_score, "points")
print(username1, "lost with", player1_score, "points")
winner_score = int(winner.split(": ")[1])
file_written = False
try:
file = open("Scores.txt", "r")
data = file.readlines()
file.close()
for x in range(len(data)):
if winner_score > int(data[x].split(": ")[1]):
data.insert(x, winner + "\n")
if len(data) > 5:
data.pop(5)
file_written = True
break
if len(data) < 5:
if not file_written:
data.append(winner + "\n")
file = open("Scores.txt", "w")
for x in data:
file.write(x.replace("\n", "") + "\n")
except FileNotFoundError:
file = open("Scores.txt", "w")
file.write(winner + "\n")
file.close()
print("")
file = open("Scores.txt", "r")
print("Highscores:")
for line in file:
if line != "":
print(line.replace("\n", ""))
file.close()
Das ist ein bisschen besser. Lassen Sie uns jetzt die tatsächliche Implementierung diskutieren.
Hier:
try:
file = open("Users.txt", "r")
except FileNotFoundError:
raise SystemExit("User file not found")
file = open("Users.txt", "r")
Sie versuchen, eine Datei zu öffnen, und wenn sie nicht vorhanden ist, lösen Sie eine Ausnahme aus. Daran ist nicht unbedingt etwas auszusetzen. Beachten Sie jedoch, wie Sie die Datei zweimal öffnen, wenn keine Ausnahme vorliegt. Wenn Sie überprüfen möchten, ob eine Datei vorhanden ist oder nicht, können Sie mit dem osModul überprüfen, ob ein Dateipfad vorhanden ist, und eine Funktion erstellen, die dies tut. Machen Sie den Dateipfad auch zu einer Konstante und setzen Sie ihn oben in Ihr Skript:
import os
USERS_FILEPATH = '/path/to/Users.txt'
def check_file(filepath):
"""Verify if a filepath exists.
Return True if a filepath exists. Otherwise raise an exception.
Arguments:
filepath (str): Path to a file
Returns:
True or raise an exception
"""
if not os.path.exists(filepath):
raise OSError('User filepath {} not found.'.format(filepath))
return True
Um die Funktion aufzurufen, können Sie Folgendes tun:
if check_path(USERS_FILEPATH):
# do things here
Hier gibt es ein paar neue Dinge. Beachten Sie zunächst, wie ich dieser Funktion eine Dokumentzeichenfolge hinzugefügt habe und wie einfach es ist, festzustellen, was sie tatsächlich tut. Beachten Sie zweitens, wie einfach es ist, diese Funktion für andere Dateien zu verwenden, die Ihr Spiel möglicherweise verwendet. Schauen Sie sich drittens an, wie die Zeichenfolge formatiert ist, wenn ich eine Ausnahme auslöse. Weitere Informationen zur Formatierung von Python-Zeichenfolgen finden Sie hier .
// muss erstmal gehen