3
\$\begingroup\$

Could someone tell me their opinion on my leaderboard system?

library

#!/usr/bin/env python3
def write_score(score, name, scores, filename, splitter=','):
    """writes a score with a name to a file, in a specified format"""
    score_tuple = (score,name)
    scores.append(score_tuple)
    with open(filename,'w') as f:
        for s in scores:
            f.write(str(s[0]) + splitter + s[1] + '\n')
        f.close()

def read_scores(filename, splitter=','):
    """reads scores and names from a file, and returns a list of each"""
    with open(filename) as f:
        raw_scores = f.read().strip().split('\n')
        f.close()

    scores = []
    names = []

    for score in raw_scores:
        score_split = score.split(splitter)
        scores.append(int(score_split[0]))
        names.append(score_split[1])
    return scores, names

def sort_scores(scores, names,reverse_bool=True):
    """sorts the scores from greatest to least and returns in a list of tuples format"""
    zipped = sorted(list(zip(scores,names)), reverse=reverse_bool)
    return zipped

def print_scores(score_list, seperator=' ', top_amount=5):
    """prints the number of leaderboard scores stated"""
    for score_tuple in score_list[:top_amount]:
        print(str(score_tuple[0]) + seperator + score_tuple[1])

def has_better_score(score, scores, leaderboard_len=5):
    """returns if the score should be written to a file"""
    if (len(scores) > leaderboard_len and score >= scores[leaderboard_len - 1][0]) or len(scores) <= leaderboard_len:
        return True
    return False

leaderboard.txt

123 | jimmy
16 | bill
12 | Pete
10 | Jim2
10 | Jim
6 | henry is cool
5 | Bob
3 | Jane
223 | billy

Small Program

import leaderlib as ll
if __name__ == '__main__':
    try:
        while True:
            scores, names = ll.read_scores('leaderboard.txt',' | ')
            sorted_scores = ll.sort_scores(scores, names)
            ll.print_scores(sorted_scores, ' = ', 5)

            new_name = input('Name > ')
            new_score = int(input('Score > '))
            if ll.has_better_score(new_score, sorted_scores, 5):
                ll.write_score(new_score, new_name, sorted_scores, 'leaderboard.txt', ' | ')
            else:
                print('not on the leaderboard...')
            print('\n\n\n')
    except KeyboardInterrupt:
        exit()
\$\endgroup\$
3
\$\begingroup\$

The customary name for what you called splitter would be delimiter or sep (the letter is used e.g. in split, the former for example in numpy). Regardless of what you choose, you should be consistent, right now you use both splitter and then later separator.

When using the with..as construct (as you should), you don't need to manually close the file. This is one of the reasons of why you should use it in the first place.

In your function write_score, you should use str.join.

In read_scores you can directly iterate over the lines of the file, which is a lot more memory-efficient. You can also use tuple assignment to make it clearer what is what.

In sort_scores, you can use sorted directly on zip, there is no need to cast it to a list first. You can also return the result right away.

In has_better_score you can just return the result of the comparisons.

In print_scores you can use str.join again.

#!/usr/bin/env python3

def write_score(score, name, scores, filename, splitter=','):
    """writes a score with a name to a file, in a specified format"""
    scores.append((score, name))
    with open(filename,'w') as f:
        for s in scores:
            f.write(splitter.join(map(str, s)) + '\n')

def read_scores(filename, splitter=','):
    """reads scores and names from a file, and returns a list of each"""
    scores = []
    names = []

    with open(filename) as f:
        for score in f:
            score, name = score.strip().split(splitter)
            scores.append(int(score))
            names.append(name)
    return scores, names

def sort_scores(scores, names, reverse_bool=True):
    """sorts the scores from greatest to least and returns in a list of tuples format"""
    return sorted(zip(scores,names), reverse=reverse_bool)

def print_scores(score_list, splitter=' ', top_amount=5):
    """prints the number of leaderboard scores stated"""
    for score_tuple in score_list[:top_amount]:
        print(splitter.join(map(str, score_tuple)))

def has_better_score(score, scores, leaderboard_len=5):
    """returns if the score should be written to a file"""
    return (len(scores) > leaderboard_len and score >= scores[leaderboard_len - 1][0]) or len(scores) <= leaderboard_len:

In your "small program", catching KeyboardException and then just exiting is not really different from letting the exception rise all the way to the top. Also, exit() should only be used in the interactive session, use sys.exit() in a script instead (because it allows passing of a return value).

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.