MFC随机博弈黑白棋
隨機博弈黑白棋
隨機博弈黑白棋
TxyITxs | 隨機博弈黑白棋 | 2019.04.21
摘要
通過隨機落子,實現黑白棋的博弈。無任何落子規則,棋子死活與圍棋中棋子的死活一致,即存在至少一口氣。動態模擬雙方博弈,但棋盤無落子位置時停止。
設計思路
主要基于時鐘來實現動態博弈,考慮博弈的持續性數據修改,黑白雙方需要互斥訪問數據以及正確界面繪制,通過設置兩個時鐘,一個時鐘主要負責界面背景,棋盤網格,黑白棋子繪制,繪制完后,釋放數據使用權;另一個時鐘主要負責博弈落子(即修改數據),通過在可落子位置隨機選擇一個,然后釋放數據使用權。
棋盤數據使用N*N大小的一維數組Tdata存儲,棋子坐標(x,y)對應的數組通過x*N+y計算。棋盤所用可落子位置通過向量vector<CPoint>Tpos來存儲,通過隨機產生一個索引來得到一個落子位置,然后將該索引對應Tpos的位置刪除,修改Tdata中對應位置的值。
1. UI設計
1.1 利用基于對環框的MFC程序框架來搭建UI界面,主要涉及到界面背景色繪制,棋盤網格繪制,以及通過訪問數據繪制棋子。
1.2 棋盤背景繪制
void TChessBgUI(CClientDC *dc)
{
CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
CBrush *pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(GRAY_BRUSH));
dc->SelectObject(&pen);
dc->SelectObject(pbrush);
CRect bg;
GetClientRect(bg);
dc->FillRect(bg, pbrush);
}
1.3 棋盤網格繪制,繪制的起始位置CPoint Tst,繪制的結束位置CpointTed,網格間距Tchline,網格的大小N*N。
void TChessUI(CClientDC *dc)
{
CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
dc->SelectObject(&pen);
for (int i = Tst.x; i <= Ted.x; i += Tchline)
{
dc->MoveTo(i, Tst.y);
dc->LineTo(i, Ted.y);
}?????????????
for (int j = Tst.y; j <= Ted.y; j += Tchline)
{
dc->MoveTo(Tst.x, j);
dc->LineTo(Ted.x, j);
}
}
?
1.4 繪制棋子
void TshowLayout(CClientDC *dc)
{
CPen pen(PS_SOLID, 1, RGB(0, 0, 0));
CBrush *pbrush = NULL;
dc->SelectObject(&pen);
int PieceSize = 10;
for (int i = 0; i < Tcount; i++)
{
for (int j = 0; j < Tcount; j++)
{
??????? if (Tdata[i*Tcount + j] ==1)
??????? {
?????????????? pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH));
??????? dc->SelectObject(pbrush);
??????? dc->Ellipse(Tst.x+Tchline*i-PieceSize, Tst.y+Tchline *j- PieceSize, Tst.x + Tchline*i + PieceSize, Tst.y + Tchline *j + PieceSize);
??????? }
??????? if (Tdata[i*Tcount + j] == -1)
??????? {
?????????????? pbrush = CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH));
??????? dc->SelectObject(pbrush);
??????? dc->Ellipse(Tst.x + Tchline*i - PieceSize, Tst.y + Tchline *j - PieceSize, Tst.x + Tchline*i + PieceSize, Tst.y + Tchline *j + PieceSize);
??????? }
}
}
}
2.數據存儲
? 2.1 全局變量,需要初始化。
vector<int> Tdata;數值0、1、-1,0代表該位置為空,1代表白棋,-1代表黑棋
?????? vector<CPoint> Tpos;棋盤可落子位置
?????? int Tcount =19;棋盤大小
?????? int Tchline = 30;網格間距
?????? CPoint? Tst;棋盤起始位置
?????? CPoint Ted;1棋盤結束位置
?????? int Twhite = -1;先手指示器
?????? bool Ttime = false;時鐘調度指示器
3.博弈算法
3.1 隨機落子模擬
void SimulationData()
{
default_random_engine dre;//隨機數引擎
dre.seed((unsigned)time(NULL));
int pos = -1;
if(!Tpos.empty())
{
pos = dre() % Tpos.size();
if (Twhite==1)
{
??????? Tdata[Tpos[pos].x*Tcount + Tpos[pos].y] = 1;
??????? Twhite = -Twhite;//此時指示需提子的棋子顏色,以及下次落子的顏色
}
else if(Twhite == -1)
{
??????? Tdata[Tpos[pos].x*Tcount + Tpos[pos].y] = -1;
??????? Twhite = -Twhite;
}
Tpos.erase(Tpos.begin() + pos);
}
}
?
隨機索引位置的產生,利用C++ 11新特性,使用隨機數random類來產生,頭文件#include<random>。
?
?
?
3.2 ?提子過程,將棋盤上Twhite指示的棋子的死子提出,增加棋盤落子可用位置;
?
void grape()
{
if (Twhite == 0)return;
else
{
vector<CPoint> grap;
vector<bool> visi;
visi.resize(Tcount*Tcount, false);
grap.clear();
for (int i = 0; i < Tcount; i++)
{
??????? for (int j = 0; j < Tcount; j++)
??????? {
?????????????? if (Tdata[i*Tcount + j] == Twhite)
?????????????? {
???????????????????visi.resize(Tcount*Tcount, false);??
? ? ? ? ? ? ? ? ? ? if (TisLive(i, j,visi) == false)
????????????????????? ?????? grap.push_back(CPoint(i, j));
?????????????? }
??????? }
}
while (!grap.empty())
{
??????? CPoint p=grap.front();
??????? Tdata[p.x*Tcount+p.y] = 0;
??????? Tpos.push_back(p);
??????? grap.erase(grap.begin());
}
}
?}
?
3.3 提子過程需要判斷棋子的死活,利用深度搜索算法,判斷棋子s(x,y)的死活,則需判斷其四鄰接棋子的死活,若s,為活棋,無需提子,返回ture,若s為死棋,則返回false;通過遞歸來實現。
bool TisLive(unsigned int i, unsigned int j, vector<bool>& visi)
{
if (i<0 || i>Tcount || j<0 || j>Tcount)
?????? ?????? return false;
?????? else if (Tdata[i*Tcount + j] == -Twhite)
?????? {
????????????? return false;
?????? }
?????? else if (Tdata[i*Tcount + j] == 0)
?????? {
????????????? return true;
?????? }
?????? else if(Tdata[i*Tcount+j]==Twhite&&visi[i*Tcount + j]==false)
?????? {
????????????? visi[i*Tcount + j] = true;
????????????? if (TisLive(i - 1, j,visi))
return true;
????????????? if(TisLive(i, j - 1, visi))
?return true;
????????????? if(TisLive(i+1, j, visi))??
return true;
????????????? if(TisLive(i , j+ 1, visi))
?return true;
?????? }
??????
?? ??? ?else return false;
?? ??? ?return false;
????? }
3.4 onTimer函數
? ? ? ?在初始化函數中設置兩個時鐘;
???? ?SetTimer(0, 1000, NULL);
? ? ? SetTimer(1, 10, NULL);
? ? ? ?兩個時鐘總用一個onTimer,通過nIDEvent來識別執行此函數的時鐘。一個時鐘負責UI繪制,一個時鐘模擬數據變化。
void CWhiteBlackChessDlg::OnTimer(UINT_PTR nIDEvent)
{
?? ?CClientDC dc(this);
?? ?switch (nIDEvent)
?? ?{
?? ?case 0:
?? ?{
?? ??? ?if (Ttime)
?? ??? ?{?? ?
?? ??? ??? ??? ?SimulationData();
?? ??? ??? ??? ?grape();
?? ??? ??? ??? ?Ttime = false;
?? ??? ?}
?? ?}
?? ??? ?break;
?? ?case 1:
?? ?{?? ?
?? ??? ?
?? ??? ?if (!Ttime)
?? ??? ?{?? ??? ?
?? ??? ??? ?TChessBgUI(&dc);
?? ??? ??? ?TChessUI(&dc);
?? ??? ??? ?TshowLayout(&dc);
?? ??? ??? ?Ttime = true;?? ??? ?
?? ??? ??? ?if (Tpos.empty())
?? ??? ??? ?{
?? ??? ??? ??? ?KillTimer(0);?? ?
?? ??? ??? ??? ?KillTimer(1);
?? ??? ??? ?}
?? ??? ?}
?? ?}break;
?? ?default:break;
?? ?}
?? ?CDialogEx::OnTimer(nIDEvent);
}
?
3.5 初始化函數
void TInit()
?? ?{
?? ??? ?Tdata.resize(Tcount*Tcount,0);
?? ??? ?Tpos.resize(Tcount*Tcount);
?? ??? ?Tst.SetPoint(30, 30);
?? ??? ?Ted.SetPoint(Tchline * Tcount, Tchline * Tcount);
?? ??? ?for (int i = 0; i < Tcount; i++)
?? ??? ?{
?? ??? ??? ?for (int j = 0; j < Tcount; j++)
?? ??? ??? ??? ?Tpos[i*Tcount + j] = CPoint(i, j);
?? ??? ?}
?? ?}
總結
?
總結
以上是生活随笔為你收集整理的MFC随机博弈黑白棋的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的基础知识——多线程gdb调试
- 下一篇: 牛客16426 玩具谜题