自己写的一个棋类小游戏,支持3D和2D显示,支持联机对弈和ai对弈。这是上小学时经常玩的一个小游戏,不知道叫什么名字,我们那叫“憋尿罐”。分为两个阶段,前期可以用 ‘顶’、‘夹‘、’挑’ 来吃棋子,后期只剩一个棋子时有点像民间游戏围虎棋,将最后一个棋子围入‘尿罐’即胜利。
棋类游戏的ai算法一般使用Minimax极小化极大算法。
Minimax极小化极大算法:
用p代表棋局的某一状态,f(p)为棋局的评分函数,对我方越有利评分越大,对对方越有利评分越低。minimax(p,depth)为估值函数。
基本思想
(1)当轮到对手走步时,考虑最坏的情况(即f(p)取极小值)
(2)当轮到我方走步时,考虑最好的情况(即f(p)取极大值)
(3)交替使用(1)和(2)两种方法传递倒推值。
假设某棋局推演4步的状态树如下:
先给末端状态评分
从第4步往上反推极大值,第3步我方所能得到的分数是第4步中最差的(因为下一步对手总是会选择对我方最不利的极小值)。
从第3步往上反推极小值,第2步对方所能得到的分数是第3步中最高的(因为下一步我方总是会选择对我方最有利的极大值)。
伪代码:A表示己方,B表示对方
int MiniMax(int level, int p)
{
if (level == 0)
return f(p);//局面的评分
int best;
if(player == A)
{
best = INT_MIN;
while(sub) //子树不空
{
best = max(best, MiniMax(level-1,sub)); //取更高分给己方
next sub;
}
}
else //player == B
{
best = INT_MAX;
while(sub) //子树不空
{
best = min(best, MiniMax(level-1,sub)); //取更低分给对方
next sub;
}
}
return best;
}
α-β剪枝算法:
最小最大值算法遍历所有的子树,效率较低,一般会用α-β剪枝算法优化。对于alpha-beta剪枝算法,还需要配合排序,优先搜索最好的走法,增加剪枝的效率。
α代表下界,β代表上界,在(α,β)的范围内进行搜索,初始值(α,β)= (-∞,+∞)
(1)对手B负责更新上限,目的是使上限尽可能的小,留给 player A最不利的局面。
(2)己方A负责更新下限,目的是使下限尽可能的大,保证最次的情况下也可以提高分数,留给自己最好的局面
进行深度优先搜索,当生成结点到达规定深度时,立即进行静态估计,一旦某一个非端点的节点可以确定倒推值的时候立即赋值,节省下其他分支拓展到节点的开销。
剪枝规则:
(1)α剪枝,任一极小层节点的β值不大于他任一前驱极大值层节点的α值,即α(前驱层)≥β(后继层),则可以终止该极小层中这个MIN节点以下的搜索过程。这个MIN节点的倒推值确定为这个β值。
(2)β剪枝,任一极大层节点的α值不小于它任一前驱极小值层节点的β值,即β(前驱层)≤α(后继层),则可以终止该极大值层中这个MAX节点以下的搜索过程。这个MAX节点的倒推值确定为这个α值。
伪代码:
int AlphaBeta(int level,State p,int alpha,int beta,char player=='A')
{
if (level == 0 || no sub)
return f(p);//局面的评分
int best;
if(player == 'A')
{
best = INT_MIN;
while(sub) //子树不空
{
best = max(best, AlphaBeta(level-1,sub,alpha,beta,'B')); //A取更高分给A
alpha = max(alpha,best);
if(beta < alpha) //beta裁剪,B的下一步使A获得的分数更高 => 剪枝(B不会选这个节点)
break;
next sub;
}
}
else//player == B
{
best = INT_MAX;
while(sub) //子树不空
{
best = min(best, AlphaBeta(level-1,sub,alpha,beta,'A')); //B取更低分给A
beta = min(beta,best);
if( beta < alpha) //alpha裁剪
break;
next sub;
}
}
return best;
}
//初始调用
AlphaBeta(depth,p,INT_MIN,INT_MAX,'A');
其它比如评分函数 直接见源码:
//========================================================
// @Date: 2016.05
// @File: SourceDemoClient/Urinal/MiniGameUrinal.h
// @Brief: MiniGameUrinal
// @Author: LouLei
// @Email: twopointfive@163.com
// @Copyright (Crapell) - All Rights Reserved
//========================================================
#ifndef __MiniGameUrinal__H__
#define __MiniGameUrinal__H__
#include "Math/MathLib.h"
#include "Render/Texture.h"
#include "Rpg/MiniGame.h"
//
enum ManSide
{
SideNull = -1,
SideRed=0, //红方
SideBlack=1, //黑方
};
enum MiniUrinalCmd
{
CMD_ManMove , //棋子走动
CMD_ManPass , //棋子走动
CMD_GameOver,
CMD_Restart ,
};
const char* UrinalCmdToString(int enumeration);
namespace RendSys
{
class MC_Frame;
}
class UrinalPlayerRole;
class MiniGameUrinal:public MiniGame
{
public:
//棋子移动方向
enum Dir
{
UP =0,
DOWN ,
LEFT ,
RIGHT,
UPLEFT,
UPRIGHT,
DOWNLEFT,
DOWNRIGHT,
};
//交叉点
class Block
{
public:
int id; //index
vec2I pos; //棋子位置(格子)
vec2I pixelPos; //屏幕位置
int man; //棋子
Block* neighbour[8];//邻居
int flag;
};
MiniGameUrinal();
virtual ~MiniGameUrinal();
virtual bool KeepResource(bool once,int& circle,String& nextTip);
virtual bool Start();
virtual bool Stop();
virtual bool Render();
virtual void RenderUI();
virtual bool Update();
virtual bool Free();
virtual bool IsEnd();
//三种类型结构
virtual MiniPlayer* CreatePlayer();
virtual MiniPlayer* CreateRobot ();
virtual MiniPlayer* CreateRole ();
void OnLButtonDown(const vec2I& pos);
//坐标转换
bool ScreenToBlock(const vec2I &pos,Block **block);
bool BlockToScreen(Block *block,vec2I &pos);
bool WorldToBlock(const vec3 &pos,Block **block);
bool BlockToWorld(Block *block,vec3 &pos);
bool CanGo (int from,int to);
bool SendGo(int from,int to);
bool TryGo (int from,int to);
//弃手
bool Pass();
bool SendPass();
//吃子
bool CheckFlip(Block* blockMap,int to);
//计数
int Count(Block* blockMap,int side);
//获得老虎位置
Block* Tiger(Block* blockMap);
//主思考函数
bool UrinalThink(int side,int &resFrom, int &resTo);
//评分函数
int Envalue(Block* blockMap,int side);
//AlphaBeta剪枝的minimax极小极大算法
int AlphaBeta(Block* blockMap,int depth,int alpha,int beta,ManSide side,vec2I& resOP,bool minmax =true,bool first=true);
//搜集所有可能的走法
void Search(Block* blockMap,int side,void* opList);
//根据初状态及走法构造子状态
void MakeSub(Block* blockMap,Block* subMap,vec2I& op);
//拓扑邻居
void MakeNeighbour(Block* blockMap,Block* subMap);
UrinalPlayer* GetTurnPlayer();
//处理游戏网络命令包
virtual int ProcessPacketCmd(PacketBase* packet);
//virtual const char* CmdToString(const char* stream,int len);
//private:
#define BlockNum 29
Block m_blocks[BlockNum];//旗子图
int m_curSide;
Block* m_fromPoint;//
Block* m_toPoint;//
vec2I Offset;//左上角
TexturePtr m_textureMan[2]; //2种图标
TexturePtr m_textureFrom;
TexturePtr m_textureSelectGreen;
TexturePtr m_textureSelectRed;
TexturePtr m_textureBoard;
TexturePtr m_textureCantgo;
TexturePtr m_thinkTexture[4];
//
MC_Frame* m_movieMan[2];
MC_Frame* m_movieCannot;
MC_Frame* m_movieFrom;
MC_Frame* m_movieSelectRed;
MC_Frame* m_movieSelectGreen;
MC_Frame* m_movieBoard;
MovieClip* m_movieThink;
UrinalPlayerRole* m_myRolePlayer;
//非观战时 viewside==myside
int m_myViewSide;
};
extern MiniGameUrinal* G_UrinalGame;
#endif
//========================================================
// @Date: 2016.05
// @File: SourceDemoClient/Urinal/MiniGameUrinal.cpp
// @Brief: MiniGameUrinal
// @Author: LouLei
// @Email: twopointfive@163.com
// @Copyright (Crapell) - All Rights Reserved
//========================================================
#include "General/Pch.h"
#include "General/Window.h"
#include "General/Timer.h"
#include "Gui/GuiMgr.h"
#include "Gui/RpgGuis.h"
#include "Input/InputMgr.h"
#include "Urinal/UrinalPlayer.h"
#include "Urinal/MiniGameUrinal.h"
#include "Urinal/MiUrinal_PlayGui.h"
#include "Render/RendDriver.h"
#include "Render/Shader.h"
#include "Render/MC_Misc.h"
#include "Rpg/SyncGameInfo.h"
#include "Packet/PacketMiniGame.h"
//#include "Rpg/MiniGame.h"
#include "Net/PacketList.h"
#include "General/Pce.h"
#include "Render/Camera.h"
const int ManWidth2D = 35; //棋格宽度
const int GameRectWidth2D = 512; //棋盘的长宽,
const int BoardHeight2D = 317;
static float BoardCellWidth3D = 7.12f; //棋格宽度
static float BoardWidth3D = 64; //棋盘的长宽,
static float BoardHeight3D = 36;
static int ThinkDepth = 3; //思考的步数
//棋子类型
const char* UrinalCmdToString(int enumeration)
{
switch(enumeration)
{
case CMD_ManMove :return "CMD_ManMove ";
case CMD_GameOver:return "CMD_GameOver";
case CMD_Restart :return "CMD_Restart ";
default :return "CMD_unknow";
}
return "CMD_unknow";
}
static int opDir[] =
{
MiniGameUrinal::DOWN,//UP ,
MiniGameUrinal::UP,//DOWN ,
MiniGameUrinal::RIGHT,//LEFT ,
MiniGameUrinal::LEFT,//RIGHT,
MiniGameUrinal::DOWNRIGHT,//UPLEFT,
MiniGameUrinal::DOWNLEFT,//UPRIGHT,
MiniGameUrinal::UPRIGHT,//DOWNLEFT,
MiniGameUrinal::UPLEFT,//DOWNRIGHT,
};
MiniGameUrinal* G_UrinalGame;
MiniGameUrinal::MiniGameUrinal()
:m_movieThink(NULL)
{
G_UrinalGame = this;
CmdEnumToString = UrinalCmdToString;
}
MiniGameUrinal::~MiniGameUrinal()
{
G_UrinalGame = NULL;
}
bool MiniGameUrinal::Start()
{
m_myRolePlayer = NULL;
if(!MiniGame::Start())
return false;
for(int i=0;i<BlockNum;++i)
{
m_blocks[i].id = i;
m_blocks[i].man = SideNull;
for(int d=0;d<8;++d)
{
m_blocks[i].neighbour[d] = NULL;
}
}
for(int i=0;i<25;++i)
{
m_blocks[i].pos = vec2I(i/5,i%5);
}
m_blocks[25].pos = vec2I(5,1);
m_blocks[26].pos = vec2I(5,2);
m_blocks[27].pos = vec2I(5,3);
m_blocks[28].pos = vec2I(6,2);//尿罐
m_blocks[ 0].pixelPos = vec2I(16,50);
m_blocks[ 1].pixelPos = vec2I(23,109);
m_blocks[ 2].pixelPos = vec2I(30,164);
m_blocks[ 3].pixelPos = vec2I(32,228);
m_blocks[ 4].pixelPos = vec2I(38,288);
m_blocks[ 5].pixelPos = vec2I(67,47);
m_blocks[ 6].pixelPos = vec2I(78,110);
m_blocks[ 7].pixelPos = vec2I(87,165);
m_blocks[ 8].pixelPos = vec2I(90,229);
m_blocks[ 9].pixelPos = vec2I(89,285);
m_blocks[10].pixelPos = vec2I(139,39);
m_blocks[11].pixelPos = vec2I(144,106);
m_blocks[12].pixelPos = vec2I(146,164);
m_blocks[13].pixelPos = vec2I(154,223);
m_blocks[14].pixelPos = vec2I(154,283);
m_blocks[15].pixelPos = vec2I(213,38);
m_blocks[16].pixelPos = vec2I(215,98);
m_blocks[17].pixelPos = vec2I(215,163);
m_blocks[18].pixelPos = vec2I(215,220);
m_blocks[19].pixelPos = vec2I(217,282);
m_blocks[20].pixelPos = vec2I(279,34);
m_blocks[21].pixelPos = vec2I(283,93);
m_blocks[22].pixelPos = vec2I(288,158);
m_blocks[23].pixelPos = vec2I(292,220);
m_blocks[24].pixelPos = vec2I(295,280);
m_blocks[25].pixelPos = vec2I(350,95);
m_blocks[26].pixelPos = vec2I(361,159);
m_blocks[27].pixelPos = vec2I(370,219);
m_blocks[28].pixelPos = vec2I(455,149);
for(int i=0;i<5;++i)
{
m_blocks[0+i*5].man = SideRed;
m_blocks[4+i*5].man = SideBlack;
}
Block* block = m_blocks;
for(int x=0;x<5;++x)
{
for(int y=0;y<5;++y,++block)
{
if(x>0) block->neighbour[LEFT] = block-5;
if(x<4) block->neighbour[RIGHT] = block+5;
if(y>0) block->neighbour[DOWN] = block-1;
if(y<4) block->neighbour[UP] = block+1;
//有些格子不能走斜线
if((x+y)%2==0)
{
if(x>0 && y>0) block->neighbour[DOWNLEFT] = block-6;
if(x>0 && y<4) block->neighbour[UPLEFT] = block-4;
if(x<4 && y>0) block->neighbour[DOWNRIGHT] = block+4;
if(x<4 && y<4) block->neighbour[UPRIGHT] = block+6;
}
}
}
//
m_blocks[22].neighbour[DOWNRIGHT] = &m_blocks[25];
m_blocks[22].neighbour[RIGHT] = &m_blocks[26];
m_blocks[22].neighbour[UPRIGHT] = &m_blocks[27];
m_blocks[25].neighbour[UPLEFT] = &m_blocks[22];
m_blocks[25].neighbour[UP] = &m_blocks[26];
m_blocks[25].neighbour[UPRIGHT] = &m_blocks[28];
m_blocks[26].neighbour[UP] = &m_blocks[27];
m_blocks[26].neighbour[DOWN] = &m_blocks[25];
m_blocks[26].neighbour[LEFT] = &m_blocks[22];
m_blocks[26].neighbour[RIGHT] = &m_blocks[28];
m_blocks[27].neighbour[DOWNLEFT] = &m_blocks[22];
m_blocks[27].neighbour[DOWN] = &m_blocks[26];
m_blocks[27].neighbour[DOWNRIGHT] = &m_blocks[28];
m_blocks[28].neighbour[UPLEFT] = &m_blocks[27];
m_blocks[28].neighbour[LEFT ] = &m_blocks[26];
m_blocks[28].neighbour[DOWNLEFT] = &m_blocks[25];
//check
for(int i=0;i<BlockNum;++i)
{
Block* block = &m_blocks[i];
for(int d=0;d<8;++d)
{
if (block->neighbour[d]
&& block->neighbour[d]->neighbour[opDir[d]] != block)
{
Assert(0,"MiniGameUrinal map not nodirected");
}
}
}
m_curSide=SideRed;
m_fromPoint = NULL;
m_toPoint = NULL;
if (m_movieScene)
{
Frame frame;
frame.SetPos(m_startPos);
m_movieScene->SetProgramFrame(&frame);
m_movieScene->Advance();
}
m_gameState = MS_Gamming;
m_myViewSide = SideBlack;
//
for(int i = 0; i < m_allPlayerNum; i++)
{
dynamic_cast<UrinalPlayer*>(m_miniPlayer[i])->m_side = i==0?SideRed:SideBlack;
//dynamic_cast<UrinalPlayer*>(m_miniPlayer[i])->m_side = i==0?SideBlack:SideRed;
}
if(m_myRolePlayer)
m_myViewSide = m_myRolePlayer->m_side;
//进入miniplaygui,(选人、选关卡都已在房间里进行完毕)。
if(GetStyle()) G_GuiMgr->PushGui(GetStyle()->playGUI.c_str(),GL_DIALOG);
//
for(int i = 0; i < m_allPlayerNum; i++)
{
if(m_miniPlayer[i])
m_miniPlayer[i]->Start();
}
//设置摄像机
CameraCtrlerTarget* ctrler = new CameraCtrlerTarget;
ctrler->SetDistToTar(60);
ctrler->SetTarPos(m_startPos);
G_Camera->PushCtrler(ctrler);
G_Camera->SetEuler(0, -60, 0);
//片头摄像机
PushIntroCamera();
return true;
}
MiniPlayer* MiniGameUrinal::CreatePlayer()
{
return new UrinalPlayer;
}
MiniPlayer* MiniGameUrinal::CreateRobot()
{
return new UrinalPlayerRobot;
}
MiniPlayer* MiniGameUrinal::CreateRole()
{
m_myRolePlayer = new UrinalPlayerRole;
return m_myRolePlayer;
}
bool MiniGameUrinal::Stop()
{
G_Camera->PopCtrler();
//CameraCtrlerTarget* ctrlerTarget = G_Camera->IsCurCtrler<CameraCtrlerTarget>();
//if(ctrlerTarget)
// ctrlerTarget->SetTarEntity(G_MyRole);
{
if (m_myPlayer && m_myPlayer->m_liveNum>0)
{
G_GuiMgr->GetGui<Rpg_ResultDialog>()->ShowResult(true);
}
else
{
G_GuiMgr->GetGui<Rpg_ResultDialog>()->ShowResult(false);
}
G_GuiMgr->PushGui("Rpg_ResultDialog",GL_DIALOGBOTTOM);
}
return MiniGame::Stop();
}
bool MiniGameUrinal::SendGo(int from,int to)
{
C2SPacketMiniGameCmd packet;
packet.WriteHeader();
packet.WriteValue(CMD_ManMove);
packet.WriteValue(from);
packet.WriteValue(to);
G_MiniGame->SendPacketToOther(&packet);
return true;
}
bool MiniGameUrinal::TryGo(int from,int to)
{
if(!CanGo(from,to))
{
PlaySound__("data/sound/poker/cannot.wav");
return false;
}
m_blocks[to].man = m_blocks[from].man;
m_blocks[from].man = SideNull;
if(CheckFlip(m_blocks,to))
{
//吃子
PlaySound__("data/sound/ui_click.wav");
}
else
{
PlaySound__("data/sound/billiards/shoot.wav");
}
m_fromPoint = &m_blocks[from];
m_toPoint = &m_blocks[to];
GetTurnPlayer()->m_manSelected = to;
m_turnTime = 0;
//换手
m_curSide=!m_curSide;
GetTurnPlayer()->m_manSelected=-1;
return true;
}
bool MiniGameUrinal::Pass()
{
PlaySound__("data/sound/poker/cannot.wav");
m_turnTime = 0;
//换手
m_curSide=!m_curSide;
GetTurnPlayer()->m_manSelected=-1;
return true;
}
bool MiniGameUrinal::SendPass()
{
C2SPacketMiniGameCmd packet;
packet.WriteHeader();
packet.WriteValue(CMD_ManPass);
G_MiniGame->SendPacketToOther(&packet);
return true;
}
UrinalPlayer* MiniGameUrinal::GetTurnPlayer()
{
//return dynamic_cast<UrinalPlayer*>(m_miniPlayer[m_curSide]);
for(int i = 0; i < m_allPlayerNum; i++)
{
UrinalPlayer* player = dynamic_cast<UrinalPlayer*>(m_miniPlayer[i]);
if (player->m_side == m_curSide)
{
return player;
}
}
return NULL;
}
bool MiniGameUrinal::ScreenToBlock(const vec2I &pos,Block **block)
{
*block = NULL;
vec2I point = pos - Offset;
for(int i=0;i<BlockNum;++i)
{
if ((point-m_blocks[i].pixelPos).Length()<ManWidth2D/2.2f)
{
*block = &m_blocks[i];
return true;
}
}
return false;
}
bool MiniGameUrinal::BlockToScreen(Block *block,vec2I &pos)
{
pos = block->pixelPos;
pos += Offset - vec2I(ManWidth2D/2,ManWidth2D/2);
return true;
}
bool MiniGameUrinal::WorldToBlock(const vec3 &pos_,Block **block)
{
vec3 pos = pos_ - m_startPos;
pos.x = pos.x + BoardWidth3D/2;
pos.z = pos.z + BoardHeight3D/2;
pos.x /= BoardWidth3D;
pos.y /= BoardHeight3D;
pos.x *= m_textureBoard->GetWidth();
pos.y *= m_textureBoard->GetHeight();
return ScreenToBlock(vec2I(pos.x,pos.y),block);
}
bool MiniGameUrinal::BlockToWorld(Block *block,vec3 &pos_)
{
if (block==NULL)
{
return false;
}
float x = float(block->pixelPos.x)/m_textureBoard->GetWidth();
float y = float(block->pixelPos.y)/m_textureBoard->GetHeight();
pos_.x = x*BoardWidth3D - BoardWidth3D/2 ;
pos_.z = y*BoardHeight3D - BoardHeight3D/2 ;
pos_.y = 10;
pos_ += m_startPos;
return true;
}
bool MiniGameUrinal::CanGo(int from,int to)
{
if (from<0||from>=BlockNum
||to<0||to>=BlockNum
||from==to)
{
return false;
}
Block* fromBlock = &m_blocks[from];
Block* toBlock = &m_blocks[to];
//cur turn
if(fromBlock->man!=m_curSide)
{
return false;
}
//米子移动
vec2I dif = toBlock->pos - fromBlock->pos;
int dir = -1;
if (dif.x==0 && dif.y<0) dir = DOWN;
else if(dif.x==0 && dif.y>0) dir = UP;
else if(dif.y==0 && dif.x<0) dir = LEFT;
else if(dif.y==0 && dif.x>0) dir = RIGHT;
else if(dif.y>0 && dif.x<0) dir = UPLEFT;
else if(dif.y>0 && dif.x>0) dir = UPRIGHT;
else if(dif.y<0 && dif.x<0) dir = DOWNLEFT;
else if(dif.y<0 && dif.x>0) dir = DOWNRIGHT;
if(dir==-1)
{
return false;
}
int step = max(abs(dif.x),abs(dif.y));
Block* block = fromBlock;
//路线上是否有棋子:
for(int i=0;i<step;++i)
{
block = block->neighbour[dir];
if(block==NULL)
{
return false;
}
if(block==NULL || block->man!=SideNull)
{
return false;
}
}
return true;
}
bool MiniGameUrinal::KeepResource(bool once,int& circle,String& nextTip)
{
G_TextureMgr->AddTexture(m_textureMan[0],"data/minigame/urinal/redman.png");
G_TextureMgr->AddTexture(m_textureMan[1],"data/minigame/urinal/blackman.png");
G_TextureMgr->AddTexture(m_textureFrom,"data/minigame/urinal/from.png");
G_TextureMgr->AddTexture(m_textureSelectGreen,"data/minigame/urinal/select.png");
G_TextureMgr->AddTexture(m_textureSelectRed,"data/minigame/urinal/select2.png");
G_TextureMgr->AddTexture(m_textureBoard,"data/minigame/urinal/board.png");
G_TextureMgr->AddTexture(m_textureCantgo,"data/minigame/urinal/cantgo.png");
G_TextureMgr->AddTexture(m_thinkTexture[0],"data/minigame/poker/think00.png");
G_TextureMgr->AddTexture(m_thinkTexture[1],"data/minigame/poker/think01.png");
G_TextureMgr->AddTexture(m_thinkTexture[2],"data/minigame/poker/think02.png");
G_TextureMgr->AddTexture(m_thinkTexture[3],"data/minigame/poker/think03.png");
//
char buf[256];
if (m_movieScene == NULL)
{
LoadConfig loader(LoadConfig::GenDonotReShrinkBound, true, true);
m_movieScene = new RendSys::MovieClip;
m_movieScene->LoadFromFile("data/minigame/urinal/urinal.movie", &loader);
Frame frame;
frame.SetPos(m_startPos);
m_movieScene->SetProgramFrame(&frame);
m_movieScene->Advance();
}
if (m_movieScene->IsLoadComplete() == false)
{
m_gameState = MS_End;
return false;
}
for (int i=0;i<2;i++)
{
sprintf(buf,"man%02d",i);
m_movieMan[i] = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip(buf));
}
m_movieCannot = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip("cannot"));
m_movieFrom = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip("select0"));
m_movieSelectRed = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip("select1"));
m_movieSelectGreen = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip("select2"));
m_movieFrom->ZeroFrames();
m_movieBoard = dynamic_cast<MC_Frame*>(m_movieScene->GetMovieClip("board"));
if (m_movieThink == NULL)
{
LoadConfig loader(LoadConfig::GenDonotReShrinkBound, true, true);
m_movieThink = new RendSys::MovieClip;
m_movieThink->LoadFromFile("data/minigame/poker/think.movie", &loader);
}
return true;
}
void MiniGameUrinal::OnLButtonDown(const vec2I& pos)
{
if (m_myRolePlayer && m_curSide == m_myRolePlayer->m_side)
{
Block* block;
if (m_3dMode)
{
vec3 dir;
vec3 start;
G_Camera->GetMouseTray(start,dir);
RayMovieCollideRes res;
res.vStart = start;
res.vEnd = start+PICKLEN*dir;
res.justBond = false;
if(m_movieBoard->IntersectTray(res))
{
if(WorldToBlock(res.resPos,&block))
{
if(block->man==m_myRolePlayer->m_side)
{
m_myRolePlayer->m_manSelected = block->id;
}
else if(m_myRolePlayer->m_manSelected!=-1)
{
int man = m_myRolePlayer->m_manSelected;
if(TryGo(man,block->id))
SendGo(man,block->id);
}
}
}
}
else
{
if(ScreenToBlock(pos,&block))
{
if(block->man==m_myRolePlayer->m_side)
{
m_myRolePlayer->m_manSelected= block->id;
}
else if(m_myRolePlayer->m_manSelected!=-1)
{
int man = m_myRolePlayer->m_manSelected;
if(TryGo(man,block->id))
SendGo(man,block->id);
}
}
}
}
}
bool MiniGameUrinal::Render()
{
if (m_3dMode)
{
if(m_movieScene==NULL
||m_movieScene->IsLoadComplete()==false)
return false;
G_RendDriver->SetRenderStateEnable(RS_DEPTH_TEST,true);
m_movieScene->RendClip();
m_movieThink->RendClip();
MC_Frame* frameMovie = NULL;
//棋子
mat4 trans;
mat4 scale;
Block* block;
vec3 pos;
for(int i=0;i<BlockNum;i++)
{
block = &m_blocks[i];
if(block->man!=SideNull)
{
BlockToWorld(block,pos);
trans.FromTranslate(pos);
G_RendDriver->PushMatrix();
G_RendDriver->MultMatrix(trans);
frameMovie = G_UrinalGame->m_movieMan[block->man==SideRed?0:1];
frameMovie->BeginCommonRendState(NormalPass);
frameMovie->GetMesh()->Render();
frameMovie->EndCommonRendState();
G_RendDriver->PopMatrix();
}
}
//from
if(m_fromPoint)
{
BlockToWorld(m_fromPoint,pos);
trans.FromTranslate(pos);
G_RendDriver->PushMatrix();
G_RendDriver->MultMatrix(trans);
frameMovie = G_UrinalGame->m_movieFrom;
frameMovie->BeginCommonRendState(NormalPass);
frameMovie->GetMesh()->Render();
frameMovie->EndCommonRendState();
G_RendDriver->PopMatrix();
//Frame frame;
//frame->SetPos(pos);
//m_movieFrom->SetProgramFrame(&frame);
}
//to
if (m_myRolePlayer
&&m_curSide == m_myRolePlayer->m_side)
{
vec3 dir;
vec3 start;
G_Camera->GetMouseTray(start,dir);
RayMovieCollideRes res;
res.vStart = start;
res.vEnd = start+PICKLEN*dir;
res.justBond = false;
if(m_movieBoard->IntersectTray(res))
{
//G_RendDriver->Color3f(1,1,1);
//G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D,false);
//G_RendDriver->SetRenderStateEnable(RS_DEPTH_TEST,false);
//G_RendDriver->RendBegin(RS_POINTS);
//G_RendDriver->Vertex3fv(&res.resPos.x);
//G_RendDriver->RendEnd();
//G_RendDriver->SetRenderStateEnable(RS_TEXTURE_2D,true);
if(m_myRolePlayer->m_manSelected==-1)
{
//点选
if(WorldToBlock(res.resPos,&block))
{
if (block->man==m_myRolePlayer->m_side)
{
//高亮放大棋子 可以选择
BlockToWorld(block,pos);
trans.FromTranslate(pos);
scale.FromScale(1.2f,1.2f,1.2f);
G_RendDriver->PushMatrix();
G_RendDriver->MultMatrix(trans*scale);
frameMovie = G_UrinalGame->m_movieMan[block->man==SideRed?0:1];
frameMovie->BeginCommonRendState(NormalPass);
frameMovie->GetMesh()->Render();
frameMovie->EndCommonRendState();
G_RendDriver->PopMatrix();
}
}
}
else
{
//落子
Block* fromBlock = &m_blocks[m_myRolePlayer->m_manSelected];
if(WorldToBlock(res.resPos,&block)==false
||MiniGameUrinal::CanGo(fromBlock->id,block->id)==false)
{
//不可以落子
BlockToWorld(block,pos);
trans.FromTranslate(pos);
G_RendDriver->PushMatrix();
G_RendDriver->MultMatrix(trans);
frameMovie = G_UrinalGame->m_movieCannot;
frameMovie->BeginCommonRendState(NormalPass);
frameMovie->GetMesh()->Render();
frameMovie->EndCommonRendState();
G_RendDriver->PopMatrix();
}
else
{
//拖拽棋子
BlockToWorld(block,pos);
int manID = fromBlock->man==SideRed?0:1;
//pos = res.resPos;
//pos.x -= BoardCellWidth3D/2;
//pos.z -= BoardCellWidth3D/2;
trans.FromTranslate(pos);
scale.FromScale(0.9f,0.9f,0.9f);
G_RendDriver->PushMatrix();
G_RendDriver->MultMatrix(trans*scale);
frameMovie = G_UrinalGame->m_movieMan[manID];
frameMovie->BeginCommonRendState(NormalPass);
frameMovie->GetMesh()->Render();
frameMovie->EndCommonRendState();
G_RendDriver->PopMatrix();
}
}
}
}
//
for(int i = 0; i < m_allPlayerNum; i++)
{
dynamic_cast<UrinalPlayer*>(m_miniPlayer[i])->Render();
}
}
else
{
G_RendDriver->BeginUI();
G_RendDriver->Color4f(1,1,1,1);
G_RendDriver->SetRenderStateEnable(RS_BLEND,true);
G_RendDriver->BlendFunc(Blend_Filter);
G_ShaderMgr->PushShader();
Texture* tex = m_textureBoard;
tex->Bind();
Offset.x = (G_Window->m_iWidth - GameRectWidth2D)/2;
Offset.y = (G_Window->m_iHeight - BoardHeight2D)/2;
G_RendDriver->DrawTextureRect(RectF(Offset.x,Offset.y,GameRectWidth2D,BoardHeight2D));
//debug
if(0)
{
G_RendDriver->Color3f(1,0,0);
G_RendDriver->DisableRendState(RS_TEXTURE_2D);
G_RendDriver->RendBegin(RS_LINES);
for(int i=0;i<BlockNum;++i)
{
Block* block = &m_blocks[i];
for(int d=0;d<8;++d)
{
Block* blockN = block->neighbour[d];
if (blockN)
{
G_RendDriver->Vertex3f(block->pixelPos.x+Offset.x,G_Window->m_iHeight-(block->pixelPos.y+Offset.y),0);
G_RendDriver->Vertex3f(blockN->pixelPos.x+Offset.x,G_Window->m_iHeight-(blockN->pixelPos.y+Offset.y),0);
}
}
}
G_RendDriver->RendEnd();
G_RendDriver->Color3f(1,1,1);
G_RendDriver->EnableRendState(RS_TEXTURE_2D);
}
//棋子
Block* block;
vec2I pos;
for(int i=0;i<BlockNum;i++)
{
block = &m_blocks[i];
if(block->man!=SideNull)
{
Texture* tex = m_textureMan[block->man];
tex->Bind();
BlockToScreen(block,pos);
G_RendDriver->DrawTextureRect(RectF(pos.x,pos.y,ManWidth2D,ManWidth2D));
}
}
//from
if(m_fromPoint)
{
Texture* tex = m_textureFrom;
tex->Bind();
BlockToScreen(m_fromPoint,pos);
G_RendDriver->DrawTextureRect(RectF(pos.x,pos.y,ManWidth2D,ManWidth2D));
}
//to
if (m_myRolePlayer
&&m_curSide == m_myRolePlayer->m_side)
{
pos.x = G_Mouse->GetMousePos().x;
pos.y = G_Mouse->GetMousePos().y;
if(m_myRolePlayer->m_manSelected==-1)
{
//点选
if(ScreenToBlock(pos,&block)
&&block->man==m_myRolePlayer->m_side)
{
//高亮棋子 可以选择
Texture* tex = m_textureMan[block->man];
tex->Bind();
BlockToScreen(block,pos);
G_RendDriver->DrawTextureRect(RectF(pos.x-2,pos.y-2,ManWidth2D+4,ManWidth2D+4));
}
}
else
{
//落子
Block* fromBlock = &m_blocks[m_myRolePlayer->m_manSelected];
Texture* tex = m_textureMan[fromBlock->man];
tex->Bind();
G_RendDriver->Color4f(1,1,1,0.5f);
G_RendDriver->DrawTextureRect(RectF(pos.x-ManWidth2D/2,pos.y-ManWidth2D/2,ManWidth2D,ManWidth2D));
if(ScreenToBlock(pos,&block)==false
||MiniGameUrinal::CanGo(fromBlock->id,block->id)==false)
{
//不可以落子
m_textureCantgo->Bind();
G_RendDriver->Color4f(1,1,1,0.5f);
G_RendDriver->DrawTextureRect(RectF(pos.x,pos.y,ManWidth2D,ManWidth2D));
}
G_RendDriver->Color4f(1,1,1,1);
}
}
for(int i = 0; i < m_allPlayerNum; i++)
{
dynamic_cast<UrinalPlayer*>(m_miniPlayer[i])->Render();
}
G_RendDriver->EndUI();
G_ShaderMgr->PopShader();
}
return true;
}
void MiniGameUrinal::RenderUI()
{
MiniGame::RenderUI();
}
bool MiniGameUrinal::Update()
{
if (m_myRolePlayer==NULL)
{
//?todo 观战
return false;
}
if(m_blocks[BlockNum-1].man!=SideNull)
{
G_GuiMgr->PopGui("MiUrinal_PlayGui");
G_GuiMgr->GetGui<Rpg_ResultDialog>()->ShowResult(true);
G_GuiMgr->PushGui("Rpg_ResultDialog",GL_DIALOGBOTTOM);
m_gameState=MS_End;
return true;
}
m_movieScene->Advance();
m_movieThink->Advance();
m_turnTime += G_Timer->GetStepTimeLimited();
vec2I pos;
pos.x = G_Mouse->GetMousePos().x;
pos.y = G_Mouse->GetMousePos().y;
//左键
if (G_UrinalGame->IsButtonUping(MOUSE_LEFT))
{
OnLButtonDown(pos);
}
for(int i = 0; i < m_allPlayerNum; i++)
{
dynamic_cast<UrinalPlayer*>(m_miniPlayer[i])->Update();
}
return true;
}
bool MiniGameUrinal::Free()
{
MiniGame::Free();
if (m_movieThink)
{
m_movieThink->FreeMovie();
delete m_movieThink;
m_movieThink = NULL;
}
return true;
}
bool MiniGameUrinal::IsEnd()
{
return m_gameState == MS_End;
}
int MiniGameUrinal::ProcessPacketCmd(PacketBase* packet)
{
int cmd;
packet->ReadValue(cmd);
switch(cmd)
{
case CMD_ManMove:
{
int from;
int to;
packet->ReadValue(from);
packet->ReadValue(to);
TryGo(from,to);
}
break;
case CMD_ManPass:
{
Pass();
}
break;
case CMD_GameOver:
// {
// int turn = 0;
// packet->ReadValue(turn);
// m_winnerTurn = turn;
// m_turnTime = 0;
// m_gameStatus = Resulting;
// char sound[256];
// if (m_winnerTurn == m_lordTurn)
// {
// sprintf(sound,"data/sound/poker/play_lord_win");
// }
// else
// {
// sprintf(sound,"data/sound/poker/play_farmer_win");
// }
// if (m_winnerTurn%2) //
// strcat(sound,"_femail.wav");
// else
// strcat(sound,".wav");
// m_players[m_winnerTurn]->m_sound->PlaySound__(sound);
// }
break;
case CMD_Restart:
// Free();
// Start();
break;
}
return 0;
}
//吃子
bool MiniGameUrinal::CheckFlip(Block* blockMap,int to)
{
Block* toBlock = &blockMap[to];
if (toBlock->man == SideNull)
{
return false;
}
int thisSide = toBlock->man;
int otherSide = toBlock->man == SideRed?SideBlack:SideRed;
int num = Count(blockMap,otherSide);
if (num==1)//围捕阶段
{
return false;
}
bool flip = false;
Block** pn = toBlock->neighbour;
for(int d=0;d<8;++d,++pn)
{
Block* n = *pn;
if (n==NULL)
{
continue;
}
Block* nn = n->neighbour[d];
//顶
if (nn
&& n->man==thisSide
&& nn->man==otherSide
&& (nn->neighbour[d] == NULL || nn->neighbour[d]->man!=otherSide)//不是坠事蛋
)
{
nn->man = thisSide;
flip = true;
num--;
if (num==1)//围捕阶段
{
return true;
}
}
Block* opn = toBlock->neighbour[opDir[d]];
//顶
if (opn
&& opn->man==thisSide
&& n->man==otherSide
&& (nn == NULL || nn->man!=otherSide)//不是坠事蛋
)
{
n->man = thisSide;
flip = true;
num--;
if (num==1)//围捕阶段
{
return true;
}
}
//夹
if (nn
&& n->man==otherSide
&& nn->man==thisSide
)
{
n->man = thisSide;
flip = true;
num--;
if (num==1)//围捕阶段
{
return true;
}
}
Block* opnn = NULL;
if(opn) opnn = opn->neighbour[opDir[d]];
//挑
if (opn
&& opn->man==otherSide
&& n->man==otherSide
&& (nn == NULL || nn->man!=otherSide)//不是坠事蛋
&& (opnn == NULL || opnn->man!=otherSide)//不是坠事蛋
)
{
n->man = thisSide;
num--;
if (num==1)//围捕阶段
{
return true;
}
opn->man = thisSide;
num--;
if (num==1)//围捕阶段
{
return true;
}
flip = true;
}
}
return flip;
}
int MiniGameUrinal::AlphaBeta( Block* blockMap,int depth,int alpha,int beta,ManSide side,vec2I&resOP,bool minmax /*=true*/,bool first/*=true*/ )
{
if (depth == 0
//|| no sub
)
return Envalue(blockMap,side);//局面的评分
Block subMap[BlockNum];//4层嵌套, 内存占用不大
//拓扑邻居
MakeNeighbour(blockMap,subMap);
vec2I op;
std::list<vec2I> opList;//所有可能的走法
int old;
int best;
if(minmax == true)//play side A
{
//搜集所有可能的走法
Search(blockMap,side,&opList);
//对于alpha-beta剪枝算法,需要配合排序,优先搜索最好的走法,增加剪枝的效率。
//opList.sort();
//直接剪枝 只保留N个最好的走法
best = INT_MIN;
old = best;
while(opList.empty()==false) //子树不空
{
op = opList.front();
opList.pop_front();
//根据初状态及走法构造子状态
MakeSub(blockMap,subMap,op);
best = max(best, AlphaBeta(subMap,depth-1,alpha,beta,side,resOP,false,false)); //A取更高分给A
alpha = max(alpha,best);
if (first && best > old)
{
resOP = op;
}
old = best;
if(beta < alpha) //beta裁剪,B的下一步使A获得的分数更高 => 剪枝(B不会选这个节点)
break;
}
}
else//player other B
{
//搜集所有可能的走法
Search(blockMap,side,&opList);
//对于alpha-beta剪枝算法,需要配合排序,优先搜索最好的走法,增加剪枝的效率。
//opList.sort();
best = INT_MAX;
while(opList.empty()==false) //子树不空
{
op = opList.front();
opList.pop_front();
//根据初状态及走法构造子状态
MakeSub(blockMap,subMap,op);
best = min(best, AlphaBeta(subMap,depth-1,alpha,beta,side,resOP,true,false)); //B取更低分给A
beta = min(beta,best);
if( beta < alpha) //alpha裁剪
break;
}
}
return best;
}
bool MiniGameUrinal::UrinalThink(int side,int &resFrom, int &resTo )
{
//无路可走 且没进罐子
if (Count(m_blocks,side)==1)
{
Block* tiger = Tiger(m_blocks);
bool way = false;
for(int d=0;d<8;++d)
{
if (tiger->neighbour[d] && tiger->neighbour[d]->man==SideNull)
{
way = true;
break;
}
}
if(way==false)
return false;
}
//minimax算法
if (0)
{
vec2I resOP;
AlphaBeta(m_blocks,ThinkDepth,INT_MIN,INT_MAX,(ManSide)side,resOP);
resFrom = resOP.x;
resTo = resOP.y;
return true;
}
//直接剪枝 只保留1个最好的走法
Block subMap[BlockNum];
//拓扑邻居
MakeNeighbour(m_blocks,subMap);
//搜集所有可能的走法
std::list<vec2I> opList;
Search(m_blocks,side,&opList);
//OutputDebugText(opList.size());
int score = 0;
for(std::list<vec2I>::iterator it=opList.begin();it!=opList.end();++it)
{
//根据初状态及走法构造子状态
MakeSub(m_blocks,subMap,*it);
int s = Envalue(subMap,side);
if (s>score)
{
score = s;
resFrom = it->x;
resTo = it->y;
}
}
return true;
}
void MiniGameUrinal::Search(Block* blockMap,int side, void* opList_ )
{
vec2I op;
std::list<vec2I>& opList = *((std::list<vec2I>*)opList_);
opList.clear();
Block* block = blockMap;
for(int i=0;i<BlockNum;++i,++block)
{
if (block->man==side)
{
for(int d=0;d<8;++d)
{
Block* to = block;
for(int step=0;step<5;++step)
{
to = to->neighbour[d];
if(to==NULL || to->man!=SideNull)
break;
op.x = block->id;
op.y = to->id;
opList.push_back(op);
}
}
}
}
}
void MiniGameUrinal::MakeSub( Block* blockMap,Block* subMap,vec2I& op )
{
拓扑邻居
//MakeNeighbour(blockMap,subMap);
//copy
for(int b=0;b<BlockNum;b++)
{
subMap[b].man = m_blocks[b].man;
}
Block* from = &subMap[op.x];
Block* to = &subMap[op.y];
//if(to==NULL || to->man!=SideNull)
// return;
to->man = from->man;
from->man = SideNull;
CheckFlip(subMap,op.y);
}
int MiniGameUrinal::Envalue( Block* blockMap,int side )
{
//使用多层minimax算法后,评分函数可以简化
int num = Count(blockMap,side);
if (num==9)//围捕阶段
{
num = 900;
Block* tiger = Tiger(blockMap);
对手走通罐子更好,对于不规则棋盘可以预生成路径
//if(tiger->neighbour[RIGHT]!=NULL
// ||(tiger->pos.y<2 && (tiger->neighbour[UP]!=NULL || tiger->neighbour[UPRIGHT]!=NULL) )
// ||(tiger->pos.y>2 && (tiger->neighbour[DOWN]!=NULL || tiger->neighbour[DOWNRIGHT]!=NULL) )
// )
//{
// num += 1000;
//}
对手活动空间越小越好
//Block* block = blockMap;
//for(int i=0;i<BlockNum;++i,++block)
//{
// if (block->man==side)
// {
// num += block->pos.x + 2-abs(tiger->pos.y-2);
// }
//}
Block* block = blockMap;
for(int i=0;i<BlockNum;++i,++block)
{
block->flag = 0;
}
num += 10000;
std::list<Block*> blockList;
blockList.push_back(tiger);
while(blockList.empty()==false)
{
Block* it = blockList.back();
blockList.pop_back();
Block** pn = it->neighbour;
for(int d=0;d<8;++d,++pn)
{
Block* n = *pn;
if (n && n->man==SideNull && n->flag==0)
{
blockList.push_back(n);
n->flag = 1;
num -= 100;//对手活动空间越小越好
}
}
}
//修正老虎进了菱形后,分数相同的话,我方不往里面进
if (tiger->pos.x>4)
{
block = blockMap;
for(int i=0;i<BlockNum;++i,++block)
{
if (block->man==side)
{
num += 5 - 2*abs(5-block->pos.x) + 2-abs(block->pos.y-2); //x*2, 否则x+y不变还是不进
}
}
}
//对手走通罐子更好
if (blockMap[28].flag)
{
num += 100000;
}
}
else if (num==1)//被围捕阶段
{
//自己越靠近罐子分数越低
Block* tiger = Tiger(blockMap);
num = 100 - tiger->pos.x*10 + abs(tiger->pos.y-2);
}
else
{
num*=100;//200~800
}
return num;
}
int MiniGameUrinal::Count( Block* blockMap,int side )
{
int num = 0;
Block* block = blockMap;
for(int i=0;i<BlockNum;++i,++block)
{
if (block->man==side)
{
num++;
}
}
return num;
}
MiniGameUrinal::Block* MiniGameUrinal::Tiger( Block* blockMap )
{
int num = 0;
Block* tigerRed;
Block* tigerBlack;
Block* block = blockMap;
for(int i=0;i<BlockNum;++i,++block)
{
if (block->man==SideRed)
{
num++;
tigerRed = block;
}
if (block->man==SideBlack)
{
tigerBlack = block;
}
}
if (num==1)
{
return tigerRed;
}
else if(num==9)
{
return tigerBlack;
}
return NULL;
}
void MiniGameUrinal::MakeNeighbour( Block* blockMap,Block* subMap )
{
for(int b=0;b<BlockNum;b++)
{
subMap[b] = blockMap[b];
for(int d=0;d<8;++d)
{
if (subMap[b].neighbour[d])
{
subMap[b].neighbour[d] = &subMap[subMap[b].neighbour[d]->id];
}
else
{
subMap[b].neighbour[d] = NULL;
}
}
}
}