游戏设计中常常使用排行榜,根据排行发送排名奖励。
排行容器的组成
记录排行的容器是一个由内部由vector和map组成的模板类
1.1.容器为什么这么设计
1.1.1vector部分的结构
其中vector里的元素使用的是pair, pair的first部分保存的是 uid(玩家ID),second部分保存的我们想要保存的任何信息(当然是一个类,或者结构体),然而vector的排列顺序是按照名词排列的
1.1.2. map的结构
map的first的部分也是uid,second部分是玩家的名词
1.1.3为什么使用vector和map的组合设计这么一个容器?
我们知道vector的优点是取值方便,map的优点是查找快速。
那么容器的有点有如下:
1.1.3.1. 获得名次快速
通过map查找uid,快速拿到名词
1.1.3.2通过名词,快速获得我们保存的信息
根据map查找的名次,我们作为vector的下标获取该玩家的信息。
下面提供排行模板类的代码实现
#ifndef _RANKLIST_H_
#define _RANKLIST_H_
#include
#include
using namespace std;
#include
#include
using boost::unordered_map;
#include "../common/const_def.h"
template<typename RankKey, int Length = 1000, class Pr = greater >
class RankList
{
public:
RankList(){}
~RankList(){ clear();}
void init(vector >& rank);
bool modify(int64 uid, RankKey key);
void remove(int64 uid, RankKey key);
void erase(int64 uid);
void clear();
unsigned size() const;
unsigned rank(int64 uid) const;//根据uid得到名次
int64 getuid(unsigned rank) const;//根据名次得到uid
const RankKey* const getkey(int64 uid) const;//根据uid得到key信息
vector partialUserList(unsigned start, unsigned length) const;//取出排名在[start + 1, start + length -1]范围内的uid;
const vector > & get_vec_rank(){return rank_;}//取出全部Key信息
//=
RankList& operator=(const RankList& myRanklist)
{
clear();
this->index_ = myRanklist.index_;
this->rank_ = myRanklist.rank_;
return *this;
}
//[]
const pair* operator[](int index){
if(index < 0 || index >= int(rank_.size()))
{
return NULL;
}
return &this->rank_[index];
}
//构造
RankList(const RankList& myRanklist){
clear();
this->index_ = myRanklist.index_;
this->rank_ = myRanklist.rank_;
}
const RankKey* getkey(int64 uid);
protected:
void sortall();
void sort(unsigned index);
void trim();
void swap(unsigned indexA, unsigned indexB);
private:
vector > rank_; //uid, uid对应的数据
unordered_mapunsigned > index_; //uid, uid对应的排名
};
template<typename RankKey, int Length, class Pr>
void RankList::clear()
{
vector > temp;
init(temp);
}
template<typename RankKey, class Pr>
static bool RankComp(const pair& l, const pair& r)
{
static Pr pr;
return pr(l.second, r.second);
}
template<typename RankKey, int Length, class Pr>
void RankList::init(vector >& rank)
{
rank_.swap(rank);
sortall();
trim();
index_.clear();
for (size_t i = 0; i < rank_.size(); i++)
index_[rank_[i].first] = i;
}
template<typename RankKey, int Length, class Pr>
const RankKey* RankList::getkey(int64 uid)
{
BOOST_AUTO(it, index_.find(uid));
if (it != index_.end())
return &rank_[it->second].second;
return NULL;
}
template<typename RankKey, int Length, class Pr>
bool RankList::modify(int64 uid, RankKey key)
{
static Pr pr;
BOOST_AUTO(it, index_.find(uid));
size_t sz = rank_.size();
// not in list now and will not be in list
if (it == index_.end() && sz >= Length && !pr(key, rank_[sz-1].second))
return false;
if (it != index_.end())
{
rank_[it->second].second = key;
}
else
{
index_[uid] = rank_.size();
rank_.push_back(make_pair(uid, key));
}
sort(index_[uid]);
trim();
return true;
}
template<typename RankKey, int Length, class Pr>
void RankList::erase(int64 uid)
{
BOOST_AUTO(it, index_.find(uid));
if (it == index_.end() || rank_.size() <= 0)
return;
unsigned indexCurrent = it->second, indexLast = rank_.size()-1;
if (indexCurrent != indexLast)
swap(indexCurrent, indexLast);
rank_.pop_back();
index_.erase(uid);
if (indexCurrent != indexLast)
sort(indexCurrent);
}
template<typename RankKey, int Length, class Pr>
unsigned RankList::rank(int64 uid) const
{
BOOST_AUTO(it, index_.find(uid));
if (it == index_.end())
return 0;
return it->second + 1;
}
template<typename RankKey, int Length, class Pr>
vector RankList::partialUserList(unsigned start, unsigned length) const
{
vector v;
if(start < 0)
return v;
size_t sz = rank_.size();
for (size_t i = start; i < sz && i < start + length; i++)
v.push_back(rank_[i].first);
return v;
}
template<typename RankKey, int Length, class Pr>
unsigned RankList::size() const
{
return rank_.size();
}
template<typename RankKey, int Length, class Pr>
void RankList::sortall()
{
std::sort(rank_.begin(), rank_.end(), RankComp);
}
template<typename RankKey, int Length, class Pr>
void RankList::sort(unsigned index)
{
static Pr pr;
while (index > 0 && pr(rank_[index].second, rank_[index-1].second))
{
swap(index-1, index);
index--;
}
size_t sz = rank_.size();
while ( sz > 0 && index < sz-1 && pr(rank_[index+1].second, rank_[index].second))
{
swap(index, index+1);
index++;
}
}
template<typename RankKey, int Length, class Pr>
void RankList::swap(unsigned indexA, unsigned indexB)
{
if (indexA >= rank_.size() || indexB >= rank_.size())
return;
std::swap(rank_[indexA], rank_[indexB]);
index_[rank_[indexA].first] = indexA;
index_[rank_[indexB].first] = indexB;
}
template<typename RankKey, int Length, class Pr>
void RankList::trim()
{
size_t sz = rank_.size();
while (sz > Length)
{
index_.erase(rank_[sz-1].first);
rank_.pop_back();
sz--;
}
}
template<typename RankKey, int Length, class Pr>
void RankList::remove(int64 uid, RankKey key)
{
static Pr pr;
BOOST_AUTO(it, index_.find(uid));
size_t sz = rank_.size();
// not in list now and will not be in list
if (it == index_.end() && sz >= Length && !pr(key, rank_[sz-1].second))
return;
int nIndex = index_[uid];
if (it != index_.end())
{
index_.erase(uid);
for(size_t i = 0; i < rank_.size(); i++)
{
if(rank_[i].first == uid)
rank_.erase(rank_.begin()+i);
}
}
sort(nIndex);
trim();
}
template<typename RankKey, int Length, class Pr>
int64 RankList::getuid(unsigned rank) const
{
if (index_.size() < rank)
return 0;
BOOST_AUTO(iter, index_.begin());
for (; iter!=index_.end(); ++iter)
{
if ((iter->second + 1)== rank)
{
return iter->first;
break;
}
}
return 0;
}
template<typename RankKey, int Length, class Pr>
const RankKey* const RankList::getkey(int64 uid) const
{
BOOST_AUTO(it, index_.find(uid));
if (it == index_.end())
return NULL;
return &(rank_[it->second].second);
}
#endif
4.进一步说明排行榜的参数,以及排序问题
排行榜模板类,需要三个参数,template
template<typename RankKey, int Length, class Pr>
bool RankList::modify(int64 uid, RankKey key)
{
static Pr pr;
BOOST_AUTO(it, index_.find(uid));
size_t sz = rank_.size();
// not in list now and will not be in list
//在容器已经满的情况下,并且是新数据,并且新数据比最后一个元素还要小的时候,就不会计入排行榜
if (it == index_.end() && sz >= Length && !pr(key, rank_[sz-1].second))
return false;
if (it != index_.end())
{
rank_[it->second].second = key;
}
else
{
index_[uid] = rank_.size();
rank_.push_back(make_pair(uid, key));
}
//其中sort的个根据我们写好的排序规则进行排序
sort(index_[uid]);
//如果超出排行的限制,那么让排行榜的大小变为我们传入参数legth的大小
trim();
return true;
}
5.排行榜的应用
//定义我们保存数据内容,以及排序规则
struct RankKey
{
RankKey(int score, int64 time, int64 uid) : m_score(score),m_time(time),m_uid(uid) {}
bool operator >(const RankKey &that) const {
if (m_score == that.m_score){
if (m_time == that.m_time)
return m_uid < that.m_uid;
else
return m_time < that.m_time;
}
else
return m_score > that.m_score;
}
int getScore(){ return m_score;}
int64 getUid(){ return m_uid;}
int64 getTime() {return m_time;}
}
RankList 100> m_levelRankList;//等级排行榜
//当有数据更新或者有新数据时调用如下
for(int i = 0; i < 10; i++)
{
int64 uid = i;
time_t newtime= time(NULL) + i * 100;
int lvl = i * 10;
m_levelRankList.modify(uid, RankKey(lvl, newtime , uid));
}
//那么我们就进行了排行,假设我们想要根据排名发送奖励
//那么我们可以调用vector partialUserList(unsigned start, unsigned length) const;//取出排名在[start + 1, start + length -1]范围内的uid;获取排名的uid,然后调用发送邮件的函数接口进行,发送奖励
//题外话,发送玩奖励一般调用clear,将排行榜清空,防止逻辑错误重复发奖
5.题外话:
其中排行榜的一般和另外一个map结合使用
mapint > m_mapAlldata//玩家uid,玩家的得分(这里保存所有玩家的数据,不管是否进入排行榜)
RankList100> m_scoreRankList;//分数排行榜(这里仅仅保存进入排行榜的数据)
一般情况下是先更新m_mapAlldata,然后更新排行榜(调用modify)
这样就实现了游戏中的排行榜
6 缺点
sort的时候,其实是对vector的操作,排序时挪动元素比较耗时