0

第一:你不必为我编写代码,除非你是一个超级棒的好人。但是由于你们都非常擅长编程并且比我和所有人都更好地理解它,所以它可能比逐段编写试图让我理解它更容易(因为它可能不是太多的代码行)。

所以 - 我需要列出一个在新条目时更新的高分列表。 所以这里是:

第一步——完成

我有玩家输入的输入,它已被用作一些计算的数据:

import time
import datetime

print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()
a = raw_input("Enter weight: ")    
b = raw_input("Enter height: ")
c = a/b

第二步——制作高分榜

在这里,我需要某种字典或可以读取先前条目并检查分数(c)是否(至少)优于“高分”中最后一个分数的东西,如果是,它会提示您输入您的姓名。

输入您的姓名后,它会将您的姓名、您的abc和时间发布在高分列表中。

这是我想出的,它绝对行不通:

list = [("CPU", 200, 100, 2, time1)]
player = "CPU"
a = 200
b = 100
c = 2
time1 = "20.12.2012, 21:38"
list.append((player, a, b, c, time1))
list.sort()

import pickle
scores = open("scores", "w")
pickle.dump(list[-5:], scores)
scores.close()

scores = open("scores", "r")
oldscores = pickle.load(scores)
scores.close()
print oldscores()

知道我做了一些非常愚蠢的事情,但无论如何,感谢您阅读本文,我希望您能帮助我解决这个问题。:-)

4

4 回答 4

4

首先,不要list用作变量名。它遮蔽了内置list对象。其次,避免只使用简单的日期字符串,因为使用对象要容易得多datetime,它支持正确的比较和简单的转换。

这是您的代码的完整示例,其中包含帮助划分步骤的各个函数。我试图不使用任何更高级的模块或功能,因为您显然只是在学习:

import os
import datetime
import cPickle

# just a constants we can use to define our score file location
SCORES_FILE = "scores.pickle"

def get_user_data():
    time1 = datetime.datetime.now()
    print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")

    a = None
    while True:
        a = raw_input("Enter weight: ")    
        try:
            a = float(a)
        except:
            continue
        else:
            break

    b = None
    while True:
        b = raw_input("Enter height: ")    
        try:
            b = float(b)
        except:
            continue
        else:
            break

    c = a/b

    return ['', a, b, c, time1]

def read_high_scores():
    # initialize an empty score file if it does
    # not exist already, and return an empty list
    if not os.path.isfile(SCORES_FILE):
        write_high_scores([])
        return []

    with open(SCORES_FILE, 'r') as f:
        scores = cPickle.load(f)
    return scores

def write_high_scores(scores):
    with open(SCORES_FILE, 'w') as f:
        cPickle.dump(scores, f)

def update_scores(newScore, highScores):
    # reuse an anonymous function for looking
    # up the `c` (4th item) score from the object
    key = lambda item: item[3]

    # make a local copy of the scores
    highScores = highScores[:]

    lowest = None
    if highScores:
        lowest = min(highScores, key=key)

    # only add the new score if the high scores
    # are empty, or it beats the lowest one
    if lowest is None or (newScore[3] > lowest[3]):
        newScore[0] = raw_input("Enter name: ")
        highScores.append(newScore)

    # take only the highest 5 scores and return them
    highScores.sort(key=key, reverse=True)
    return highScores[:5]

def print_high_scores(scores):
    # loop over scores using enumerate to also
    # get an int counter for printing
    for i, score in enumerate(scores):
        name, a, b, c, time1 = score
        # #1    50.0    jdi    (20.12.2012, 15:02)
        print "#%d\t%s\t%s\t(%s)" % \
            (i+1, c, name, time1.strftime("%d.%m.%Y, %H:%M"))


def main():
    score = get_user_data()
    highScores = read_high_scores()

    highScores = update_scores(score, highScores)

    write_high_scores(highScores)
    print_high_scores(highScores)

if __name__ == "__main__":
    main()

它现在所做的只是在没有高分或超过最低分的情况下添加新分数。如果之前的分数少于 5 个,您可以修改它以始终添加一个新分数,而不是要求它击败最低的分数。然后在 highscores >= 5 的大小之后执行最低检查

于 2012-12-20T22:58:59.003 回答
2

我注意到的第一件事是您没有告诉list.sort()排序应该基于每个条目的最后一个元素。默认情况下,list.sort()将使用 Python 的默认排序顺序,它将根据每个条目的第一个元素(即名称)对条目进行排序,然后对第二个元素、第三个元素等进行排序。因此,您必须告诉list.sort()使用哪个项目进行排序:

from operator import itemgetter
[...]
list.sort(key=itemgetter(3))

这将根据每个元组中索引为 3 的项(即第四项)对条目进行排序。

此外,print oldscores()肯定不会工作,因为oldscores它不是一个函数,因此你不能用()操作员调用它。print oldscores可能更好。

于 2012-12-20T21:48:53.497 回答
1

你绝对不想要这里的字典。字典的重点是能够将键映射到值,而无需任何排序。你想要的是一个排序列表。你已经明白了。

好吧,正如 Tamás 指出的那样,您实际上得到了一个按球员姓名排序的列表,而不是分数。最重要的是,您希望按向下顺序排序,而不是向上排序。你可以使用 decorate-sort-undecorate 模式,或者一个键函数,或者其他什么,但是你需要做一些事情。此外,您已将其放在名为 的变量中list,这是一个非常糟糕的主意,因为这已经是list类型的名称。

无论如何,您可以使用标准库中的模块来确定是否将某些内容添加到 sortedlist中,以及在哪里插入。bisect但是使用类似的东西可能更简单SortedCollectionor blist

这是一个例子:

highscores = SortedCollection(scores, key=lambda x: -x[3])

现在,当你完成游戏时:

highscores.insert_right((player, a, b, newscore, time1))
del highscores[-1]

而已。如果您实际上不在前 10 名,您将被添加到第 11 名,然后被删除。如果您在前 10 名中,您将被添加,而旧的 #10 现在将成为 #11 并被删除。

如果您不想像以前的街机游戏那样用 10 个假分数预先填充列表,只需将其更改为:

highscores.insert_right((player, a, b, newscore, time1))
del highscores[10:]

现在,如果已经有 10 个分数,当你被添加时,#11 将被删除,但如果只有 3 个,则什么都不会被删除,现在有 4 个。

同时,我不确定您为什么要将新分数写入pickle文件,然后再读回相同的内容。您可能希望在将高分添加到列表之前进行读取,然后在之后进行写入添加它。

您还问如何“美化列表”。嗯,这有三个方面。

首先,在代码中,(player, a, b, c, time1)意义不大。当然,为变量提供更好的名称会有所帮助,但最终您仍然会归结为这样一个事实,即在访问列表时,您必须entry[3]获得分数或entry[4]时间。

至少有三种方法可以解决这个问题:

  • 存储 s 的一个list(或SortedCollectiondict而不是tuples。代码变得更加冗长,但更具可读性。你写{'player': player, 'height': a, 'weight': b, 'score': c, 'time': time1},然后在访问列表时,你做entry['score']而不是entry[3].
  • 使用namedtuples 的集合。现在你实际上可以只插入ScoreEntry(player, a, b, c, time1),或者你可以插入ScoreEntry(player=player, height=a, weight=b, score=c, time=time1),以在给定的情况下更具可读性,并且它们的工作方式相同。您可以访问entry.scoreor as entry[3],再次使用更具可读性的那个。
  • 为分数条目编写一个明确的类。这与上一个非常相似,但是要编写更多代码,并且您不能再进行索引访问,但从好的方面来说,您不必了解namedtuple.

其次,如果你只是print条目,它们看起来像一团糟。处理这个问题的方法是字符串格式化。而不是print scores,你做这样的事情:

print '\n'.join("{}: height {}, weight {}, score {} at {}".format(entry) for entry in highscores)

如果您使用 a classornamedtuple而不仅仅是 a tuple,您甚至可以按名称而不是按位置进行格式化,从而使代码更具可读性。

最后,高分文件本身就是一团乱七八糟的东西,因为pickle它不适合人类消费。如果您希望它是人类可读的,您必须选择一种格式,并编写代码来序列化该格式。幸运的是,CSV 格式非常易于阅读,并且大部分代码已经在csv模块中为您编写好了。(您可能想查看DictReaderDictWriter类,特别是如果您想编写标题行。同样,为了提高可读性,需要权衡更多代码。)

于 2012-12-20T22:26:20.190 回答
1

这是我注意到的事情。

这些行似乎顺序错误:

print "Current time:", time1.strftime("%d.%m.%Y, %H:%M")
time1 = datetime.datetime.now()

当用户输入身高和体重时,它们将被读取为字符串,而不是整数,因此您将在此行收到 TypeError:

c = a/b

您可以通过将 a 和 b 转换为像这样浮动来解决此问题:

a = float(raw_input("Enter weight: "))

但是您可能需要将其包装在 try/catch 块中,以防用户放入垃圾,基本上是任何不能转换为浮点数的东西。把整个事情放在一段时间里,直到他们做对了。

所以,像这样:

b = None
while b == None:
    try:
        b = float(raw_input("Enter height: "))
    except:
        print "Weight should be entered using only digits, like '187'"

因此,在第二部分中,您不应该将list其用作变量名,因为它是内置的,我将使用high_scores.

# Add one default entry to the list
high_scores = [("CPU", 200, 100, 2, "20.12.2012, 4:20")]

你说你想对照高分检查球员得分,看看它是否最好,但如果是这样,为什么要列出一个列表?为什么不只是一个条目?无论如何,这让我很困惑,不确定你是否真的想要一个高分列表,或者只是一个高分。

所以,无论如何,让我们添加分数:

假设您已将他们的名字放入name变量中。

high_score.append((name, a, b, c, time1))

然后应用@Tamás 的另一个答案

于 2012-12-20T22:26:56.840 回答