如何跟踪球员的排名?

时间:2016-08-15 15:18:41

标签: python algorithm python-3.x ranking rank

我有一个Player类,其score属性:

class Player(game_engine.Player):

    def __init__(self, id):
        super().__init__(id)
        self.score = 0

当玩家成功/未能完成目标时,此分数会增加/减少。现在我需要通过类似

之类的玩家总数告诉玩家他的排名
print('Your rank is {0} out of {1}')

首先,我想到了所有玩家的列表,以及玩家什么时候发生的事情:

  1. 我检查他的分数是增加还是减少
  2. 在列表中找到他
  3. 移动他直到他的分数在正确的位置
  4. 但这会非常慢。可以有成千上万的玩家,并且玩家可以将他自己的分数重置为0,这意味着我必须将所有人都移到他后面。即使找到玩家也是O(n)。

    我正在寻找的是一种高性能解决方案。尽管应该使用常识,但RAM的使用并不那么重要。我怎样才能更快地改进系统?

    更新信息我每次离开游戏服务器时都会使用SQLAlchemy将玩家的数据存储到MySQL数据库中,并且每次加入服务器时都会加载它。这些是通过'player_join''player_leave'事件处理的:

    @Event('player_join')
    def load_player(id):
        """Load player into the global players dict."""
        session = Session()
        query = session.query(Player).filter_by(id=id)
        players[id] = query.one_or_none() or Player(id=id)
    
    @Event('player_leave')
    def save_player(id):
        """Save player into the database."""
        session = Session()
        session.add(players[id])
        session.commit()
    

    此外,玩家的分数会在'player_kill'事件后更新:

    @Event('player_kill')
    def update_score(id, target_id):
        """Update players' scores upon a kill."""
        players[id].score += 2
        players[target_id].score -= 2
    

3 个答案:

答案 0 :(得分:6)

Redis排序集帮助解决了这个问题(文档使用排行榜作为示例用法)http://redis.io/topics/data-types-intro#redis-sorted-sets

  • 您关心的关键命令是ZADD(更新玩家等级)和ZRANK(获得特定玩家的等级)。这两个操作都是O(log(N))复杂度。

Redis可以用作玩家排名的缓存。应用程序启动时,从SQL数据中填充redis。在mysql中更新玩家分数时也会更新redis。

如果您有多个服务器进程/线程并且它们可以同时触发玩家得分更新,那么您还应该考虑mysql / redis更新竞争条件,例如:

  • 仅从数据库触发器更新redis;或
  • 序列化球员得分更新;或
  • 让数据暂时不同步,并在延迟后再次进行缓存更新;或
  • 让数据暂时不同步,并以固定的时间间隔执行完整的缓存重建

答案 1 :(得分:4)

您遇到的问题是您希望对数据库进行实时更新,每次都需要进行数据库查询。如果您在内存中保留一个分数列表,并以更合理的频率更新(例如每小时一次,甚至每分钟一次,如果您的玩家真的关心他们的等级),那么玩家仍将体验真实的时间进度与得分排名,他们无法确定更新中是否存在短暂滞后。

通过内存中的分数排序列表,您可以立即获得玩家的等级(即时,我的意思是内存中的O(lg n)查找),代价是要缓存的内存,当然还有时间到您希望更新缓存。与每次有人想要查看其排名的100k记录的db查询相比,这是一个更好的选择。

在排序列表上详细说明,您必须查询数据库以获取它,但您可以继续使用它一段时间。也许你存储了last_update,并且只有当这个列表“太旧”时才重新查询数据库。所以你不要一直尝试更新,而是快速更新,而只是觉得它就像是实时的。

为了几乎立即找到某人的排名,您可以使用bisect模块,该模块支持在排序列表中进行二进制搜索。得分时会对分数进行排序。

from bisect import bisect_left

# suppose scores are 1 through 10
scores = range(1, 11)

# get the insertion index for score 7
# subtract it from len(scores) because bisect expects ascending sort
# but you want a descending rank
print len(scores) - bisect_left(scores, 7)

这表示7分是4级,这是正确的。

答案 2 :(得分:0)

可以使用SQLAlchemy的sort_by函数来提取这类信息。如果您执行如下查询:

<?php

if(isset($_POST['create_post'])){

     $post_title = $_POST['title'];
     $post_author = $_POST['authror'];
     $post_category_id= $_POST['post_category_id'];
     $post_status = $_POST['post_status'];
     $post_image = $_FILES['image']['name'];
     $post_image_temp = $_FILES['image']['tmp_name']; // this is change 
     $post_tags= $_POST['post_tags'];
     $post_content = $_POST['post_content'];
     $post_date = date('d-m-y');
     $post_comment_count = 4;
     move_uploaded_file($post_image_temp, "../images/$post_image");
}

?>

<form action = "" method="post" enctype="multipart/form-data">


    <div class="form-group">
        <label for="title"> Title</label>
        <input type="text" class="form-control" name="title">
    </div>

    <div class="form-group">
        <label for="Category"> Category</label>
        <input type="text" class="form-control" name="category">
    </div>

    <div class="form-group">
        <label for="Author"> Author</label>
        <input type="text" class="form-control" name="author">
    </div>

    <div class="form-group">
        <label for="post-status"> Status</label>
        <input type="text" class="form-control" name="post_status">
    </div>

    <div class="form-group">
        <label for="post_image"> Image</label>
        <input type="file" name="image">
    </div>

    <div class="form-group">
        <label for="post_tags"> Tags</label>
        <input type="text" class="form-control" name="post_tags">
    </div>
    <div class="form-group">
        <label for="post_content"> Content</label>
        <textarea type="text" class="form-control" name="post_content" id="" cols="30" rows="10"></textarea>
    </div>   
    <div class="form-group">
        <input class="btn btn-primary" type="submit" name="create_post" value="publish post">
    </div>

</form>

您将按照分数排序玩家列表。请记住,每次执行此操作时,都要使用数据库执行I / O操作,这可能会相当慢,而不是保存数据python变量。