Jeu de dés NEA Computing Task 2

Oct 21 2020

J'ai récemment été défini la tâche 2 comme indiqué ci-dessous et je réalise que quelqu'un répond à la question sur ce site ici mais je voulais un nouvel avis

TÂCHE 2:

  1. Permet à deux joueurs d'entrer leurs coordonnées, qui sont ensuite authentifiées pour s'assurer qu'ils sont des joueurs autorisés.
  2. Permet à chaque joueur de lancer deux dés à 6 faces.
  3. Calcule et produit les points pour chaque tour et le score total de chaque joueur.
  4. Permet aux joueurs de jouer 5 tours.
  5. Si les deux joueurs ont le même score après 5 tours, permet à chaque joueur de lancer 1 dé chacun jusqu'à ce que quelqu'un gagne.
  6. Sortants qui a gagné à l'issue des 5 tours.
  7. Stocke le score du gagnant et son nom dans un fichier externe.
  8. Affiche le score et le nom du joueur des 5 meilleurs scores gagnants du fichier externe.
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()

Toute réflexion / optimisation serait grandement appréciée

Réponses

5 hjpotter92 Oct 21 2020 at 15:21

Bienvenue dans Code Review!

PEP-8

En python, il est courant (et recommandé) de suivre le guide de style PEP-8 pour écrire du code propre, maintenable et cohérent.

Les fonctions et les variables doivent être nommées dans a lower_snake_case, les classes comme UpperCamelCaseet les constantes comme UPPER_SNAKE_CASE.

chaînes f

La f-string est nouvellement introduite dans python 3; donc au lieu d'avoir, print("string", variable, "string")vous pouvez faire:

print(f"string {variable} string")

pour le même effet.

Les fonctions

Divisez votre code en fonctions individuelles plus petites, en effectuant des tâches uniques. Quelques exemples seraient, la récupération de l'utilisateur / mot de passe users.txt, la validation des détails de l'utilisateur, la lecture des informations d'identification de l'utilisateur, etc.

if __name__ bloquer

Pour les scripts, il est recommandé de placer votre fonction exécutable dans la if __name__ == "__main__"clause.

Contrôle du flux

Vous essayez d'ouvrir le fichier des utilisateurs deux fois (il en va de même pour le fichier de scores). Ceci est suivi d'une définition de fonction, suivie d'un code pour amener les utilisateurs à se connecter. Ensuite, vous avez votre première instruction d'importation suivie d'une autre définition de fonction et de nouveau code plus tard.

Essayez de regrouper les blocs. En python (et presque tous les langages de programmation), les importations sont la toute première chose (après shebang). Puis les constantes globales, les définitions de fonctions / classes et ensuite le code de script lui-même.

with déclarations

Au lieu de gérer les descripteurs de fichiers ouverts et de contrôler plus tard la fermeture de ceux-ci, python a une withinstruction qui les encapsule pour vous:

with open(your_file, mode) as f:
    something = f.read()
    # f.write(something)

cette auto ferme (et garde dans la portée) le descripteur de fichier.

Attributs associés

Vous avez un élément player, qui a des propriétés associées telles que nom d'utilisateur, player_id, score. Cela peut être mis dans une classe, au lieu de conserver 10 variables différentes pour chaque joueur.

Méthodes intégrées

À de nombreux endroits de votre code, vous effectuez un fichier .replace("\n", ""). Ce n'est pas vraiment nécessaire, car les objets chaîne en python ont une .strip()méthode qui nettoie tous les espaces (et les nouvelles lignes).

De même, vous essayez de capturer un FileNotFoundErroruniquement pour générer une autre erreur pour la même raison. Laissez l'erreur spécifiquement définie pour le travail faire cela. Pas besoin de capturer si vous voulez que le programme échoue quand même en cas de fichier manquant.


Récrire

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()

REMARQUE

La réécriture permet plusieurs joueurs, ainsi que l'option de définir plusieurs tours ( NUM_PLAYERSet NUM_ROUNDS).

4 GrajdeanuAlex Oct 21 2020 at 14:37

Il y a beaucoup de choses dans votre code qui peuvent être refactorisées mais commençons par le début avec quelques guides de style Python (également appelés PEP8 )

Importations

Il est recommandé d'écrire toutes les importations en haut de votre fichier.

Appellation

En Python, le nom des fonctions et des variables doit être snake_case d. Autrement dit, au lieu de def Roll()vous devriez avoir def roll(), au lieu de Player1Scorevous devriez avoir player1_scoreet ainsi de suite. Vous avez l'idée. En savoir plus ici

Dites NON à `;` en Python!

Ne pas utiliser ;en Python. Ce n'est pas nécessaire et cela me rappelle les moments difficiles où j'utilisais C / C ++. Tu ne veux pas que je sois triste, n'est-ce pas? :(

Général

Il est généralement recommandé d'éviter les blocs de code en ligne. C'est difficile à suivre et cela n'a aucun avantage. Ce:

if Player1Score > Player2Score: Winner = 1

Doit être écrit comme suit:

if Player1Score > Player2Score: 
    Winner = 1

Cela dit, votre code jusqu'à présent, en tenant compte des conseils ci-dessus, ressemblerait à ceci:

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()

Maintenant, c'est un peu mieux. Discutons maintenant de la mise en œuvre réelle.

Ici:

try:
    file = open("Users.txt", "r")
except FileNotFoundError:
    raise SystemExit("User file not found")
file = open("Users.txt", "r")

Vous essayez d'ouvrir un fichier, et s'il n'existe pas, vous déclenchez une exception. Il n'y a rien de mal à cela, mais remarquez comment vous ouvrez le fichier deux fois si aucune exception n'est levée. Si vous voulez vérifier si un fichier existe ou non, vous pouvez utiliser le osmodule pour confirmer si un chemin de fichier existe et créer une fonction qui le fait. Faites également du chemin du fichier une constante et placez-le en haut de votre script:

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

Pour appeler la fonction, vous pouvez faire:

if check_path(USERS_FILEPATH):
    # do things here

Il y a quelques nouveautés ici. Tout d'abord, remarquez comment j'ai ajouté une docstring à cette fonction et combien il est facile de dire ce qu'elle fait réellement. Deuxièmement, remarquez à quel point il est facile d'utiliser cette fonction sur tous les autres fichiers que votre jeu pourrait utiliser. Troisièmement, regardez comment la chaîne est formatée lorsque je lève une exception. Vous pouvez en savoir plus sur le formatage des chaînes de Python ici .

// faut y aller pour l'instant