C++ 三消游戏基本实现

最近在研究三消算法,我想试试在完全不借助网络资源的情况下搞定这个东西,所以有些地方可能不是最优的。

代码留此备忘。

 

1. 3x_desk_event.h

  1 #pragma once
  2 
  3 #ifndef __3X_DESK_EVENT_H_
  4     #define __3X_DESK_EVENT_H_
  5 
  6 #include "3x_desk_inc.h"
  7 #include <vector>
  8 #include <map>
  9 
 10 /////////////////////// Event Action Datas ///////////////////////
 11 
 12 #define StateEventCallBackT std::function<void(EAction, const __Event&)>
 13 
 14 struct Action {};
 15 struct Bonus : public Action
 16 {
 17     EBonusType _bonus = EBonusType::BONUS_None;
 18     uint32_t _count = 0;    //The max-count of line. {[3, 5]} & {0}
 19     std::vector<Coord> _cleared;  
 20 };
 21 
 22 struct Exchange : public Action
 23 {
 24     Coord _coordA;
 25     Coord _coordB;
 26 };
 27 
 28 struct Replenish : public Action
 29 {
 30     std::vector<Coord> _news;
 31     std::vector<Coord> _changed;
 32 };
 33 
 34 struct Refresh : public Action
 35 {
 36     std::vector<Coord> _src;
 37     std::vector<Coord> _become;
 38 };
 39 
 40 struct Select : public Action
 41 {
 42     Coord _coord;
 43     int _ex = 0;
 44 };
 45 
 46 struct NoneAction : public Action
 47 {
 48 public:
 49     union __Act
 50     {
 51     public:
 52         Exchange _exchange;
 53         Select _select;
 54         __Act()
 55         {
 56             new(&_exchange) Exchange;
 57             new(&_select) Select;
 58         }
 59         ~__Act(){}
 60     };
 61 
 62     ~NoneAction()
 63     {
 64         switch (_type)
 65         {
 66         case EAction::ACTION_EXCHANGE:
 67             _info._exchange.~Exchange();
 68             break;
 69         
 70         case EAction::ACTION_BONUS_BOMB:
 71             _info._select.~Select();
 72             break;
 73 
 74         default:
 75             break;
 76         }
 77     }
 78 
 79     void Init(EAction t, Action* pAct)
 80     {
 81         if(t == EAction::ACTION_None)
 82             return;
 83 
 84         _type = t;
 85 
 86         if(pAct == nullptr)
 87             return;
 88 
 89         switch (t)
 90         {
 91         case EAction::ACTION_EXCHANGE:
 92             _info._exchange = *((Exchange*)pAct);
 93             break;
 94         
 95         case EAction::ACTION_BONUS_BOMB:
 96             _info._select = *((Select*)pAct);
 97             break;
 98 
 99         default:
100             break;
101         }
102     }
103 
104     inline EAction GetType() const noexcept { return _type; }
105     inline __Act GetInfo() const noexcept { return _info; }
106 
107 private:
108     __Act _info;
109     EAction _type = EAction::ACTION_None;
110 };
111 
112 /////////////////////// Event types ///////////////////////
113 template<EAction E> struct __action_type{};
114 template<> struct __action_type<EAction::ACTION_EXCHANGE>
115 {
116     using type = Exchange;
117 };
118 template<> struct __action_type<EAction::ACTION_BONUS>
119 {
120     using type = Bonus;
121 };
122 template<> struct __action_type<EAction::ACTION_REPLENISH>
123 {
124     using type = Replenish;
125 };
126 template<> struct __action_type<EAction::ACTION_REFRESH>
127 {
128     using type = Refresh;
129 };
130 template<> struct __action_type<EAction::ACTION_BONUS_BOMB>
131 {
132     using type = Bonus;
133 };
134 template<> struct __action_type<EAction::ACTION_None>
135 {
136     using type = NoneAction;
137 };
138 template<> struct __action_type<EAction::ACTION_FINAL>
139 {
140     using type = bool;
141 };
142 
143 template<> struct __action_type<EAction::ACTION_BONUS_BOMB_INPUT>
144 {
145     using type = Select;
146 };
147 
148 template<> struct __action_type<EAction::ACTION_EXCHANGE_INPUT>
149 {
150     using type = Exchange;
151 };
152 
153 /////////////////////// Events ///////////////////////
154 struct __Event {};
155 template<EAction t> struct Event 
156     : public __Event
157 {
158 public:
159     enum { _type = t };
160     Event(){}
161     ~Event(){};
162 
163 public:
164     inline constexpr const EAction GetType() const { return t; }
165     typename __action_type<t>::type& Info() noexcept
166     {
167         return _info;
168     }
169 
170     const typename __action_type<t>::type& GetInfo() const noexcept
171     {
172         return _info;
173     }
174 
175 private:
176     typename __action_type<t>::type _info;
177 };
178 
179 
180 
181 #endif

 

2.  3x_desk_inc.h

  1 #pragma once
  2 
  3 #ifndef __3X_DESK_INC_H_
  4     #define __3X_DESK_INC_H_
  5 
  6 #include <stdint.h>
  7 #include <array>
  8 
  9 enum EDeck : uint8_t
 10 {
 11     DECK_None       ,
 12     DECK_BLOCK      ,
 13     DECK_WALL       ,
 14     DECK_FREEZE     ,
 15 };
 16 
 17 enum EElem : uint8_t
 18 {
 19     ELEM_None,
 20     ELEM_A,
 21     ELEM_B,
 22     ELEM_C,
 23     ELEM_D,
 24     ELEM_E,
 25     ELEM_F,
 26     ELEM_G,
 27     ELEM_H,
 28     ELEM_I,
 29     ELEM_J,
 30 
 31     __ELEM_MAX_ ,
 32     __ELEM_COUNT_ = __ELEM_MAX_ - 1
 33 };
 34 
 35 enum EAction
 36 {
 37     ACTION_None,        //开始
 38     ACTION_EXCHANGE,    //交换
 39     ACTION_BONUS,       //消除
 40     ACTION_REPLENISH,   //充满
 41     ACTION_REFRESH,     //刷新/重排
 42     ACTION_BONUS_BOMB,  //特殊消除
 43     ACTION_FINAL,       //结束
 44 
 45     //作为输入指令, 不进行类型偏特化,仅作为输入时的状态
 46     ACTION_EXCHANGE_INPUT,  
 47     ACTION_BONUS_BOMB_INPUT,
 48 };
 49 
 50 enum EBonusType : uint32_t
 51 {
 52     __BONUS_BOMB_           = 0xB000,
 53     BONUS_None              = 0,            //nothing
 54     BONUS_ACROSS            = (1 << 0),     //---
 55     BONUS_VERTICAL          = (1 << 1),     //|||
 56     BONUS_CROSS             = BONUS_ACROSS|BONUS_VERTICAL, // +++ / LLL
 57     BONUS_BOMB_ACROSS       = (__BONUS_BOMB_ | BONUS_ACROSS),
 58     BONUS_BOMB_VERTICAL     = (__BONUS_BOMB_ | BONUS_VERTICAL),
 59     BONUS_BOMB_CROSS        = (__BONUS_BOMB_ | BONUS_CROSS),
 60     BONUS_BOMB_BY_SELECT    = (__BONUS_BOMB_ | 0xFE),
 61     BONUS_BOMB_CLEAR        = (__BONUS_BOMB_ | 0xFF),
 62 
 63 };
 64 
 65 using Axis = int;   //signed intager must!
 66 enum AxisType { AxisX, AxisY };
 67 
 68 struct Coord
 69 {
 70     Coord(Axis x, Axis y)
 71         : _x(x), _y(y)
 72     {}
 73 
 74     Coord(Axis x, Axis y, EElem e)
 75         : _x(x), _y(y), _e(e)
 76     {}
 77 
 78     Coord() : Coord(0, 0)
 79     {}
 80 
 81     Coord& operator()(Axis x, Axis y)
 82     {
 83         _x = x, _y = y;
 84         return *this;
 85     }
 86 
 87     bool operator< (const Coord& r)
 88     {
 89         return _x < r._x ? true : _y < r._y;
 90     }
 91 
 92     bool operator==(const Coord& r)
 93     {
 94         return _x == r._x && _y == r._y;
 95     }
 96 
 97     Axis _x = 0, _y = 0;
 98 
 99     //@notice: This value is uarely used.
100     EElem _e = EElem::ELEM_None;
101 };
102 
103 
104 #endif

 

 

3. 3x_desk_state.h

 1 /**
 2  * @file 3x_desk_state.h
 3  * @author EvenOyan 
 4  * @brief   三消游戏的状态机,
 5  *          一共分为 开始、交换、消除、下落、刷新、结束 6个状态,
 6  *          每个状态都在执行属于自己的事情,
 7  * @notice: 此状态机的实现稍为复杂,
 8  *          使用了大量的模版特化,静态判断,宏替换,
 9  *          在试图更改此代码时,务必注意细节,
10  *      
11  * @version 0.3
12  * @date 2021-02
13  * 
14  * @copyright Copyright (c) 2021
15  * 
16  */
17 
18 #pragma once 
19 
20 #ifndef __3X_DESK_STATE_H
21     #define __3X_DESK_STATE_H
22 
23 #include "3x_desk.h"
24 
25 #include "../../stdinc/lc_random.h"
26 #include "../../stdinc/lc_singleton.h"
27 
28 #include <functional>
29 #include <unordered_set>
30 
31 /////////////////////// Defines ///////////////////////
32 
33 #define InputFuncBegin(state)\
34     template<EAction action>  \
35     void __3x_state<state>::Input(Desk* pDesk, const Event<action>& in, const StateEventCallBackT& cb){\
36         if(pDesk == nullptr) return;
37 
38 #define UpdateFuncBegin(state)\
39     template<EAction action>  \
40     void __3x_state<state>::Update(Desk* pDesk, const Event<action>& in, const StateEventCallBackT& cb){\
41         if(pDesk == nullptr) return;
42 
43 #define FuncEnd() }
44 
45 #define __IUpdate(ev)\
46     Update<stateB>(pDesk, ev, cb)
47 
48 #define __IGoTo(stateB, ev)\
49     __3x_state<stateB>::Input<(EAction)_state>(pDesk, ev, cb);    \
50 
51 #define Start_3x_State(pDesk, stateB, ev, fc)\
52     __3x_state<EAction::ACTION_None>::Input<stateB>(pDesk, ev, fc);
53 
54 //e.g.: state A->B->C  =>  B.Input(A); C = B.Update();
55 #define __StateClassDefine(state) \
56     template<> \
57     class __3x_state<state>  \
58         : public Singleton<__3x_state<state> >   \
59     {   \
60     public: \
61         __3x_state(token){} \
62     \
63     private: \
64         enum { _state = state }; \
65         \
66     public: \
67         template<EAction action>  /*输入状态*/\
68         static void Input(Desk* pDesk, const Event<action>& in/*输入参数*/, const StateEventCallBackT& cb);\
69         template<EAction action>  /*输入状态*/\
70         static void Update(Desk* pDesk, const Event<action>& in/*输入参数*/, const StateEventCallBackT& cb);\
71     \
72     };
73 
74 //used at 'input'
75 #define __StateAccept(stateA)\
76     if constexpr(stateA == action) { __3x_state<(EAction)_state>::Update<stateA>(pDesk, in, cb); return; }
77 
78 /////////////////////// State Graph ///////////////////////
79 template<EAction E>class __3x_state {};
80 template<> class __3x_state<EAction::ACTION_None>;
81 template<> class __3x_state<EAction::ACTION_EXCHANGE>;
82 template<> class __3x_state<EAction::ACTION_BONUS_BOMB>;
83 template<> class __3x_state<EAction::ACTION_BONUS>;
84 template<> class __3x_state<EAction::ACTION_REPLENISH>;
85 template<> class __3x_state<EAction::ACTION_REFRESH>;
86 template<> class __3x_state<EAction::ACTION_FINAL>;
87 
88 __StateClassDefine(EAction::ACTION_EXCHANGE);
89 __StateClassDefine(EAction::ACTION_BONUS_BOMB);
90 __StateClassDefine(EAction::ACTION_None);
91 __StateClassDefine(EAction::ACTION_BONUS);
92 __StateClassDefine(EAction::ACTION_REPLENISH);
93 __StateClassDefine(EAction::ACTION_REFRESH);
94 __StateClassDefine(EAction::ACTION_FINAL);
95 
96 #include "3x_desk_state.inl"
97 
98 #endif

 

4. 3x_desk_state.inl

#include <iostream>
#include <unordered_set>
//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_None)
    //开始状态,
    __StateAccept(EAction::ACTION_EXCHANGE_INPUT);
    __StateAccept(EAction::ACTION_BONUS_BOMB_INPUT);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_None)
    Event<EAction::ACTION_None> ev;
    if(in.GetType() == EAction::ACTION_EXCHANGE_INPUT)
    {
        std::cout << "exchange input: " << in.GetInfo()._coordA._x << in.GetInfo()._coordA._y << in.GetInfo()._coordB._x << in.GetInfo()._coordB._y<< std::endl;
        ev.Info().Init(EAction::ACTION_EXCHANGE, (Action*)&(in.GetInfo()));
        __IGoTo(EAction::ACTION_EXCHANGE, ev);
        return;
    }
    
    if(in.GetType() == EAction::ACTION_BONUS_BOMB_INPUT)
    {
        ev.Info().Init(EAction::ACTION_BONUS_BOMB, (Action*)&(in.GetInfo()));
        __IGoTo(EAction::ACTION_BONUS_BOMB, ev);   
        return;
    }

FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_FINAL)
    __StateAccept(EAction::ACTION_REFRESH);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_FINAL)
    Event<EAction::ACTION_FINAL> ev;
    cb(EAction::ACTION_FINAL, ev); //响应 final 状态
FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_EXCHANGE)
    std::cout << "exchange input function " << std::endl;
    __StateAccept(EAction::ACTION_None);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_EXCHANGE)
    //仅接受初始状态,在交换过后需要进入‘消除’状态
    //在当前状态直接判断是否可以消除,可以简化许多操作
    //所以在此,仅当可以消除时,才可以进入下一个状态   

    if(in.GetType() != EAction::ACTION_None)
    {
        std::cout << "in.GetType() != EAction::ACTION_None " << std::endl;
        return;
    }
        
    const auto& act = in.GetInfo().GetInfo()._exchange;
    
    //尝试交换
    if(!pDesk->Exchange(act._coordA, act._coordB))
    {
        std::cout << "!pDesk->Exchange(act._coordA, act._coordB)) " << std::endl;
        return;
    }

    if(pDesk->IsInBonus(act._coordA)
        || pDesk->IsInBonus(act._coordB) )
    {
        std::cout << "can bonus." << std::endl;
        //只有可消除才会响应交换事件,进入Bonus状态
        Event<EAction::ACTION_EXCHANGE> ev;
        ev.Info()._coordA = act._coordA;
        ev.Info()._coordA._e = pDesk->get<EElem>(act._coordA._x, act._coordA._y);
        ev.Info()._coordB = act._coordB;
        ev.Info()._coordB._e = pDesk->get<EElem>(act._coordB._x, act._coordB._y);
        cb(EAction::ACTION_EXCHANGE, ev);
        __IGoTo(EAction::ACTION_BONUS, ev);
        return ;
    }
    else
    {
        //发现交换过后并不能消除,还原
        std::cout << "can not bonus." << std::endl;
        pDesk->Exchange(act._coordA, act._coordB);
        return ;
    }
    return ;
    
FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_BONUS)
    if(in.GetType() != action)
        return;
    //接受‘交换’和‘充满’两个状态,
    __StateAccept(EAction::ACTION_EXCHANGE);
    __StateAccept(EAction::ACTION_REPLENISH);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_BONUS)
    //1. Check the 'Event& in', 
    //  1.1 If the action is 'ACTION_EXCHANGE', check bonus is can do or not.
    //    1.1.1 If not, reduce reversive, return FALSE(-1).
    //    1.1.2 If can do, do bonus.
    //  1.2 If the action is 'NoneAction', SPECIAL-SKILLs perhaps...[?]

    //2. Do bonus,
    // 2.1 If bonus is happend, return 1, or return 2.
    std::vector<Coord> changes;
    const auto& act = in.GetInfo();
    //上个阶段是“交换”
    if constexpr(action == EAction::ACTION_EXCHANGE)
    {
        changes.push_back(act._coordA);
        changes.push_back(act._coordB);
    }
    //上一个阶段是“补充”,需要计算补充而来的新元素是否可以消除
    else if(action == EAction::ACTION_REPLENISH)
    {
        changes = act._changed;
    }

    bool isBonus = false;   //消除事件是否发生
    std::vector<Coord> cleared; //如果消除事件发生,被消除的坐标
    std::vector<Coord> bonusUnit;
    std::unordered_set<int> signTotal;  //

    for (const auto& coord : changes)
    {
        if(signTotal.find(pDesk->CoordToNum(coord)) != signTotal.end())
            continue;

        std::unordered_set<int> sign; 
        
        int bonusType = EBonusType::BONUS_None;
        std::vector<Coord> bonus;   //There are coord with elem-type. It's chche.
        int count = 0, maxCountLine = 0;
        bonusType |= (int)(pDesk->Bonus(coord, bonus, count));
        maxCountLine = std::max(maxCountLine, count);

        if(bonusType == EBonusType::BONUS_None)
            continue;

        std::vector<int> stack;
        //to stack
        for(const Coord& b : bonus)
            stack.push_back(pDesk->CoordToNum(b));

        bonus.clear();

        while(!stack.empty())
        {
            int num = stack.back();
            stack.pop_back();
            if(sign.find(num) != sign.end())
                continue;

            sign.insert(num);

            Coord next = pDesk->NumToCoord(num);
            bonusUnit.push_back(next);

            bonusType |= pDesk->Bonus(next, bonus, count);
            maxCountLine = std::max(maxCountLine, count);

            for(const Coord& b : bonus)
                stack.push_back(pDesk->CoordToNum(b));
        }

        if(bonusType != EBonusType::BONUS_None)
        {
            isBonus = true;

            Event<EAction::ACTION_BONUS> cbEvent;
            cbEvent.Info()._bonus = (EBonusType)bonusType;
            cbEvent.Info()._cleared = bonusUnit;
            cbEvent.Info()._count = maxCountLine;
            cb(EAction::ACTION_BONUS, cbEvent);    //事件响应:每一个单元的消除都会执行一次事件响应,与次数无关

            for (const auto& bu : bonusUnit)
                signTotal.insert(pDesk->CoordToNum(bu));
        }

        cleared.insert(cleared.end(), bonusUnit.begin(), bonusUnit.end());
        bonusUnit.clear();

        ///////////////////////////////////////////////////////////
        // //bonus happen.
        // std::vector<Coord> bonus;   //There are coord with elem-type.
        // int count = 0;
        // //尝试消除,不真正更改棋盘
        // //返回值 bonus 携带元素类型,
        // EBonusType t = pDesk->Bonus(coord, bonus, count);
        // if(bonus.empty())
        //     continue;   //It's have no bonus.

        // {
        //     //in cache
        //     for(const auto& c : bonus)
        //         cache[pDesk->CoordToNum(c)] = c;
        // }

        // isBonus = true;

        // Event<EAction::ACTION_BONUS> cbEvent;
        // cbEvent.Info()._bonus = t;
        // cbEvent.Info()._cleared = bonus;
        // cbEvent.Info()._count = count;
        // cb(EAction::ACTION_BONUS, cbEvent);    //事件响应:每一个单元的消除都会执行一次事件响应,与次数无关
    }

    Event<EAction::ACTION_BONUS> ev;
    if(isBonus)
    {
        //set block.
        for (auto&& each : cleared)
            pDesk->get<EElem>(each._x, each._y) = EElem::ELEM_None;

        //Next state is 'ACTION_REPLENISH'.
        ev.Info()._cleared = cleared;  
        __IGoTo(EAction::ACTION_REPLENISH, ev);

        //////////////////////test
        for (size_t i = 0; i < pDesk->_height; i++)
        {
            for (size_t j = 0; j < pDesk->_width; j++)
            {
                std::cout << (int)(pDesk->get<EElem>(j, i)) << " ";
            }
            std::cout << std::endl;
        }
        std::cout << std::endl;
        //////////////////////

        return; // 已经成功执行了消除操作,进入‘充满’状态; 
    }
    else
    {
        //Next state is 'ACTION_REFRESH'.
        __IGoTo(EAction::ACTION_REFRESH, ev);
        return; // 没有发生消除事件,进入‘判断重排’状态;
    }
    
    return; //发生未知错误,状态机停止此刻,不阻塞,不更新

FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_REPLENISH)
    //仅接受‘bonus’状态,更新本状态后,再次进入‘bonus’状态,
    //开始进入好运连续消除的状态循环
    __StateAccept(EAction::ACTION_BONUS);
    __StateAccept(EAction::ACTION_BONUS_BOMB);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_REPLENISH)
    const auto& act = in.GetInfo();

    std::set<Axis> xs;
    std::unordered_map<Axis, Axis> changed; //x:y
    
    for (const auto& each : act._cleared)
    {
        xs.insert(each._x);

        //changed
        //only insert y-max for each x.
        auto it = changed.find(each._x);
        if(it == changed.end() || it->second < each._y)
            changed[each._x] = each._y;
    }

    Event<EAction::ACTION_REPLENISH> ev;
    for(const auto[x, y] : changed)
    {
        for(Axis i = y; i >= 0; --i)
        {
            ev.Info()._changed.emplace_back(x, i);
        }
    }

    std::vector<Coord> newCoords;
    for (Axis x : xs)
    {
        std::vector<EElem>& eachX = pDesk->getcolume<EElem>(x);
        int blockCnt = 0,
            lastBlock = -1; //b
        for (Axis i = eachX.size() - 1; i >= 0; --i) //i
        {
            if(eachX[i] == EElem::ELEM_None)
            {
                blockCnt++;

                /**
                 * -------1. find 'b', it's block
                 * x
                 * o
                 * x
                 * x <- b
                 * o
                 * -------
                 */ 
                if(lastBlock == -1)
                    lastBlock = i;
                continue;
            }

            /**
             * -------2. find 'i', it's not block.
             * x
             * o <- i
             * x
             * x <- b
             * o
             * -------
             */
            if(lastBlock > -1 && eachX[i] != EElem::ELEM_None)
            {
                /**
                 * -------3. swap
                 * x
                 * x <- i
                 * x
                 * o <- b
                 * o
                 * -------
                 */
                std::swap(eachX[lastBlock], eachX[i]);

                /**
                 * -------4. reset 'b'
                 * x
                 * x <- i
                 * x <- b
                 * o
                 * o
                 * -------
                 */
                lastBlock--;
                continue;
            }
        }

        for (size_t i = 0; i < blockCnt; i++)
            newCoords.emplace_back(x, i, pDesk->GetRandomElem());
    }

    for(auto&& neweach : newCoords)
    {
        pDesk->set<EElem>(neweach._x, neweach._y, neweach._e);
    }

    ev.Info()._news = newCoords;
    cb(EAction::ACTION_REPLENISH, ev);
    __IGoTo(EAction::ACTION_BONUS, ev);

FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////
InputFuncBegin(EAction::ACTION_REFRESH)
    //仅接受‘bonus’状态,
    //在‘bonus’状态没有成功时,输入此状态,
    //更新之后,进入‘结束’状态
    //所以,务必保证此状态更新之后,不会导致存在可消除的情况
    __StateAccept(EAction::ACTION_BONUS);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_REFRESH)
    Event<EAction::ACTION_REFRESH> ev;
    if(!pDesk->IsDead())
    {
        __IGoTo(EAction::ACTION_FINAL, ev);
        return;
    }

    std::vector<Coord> recordA, becomeB;
    pDesk->Refresh(recordA, becomeB);
    ev.Info()._src = recordA;
    ev.Info()._become = becomeB;
    cb(EAction::ACTION_REFRESH, ev);
    __IGoTo(EAction::ACTION_FINAL, ev);
FuncEnd()

//////////////////////////////////////////////////////////////////////////////////////////

//特殊消除,具体需求为:
//  消除 N 个某种颜色的元素
InputFuncBegin(EAction::ACTION_BONUS_BOMB)
    //存在执行消除事件的情况,进入‘充满’状态
    //特殊情况:棋盘中不存在所谓的“某种颜色”,消除未被执行
    __StateAccept(EAction::ACTION_None);
FuncEnd()

UpdateFuncBegin(EAction::ACTION_BONUS_BOMB)
    if(in.GetType() != EAction::ACTION_BONUS_BOMB)
        return;

    const auto& act = in.GetInfo();
    
    const Coord& coord = act.GetInfo()._select._coord;
    EElem e = pDesk->get<EElem>(coord._x, coord._y);
    if(e == EElem::ELEM_None)
        return; //error

    int i = 0;
    std::vector<Coord> bonus;
    pDesk->foreach<EElem>([&](Axis x, Axis y, const EElem& each) -> bool
    {
        if(each != e)
            return true;

        pDesk->get<EElem>(x, y) = EElem::ELEM_None;
        ++i;
        bonus.emplace_back(x, y, each);
        return true;
    });

    Event<EAction::ACTION_BONUS_BOMB> ev;
    if(i == 0)
    {
        cb(EAction::ACTION_BONUS_BOMB, ev);
        __IGoTo(EAction::ACTION_REFRESH, ev);
        return;
    }

    ev.Info()._bonus = EBonusType::BONUS_BOMB_BY_SELECT;
    ev.Info()._cleared = bonus;
    ev.Info()._count = i;
    
    cb(EAction::ACTION_BONUS_BOMB, ev);
    __IGoTo(EAction::ACTION_REPLENISH, ev);

FuncEnd()

 

5.  3x_desk.h

  1 #pragma once
  2 
  3 #ifndef __LC_3X_DESK_H_
  4     #define __LC_3X_DESK_H_
  5 
  6 #include <array>
  7 #include <vector>
  8 #include <functional>
  9 #include <unordered_map>
 10 #include <set>
 11 #include "lc_random.h"
 12 #include <bitset>
 13 #include "../__lc_component.h"
 14 #include "3x_desk_event.h"
 15 
 16 
 17 class Desk
 18 {
 19 public:
 20     struct ElemWeight
 21     {
 22         EElem _ele;
 23         int _weight = 0;
 24     };
 25 
 26     struct TryBonusInfo
 27     {
 28         int _count = 0;
 29         EElem _elem = EElem::ELEM_None;
 30         std::vector<Coord> _bouns;
 31         Coord _coordA;
 32         Coord _coordB;
 33     };
 34 
 35 public:
 36     Desk() = delete;
 37     Desk(Axis w, Axis h);
 38 
 39     ~Desk(){}
 40 
 41     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 42     void foreach(std::function<bool(Axis x, Axis y, T& e)> && cb);
 43 
 44     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 45     void foreach(std::function<bool(Axis x, Axis y, const T& e)> && cb) const;
 46 
 47     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 48     T& get(Axis x, Axis y);
 49 
 50     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 51     T get(Axis x, Axis y) const;
 52 
 53     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 54     bool set(Axis x, Axis y, T e);
 55 
 56     template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* = nullptr >
 57     std::vector<T>& getcolume(Axis x);
 58     
 59     inline bool IsValidCoord(Axis x, Axis y) const noexcept;
 60     bool IsFreeCoord(Axis x, Axis y) const;
 61 
 62     bool IsNeighbour(const Coord& coordA, const Coord& coordB) const;
 63 
 64     inline int CoordToNum(const Coord& coordA) const noexcept { return coordA._y * _width + coordA._x; }
 65     inline Coord NumToCoord(int num) const noexcept {Axis x = num % _width; Axis y = num / _width; return { x, y, get<EElem>(x, y) }; };
 66 
 67 public:
 68     void Init(const std::vector<ElemWeight>& weight);
 69     bool IsDead() const;
 70 
 71     //This function is a blur check. 
 72     //If it returns true then bonus must be available,
 73     //and not necessarily if it returns false.
 74     bool IsCanBonus(const Coord& coord, bool isAccurate = false) const;   //@CHECK-RES:1
 75     
 76     bool IsInBonus(const Coord& coord) const;    //@CHECK-RES:1
 77     inline bool IsElemEqual(Axis x, Axis y, EElem e) const noexcept;    //@CHECK-RES:1
 78 
 79 public:
 80     bool IsCanExchange(const Coord& coordA, const Coord& coordB, bool isForce = false) const;
 81     void DoExchange(const Coord& coordA, const Coord& coordB, const StateEventCallBackT& cb, bool isForce = false);
 82 
 83     //则仅仅尝试计算,不真正执行消除而导致的更改棋盘。
 84     EBonusType Bonus(const Coord& coordA, std::vector<Coord>& out, int& maxCountLine) const;
 85     
 86     int FindBonus(std::vector<TryBonusInfo>& out, bool isBest = false);
 87 
 88     EElem GetRandomElem() const;
 89 
 90     //@return Is function 'ResetDesk' called?
 91     bool Refresh(std::vector<Coord>& recordA, std::vector<Coord>& becomeB, int times = 0);
 92     bool Exchange(const Coord& coordA, const Coord& coordB, bool isForce = false);  //@CHECK-RES:1
 93 
 94 //private:
 95     void ResetWeight(const std::vector<ElemWeight>& weight);
 96     void ResetDesk();   //@CHECK-RES:01
 97 
 98     template<AxisType AT>
 99     std::bitset<5> BonusSign(const Coord & coord) const;    //@CHECK-RES:01
100 
101     bool IsBonusBySign(const std::bitset<5>& sign) const;   //@CHECK-RES:1
102     
103     template<AxisType AT>
104     std::bitset<5> TestSign(Coord&& coord) const;   //@CHECK-RES:1
105 
106     bool FindNeverBonusCoordWith(EElem e, Coord& out, uint64_t startWith = 0) const;
107     void TestNonBonusWith(Coord coord, std::set<EElem>& out) const;
108 
109 public:
110     
111 
112 //private:
113     Axis _width = 0;
114     Axis _height = 0;
115 
116     std::vector< //w
117         std::vector<EElem>  //h
118     > _eles;
119 
120     std::vector<
121         std::vector<EDeck> 
122     > _decks;
123 
124     //std::unordered_map<EElem, int> _pool;
125     RandomItem<ElemWeight> _randPool;
126 };
127 
128 #include "3x_desk.inl"
129 
130 #endif

 

6. 3x_desk.cpp

  1 #include "3x_desk.h"
  2 #include "3x_desk_state.h"
  3 #include "inc.h"
  4 #include "lc_logmgr.h"
  5 #include "custom/CFG_CTM_Elem.h"
  6 #include <algorithm>
  7 
  8 Desk::Desk(Axis w, Axis h)
  9     : _width(w)
 10     , _height(h)
 11 {
 12     if(w < 5 || h < 5)
 13     {
 14         LogPrint("ERROR", "The Desk height or width too less. W: ", w, "; H: ", h);
 15         return;
 16     }
 17 
 18     _eles.resize(w);
 19     for (auto& eachW : _eles)
 20     {
 21         eachW.resize(h);
 22         for(EElem& eachH : eachW)
 23             eachH = EElem::ELEM_None;
 24     }
 25 
 26     _decks.resize(w);
 27     for (auto& eachW : _decks)
 28     {
 29         eachW.resize(h);
 30         for(EDeck& eachH : eachW)
 31             eachH = EDeck::DECK_BLOCK;
 32     }
 33 
 34     std::vector<ElemWeight> pool;
 35     const int defaultW = 2310;
 36     for (uint8_t i = (uint8_t)EElem::ELEM_None + 1; i <= (uint8_t)EElem::ELEM_None + CFG_ELEM.Size(); i++)
 37     {
 38         EElem e = EElem(i);
 39         pool.push_back({e, defaultW});
 40     }
 41 
 42     Init(pool);
 43 }
 44 
 45 void Desk::ResetWeight(const std::vector<ElemWeight>& weight)
 46 {
 47     _randPool.Init(weight, [](const ElemWeight& item)->int{ return item._weight; });   
 48 }
 49 
 50 bool Desk::IsFreeCoord(Axis x, Axis y) const
 51 {
 52     if(!IsValidCoord(x, y))
 53         return false;
 54 
 55     EDeck deck = get<EDeck>(x, y);
 56     return !(deck == EDeck::DECK_WALL || deck == EDeck::DECK_FREEZE);
 57 }
 58 
 59 bool Desk::IsNeighbour(const Coord& coordA, const Coord& coordB) const
 60 {
 61     if(!(IsValidCoord(coordA._x, coordA._y) 
 62         && IsValidCoord(coordB._x, coordB._y))
 63     ) return false;
 64 
 65     return 
 66         (coordA._x == coordB._x && std::abs(coordA._y - coordB._y) == 1) 
 67         || (coordA._y == coordB._y && std::abs(coordA._x - coordB._x) == 1) ;
 68 }
 69 
 70 bool Desk::IsInBonus(const Coord& coord) const
 71 {
 72     if(!IsValidCoord(coord._x, coord._y))
 73         return false;
 74 
 75     return IsBonusBySign(BonusSign<AxisType::AxisX>(coord)) 
 76         || IsBonusBySign(BonusSign<AxisType::AxisY>(coord));
 77 }
 78 
 79 bool Desk::IsBonusBySign(const std::bitset<5>& sign) const
 80 {
 81     if(!sign[2])
 82         return false;
 83 
 84     return (sign[0] && sign[1])
 85         ||(sign[3] && sign[4]) 
 86         || (sign[1] && sign[3]);
 87 }
 88 
 89 bool Desk::IsDead() const
 90 {
 91     bool ret = true;
 92     foreach<EElem>([this, &ret](Axis x, Axis y, const EElem& e) -> bool
 93     {
 94         if(e == EElem::ELEM_None)
 95             return true;
 96 
 97         if(IsInBonus({x, y}) || IsCanBonus({x, y}))
 98         {
 99             ret = false;
100             return false;
101         }
102         return true;        
103     });
104     
105     return ret;
106 }
107 
108 inline bool Desk::IsElemEqual(Axis x, Axis y, EElem e) const noexcept
109 {
110     return IsValidCoord(x, y) 
111         && (e != EElem::ELEM_None) 
112         && (get<EElem>(x, y) == e)
113     ;
114 }
115 
116 bool Desk::IsCanBonus(const Coord& coord, bool isAccurate) const
117 {
118     bool isCan = false;
119     const EElem e = get<EElem>(coord._x, coord._y);
120 
121     {
122         /**
123          *   3.
124          *      X P X
125          *      P X P
126          *      X C X
127          *      X C X
128          *      P X P
129          *      X P X
130          */
131 
132         for (Axis i = coord._y - 1; i < coord._y + 1; i++)
133         {
134             if(i == coord._y)
135                 continue;
136 
137             if(!IsElemEqual(coord._x, i, e))
138                 continue;
139 
140             Axis x1, x2, x3
141                 , y1, y2, y3, y4;
142             x1 = coord._x - 1;
143             x2 = coord._x + 1;
144             x3 = coord._x;
145 
146             Axis ymin = std::min(coord._y, i);
147             Axis ymax = std::max(coord._y, i);
148 
149             y1 = ymin - 1;
150             y2 = ymax + 1;
151             y3 = ymin - 2;
152             y4 = ymax + 2;
153             
154             isCan = IsElemEqual(x1, y1, e)
155                 || IsElemEqual(x1, y2, e)
156                 || IsElemEqual(x2, y1, e)
157                 || IsElemEqual(x2, y2, e)
158                 || IsElemEqual(x3, y3, e)
159                 || IsElemEqual(x3, y4, e);
160         }
161 
162         if(isCan) 
163             return true;
164     }
165 
166     {
167         /**
168          *   2.
169          *      X P X X P X
170          *      P X C C X P
171          *      X P X X P X
172          */
173         
174         for (Axis i = coord._x - 1; i < coord._x + 1; i++)
175         {
176             if(i == coord._x)
177                 continue;
178 
179             if(!IsElemEqual(i, coord._y, e))
180                 continue;
181 
182             Axis x1, x2, x3, x4
183                 , y1, y2, y3;
184             Axis xmin = std::min(i, coord._x);
185             Axis xmax = std::max(i, coord._x);
186 
187             x1 = xmin - 1;
188             x2 = xmax + 1;
189             x3 = xmin - 2;
190             x4 = xmax + 2;
191 
192             y1 = coord._y - 1;
193             y2 = coord._y + 1;
194             y3 = coord._y;
195             
196             isCan = IsElemEqual(x1, y1, e)
197                 || IsElemEqual(x1, y2, e)
198                 || IsElemEqual(x2, y1, e)
199                 || IsElemEqual(x2, y2, e)
200                 || IsElemEqual(x3, y3, e)
201                 || IsElemEqual(x4, y3, e);
202         }
203         
204         if(isCan) 
205             return true;
206     }
207 
208     {
209         /**
210          *   1.
211          *      P X P
212          *      X C X
213          *      P X P
214          */
215 
216         static const Coord edge[2] = {{-1, -1}, {1, 1}};
217         for (size_t i = 0; i < 2; i++)
218         {
219             const Coord& edgei = edge[i];
220             Coord cur(edgei._x + coord._x, edgei._y + coord._y);
221             if(!IsElemEqual(cur._x, cur._y, e))
222                 continue;
223 
224             else    //found one.
225             {
226                 //check x
227                 cur._x = cur._x + -2 * edgei._x;
228                 if(!IsElemEqual(cur._x, cur._y, e))
229                 {
230                     //check y
231                     cur._y = cur._y + -2 * edgei._y;
232 
233                     //finding two.
234                     isCan = IsElemEqual(cur._x, cur._y, e);
235                 }
236                 else
237                     isCan = true;
238 
239                 
240             }
241         }
242 
243         if(isCan) 
244             return true;
245     }
246 
247     if(!isAccurate)
248         return false;
249 
250     {
251         /**
252          *  4. 
253          *      P P X P P
254          *      X X C X X 
255          *      P P X P P 
256          */ 
257 
258         //check line
259         std::bitset<5> signs[4] = 
260         {
261             TestSign<AxisType::AxisX>({coord._x, coord._y - 1, e}),
262             TestSign<AxisType::AxisX>({coord._x, coord._y + 1, e}),
263             TestSign<AxisType::AxisY>({coord._x - 1, coord._y, e}),
264             TestSign<AxisType::AxisY>({coord._x + 1, coord._y, e})
265         };
266         for (size_t i = 0; i < 4; i++)
267         {
268             if(IsBonusBySign(signs[i]))
269                 return true;
270         }
271         
272     }
273     
274     return false;
275 }
276 
277 EElem Desk::GetRandomElem() const
278 {
279     auto* pItem = _randPool.Get();
280     if(pItem == nullptr)
281         return EElem::ELEM_None;
282     return pItem->_ele;
283 }
284 
285 bool Desk::Refresh(std::vector<Coord>& recordA, std::vector<Coord>& becomeB, int times)
286 {
287     if(times >= 10)
288     {
289         ResetDesk();
290         return true;
291     }
292 
293     const uint32_t total = _width * _height;
294     recordA.reserve(total);
295     becomeB.reserve(total);
296     for (size_t i = 0; i < total - 2; i++)
297     {
298         Axis x1 = i % _width;
299         Axis y1 = i / _width;
300 
301         std::set<EElem> nonBonus;
302         TestNonBonusWith(Coord(x1, y1), nonBonus);
303         if(nonBonus.empty())
304             return Refresh(recordA, becomeB, times + 1);
305 
306         bool isFound = false;
307         for(EElem nonEle : nonBonus)
308         {
309             Coord found;
310             if(!FindNeverBonusCoordWith(nonEle, found, i + 1))
311                 continue;
312 
313             isFound = true;
314             std::swap(_eles[x1][y1], _eles[found._x][found._y]);
315             recordA.push_back({x1, y1});
316             becomeB.push_back(found);
317             break;
318         }
319 
320         if(!isFound)
321             return Refresh(recordA, becomeB, times + 1);
322     }
323 
324     if(IsDead())
325         return Refresh(recordA, becomeB, times + 1);
326     
327     return false;
328 }
329 
330 //寻找一个坐标 out,这个坐标若是被设置成 e, 则不会被成为 InBonus.
331 //@return  Is found.
332 bool Desk::FindNeverBonusCoordWith(EElem e, Coord& out, uint64_t startWith) const
333 {
334     const uint32_t total = _width * _height;
335     if(startWith >= total) 
336         startWith = 0;
337 
338     bool ret = false;
339     uint64_t r = RandomAvg::Instance().Get(startWith, total);
340     loop_start_by(startWith, total, r, [&](int64_t i)
341     {
342         Axis x1 = i % _width;
343         Axis y1 = i / _width;
344 
345         auto sign1x = TestSign<AxisType::AxisX>({x1, y1, e});
346         if(IsBonusBySign(sign1x))
347             return true;
348 
349         auto sign1y = TestSign<AxisType::AxisY>({x1, y1, e});
350         if(IsBonusBySign(sign1y))
351             return true;
352         
353         out(x1, y1);
354         ret = true;
355         return false;
356     });
357     
358     return ret;
359 }
360 
361 //判断 coord 位置的元素,接受哪些元素类型(out)才不会成为 InBonus。
362 void Desk::TestNonBonusWith(Coord coord, std::set<EElem>& out) const
363 {
364     if(!IsValidCoord(coord._x, coord._y))
365         return;
366 
367     for(uint8_t i = (uint8_t)EElem::ELEM_None + 1; i <= (uint8_t)EElem::ELEM_None + CFG_ELEM.Size(); ++i)
368     {
369         EElem e = (EElem)i;
370         auto sign1x = TestSign<AxisType::AxisX>({coord._x, coord._y, e});
371         if(IsBonusBySign(sign1x))
372             continue;
373 
374         auto sign1y = TestSign<AxisType::AxisY>({coord._x, coord._y, e});
375         if(IsBonusBySign(sign1y))
376             continue;
377 
378         out.insert(e);
379     }
380 }
381 
382 void Desk::ResetDesk()
383 {
384     const uint32_t total = _width * _height;
385     std::vector<bool> symbol;   //标识
386     symbol.resize(total, false);
387 
388     //为了保证生成的棋盘不是一个死局,
389     //那么就在它还是一个空棋盘的时候,在随机位置 r 设置一个可以消除的情况,
390     //并将标识设置为 true
391     {
392         uint64_t r = RandomAvg::Instance().Get(0, total);
393         Axis x2 = r % _width;
394         Axis y2 = r / _width;
395         
396         //为了后面的处理简单,则保证随机的位置 r 不在棋盘的边缘
397         //至少空余 2 个位置
398         x2 = x2 % ((_width - 2) - 2) + 2;   
399         y2 = y2 % ((_height - 2) - 2) + 2;
400 
401         //随机一个种类的元素
402         uint64_t rElem = _randPool.Get()->_ele;
403         _eles[x2][y2] = (EElem)rElem;
404         _eles[x2][y2 - 1] = (EElem)rElem;
405         _eles[x2 + 1][y2 + 1] = (EElem)rElem;
406         symbol[y2 * _width + x2] = true;
407         symbol[(y2 - 1) * _width + x2] = true;
408         symbol[(y2 + 1) * _width + (x2 + 1)] = true;
409     }
410 
411     //开始初始化棋盘
412     for (uint32_t i = 0; i < total; i++)
413     {
414         const Axis x1 = i % _width;
415         const Axis y1 = i / _width;
416 
417         if(symbol[i])
418             continue;
419 
420         bool isSet = false;
421         //随机每个格子
422         uint64_t r = _randPool.Get()->_ele;
423         //此循环的意义在于,若是随机的元素 r 被设置后,导致了存在可消除的情况,
424         //那么则采用其他元素类型 [[unlikely]]
425         loop_start_by((int64_t)EElem::ELEM_None + 1, (int64_t)EElem::ELEM_None + CFG_ELEM.Size() + 1, r, [&](int64_t ie) -> bool
426         {
427             EElem e = (EElem)ie;
428             
429             //测试位置(x1, y1)若是被设置了元素 e,
430             auto sign1x = TestSign<AxisType::AxisX>({x1, y1, e});
431             //是否可以消除,
432             if(IsBonusBySign(sign1x))
433                 return true; //如果可消除,则 continue.
434                 
435             auto sign1y = TestSign<AxisType::AxisY>({x1, y1, e});
436             if(IsBonusBySign(sign1y))
437                 return true;
438 
439             _eles[x1][y1] = e;
440             symbol[i] = true;
441             isSet = true;
442 
443             return false;
444         });
445 
446         //如果所有元素的测试均未通过,则重新初始化棋盘,
447         //在棋盘大小以及元素种类个数设置合理的情况下,这种情况几乎不可能发生
448         //在 8x8 的棋盘,并最多存在6种不同元素情况下,经过 3x50W 次测试,没有命中
449         if(!isSet)
450         {
451             ResetDesk();
452             break;
453         }
454             
455     }
456     
457 }
458 
459 bool Desk::IsCanExchange(const Coord& coordA, const Coord& coordB, bool isForce) const
460 {
461     if(IsElemEqual(coordA._x, coordA._y, get<EElem>(coordB._x, coordB._y)))
462     {
463         return false;
464     }
465         
466     if(!isForce && !IsNeighbour(coordA, coordB))
467     {
468         return false;
469     }
470 
471     if(isForce && IsValidCoord(coordA._x, coordA._y) && IsValidCoord(coordB._x, coordB._y))
472     {
473         return false;
474     }
475 
476     if(!(IsFreeCoord(coordA._x, coordA._y) && IsFreeCoord(coordB._x, coordB._y)))
477     {
478         return false;
479     }
480 
481     return true;
482 }
483 
484 bool Desk::Exchange(const Coord& coordA, const Coord& coordB, bool isForce)
485 {
486     if(!IsCanExchange(coordA, coordB, isForce))
487         return false;
488 
489     std::swap(_eles[coordA._x][coordA._y], _eles[coordB._x][coordB._y]);
490     return true;
491 }
492 
493 void Desk::Init(const std::vector<ElemWeight>& weight)
494 {
495     ResetWeight(weight);
496     ResetDesk();
497 }
498 
499 EBonusType Desk::Bonus(const Coord& coordA, std::vector<Coord>& out, int& maxCountLine) const
500 {
501     if(!IsValidCoord(coordA._x, coordA._y))
502         return EBonusType::BONUS_None;
503 
504     EElem e = get<EElem>(coordA._x, coordA._y);
505     if(e == EElem::ELEM_None)
506         return EBonusType::BONUS_None;
507 
508     EBonusType ret = EBonusType::BONUS_None;
509     bool isA = false, isV = false;  // 横向/纵向是否存在消除
510     int cA = 0, cV = 0; // 横向/纵向消除数量
511 
512     std::vector<Coord> outA, outV;
513     ///判断横向消除
514     {
515         //0,1,2,3,4 这 5 个元素以 2 位置为判断点,相同元素标识位为 1 
516         std::bitset<5> signX = TestSign<AxisType::AxisX>({coordA._x, coordA._y, e});
517         for (size_t i = 1; i >= 0; i--) //<<-- //判断点的左侧两个元素 1, 0
518         {
519             if(signX[i])
520             {
521                 Axis x = coordA._x + i - 2, 
522                      y = coordA._y;
523 
524                 //@Notice 需要设置点位置的元素类型,尽可能使返回值充分
525                 outA.emplace_back(x, y, e);
526             }
527             else break; //顺序判断,一旦存在不相同,则立即退出
528         }
529 
530         //加入判断点本身
531         outA.push_back(Coord(coordA._x, coordA._y, e));
532 
533         for (size_t i = 3; i < 5; i++) //-->> //判断点的右侧两个元素 3, 4
534         {
535             if(signX[i])
536             {
537                 Axis x = coordA._x + i - 2, 
538                      y = coordA._y;
539 
540                 outA.emplace_back(x, y, e);  
541             }
542                 
543             else break;
544         }
545     }
546 
547     ///判断纵向消除
548     {
549         std::bitset<5> signY = BonusSign<AxisType::AxisY>(coordA);
550         for (size_t i = 1; i >= 0; i--) //<<--
551         {
552             if(signY[i])
553             {
554                 Axis x = coordA._x, 
555                      y = coordA._y + i - 2;
556                 outV.emplace_back(x, y, e);
557             }
558             else break;
559         }
560 
561         //判断点不可以包含两次
562         if(outA.size() < 3) outV.push_back(Coord(coordA._x, coordA._y, e));
563 
564         for (size_t i = 3; i < 5; i++) //-->>
565         {
566             if(signY[i])
567             {
568                 Axis x = coordA._x, 
569                      y = coordA._y + i - 2;
570                 outV.emplace_back(x, y, e);
571             }
572                 
573             else break;
574         }
575     }
576 
577     //执行横向消除
578     cA = outA.size();
579     if(cA >= 3) //大于等于 3 个可消除
580     {
581         isA = true;
582         for (auto& each : outA)
583             out.push_back(each);
584     }
585 
586     //执行纵向消除
587     cV = outV.size();
588     if(cV >= 3 || (isA && outV.size() >= 2))
589     {
590         isV = true;
591         for (const auto& each : outV)
592             out.push_back(each);
593     }
594 
595     //判断消除形状
596     maxCountLine = std::max(cA, cV);
597     if(isV && isA)  return EBonusType::BONUS_CROSS;     //十字形消除(T / L)
598     if(isA)         return EBonusType::BONUS_ACROSS;    //横向消除
599     if(isV)         return EBonusType::BONUS_VERTICAL;  //纵向消除
600     return EBonusType::BONUS_None;
601 }
602 
603 int Desk::FindBonus(std::vector<TryBonusInfo>& out, bool isBest)
604 {
605     std::array<Coord, 4> side = { Coord{-1, 0}, Coord{1, 0}, Coord{0, -1}, Coord{0, 1} };
606 
607     std::vector<Coord> bonus;
608     int count = 0;
609     int tmp = 0;
610     foreach<EElem>([&](Axis x, Axis y, const EElem& e)->bool
611     {
612         TryBonusInfo info;
613         Coord coordA(x, y, e);  //the coordA is checked point.
614         for (size_t i = 0; i < side.size(); i++)
615         {
616             Axis bx = x + side[i]._x;
617             Axis by = y + side[i]._y;
618             
619             if(!IsValidCoord(bx, by))
620                 continue;
621 
622             EElem be = get<EElem>(bx, by);
623             if(be == e)
624                 continue;
625             
626             Coord coordB(bx, by, be);
627             bonus.clear();
628 
629             bool isExchange = Exchange(coordA, coordB, false);
630             if(!isExchange)
631                 continue;
632 
633             EBonusType bonusType = Bonus(coordA, bonus, tmp);
634             if(bonusType == EBonusType::BONUS_None)
635             {
636                 Exchange(coordA, coordB, false);
637                 continue;
638             }
639 
640             const size_t s = bonus.size();
641 
642             info._bouns = bonus;
643             info._count = s;
644             info._elem = bonus.front()._e;
645             info._coordA = coordA;
646             info._coordB = coordB;
647 
648             if(!isBest)
649             {
650                 out.push_back(info);
651                 Exchange(coordA, coordB, false);
652                 continue;
653             }
654 
655             if(s > count)
656             {
657                 out.clear();
658                 out.push_back(info);
659 
660                 count = s;
661             }
662             else if(s == count && s > 0)
663             {
664                 out.push_back(info);
665             }
666 
667             Exchange(coordA, coordB, false);
668         }
669 
670         return true;
671     });
672     
673     return count;
674 }
675 
676 ///////////////////////
677 
678 void Desk::DoExchange(const Coord& coordA, const Coord& coordB, const StateEventCallBackT& cb, bool isForce)
679 {
680     Event<EAction::ACTION_EXCHANGE_INPUT> ev;
681     ev.Info()._coordA = coordA;
682     ev.Info()._coordB = coordB;
683     Start_3x_State(this, EAction::ACTION_EXCHANGE_INPUT, ev, cb);
684 }

 

7. 3x_desk.inl

  1 template<typename T, 
  2     typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* 
  3 >
  4 void Desk::foreach(std::function<bool(Axis w, Axis h, T& e)> && cb)
  5 {
  6     for (Axis j = 0; j < _width; j++)
  7     {
  8         for (Axis i = 0; i < _height; i++)
  9         {
 10             bool is = false;
 11             if constexpr(std::is_same<T, EElem>::value)
 12                 is = cb(j, i, _eles[j][i]);
 13             else if(std::is_same<T, EDeck>::value)
 14                 is = cb(j, i, _decks[j][i]);
 15             else break;
 16 
 17             if(!is) break;
 18         }
 19     }
 20     
 21     return;
 22 }
 23 
 24 template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* >
 25 void Desk::foreach(std::function<bool(Axis w, Axis h, const T& e)> && cb) const
 26 {
 27     for (Axis j = 0; j < _width; j++)
 28     {
 29         for (Axis i = 0; i < _height; i++)
 30         {
 31             bool is = false;
 32             if constexpr(std::is_same<T, EElem>::value)
 33                 is = cb(j, i, _eles[j][i]);
 34             else if(std::is_same<T, EDeck>::value)
 35                 is = cb(j, i, _decks[j][i]);
 36             else break;
 37 
 38             if(!is) break;
 39         }
 40     }
 41     return;
 42 }
 43 
 44 
 45 
 46 template<typename T, 
 47     typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* 
 48 >
 49 T& Desk::get(Axis x, Axis y)
 50 {
 51     if constexpr(std::is_same<T, EElem>::value)
 52     {
 53         return _eles[x][y];
 54     }
 55         
 56     else if(std::is_same<T, EDeck>::value)
 57     {
 58         return _decks[x][y];
 59     }  
 60 }
 61 
 62 template<typename T, 
 63     typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* 
 64 >
 65 bool Desk::set(Axis x, Axis y, T e)
 66 {
 67     if(!IsValidCoord(x, y))
 68         return false;
 69 
 70     get<T>(x, y) = e;
 71     return true;
 72 }
 73 
 74 template<typename T, 
 75     typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* 
 76 >
 77 T Desk::get(Axis x, Axis y) const
 78 {
 79     if constexpr(std::is_same<T, EElem>::value)
 80     {
 81         if(!IsValidCoord(x, y))
 82             return EElem::ELEM_None;
 83         return _eles[x][y];
 84     }
 85         
 86     else if(std::is_same<T, EDeck>::value)
 87     {
 88         if(!IsValidCoord(x, y))
 89             return EDeck::DECK_None;
 90         return _decks[x][y];
 91     }
 92 }
 93 
 94 template<AxisType AT>
 95 std::bitset<5> Desk::BonusSign(const Coord & coord) const
 96 {
 97     EElem e = get<EElem>(coord._x, coord._y);
 98     if(e == EElem::ELEM_None)
 99         return 0;
100 
101     size_t i = 0;
102     std::bitset<5> ret;
103 
104     if constexpr(AT == AxisType::AxisY)
105     {
106         for (Axis y = coord._y - 2; y <= coord._y + 2; y++, i++)
107         {
108             if(!IsValidCoord(coord._x, y))
109             {
110                 ret[i] = 0;
111                 continue;
112             }
113 
114             ret[i] = get<EElem>(coord._x, y) == e ? 1 : 0;
115         }
116         return ret;
117     }
118 
119     if constexpr(AT == AxisType::AxisX)
120     {
121         for (Axis x = coord._x - 2; x <= coord._x + 2; x++, i++)
122         {
123             if(!IsValidCoord(x, coord._y))
124             {
125                 ret[i] = 0;
126                 continue;
127             }
128             ret[i] = get<EElem>(x, coord._y) == e ? 1 : 0;
129         }
130         return ret;
131     }
132 
133     return 0;
134 }
135 
136 template<AxisType AT>
137 std::bitset<5> Desk::TestSign(Coord&& coord) const
138 {
139     EElem e = coord._e;
140     if(e == EElem::ELEM_None)
141         return 0;
142 
143     size_t i = 0;
144     std::bitset<5> ret;
145 
146     if constexpr(AT == AxisType::AxisY)
147     {
148         for (Axis y = coord._y - 2; y <= coord._y + 2; y++, i++)
149         {
150             if(!IsValidCoord(coord._x, y))
151             {
152                 ret[i] = 0;
153                 continue;
154             }
155 
156             ret[i] = get<EElem>(coord._x, y) == e ? 1 : 0;
157         }
158         ret[2] = 1;
159         return ret;
160     }
161 
162     if constexpr(AT == AxisType::AxisX)
163     {
164         for (Axis x = coord._x - 2; x <= coord._x + 2; x++, i++)
165         {
166             if(!IsValidCoord(x, coord._y))
167             {
168                 ret[i] = 0;
169                 continue;
170             }
171             ret[i] = get<EElem>(x, coord._y) == e ? 1 : 0;
172         }
173         ret[2] = 1;
174         return ret;
175     }
176 
177     return 0;
178 }
179 
180 template<typename T, typename std::enable_if<std::is_same<T, EElem>::value||std::is_same<T, EDeck>::value, T>::type* >
181 std::vector<T>& Desk::getcolume(Axis x)
182 {
183     if constexpr(std::is_same<T, EElem>::value )
184         return _eles[x];
185     else
186         return _decks[x];
187 }
188 
189 inline bool Desk::IsValidCoord(Axis x, Axis y) const noexcept
190 {
191     return x < _width && y < _height && x >= 0 && y >= 0;
192 }

 

Tags: