NEA Computing Task 2 Würfelspiel

Oct 21 2020

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:

  1. Ermöglicht zwei Spielern die Eingabe ihrer Daten, die dann authentifiziert werden, um sicherzustellen, dass sie autorisierte Spieler sind.
  2. Ermöglicht jedem Spieler, zwei 6-seitige Würfel zu würfeln.
  3. Berechnet und gibt die Punkte für jede Runde und die Gesamtpunktzahl jedes Spielers aus.
  4. Ermöglicht den Spielern, 5 Runden zu spielen.
  5. Wenn beide Spieler nach 5 Runden die gleiche Punktzahl haben, kann jeder Spieler jeweils 1 Würfel werfen, bis jemand gewinnt.
  6. Outputs, die am Ende der 5 Runden gewonnen haben.
  7. Speichert die Punktzahl des Gewinners und seinen Namen in einer externen Datei.
  8. 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

5 hjpotter92 Oct 21 2020 at 15:21

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 .

4 GrajdeanuAlex Oct 21 2020 at 14:37

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