1

I have a problem where the function just overwrites the line thats already there in a .txt file. The function is supposed to write a highscore to a file when the game quits (I have made a snake game by following a youtube tutorial). I can't quite figure out why it won't start on a new line, can anyone please explain the logic behind it, and how I fix it? I read somewhere that instead of "w" in f.open(), I should type "rb" or something. Since I'm kinda new to this "writing-to-file" thingy, I find it difficult.

Also, I want to sort the highscores from highest to lowest in the file (in other words, sort finalScore from highest to lowest). I have no idea how I should go on and code that, so I'd appreicate some help. You see, I want to print out the current highscores to the console (in order to make a scoreboad)

Heres the code:

import random
import time

name = "Andreas"
finalScore = random.randint(1,10)

def scoreToFile(finalScore):
    #Has to be generated here, since we need the exact current time
    currentTime = time.strftime("%c")
    print("Sucsessfully logged score (finalScore) to highscores.txt")
    f = open("highscores.txt", "w")
    #fileOutput = [(currentTime, ":", name, "-", finalScore)]
    fileOutput = [(finalScore, "-", name, currentTime)]
    for t in fileOutput:
        line = ' '.join(str(x) for x in t)
        f.write(line + "\n")
    f.close()

scoreToFile(finalScore)

Anyways, merry christmas my fellow python geeks! :D

a_amundsen
  • 55
  • 6
  • Do `f = open("highscores.txt", 'a')`. Note the [`a` flag, which means "append" and not "overwrite"](http://stackoverflow.com/q/1466000/198633). – inspectorG4dget Dec 25 '14 at 15:52

3 Answers3

3

1) one option is to open the file in append mode. replace:

f = open("highscores.txt", "w")

with:

f = open("highscores.txt", "a")

2) another option is to replace this block,

f = open("highscores.txt", "w")
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
    line = ' '.join(str(x) for x in t)
    myfile.write(line + "\n")
f.close()

and use a with style

with open("highscores.txt", "a") as myfile:
    #fileOutput = [(currentTime, ":", name, "-", finalScore)]
    fileOutput = [(finalScore, "-", name, currentTime)]
    for t in fileOutput:
        line = ' '.join(str(x) for x in t)
        myfile.write(line + "\n")

I prefer the second style as it is more safe and clean.

2

Mode w overwrites an existing file; mode 'a' appends to it. Also, the best way to handle a file is usually with the with statement, which ensures the closing on your behalf; so:

fileOutput = [(finalScore, "-", name, currentTime)]
with open("highscores.txt", "a") as f:
    for t in fileOutput:
        line = ' '.join(str(x) for x in t)
        f.write(line + "\n")

For sorting, you need be able to extract the final score as a number from a line:

def minus_score(line):
    return -int(line.split()[0])

then the total work will be done as:

def sorted_by_score():
    with open("highscores.txt", "r") as f:
        result = list(f)
    return sorted(result, key=minus_score)

This will give you a list lines sorted in ascending order of score (the latter's the reason score is negating the number, though one might also choose to have it just return the number and reverse the sorting), for you to loop on and further process.

Added: so on the OP's request here's how the whole program might be (assuming the existence of a function that either plays a game and returns player name and final score, or else returns None when no more games are to be played and the program must exit).

import time

def play_game():
    """ play a game and return name, finalscore;
        return None to mean no more games, program finished.
        THIS function you'd better code yourself!-)
    """

def scoreToFile(name, finalScore):
    """ Add a name and score to the high-scores file. """
    currentTime = time.strftime("%c")
    fileOutput = finalScore, "-", name, currentTime
    line = ' '.join(str(x) for x in fileOutput)
    with open("highscores.txt", "a") as f:
        f.write(line + "\n")

def minus_score(line):
    """ just for sorting purposes, not called directly. """
    return -int(line.split()[0])

def sorted_by_score():
    """ return list of score lines sorted in descending order of score. """
    with open("highscores.txt", "r") as f:
        return sorted(f, key=minus_score)

def main():
    while True:
        game_result = play_game()
        if game_result is None: break
        scoreToFile(*game_result)
    for line in sorted_by_score:
        print(line.strip())
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • I don't quite understand... Where in the code should I call the functions, and is `line` in `minus_score` the same as in the scoreToFile function? Could you just paste the whole code in here? Thanks for a detailed answer anywyas! :) – a_amundsen Dec 25 '14 at 18:10
  • As to **when** you should call `sorted_by_score`: you only said "I want to sort the highscores" - so **when** you call it, is exactly when you want to get "the highscores" sorted (to loop on, printing some on the console, or whatever). You never say **when** you want to sort them, and I can't read your mind, so I can't tell **when** to call `sorted_by_score` -- the two "whens" being one and the same -- be that immediately after `scoreToFile`, or at the end, or whenever. You never call `minus_score` directly; the built-in `sorted` calls it **for** you to produce the sorting you desire. – Alex Martelli Dec 25 '14 at 18:36
  • Oh, and, to clarify: I cannot "just paste the whole code in here" because of your specification's lack of clarity as to what should be done when (do you want to "print to console" after each new score is added, or, when your whole program is done, or, at what other time(s) -- I can't guess that!). – Alex Martelli Dec 25 '14 at 18:39
  • Alright, here is what I want. Firstly, I have a snake game, and each time the game is over, the score is written to a file. Furthermore, I want to sort the scores, prefarably when the program is done, but I dont really know whats best. Before the program quits however, I want the result in "highscores.txt" to be printed out, sorted of course. Can't you tell me how you would have done it? – a_amundsen Dec 25 '14 at 18:46
  • Sure, coming up -- assuming the existence of a function that either plays a game and returns its score &c, or returns None to say the game is over. – Alex Martelli Dec 25 '14 at 18:54
  • If there a way I can send you the game file, I'd be happy to! :) – a_amundsen Dec 25 '14 at 18:57
1

As others have mentioned, the problem is you're not opening the file in append mode, so it overwrites it every time rather than adding to it.

However, if you also want to keep the data in the file sorted, you do want to overwrite it each time, since the order of its contents will likely have been changed with the addition. To do that requires first reading it contents in, updating the data, and then writing it back out.

Here's a modified version of your function that does that. I also changed how the data in file is stored to what is known as Comma (or Character) Separated Values (CSV) format, because Python includes acsvmodule which makes it very easy to read, write, and do other things with such files.

import csv
import random
import time

highscores_filename = "highscores.txt"
HighScoresFirst = True  # Determines sort order of data in file

def scoreToFile(name, finalScore):
    currentTime = time.strftime("%c")
    # Try reading scores from existing file.
    try:
        with open(highscores_filename, "r", newline='') as csvfile:
            highscores = [row for row in csv.reader(csvfile, delimiter='-')]
    except FileNotFoundError:
        highscores = []
    # Add this score to the end of the list.
    highscores.append([str(finalScore), name, currentTime])
    # Sort updated list by numeric score.
    highscores.sort(key=lambda item: int(item[0]), reverse=HighScoresFirst)
    # Create/rewrite highscores file from highscores list.
    with open(highscores_filename, "w", newline='') as csvfile:
        writer = csv.writer(csvfile, delimiter='-')
        writer.writerows(highscores)
    print("successfully logged score (finalScore) to highscores.txt")

# Simulate using the function several times.
name = "Name"
for i in range(1, 4):
    finalScore = random.randint(1,10)
    scoreToFile(name + str(i), finalScore)
    time.sleep(random.randint(1,3))  # Pause so time values will vary.
martineau
  • 119,623
  • 25
  • 170
  • 301