背景介绍

排行榜通常是游戏中为了激发玩家的一种策略,那么对于开发人员来说如何完成一个排行榜的设计呢?如果这个排行榜是动态的如何才能高效的对比出结果呢?如果排行榜实时性较高如何给用户展示出用户是进步了还是退步了呢?带着这些问题我们一步步开始探究。可能我实现的方式并不高效期待你能够提出宝贵的意见。所有代码可以到这里github

排名

假设我们要实现排行榜功能肯定避免不了排序,那么是怎么排序比较好呢?当用户查询自己的战绩时再去查数据库?那样就会使用户体验变差了!如果用户量非常大可能排序会让用户不耐烦。好那么我们就使用后台定时去计算排名,这样就可以避免用户体验差了,我们可以选择用redis缓存结果。好开始写demo

a=[
{
'score': 123,
'name': 'frank'
},
{
'name': 'jack',
'score': 44
},
{
'name': 'susan',
'score': 188
},
{
'name': 'lisa',
'score': 99
}
] b=[
{
'score': 12,
'name': 'frank'
},
{
'name': 'jack',
'score': 44
},
{
'name': 'susan',
'score': 223
},
{
'name': 'lisa',
'score': 99
}
]

可以看到我们的数据目前是非常简单的,只有用户名和战绩分数两个项。下面我们开始给他排序,注意我们需要根据score来排序,使用python非常容易实现。

sorted(a, lambda x,y: cmp(x.get('score'), y.get('score')))



可以看到我们目前的链表是按照score来排序了!那么如何才能知道用户是上升了几名还是下降了几名呢?这个很容易想到与上次数据对比即可,这样我们就有下面的函数了。

def trend_pk(old_list, new_list):
return a_new_list

如果要实现两个列表的对比难免要一个循环语句,这时候我们是循环new_list还是old_list呢?显然我们知道old_list可以为空,也就是第一次执行排名对比的时候。好那么我们就可以选择new_list来循环了。

def trend_pk(old_list, new_list):
for i in new_list:
pass
return new_list

有了循环语句我们自然需要改变些什么,设想如果一个用户出现在排行榜上了他肯定应该得到些什么,也许是奖品也许只是一个名次等。好那么我们需要判断这个用户是不是第一次上榜,所以自然想到去看看历史列表有没有他。

def trend_pk(old_list, new_list):
for i in new_uid_list:
if i in old_uid_list:
pass
else:
pass
return new_list

回头看看数据结构,我们发现i其实是这样的。

    {
'score': 123,
'name': 'frank'
}

这个可不是用户的唯一标示,我们需要的是用户名或者身份证id等。所以我们需要提取一下用户的唯一标示也就是这里的用户名。

def trend_pk(old_list, new_list):
old_uid_list = [i.get('name') for i in old_list]
new_uid_list = [i.get('name') for i in new_list]
for i in new_list:
if i in old_uid_list:
pass
else:
pass
return new_list

现在我们有了一个uid_list列表,我可以很轻松的得到用户名,但是我们希望这些用户是已经按照分数排好序的。我们可以这样做:

def trend_pk(old_list, new_list):
# You should read both list from redis
old_list = sorted(old_list, lambda x,y: cmp(x.get("score"), y.get("score")))
new_list = sorted(new_list, lambda x,y: cmp(x.get("score"), y.get("score")))
old_uid_list = [i.get('name') for i in old_list]
new_uid_list = [i.get('name') for i in new_list]
for i in new_uid_list:
if i in old_uid_list:
pass
else:
pass
return new_list

上面的代码的if语句只判断uid是否在列表里面即可,此时我们需要做的就是对uid是否有历史记录分别做不同处理。

def trend_pk(old_list, new_list):
# You should read both list from redis
old_list = sorted(old_list, lambda x,y: cmp(x.get("score"), y.get("score")))
new_list = sorted(new_list, lambda x,y: cmp(x.get("score"), y.get("score")))
old_uid_list = [i.get('name') for i in old_list]
new_uid_list = [i.get('name') for i in new_list]
for i in new_uid_list:
index = new_uid_list.index(i)
if i in old_uid_list:
old_index = old_uid_list.index(i)
new_list[index].update({'trend': trend_pk_tool(old_index, index)}) else:
new_list[index].update({'trend':'1'})
return new_list

首先我通过list自带的index函数获取到当前列表的位置,然后如果这个用户有历史记录我就去获取历史记录位置,然后通过一个函数对比这两个位置。不着急待会告诉大家这就对比函数怎么写的。继续看else部分表示如果这个用户没有历史记录,说明他第一次有记录那么这就是有进步。如果用户还关心自己当前到底排在第几名怎么办呢?

def trend_pk(old_list, new_list):
# You should read both list from redis
old_list = sorted(old_list, lambda x,y: cmp(x.get("score"), y.get("score")))
new_list = sorted(new_list, lambda x,y: cmp(x.get("score"), y.get("score")))
old_uid_list = [i.get('name') for i in old_list]
new_uid_list = [i.get('name') for i in new_list]
for i in new_uid_list:
index = new_uid_list.index(i)
if i in old_uid_list:
old_index = old_uid_list.index(i)
new_list[index].update({'trend': trend_pk_tool(old_index, index)})
else:
new_list[index].update({'trend':'1'})
new_list[index].update({'rank':str(index)})
return new_list

可以看到在return之前我们增加了一个rank字段。下面来看看我的trend_pk_tool是什么工具函数吧。

def trend_pk_tool(o, n):
if o > n:
return '1'
elif o == n:
return '0'
else:
return '-1'

简单吧!根据不同的对比结果返回不同的值,或许你还有跟简单的方法来写这个函数哦!想想看~~

快速获取个人战绩

为了快速获取增加的战况,首先肯定知道自己的用户名才能获得自己的战绩。好我们可以用字典的方式,那么刚才的列表要怎么变成列表呢?

def map_uid_to_score_info(score_list):
redis_cache = {}
for i in score_list:
uid = i.get('name')
redis_cache.update({
uid:{
'score':i.get('score'),
'trend':i.get('trend'),
'rank':i.get('rank')
}
}) return redis_cache

好了!这次代码给得非常干脆,现在我们来看看运行结果吧!

if __name__ == '__main__':
ret = trend_pk(a, b)
redis_cache = map_uid_to_score_info(ret)
print redis_cache

拿着我的demo给产品经理确认这数据是不是他想要的,他看了看说不对吧!这个成绩低的怎么排名在前面呢?这个问题怎么很简单只需要在sorted时指定一个参数即可。看看下面代码:

	old_list = sorted(old_list, lambda x,y: cmp(x.get("score"), y.get("score")), reverse=True)
new_list = sorted(new_list, lambda x,y: cmp(x.get("score"), y.get("score")), reverse=True)

当榜单出现并列名次时如何解决

今天跟产品讨论了一个问题,如果出现并列第一的奖品该如何分配?榜单该如何排序?对于产品可能会想通过增加条款或者其他方式解决。而我首先想到的是按照第二条件进行再次排序。所以我想了想如何通过sorted进行多项匹配排序。

sample = [('d', 2), ('a', 4), ('b', 3), ('c', 2)]
print sorted(sample, key=lambda x:(x[1], x[0]))

运行结果是先按照数字排序,再按照字母排序的。

总结

那么在实战项目中我们需要注意些什么呢?首先我们需要用数据库,mysql查询到最新的记录,排名完成后保存到redis中。现实场景中可还可能对不同的排名派发奖品,如何将这些奖品进行兑换也是需要考虑的。

游戏排行榜-Python实现的更多相关文章

  1. nie题目-游戏排行榜设计

    一个mmorpg游戏,玩家众多,需要对玩家战斗力进行排行,并且战斗力变化时需要及时刷新.需要设计一个这样的排行榜. 关于海量数据排行榜的做法,云风在他的博客里给过思路,谈谈陌陌争霸在数据库方面踩过的坑 ...

  2. 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三

    编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三

  3. 【放松一下】北美小游戏排行榜TOP10——“点击英雄”

    大家平时工作都比較忙,在这里推荐一款游戏让大家放松一下.让你体验到指数爆炸般挣钱的快感. 北美小游戏排行榜TOP10--"点击英雄" 简要说明: 游戏能够挂机,关闭电脑.游戏也会自 ...

  4. 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile

      通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...

  5. 通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数

    通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账 ...

  6. 通过游戏学python 3.6 第一季 第二章 实例项目 猜数字游戏--核心代码--猜测次数 可复制直接使用 娱乐 可封装 函数

      猜数字游戏--核心代码--猜测次数   #猜数字--核心代码--猜测次数 number=33 amount=3 count=0 while count<=amount: conversion ...

  7. cocosCreator微信小游戏排行榜思路

    cocosCreator制作微信小游戏排行榜实现方案: 游戏认知:项目分为主域和子域,主域就是游戏主程部分,子域为单独处理微信排行榜公共域数据的. 游戏主域里创建一个节点,添加WXSubContext ...

  8. 猜数字游戏--基于python

    """题目:练习使用python写一个猜数字的游戏,数字范围0-100,每次猜错,需要给出缩小后的范围,每个人只有10次的猜测机会,猜测机会用完游戏结束!"&q ...

  9. 模仿学习小游戏外星人入侵-Python学习,体会“函数”编程

    游戏类如下: # !/usr/bin/python # -*- coding:utf-8 -*- """ Author :ZFH File :alien.py Softw ...

随机推荐

  1. web.xml运行序列总结

    在整个订单<context-param>--<listener>--<filter>--<servlet>. 其中,内的各类别中的序列被运行.和< ...

  2. uml系列(四)——类图

    类图是uml的核心.学习类图,总共须要掌握三个部分:类:类之间的关系:类图怎么画. 首先,类.老规矩,先来张图. 类是什么:举个简单的样例:猫.狗.猪三个都是动物.这里面的"动物" ...

  3. 微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件

    原文:微软BI 之SSIS 系列 - 使用 Script Task 访问非 Windows 验证下的 SMTP 服务器发送邮件 开篇介绍 大多数情况下我们的 SSIS 包都会配置在 SQL Agent ...

  4. SpringMVC全注解

    SpringMVC全注解不是你们那么玩的 前言:忙了段时间,忙得要死要活,累了一段时间,累得死去活来. 偶尔看到很多零注解配置SpringMVC,其实没有根本的零注解. 1)工程图一张: web.xm ...

  5. jsoneditor显示Json data

    Git开源地址:https://github.com/josdejong/jsoneditor/blob/master/docs/api.md 1.引用JS文件 <!-- jsoneditor ...

  6. 领域驱动设计(DDD)的实际应用

    领域驱动设计(DDD)的实际应用   笔者先前参与了一个有关汽车信息的网站开发,用于显示不同品牌的汽车的信息,包括车型,发动机型号,车身尺寸和汽车报价等信息.在建模时,我们只需要创建名为Car的实体( ...

  7. 使用Eclipse开始Java编程

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/start-java-programing ...

  8. android获取存储卡使用情况

    package com.aib.com; import java.io.File; import android.app.Activity; import android.os.Bundle; imp ...

  9. [实验]通过内核Patch去掉iOS-v4.3.3的沙盒特性

    环境: 1.Mac OS X 10.9.2 2.xcode 5.1.1 3.gcc 4.8 4.redsn0w 0.9.15b3 前提: 1.获取 iOS 4.3.3 的kernelcache,并解密 ...

  10. nginx日志简单分析工具

    自己有个tony6.com的服务器,上面挂着我的博客,web服务器是nginx. 由于最近一直在折腾python,所以简单写了个nginx日志分析工具,它可以分析出每个IP的点击数量和IP所在地. # ...