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 }
C++ 三消游戏基本实现的更多相关文章
- 消消乐、candy crush类三消游戏程序逻辑分析
最近在开发一款类似消消乐的三消游戏,在碰到实现斜方向下落的时候卡住了很长时间.好几天没有思路,原本的思路是一次性预判多个宝石的一连串运动路径,运用缓动运动队列来实现宝石运动路径,例如 下落->滑 ...
- 三消游戏FSM状态机设计图
三消游戏FSM状态机设计图 1) 设计FSM图 2) smc配置文件 ///////////////////////////////////////////////////////////////// ...
- cocos2d-x 3.2 它 三消游戏——万圣节大作战
***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...
- Unity3d开发“类三消”游戏
新建一个Project,导入图片素材和声音文件,把图片的Texture Type都修改为Sprite(2D and UI)[1].新建一个命名为Background的GameObject,为之添加背景 ...
- 最近用unity写三消游戏,mark一个准备用的unity插件,用来控制运动。
http://www.pixelplacement.com/itween/index.php itween 听说还不错!
- cocos2d-x 消类游戏,类似Diamond dash 设计
前几天刚刚在学习cocos2d-x,无聊之下自己做了一个类似Diamond dash的消类游戏,今天放到网上来和大家分享一下.我相信Diamond dash这个游戏大家都玩过,游戏的规则是这样的,有一 ...
- cocos2d 消除类游戏简单的算法 (一)
1. 游戏视频演示 2.三消游戏我的理解 上面视频中的游戏.我做了2个星期时间,仅仅能算个简单Demo,还有bug.特效也差点儿没有.感觉三消游戏主要靠磨.越磨越精品. 市场上三消游戏已经超级多了.主 ...
- 游戏Demo(持续更新中...)
格斗游戏 主要用于联系Unity的动画系统,并加入了通过检测按键触发不同的技能. WASD控制方向,AD为技能1,SW为技能2,右键跳跃,连续单机普通连招. 本来是要用遮罩实现跑动过程中的攻击动作,但 ...
- Unity三消算法
消除算法图文详解 三消算法首要实现的就是找到所有三个或三个以上的可消除对象,但直接找到这些对象是不太现实的,所以我们要将需求拆分.可不可以先获取所有图案相连的对象,进而在获取三消对象,这个算法也是众多 ...
随机推荐
- [MongoDB知识体系] 一文全面总结MongoDB知识体系
MongoDB教程 - Mongo知识体系详解 本系列将给大家构建MongoDB全局知识体系.@pdai MongoDB教程 - Mongo知识体系详解 知识体系 学习要点 学习资料 官网资料 入门系 ...
- ClickHouse元数据异常-MySQLHandlerFactory:Failed to read RSA key pair from server
Clickhouse版本:20.3.6.40-2 clickhouse集群三个节点,一分片,三副本,三个节点数据完全一样 1. 问题描述 在使用连接工具操作时,发现其中一个节点连接拒绝,无法操作,另外 ...
- Nodejs学习笔记(2) 阻塞/非阻塞实例 与 Nodejs事件
1. Node.js异步编程的特点 2. 阻塞与非阻塞的实例 2.1 阻塞代码实例 2.2 非阻塞代码实例 3. Node.js的事件驱动 4. 事件循环实例 1. Node.js异步编程的特点 参考 ...
- Linux 三剑客之 awk 实战详解教程
我们知道 Linux 三剑客,它们分别是:grep.sed.awk.在前边已经讲过 grep 和 sed,没看过的同学可以直接点击阅读,今天要分享的是更为强大的 awk. sed 可以实现非交互式的字 ...
- ASP.NET Core与Redis搭建一个简易分布式缓存
本文主要介绍了缓存的概念,以及如何在服务器内存中存储内容.今天的目标是利用IDistributedCache来做一些分布式缓存,这样我们就可以横向扩展我们的web应用程序. 在本教程中,我将使用Re ...
- 官方正式发布 Java 16
前言 就在2021/03/16,官方正式发布了Java 16.我们可以下载使用Java 16了. 特性 向量API(孵化) 在运行期,Vector 表示向量计算可以可靠地编译成支持CPU架构上的最佳矢 ...
- 计算机图形学中使用Turbo C++画图步骤
一.下载安装Turbo C++ 我安装的是Turbo C++ 3.2.2.0下载链接 二.画图 1.打开Turbo C++,点击右下角start turbo C++ 2.点击file ->new ...
- teprunner测试平台用例前置模块开发
本文开发内容 现在正式进入测试相关功能开发.teprunner测试平台底层是pytest,中间层是tep,还没了解的朋友可以先看看tep的文章,整个平台的设计思路和后面用例的执行都会基于这个工具.te ...
- Android Studio 如何运行单个activity
•写在前面 调试界面运行单个 Activity 可节省编译整个项目的时间提高效率: 本着提高效率的角度,特地上网百度相关知识: •解决方法 首先,在 AndroidManifest.xml 文件中,找 ...
- Android Studio 中的 button 无法使用 background 更改背景颜色
•解决方案 res/values/themes.xml下的 <style name="Theme.HelloWorld" parent="Theme.Materi ...