highscores list
Bruno Desthuilliers
bdesth.quelquechose at free.quelquepart.fr
Sat Dec 8 16:43:40 EST 2007
Shawn Minisall a écrit :
> I'm writing a game that uses two functions to check and see if a file
> called highScoresList.txt exists in the main dir of the game program.
> If it doesn, it creates one. That part is working fine. The problem is
> arising when it goes to read in the high scores from the file when I
> play again.
>
> This is the error msg python is giving me
>
> Traceback (most recent call last):
> File "<pyshell#0>", line 1, in <module>
> main()
> File "I:\PYTHON\PROJECT #3\PROJECT3.PYW", line 330, in main
> if(hasHighScore(wins) == True):
> File "I:\PYTHON\PROJECT #3\PROJECT3.PYW", line 175, in hasHighScore
> scores[i],names[i] = string.split(line,"\t")
> ValueError: need more than 1 value to unpack
>
> Here's the relavant code:
>
> def hasHighScore(score):
> #opens highScoresList.txt
> infile = open("highScoresList.txt",'r')
hardcoded file names are a bad idea. And FWIW, your file will be looked
for in the current wirking directory - which is not necessarily the
"main dir of the game program".
> scores = [0,0,0]
> names = ["","",""]
>
> #reads in scores from highScoresList.txt
> i=0
> for line in infile.readlines():
You can iterate directly over the file.
> scores[i],names[i] = string.split(line,"\t")
string.split(str, sep) is long time deprecated. Use str methods instead:
s, n = line.split('\t')
Also, this will obviously raise if the line doesn't have exactly one tab
in it. Like, ie, it's an empty line....
> names[i]=string.rstrip(names[i])
> i += 1
You can use enumerate(iterable) instead of manually counting lines.
Also, what if your file has more than 3 lines ?
> infile.close()
> #compares player's score with those in highScoresList.txt
> i=0
> for i in range(0,len(scores)):
You obviously don't know how to use Python's for loop:
for item in scores:
# now use item instead of scores[i]
> if(score > int(scores[i])):
> return True
> else:
> return False
You have a logic error here. This will only compare the first item in
your score list. You want something like:
for item in score:
if score > int(item):
return True
return False
>
> def setHighScores(score,name):
> #opens highScoresList.txt
> infile = open("highScoresList.txt",'r')
> scores = [0,0,0]
> names = ["","",""]
>
> #reads in scores from highScoresList.txt
> i=0
> for line in infile.readlines():
> scores[i],names[i] = string.split(line,"\t")
> scores[i]=int(scores[i])
> names[i]=string.rstrip(names[i])
> i += 1
> infile.close()
hem... don't you see something like a duplication here ? By all mean
extract out this code in a 'read_scores' function.
> #shuffles thru the highScoresList.txt and inserts player's score if
> higher then those in file
> i=len(scores)
> while(score > scores[i-1] and i>0):
> i -= 1
>
> scores.insert(i,score)
> names.insert(i,name)
> scores.pop(len(scores)-1)
> names.pop(len(names)-1)
OMG.
This is ten times more complicated than it needs to be.
> #writes new highScoresList.txt
> outfile = open("highScoresList.txt","w")
>
> outfile.write (" High Score Name \n")
> outfile.write ("-------------------------------------------------\n")
>
> i=0
> for i in range(0,len(scores)):
> outfile.write("\t" + str(scores[i]) + "\t\t\t" + names[i] + "\n")
If your file is formated that way, no surprise your code breaks. None of
what you write in it matches the expectations of the code that reads it.
> outfile.close()
> And here's the call to the functions at the end of my game, included in
> the error msg.
>
> #adds player's score to high score list if high enough
> if(hasHighScore(wins) == True):
> setHighScores(wins,getName(wins))
And you're doing two times the same parsing of the file....
> The answer is probably simple,
The answer to your question is indeed quite simple : either rewrite the
code that reads the file to make it matches what you wrote in the file,
or rewrite the code that writes the file to make it match the
expectations of the code that reads it. IOW : make both parts of the
code work on a same file format !-)
Also, learning to make effective use of Python's features would help !-)
Here's a possible reimplementation of your code - not tested, so it may
have bugs, but it should do the trick.
The file format is a very simple 'score:name' per line. Nothing else.
(heck, this file is for storing data, it's not meant to be seen by the
user).
The hiscore file full path must be passed when instantiating the
Hiscores object (that is, when initializing your program - you just need
one Hiscore object for the whole lifetime of your program). You must
also pass the max number of hiscores you want to keep, and an optional
flag telling if an error while reading the hiscore file should raise an
exception or be ignored (ending up using an empty hiscore list).
hiscores = HiScores('/path/to/your/file.ext', 3)
Once done, you just use it:
if hiscores.update(42, 'bibi'):
print "Yay, your a boss"
else:
print "try again..."
# hiscores.py
import sys
def _read_scores(path):
f = open(path)
# we don't expect a huge file so it's simpler to
# read it all in memory
lines = f.readlines()
f.close()
scores = []
for line in filter(None, map(str.strip, lines)):
try:
score, name = line.split(':')
score = int(score)
except ValueError, e:
# either the lines was not score:name or
# score wasn't a proper value for an int
err = "File %s : incorrect file format" \
% path
raise ValueError(err)
else:
scores.append((score, name))
# supposed to be already sorted, but we want to be sure.
# NB : natural sort will do the RightThing(tm) here
scores.sort()
return scores
def _write_scores(path, scores):
scores = "\n".join(["%s:%s" % item for item in scores])
f = open(path, 'w')
f.write(scores)
f.close()
class HiScores(object):
def __init__(self, path, nb_scores, raise_on_error=False):
self._path = path
self._nb_scores = nb_scores
try:
self._scores = _read_scores(path)[0:self.nb_scores - 1]
except (IOError, ValueError), e:
if raise_on_error:
raise
else:
# log the error
err = "Error while reading hiscore file %s : %s" \
% (path, e)
print >> sys.stderr, err
# use an empty list instead
self._scores = []
def index(self, score):
for i, s in enumerate(self._scores):
if score > s[0]:
return i
return -1
def update(self, score, name):
index = self.index(score)
if index == -1:
return False
self._scores.insert(index, (score, name))
self._scores.pop()
_write_scores(self._path, self._scores)
return True
def is_hi_score(self, score):
return self.index(score) > -1
HTH
More information about the Python-list
mailing list