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三消算法
消除算法图文详解 三消算法首要实现的就是找到所有三个或三个以上的可消除对象,但直接找到这些对象是不太现实的,所以我们要将需求拆分.可不可以先获取所有图案相连的对象,进而在获取三消对象,这个算法也是众多 ...
随机推荐
- redhat安装python3.7
下载并解压: 1 wget https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tgz 2 tar -xzvf Python-3.7.2.tgz ...
- 你想知道的 std::vector::push_back 和 std::vector::emplace_back
引言 C++ 11 后,标准库容器 std::vector 包含了成员函数 emplace 和 emplace_back.emplace 在容器指定位置插入元素,emplace_back 在容器末尾添 ...
- MySQL入门(7)——表数据的增、删、改
MySQL入门(7)--表数据的增.删.改 插入数据 使用INSERT···VALUES语句插入数据 INSERT语句最常用的格式是INSERT···VALUES: INSERT [LOW_PRIOR ...
- maven 常用命名
maven项目,在命令行中操作,非常简洁.高效,现将maven项目常用命令行总结如下: maven命令行命令总结 序号 整理 统计 命令 作用 1 基本 5 mvn -v 查看maven版本 2 mv ...
- Python爬虫知识
一.爬虫 1.概述 网络爬虫,搜索引擎就是爬虫的应用者. 2.爬虫分类 (1)通用爬虫,常见就是搜索引擎,无差别的收集数据,存储,提取关键字,构建索引库,给用户提供搜索接口. 爬取一般流程: 初始化一 ...
- 08、元组tuple
元组(tuple) 是一个有序且不可变的容器,在里面可以存放多个不同类型的元素 元组是在最后多一个逗号,用于表示它是一个元组 tuple = (11,22,'阿斯顿','媚媚',) #后面多加一个逗号 ...
- Java单链表反转图文详解
Java单链表反转图文详解 最近在回顾链表反转问题中,突然有一些新的发现和收获,特此整理一下,与大家分享 背景回顾 单链表的存储结构如图: 数据域存放数据元素,指针域存放后继结点地址 我们以一条 N1 ...
- 关于生产环境改用G1垃圾收集器的思考
背景 由于我们的业务量非常大,响应延迟要求高.目前沿用的老的ParNew+CMS已经不能支撑业务的需求.平均一台机器在1个月内有1次秒级别的stop the world.对系统来说是个巨大的隐患.所以 ...
- 全网最详细的Linux命令系列-cd命令
Linux cd 命令可以说是Linux中最基本的命令语句,其他的命令语句要进行操作,都是建立在使用 cd 命令上的. 所以,学习Linux 常用命令,首先就要学好 cd 命令的使用方法技巧. 命令格 ...
- 基于ZXing.Net生成一维二维码
新阁教育-喜科堂付工原创 最近很多小伙伴对一维码.二维码比较感兴趣,今天主要给大家分享一个C#生成条形码和二维码的案例. C#作为一个高级语言,特点就是快! 我们使用的是开源库ZXing,ZXing是 ...