NEA Computing Task 2 เกมลูกเต๋า

Oct 21 2020

ฉันเพิ่งตั้งภารกิจที่ 2 ตามที่แสดงด้านล่างและฉันรู้ว่ามีคนตอบคำถามในไซต์นี้ที่นี่แต่ฉันต้องการความคิดเห็นใหม่

ภารกิจที่ 2:

  1. อนุญาตให้ผู้เล่นสองคนป้อนรายละเอียดซึ่งจะได้รับการรับรองความถูกต้องเพื่อให้แน่ใจว่าเป็นผู้เล่นที่ได้รับอนุญาต
  2. อนุญาตให้ผู้เล่นแต่ละคนทอยลูกเต๋า 6 เหลี่ยมสองลูก
  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ชั้นเรียนเป็นและคงเป็นUpperCamelCaseUPPER_SNAKE_CASE

f- สตริง

แนะนำใหม่ใน python 3 คือ f-string; ดังนั้นแทนที่จะมี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)

อัตโนมัตินี้จะปิด (และเก็บไว้ในขอบเขต) ตัวอธิบายไฟล์

แอตทริบิวต์ที่เกี่ยวข้อง

คุณมีองค์ประกอบของผู้เล่นซึ่งมีคุณสมบัติที่เกี่ยวข้องเช่น username, player_id, score สิ่งนี้สามารถใส่ลงในคลาสแทนที่จะรักษาตัวแปรที่แตกต่างกัน 10 ตัวแปรสำหรับผู้เล่นแต่ละคน

วิธีการ Inbuilt

ในหลาย ๆ ที่ในโค้ดของคุณคุณกำลังทำไฟล์.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

มีโค้ดของคุณมากมายที่สามารถ refactored ได้ แต่เรามาเริ่มกันตั้งแต่ต้นด้วย Python styleguides (เรียกอีกอย่างว่าPEP8 )

การนำเข้า

ขอแนะนำให้เขียนการนำเข้าทั้งหมดที่ด้านบนของไฟล์

การตั้งชื่อ

ใน Python ชื่อของฟังก์ชันและตัวแปรควรเป็นsnake_case d นั่นคือแทนที่จะเป็นdef Roll()คุณควรมีdef roll()แทนที่จะเป็นPlayer1Scoreคุณควรมีplayer1_scoreไปเรื่อย ๆ คุณมีความคิด อ่านเพิ่มเติมได้ที่นี่

พูดว่า NO กับ `;` ใน 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โมดูลเพื่อยืนยันว่ามีพา ธ ไฟล์อยู่หรือไม่และสร้างฟังก์ชันที่ทำเช่นนั้น ทำให้ filepath เป็นค่าคงที่และวางไว้ที่ด้านบนสุดของสคริปต์ของคุณ:

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

มีสองสิ่งใหม่ที่นี่ ขั้นแรกสังเกตว่าฉันเพิ่ม docstring ลงในฟังก์ชันนี้อย่างไรและจะบอกได้ง่ายเพียงใดว่ามันทำอะไรได้บ้าง ประการที่สองสังเกตว่าการใช้ฟังก์ชันนี้กับไฟล์อื่น ๆ ที่เกมของคุณอาจใช้ประโยชน์ได้นั้นง่ายเพียงใด ประการที่สามดูว่าสตริงถูกจัดรูปแบบอย่างไรเมื่อฉันเพิ่มข้อยกเว้น คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสตริงหลามของการจัดรูปแบบที่นี่

// ต้องไปก่อน