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

代码留此备忘。

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++ 三消游戏基本实现的更多相关文章

  1. 消消乐、candy crush类三消游戏程序逻辑分析

    最近在开发一款类似消消乐的三消游戏,在碰到实现斜方向下落的时候卡住了很长时间.好几天没有思路,原本的思路是一次性预判多个宝石的一连串运动路径,运用缓动运动队列来实现宝石运动路径,例如 下落->滑 ...

  2. 三消游戏FSM状态机设计图

    三消游戏FSM状态机设计图 1) 设计FSM图 2) smc配置文件 ///////////////////////////////////////////////////////////////// ...

  3. cocos2d-x 3.2 它 三消游戏——万圣节大作战

    ***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...

  4. Unity3d开发“类三消”游戏

    新建一个Project,导入图片素材和声音文件,把图片的Texture Type都修改为Sprite(2D and UI)[1].新建一个命名为Background的GameObject,为之添加背景 ...

  5. 最近用unity写三消游戏,mark一个准备用的unity插件,用来控制运动。

    http://www.pixelplacement.com/itween/index.php itween 听说还不错!

  6. cocos2d-x 消类游戏,类似Diamond dash 设计

    前几天刚刚在学习cocos2d-x,无聊之下自己做了一个类似Diamond dash的消类游戏,今天放到网上来和大家分享一下.我相信Diamond dash这个游戏大家都玩过,游戏的规则是这样的,有一 ...

  7. cocos2d 消除类游戏简单的算法 (一)

    1. 游戏视频演示 2.三消游戏我的理解 上面视频中的游戏.我做了2个星期时间,仅仅能算个简单Demo,还有bug.特效也差点儿没有.感觉三消游戏主要靠磨.越磨越精品. 市场上三消游戏已经超级多了.主 ...

  8. 游戏Demo(持续更新中...)

    格斗游戏 主要用于联系Unity的动画系统,并加入了通过检测按键触发不同的技能. WASD控制方向,AD为技能1,SW为技能2,右键跳跃,连续单机普通连招. 本来是要用遮罩实现跑动过程中的攻击动作,但 ...

  9. Unity三消算法

    消除算法图文详解 三消算法首要实现的就是找到所有三个或三个以上的可消除对象,但直接找到这些对象是不太现实的,所以我们要将需求拆分.可不可以先获取所有图案相连的对象,进而在获取三消对象,这个算法也是众多 ...

随机推荐

  1. 【odoo14】第六章、管理模块数据

    本章代码可在原作者github下载 使用外部ID及命名空间 外部ID及XML ID用于标记记录.到目前为止,我们在视图.菜单及动作中接触了XML IDs.本节我们将进一步了解什么是XML ID. 步骤 ...

  2. 一键自签本地 TLSv3 多域名 SAN 域名证书工具 HTTPS(最新版 Chrome 浏览器策略测试通过)

    一键自动生成本地自签名SAN域名证书工具 原生OpenSSL生成自签名SAN CA域名(V3签名),在Linux.MacOS系统下签发测试通过. 用于一键快速生成开发和测试场景证书,内部平台授权和私有 ...

  3. Flex属性你真的搞清楚了吗?我深表怀疑

    背景 在使用弹性布局实现两侧宽度固定,中间宽度自适应的效果时,发现自己理解的和实际效果不一致,所以亲自实践验证了一个flex属性的诸多场景的表现,不仅解开了我之前使用过程遇到的疑惑,而且发现了许多自己 ...

  4. istio in kubernetes (二) -- 部署篇

    在 Kubernetes 部署 Istio [demo演示] 可参考官方文档(https://istio.io/latest/zh/docs/setup/install/) ,以部署1.7.4版本作为 ...

  5. ATMS中去pause Activity A.

    上文写完之后,感觉这个部分写的还是不清晰,本文继续补充一下. 首先还是看堆栈. obtain:78, PauseActivityItem (android.app.servertransaction) ...

  6. 创建线程的方式三:实现Callable接口 --- JDK 5.0新增

    /** * 创建线程的方式三:实现Callable接口. --- JDK 5.0新增 * * * 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大? * 1 ...

  7. Java生鲜电商平台-API接口设计之token、timestamp、sign 具体设计与实现

    转载:https://www.cnblogs.com/jurendage/p/12653865.html 说明:在实际的业务中,难免会跟第三方系统进行数据的交互与传递,那么如何保证数据在传输过程中的安 ...

  8. mysql大于当前时间置顶并按升序排序,小于当前时间的置尾并按降序排序

    现在用id来代替时间这样好测试 看一下测试表数据 执行按需求规则排序的sql SELECT * FROM number_generator ORDER BY id < 16 , IF(id &l ...

  9. 201871030115-康旭 实验二 软件工程个人项目—《D{0-1} KP》项目报告

    项目 内容 课程班级博客连接 课程班级 这个作业要求连接 作业链接 我的课程学习目标 (1)详细阅读<构建之法>第1章.第2章,掌握PSP流程:(2)设计实际程序掌握动态规划算法.回溯算法 ...

  10. ATMS中去拉起新的进程,并在新进程启动后调用attachApplication时,resume待启动的Activity

    相关文章: ATMS中去pause Activity A. 目录 ATMS拉起新进程 堆栈 resumeTopActivityInnerLocked:1684, ActivityStack start ...