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 }