游戏中排行榜代码实现

游戏设计中常常使用排行榜,根据排行发送排名奖励。

  1. 排行容器的组成
    记录排行的容器是一个由内部由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的下标获取该玩家的信息。

  2. 下面提供排行模板类的代码实现

#ifndef _RANKLIST_H_
#define _RANKLIST_H_

#include <vector>
#include <algorithm>
using namespace std;
#include <boost/typeof/typeof.hpp>
#include <boost/unordered_map.hpp>
using boost::unordered_map;
#include "../common/const_def.h"

template<typename RankKey, int Length = 1000, class Pr = greater<RankKey> >
class RankList
{
public:
    RankList(){}
    ~RankList(){ clear();}

    void init(vector<pair<int64, RankKey> >& 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<int64> partialUserList(unsigned start, unsigned length) const;//取出排名在[start + 1, start + length -1]范围内的uid;
    const vector<pair<int64, RankKey> > & get_vec_rank(){return rank_;}//取出全部Key信息

    //=
    RankList& operator=(const RankList& myRanklist)
    {
        clear();
        this->index_ = myRanklist.index_;
        this->rank_ = myRanklist.rank_;
        return *this;
    }

    //[]
    const pair<int64, RankKey>* 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<pair<int64, RankKey> >  rank_;   //uid, uid对应的数据
    unordered_map<int64, unsigned> index_; //uid, uid对应的排名
};


template<typename RankKey, int Length, class Pr>
void RankList<RankKey, Length, Pr>::clear()
{
    vector<pair<int64, RankKey> > temp;
    init(temp);
}

template<typename RankKey, class Pr>
static bool RankComp(const pair<int64, RankKey>& l, const pair<int64, RankKey>& r)
{                                                                                        
    static Pr pr;
    return pr(l.second, r.second);
}

template<typename RankKey, int Length, class Pr>
void RankList<RankKey, Length, Pr>::init(vector<pair<int64, RankKey> >& 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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<int64> RankList<RankKey, Length, Pr>::partialUserList(unsigned start, unsigned length) const
{
    vector<int64> 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<RankKey, Length, Pr>::size() const
{
    return rank_.size();
}

template<typename RankKey, int Length, class Pr>
void RankList<RankKey, Length, Pr>::sortall()
{
    std::sort(rank_.begin(), rank_.end(), RankComp<RankKey, Pr>);
}

template<typename RankKey, int Length, class Pr>
void RankList<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, Length, Pr>::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<RankKey, 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<int64> partialUserList(unsigned start, unsigned length) const;//取出排名在[start + 1, start + length -1]范围内的uid;获取排名的uid,然后调用发送邮件的函数接口进行,发送奖励

//题外话,发送玩奖励一般调用clear,将排行榜清空,防止逻辑错误重复发奖

5.题外话:

 其中排行榜的一般和另外一个map结合使用
 map<int64, int > m_mapAlldata//玩家uid,玩家的得分(这里保存所有玩家的数据,不管是否进入排行榜)
RankList<RankKey, 100> m_scoreRankList;//分数排行榜(这里仅仅保存进入排行榜的数据)

一般情况下是先更新m_mapAlldata,然后更新排行榜(调用modify)
这样就实现了游戏中的排行榜

6 缺点
sort的时候,其实是对vector的操作,排序时挪动元素比较耗时

猜你喜欢

转载自blog.csdn.net/weixin_37098881/article/details/81511251