NEA Computing Task 2 игра в кости

Oct 21 2020

Я недавно поставил задачу 2, как показано ниже, и я понимаю, что кто-то отвечает на вопрос на этом сайте здесь, но мне нужно новое мнение

ЗАДАЧА 2:

  1. Позволяет двум игрокам вводить свои данные, которые затем проходят аутентификацию, чтобы гарантировать, что они являются авторизованными игроками.
  2. Позволяет каждому игроку бросить два шестигранных кубика.
  3. Подсчитывает и выводит очки для каждого раунда и общий счет каждого игрока.
  4. Позволяет игрокам сыграть 5 раундов.
  5. Если оба игрока имеют одинаковый счет после 5 раундов, позволяет каждому игроку бросить по 1 кубику, пока кто-нибудь не выиграет.
  6. Выводит, кто выиграл в конце 5 раундов.
  7. Сохраняет счет победителя и его имя во внешнем файле.
  8. Отображает счет и имя игрока из 5 лучших результатов из внешнего файла.
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()

Приветствуются любые мысли / оптимизации

Ответы

5 hjpotter92 Oct 21 2020 at 15:21

Добро пожаловать в Code Review!

PEP-8

В Python принято (и рекомендуется) следовать руководству по стилю PEP-8 для написания чистого, поддерживаемого и последовательного кода.

Функции и переменные должны быть названы в lower_snake_case, классы как UpperCamelCase, а константы как UPPER_SNAKE_CASE.

ф-струны

В Python 3 впервые появилась строка f; поэтому вместо того, чтобы print("string", variable, "string")вы могли:

print(f"string {variable} string")

для того же эффекта.

Функции

Разделите свой код на отдельные более мелкие функции, выполняющие отдельные задачи. Вот несколько примеров: получение пользователя / пароля users.txt, проверка данных пользователя, чтение учетных данных пользователя и так далее.

if __name__ блокировать

Для скриптов рекомендуется помещать исполняемую функцию в if __name__ == "__main__"предложение.

Поток управления

Вы пытаетесь открыть файл пользователей дважды (то же самое верно и для файла оценок). За ним следует определение функции и код, побуждающий пользователей войти в систему. Затем у вас есть первый оператор импорта, за которым следует еще одно определение функции, а затем снова код.

Попробуйте сгруппировать блоки вместе. В Python (и почти во всех языках программирования) импорт - это самое первое (после shebang). Затем глобальные константы, определения функций / классов, а затем сам код скрипта.

with заявления

Вместо того, чтобы управлять открытыми файловыми дескрипторами, а затем контролировать их закрытие, в python есть withинструкция, которая обертывает это для вас:

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

это автоматически закрывает (и сохраняет в пределах видимости) файловый дескриптор.

Связанные атрибуты

У вас есть элемент player, который имеет связанные свойства, такие как имя пользователя, player_id, score. Это можно поместить в класс вместо того, чтобы поддерживать 10 разных переменных для каждого игрока.

Встроенные методы

Во многих местах вашего кода вы выполняете .replace("\n", ""). На самом деле это не требуется, поскольку строковые объекты в Python имеют .strip()метод, который очищает все пробелы (и новые строки).

Точно так же вы пытаетесь захватить FileNotFoundErrorтолько для того, чтобы вызвать другую ошибку по той же причине. Пусть это сделает ошибка, специально определенная для работы. Нет необходимости в захвате, если вы все равно хотите, чтобы программа вылетела из строя в случае отсутствия файла.


Переписать

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

ЗАМЕТКА

Перезапись позволяет нескольким игрокам, а также возможность установить несколько раундов ( NUM_PLAYERSи NUM_ROUNDS).

4 GrajdeanuAlex Oct 21 2020 at 14:37

В вашем коде многое можно отредактировать, но давайте начнем с самого начала с некоторых руководств по стилям Python (также называемых PEP8 ).

Импорт

Рекомендуется записывать весь импорт в верхней части файла.

Именование

В Python имя функций и переменных должно быть snake_case d. То есть вместо того def Roll(), чтобы иметь def roll(), вместо того Player1Score, чтобы иметь player1_scoreи так далее. Вы поняли идею. Подробнее об этом здесь

Скажите НЕТ `;` в Python!

Не используйте ;в Python. Это не нужно, и это заставляет меня вспомнить тяжелые времена, когда я использовал C / C ++. Вы же не хотите, чтобы я грустил? :(

генеральный

Обычно рекомендуется избегать встроенных блоков кода. За этим сложно следить, и у этого нет никаких преимуществ. Этот:

if Player1Score > Player2Score: Winner = 1

Следует записать как:

if Player1Score > Player2Score: 
    Winner = 1

Тем не менее, ваш код с учетом приведенного выше совета будет выглядеть следующим образом:

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

Теперь это немного лучше. Давайте теперь обсудим реальную реализацию.

Вот:

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

Вы пытаетесь открыть файл, и если он не существует, вы вызываете исключение. В этом нет ничего плохого, но обратите внимание, как вы открываете файл два раза, если не возникает исключения. Если вы хотите проверить, существует ли файл или нет, вы можете использовать osмодуль, чтобы подтвердить, существует ли путь к файлу, и создать функцию, которая это делает. Также сделайте путь к файлу константой и поместите его в начало вашего скрипта:

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

Чтобы вызвать функцию, вы можете:

if check_path(USERS_FILEPATH):
    # do things here

Здесь есть пара новинок. Во-первых, обратите внимание, как я добавил к этой функции строку документации и насколько легко понять, что она на самом деле делает. Во-вторых, обратите внимание, насколько легко использовать эту функцию для любых других файлов, которые может использовать ваша игра. В-третьих, посмотрите, как форматируется строка, когда я вызываю исключение. Вы можете узнать больше о форматировании строк Python здесь .

// нужно идти сейчас