Search code examples
pythonpython-3.xdictionarykeyerror

My python program keeps returning "KeyError: 20"


I have written a program in python which is supposed to return the highest scoring player for each level, however it keeps returning "KeyError: 20" for line 69 ( P_L1=players_TS[H_L1] ), and I can't work out how to fix this. In my code I open a text file called "playerScores.txt" which is formatted like this:
Moon,3,15
StarPlayer,2,18
Ellie,5,9
etc.
Here is my code:

    print("The highest scorers for each leve are:\n")
    PS3=open("PlayerScores.txt","r").read().split("\n")
    level1_scores=[]
    players_TS={}
    level2_scores=[]
    players_T2={}
    level3_scores=[]
    players_T3={}
    level4_scores=[]
    players_T4={}
    level5_scores=[]
    players_T5={}

    for line in PS3:
        lines=line.split(",")
        player=lines[0]
        level=lines[1]
        score=lines[2]

        erros=open("scoreboard_errorlog.text","w")

        if int(score)>20:
            errors.write(score)
        if int(level)>5:
            errors.write(level)

        if int(level)==1:
            level1_scores.append(int(score))
            players_TS.setdefault(score,[]).append(player)
        if int(level)==2:
            level2_scores.append(int(score))
            players_T2.setdefault(score,[]).append(player)
        if int(level)==3:
            level3_scores.append(int(score))
            players_T3.setdefault(score,[]).append(player)
        if int(level)==4:
            level4_scores.append(int(score))
            players_T4.setdefault(score,[]).append(player)
        if int(level)==5:
            level5_scores.append(int(score))
            players_T5.setdefault(score,[]).append(player)

    H_L1=max(level1_scores)
    P_L1=players_TS[H_L1]
    H_L2=max(level2_scores)
    P_L2=players_T2[H_L2]
    H_L3=max(level3_scores)
    P_L3=players_T3[H_L3]
    H_L4=max(level4_scores)
    P_L4=players_T4[H_L4]
    H_L5=max(level5_scores)
    P_L5=players_T5[H_L5]

    print("Level\tPlayer name\tScore\nLevel 1\t",P_L1,"\t",H_L1,"\nLevel 2\t",P_L2,"\t",H_L2,"\nLevel 3\t",P_L3,"\t",H_L3,"\nLevel 4\t",P_L4,"\t",H_L4,"\nLevel 5\t",P_L5,"\t",H_L5)

Help will be much appreciated.


Solution

  • The problem arises here:

    if int(level)==1:
        level1_scores.append(int(score))
        players_TS.setdefault(score,[]).append(player)
    

    Suppose score == "20". You are adding the integer 20 to level1_scores, but using the string "20" as a key in players_TS. Then, when you get to these lines

    H_L1=max(level1_scores)
    P_L1=players_TS[H_L1]
    

    H_L1 is the integer value 20, but all the keys in players_TS are strings like "20". Since 20 != "20", a KeyError results.

    Likely, you want to have score (and level, for that matter) be an integer throughout, so convert it once immediately after you parse it, then use score as is in the remainder of the loop.

    for line in PS3:
        lines = line.split(",")
        player = lines[0]
        level = int(lines[1])
        score = int(lines[2])
    
        with open("scoreboard_errorlog.text","w") as errors:
    
            if score > 20:
                errors.write(score)
            if level > 5:
                errors.write(level)
    
        if level == 1:
            level1_scores.append(score)
            players_TS.setdefault(score,[]).append(player)
        elif level == 2:
            level2_scores.append(score)
            players_T2.setdefault(score,[]).append(player)
        elif level == 3:
            level3_scores.append(score)
            players_T3.setdefault(score,[]).append(player)
        elif level == 4:
            level4_scores.append(score)
            players_T4.setdefault(score,[]).append(player)
        elif level == 5:
            level5_scores.append(score)
            players_T5.setdefault(score,[]).append(player)
    
        H_L1 = max(level1_scores)
        P_L1 = players_TS[H_L1]
        H_L2 = max(level2_scores)
        P_L2 = players_T2[H_L2]
        H_L3 = max(level3_scores)
        P_L3 = players_T3[H_L3]
        H_L4 = max(level4_scores)
        P_L4 = players_T4[H_L4]
        H_L5 = max(level5_scores)
        P_L5 = players_T5[H_L5]
    

    However, you should also be using dicts to store a lot of this data, rather than individually named variables.

    level_scores = {level: [] for level in range(1,6)}
    players = {}
    for line in PS3:
        lines = line.split(",")
        player = lines[0]
        level = int(lines[1])
        score = int(lines[2])
    
        # Use append mode, or you overwrite the file each time through the loop
        with open("scoreboard_errorlog.text", "a") as errors:
    
            if score > 20:
                errors.write(score)
            if level > 5:
                errors.write(level)
                continue
    
        level_scores[level].append(score)
        players[level].setdefault(score,[]).append(player)
    
    
    H = {}
    P = {}
    for level in range(1,6):
        H[level] = max(level_scores[level])
        P[level] = players[level][H[level]]