




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、 五子棋 五子棋是一種很受人們喜愛的游戲,它的規(guī)則簡單,但玩法變化多端,富有趣味性,適合人們消遣。這里我們就來設(shè)計(jì)一個(gè)五子棋游戲。 (一) (一) 人對人游戲 1. 1. 游戲?qū)崿F(xiàn) 人對人游戲,其實(shí)只是對游戲規(guī)則的實(shí)現(xiàn),我們只是利用五子棋游戲的規(guī)則來編程,至于真正的游戲?qū)崿F(xiàn)計(jì)算機(jī)的“智能”算法,我們將在后面講述。五子棋的規(guī)則很簡單:1,判斷是否能放下棋子(是否已經(jīng)有了棋子);2,判斷是哪種顏色下棋;3,判斷是否
2、已經(jīng)結(jié)束(是誰贏?)。 這些規(guī)則,我們將用相應(yīng)的函數(shù)來實(shí)現(xiàn)。其它,我們還將介紹其它一些功能的實(shí)現(xiàn)。如鼠標(biāo)的更換,工具欄和狀態(tài)欄的編輯,類與類之間的相互調(diào)用。 新建工程3_1,選擇單文檔,在Step 4 of 6中先中Windows Sockets復(fù)選框。如下圖: 圖3-1-1 2. 2. 資源編輯 由于我們這個(gè)程序出現(xiàn)的關(guān)于資源編輯的內(nèi)容太多,我們具體介紹如下:見下圖3-1-2,我們需要添加的有: 圖3-1-2 黑白位圖Bitmap以表示棋盤上面的
3、棋子:IDB_BLACK IDB_WHITE黑白鼠標(biāo)Cursor以替換當(dāng)前鼠標(biāo):IDC_CURSOR1黑棋子IDC_CURSOR2白棋子 說明:由于下棋時(shí)我們必須把鼠標(biāo)熱點(diǎn)設(shè)置在中間,點(diǎn)擊下圖(圖3-1-3)最右邊按扭,然后把鼠標(biāo)移動到圖像中你想設(shè)置為熱點(diǎn)的地方,按下鼠標(biāo)左鍵。 圖3-1-3 黑白圖標(biāo)Icon以顯示在狀態(tài)欄供以提示:IDI_BLACKIDI_WHITE 說明: 由于我們的圖標(biāo)支持256色,按下下圖(圖3-1-4)最右邊按扭,選擇Device里面顯示的選項(xiàng)。 圖3-1-4 菜單以供操作:開始:ID_START保存:ID_SAV
4、E打開:ID_OPEN工具欄:如上圖所示。說明:工具欄一般都是根據(jù)菜單選項(xiàng)而產(chǎn)生的,它的ID一般都能從菜單的ID 中找到。 3. 3. 變量函數(shù) 首先,為了實(shí)現(xiàn)狀態(tài)欄的應(yīng)用,我們必須更改它的變量: 在MainFrm.h文件里面,把CStatusBar m_wndStatusBar 為public 接著是在3_1View.h文件里面添加變量函數(shù): /兩個(gè)鼠標(biāo) HCURSOR hcursorwhite;HCURSOR hcursorblack; /棋盤數(shù)組int wzq1919; / colorwhite T
5、RUE時(shí)白棋下,否則黑棋下bool colorwhite; /棋子位圖CBitmap m_bmblack;CBitmap m_bmwhite; /保存文件void Save();/檢查是否結(jié)束void over(CPoint point);/鼠標(biāo)操作afx_msg void OnLButtonUp(UINT nFlags, CPoint point);/鼠標(biāo)圖形更換afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);/菜單的開始afx_msg void OnStart();/菜單的保存afx_msg vo
6、id OnSave();/菜單的打開afx_msg void OnOpen(); 4. 4. 具體實(shí)現(xiàn) 棋盤大小設(shè)置: 由于我們的游戲的棋盤大小是一定的,不能改變大小的,是應(yīng)該符合要求的。在如下函數(shù)添加設(shè)置窗口大小的語句: BOOL CMainFrame:PreCreateWindow(CREATESTRUCT& cs)if( !CFrameWnd:PreCreateWindow(cs) )return FALSE;/ TODO: Modi
7、fy the Window class or styles here by modifying/ the CREATESTRUCT cscs.dwExStyle=cs.dwExStyle|WS_EX_TOPMOST;/cs.style=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;/; /設(shè)置窗口大小:400*340cs.cx=450;cs.cy=500; return TRUE; 初始化變量: 在構(gòu)造函數(shù)里添加初始代碼: CMy3_1View:CMy3_1View()/ TODO: add construction code h
8、ere/Load鼠標(biāo)圖像和棋子位圖hcursorblack=AfxGetApp()->LoadCursor(IDC_CURSOR1);hcursorwhite=AfxGetApp()->LoadCursor(IDC_CURSOR2);m_bmwhite.LoadBitmap(IDB_WHITE);m_bmblack.LoadBitmap(IDB_BLACK);/清理棋盤 /數(shù)組值為0表示沒有棋子for(int i=0;i<19;i+)for(int j=0;j<19;j+)wzqij=0;/白棋先下 colorwhite=true; 畫棋盤: 在On
9、Draw(CDC* pDC)函數(shù)中畫棋盤,由于在游戲過程中有可能重畫棋盤,而那時(shí)棋盤上面有棋子,所以,我們在這個(gè)函數(shù)里面必須有畫棋子的語句。我們用數(shù)組的做為1表示白棋,-1表示黑棋。 void CMy3_1View:OnDraw(CDC* pDC)CMy3_1Doc* pDoc = GetDocument();ASSERT_VALID(pDoc);/ TODO: add draw code for native data here/畫背景 CBrush mybrush1; mybrush1.CreateSolidBrush(RGB(192,192,192); CRect myrect
10、1(0,0,1200,800); pDC->FillRect(myrect1,&mybrush1);/畫棋盤框線CPen mypen;CPen*myoldPen;mypen.CreatePen(PS_SOLID,1,RGB(0,0,0); myoldPen=pDC->SelectObject(&mypen); for(int i=0;i<19;i+)pDC->MoveTo(40,40+i*20);pDC->LineTo(400,40+i*20);pDC->MoveTo(40+i*20,40);pDC->LineTo(40+i*20,40
11、0); /重畫時(shí)顯示存在的棋子CDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC");for(int n=0;n<19;n+)for(int m=0;m<19;m+)if(wzqnm=1)/顯示白棋Dc.SelectObject(m_bmwhite);pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY);else if(wzqnm=-1)/顯示黑棋Dc.SelectObject(m_bmbla
12、ck);pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY); 設(shè)置鼠標(biāo): 棋盤畫好了,接下來就是下棋了。但鼠標(biāo)并沒有像我們上面說的那樣變成白棋,加函數(shù)如下: BOOL CMy3_1View:OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) / TODO: Add your message handler code here and/or call defaultif(nHitTest=HTCLIENT)/白棋下,顯示白棋鼠標(biāo)if(colorw
13、hite) /調(diào)用主框架里面的狀態(tài)欄CMainFrame*pFrm=(CMainFrame*)AfxGetApp()->m_pMainWnd;CStatusBar*pStatus=&pFrm->m_wndStatusBar;if(pStatus)pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDI_WHITE);pStatus->SetPaneText(0,"白棋下");SetCursor(hcursorwhite);/顯示黑棋鼠標(biāo)elseSetCursor(hc
14、ursorblack);CMainFrame*pFrm=(CMainFrame*)AfxGetApp()->m_pMainWnd;CStatusBar*pStatus=&pFrm->m_wndStatusBar;if(pStatus)/顯示圖像pStatus->GetStatusBarCtrl().SetIcon(0,AfxGetApp()->LoadIcon(IDI_BLACK);/顯示文字pStatus->SetPaneText(0,"黑棋下");return 1;return CView:OnSetCursor(pWnd, nHi
15、tTest, message); 現(xiàn)在運(yùn)行程序,怎樣,鼠標(biāo)變成白棋了,而且下面的狀態(tài)欄也能夠顯示鼠標(biāo)狀態(tài)了,真是一舉兩得??墒牵衷撛鯓影哑遄臃旁谄灞P上呢? 下棋操作: 這就涉及到OnLButtonDown(UINT nFlags, CPoint point)和OnLButtonUp(UINT nFlags, CPoint point)兩個(gè)函數(shù)了。要用哪一個(gè)或用兩個(gè)?用Down函數(shù)時(shí)是在鼠標(biāo)按下時(shí)放下棋子,可是,要是我們按下后意識到按錯(cuò)了怎么辦;那就改用Up函數(shù),表示當(dāng)鼠標(biāo)鍵松開時(shí)放下棋子。OK!添加函數(shù)如下: void CMy3_1View:OnLBu
16、ttonUp(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call defaultCDC *pDC=GetDC();CDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC"); /是否在棋盤內(nèi) if(point.x>30&&point.x<410&&point.y>30&&point.y<410
17、)int px=(point.x-30)/20;int py=(point.y-30)/20;/是否已經(jīng)有棋子if(colorwhite&&wzqpxpy=0)Dc.SelectObject(m_bmwhite);pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);/表示存在白棋wzqpxpy=1;/檢查是否結(jié)束over(point);/換黑棋下colorwhite=false;else if(wzqpxpy=0)Dc.SelectObject(m_bmblack); pDC->BitBlt(px*2
18、0+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=-1;over(point);colorwhite=true;CView:OnLButtonUp(nFlags, point); 由上面可以看出,當(dāng)鼠標(biāo)鍵松開時(shí)判斷,如果那個(gè)位置沒有棋子,則放下,并把棋盤數(shù)組賦相應(yīng)的值:1 或-1。 是否結(jié)束: 接著是用一個(gè)over()函數(shù)判斷是否結(jié)束,是則結(jié)束并重新開始;否則,接著把鼠標(biāo)變成對方棋子,表示對方下棋。那over()函數(shù)又是怎樣的呢? 此函數(shù)是利用剛下棋的位置為中心,檢查它各個(gè)方向上的連續(xù)五個(gè)棋子是否同色,是則結(jié)束并
19、重新開始。然而,我們又是怎樣判斷一個(gè)方向上的五個(gè)棋子的同色的?這就涉及地為什么我要把五子棋數(shù)組賦值為1和-1的問題。因?yàn)檫@樣有一個(gè)好處:利用連續(xù)五個(gè)棋子的值相加,如果它們的值的絕對值等于5,則說明是同色。當(dāng)然,這只是這樣賦值的一點(diǎn)作用,真正的作用將在后面介紹。添加如下: void CMy3_1View:over(CPoint point)/獲取鼠標(biāo)指向數(shù)組位置,即中心位置int x=(point.x-30)/20;int y=(point.y-30)/20;/計(jì)算開始判斷的坐標(biāo) xx,yyint xx,yy; if(x<4)xx=0;elsexx=x-4;if(y<4)y
20、y=0;elseyy=y-4;int i,j,a;/橫向判斷for(i=xx;i<15;i+) a=0;for(j=i;j<i+5;j+)a=a+wzqjy;/五個(gè)都是白棋 if(a=5)AfxMessageBox("白棋勝!");/重新開始OnStart();return;/五個(gè)都是黑棋if(a=-5)AfxMessageBox("黑棋勝!");OnStart();return;/豎向判斷for(i=yy;i<15;i+) a=0;for(j=i;j<i+5;j+)a=a+wzqxj; if(a=5)AfxMessageBox(
21、"白棋勝!");OnStart();return;if(a=-5)AfxMessageBox("黑棋勝!");OnStart();return;/向右下角/判斷起點(diǎn)位置if(x<y)if(xx=0)yy=y-x;elseif(yy=0)xx=x-y;/參數(shù)over=1時(shí)退出循環(huán)int over=0;doa=0;for(i=0;i<5;i+)if(xx+i)<19|(yy+i)<19)a=a+wzqxx+iyy+i;if(a=5)AfxMessageBox("白棋勝!");OnStart();return;if(
22、a=-5)AfxMessageBox("黑棋勝!");OnStart();return;/到了邊界elseover=1; xx+=1;yy+=1;while(over=0); /向左下角 if(y>(18-x)if(x>13)yy=y-(18-x);xx=18;elseyy=y-4;xx=x+4;elseif(y<5)xx=x+y;yy=0;elseyy=y-4;xx=x+4; over=0;doa=0;for(i=0;i<5;i+)if(xx-i)>=0|(yy+i)<19)a=a+wzqxx-iyy+i;if(a=5)AfxMessa
23、geBox("白棋勝!");OnStart();return;if(a=-5)AfxMessageBox("黑棋勝!");OnStart();return;/到了邊界elseover=1; xx-=1;yy+=1;while(over=0); 現(xiàn)在,我們的人對人游戲就完成了。下面介紹附加內(nèi)容。 5. 5. 附加內(nèi)容 理論上,這個(gè)游戲并無須保存,因?yàn)樗_實(shí)太小了。事實(shí)上,這個(gè)游戲有保存的功能,由于我們學(xué)習(xí)的需要。這個(gè)游戲的保存,與其說是學(xué)習(xí)文件的保存,不如說是我們學(xué)習(xí)字符串的操作。另外,這
24、個(gè)附加的內(nèi)容并不是為了當(dāng)前的學(xué)習(xí)而添加的,而是為了后面的學(xué)習(xí)和應(yīng)用而鋪墊的。 保存文件: 保存文件函數(shù)是一個(gè)菜單選項(xiàng)。它的作用就是保存當(dāng)前游戲的狀態(tài)。首先,我們應(yīng)該為我們自己的文件定義一個(gè)后綴名:.wzq;接著是打開保存文件的公共對話框,如果確定,則表示保存,那么就先獲取文件名,然后按照一定的順序保存各個(gè)點(diǎn)的數(shù)組的值,最后保存當(dāng)前是哪種顏色下棋。 void CMy3_1View:OnSave() / TODO: Add your command handler code here/設(shè)置保存的文件,后綴名wzqCFileDialog dlg(FALSE,"wzq&q
25、uot;,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"(*.WZQ)|*.wzq|All Files|*.*|",this);/如果公共類對話框?yàn)榇_定if(dlg.DoModal()=IDOK) /獲取文件名dlg.GetFileName();/否則,退出elsereturn;/字符串變量CString str;int i,j; CStdioFile file;/如果有問題,退出if(file.Open(dlg.GetFileName(),CFile:modeCreate|CFile:modeWrite|CFile:ty
26、peText)=0)AfxMessageBox("save error!");return;/循環(huán)把棋盤數(shù)組的值寫進(jìn)文件for(i=0;i<19;i+)for(j=0;j<19;j+)if(wzqij=-1)file.WriteString("-1n");if(wzqij=0)file.WriteString("0n");if(wzqij=1)file.WriteString("1n");/保存當(dāng)前下棋顏色if(colorwhite=true)file.WriteString("1n"
27、;);elsefile.WriteString("0n");/關(guān)閉文件 file.Close(); 讀取文件: 讀文件就是把我們以前保存的文件打開,讀取當(dāng)前打開文件的內(nèi)容,并給數(shù)組賦值使和文件內(nèi)容相同,然后可以繼續(xù)進(jìn)行游戲。 /與保存文件相反void CMy3_1View:OnOpen() / TODO: Add your command handler code hereCFileDialog dlg(TRUE,"wzq",NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"(*
28、.WZQ)|*.wzq|All Files|*.*|",this);if(dlg.DoModal()=IDOK) dlg.GetFileName();elsereturn; CString str;int i,j,m; CStdioFile file;if(file.Open(dlg.GetFileName(),CFile:modeRead)=0)AfxMessageBox("save error!");return;CArchive ar(&file,CArchive:load); for(i=0;i<19
29、;i+)for(j=0;j<19;j+)ar.ReadString(str);sscanf(str,"%d",&m);if(m=-1)wzqij=-1;if(m=0)wzqij=0;if(m=1)wzqij=1;ar.ReadString(str);sscanf(str,"%d",&m);if(m=1)colorwhite=true;elsecolorwhite=false;file.Close();ar.Close(); Invalidate(false); 6. 6.
30、60; 小結(jié) 當(dāng)然,這樣的程序,你只要玩一兩次就會厭了,因?yàn)橄缕鍟r(shí)要找一個(gè)伴。那么,我們是否該編一個(gè)人對機(jī)的游戲呢?請看下面! (二) (二) 人對機(jī)游戲 1、 1、 游戲?qū)崿F(xiàn) 先看上圖,玩過五子棋的人一眼就能看出這才是一場真正的戰(zhàn)斗?。ㄖ辽偎辉试S你隨便下棋。)那么,我們該如何實(shí)現(xiàn)?其實(shí),只是在原來的基礎(chǔ)上,把其中一個(gè)人用計(jì)算機(jī)代替而已。也就是說,我們只要在原來的基礎(chǔ)上,加上計(jì)算機(jī)的下棋算法
31、就好了。而計(jì)算機(jī)下棋的算法,正是一個(gè)程序的重點(diǎn)和難點(diǎn)。這里先介紹一下:計(jì)算機(jī)是沒有頭腦的,但是,計(jì)算機(jī)必須下棋。那么,它該怎么辦呢?我們必須自己編程序,“教”計(jì)算機(jī)下棋。然而,我們是否只是讓計(jì)算機(jī)隨便在棋盤上面放一顆棋子呢?事情并不是這么簡單。計(jì)算機(jī)要下的那個(gè)位置,必定是它認(rèn)為最好的!當(dāng)然,這里的最好是程序員給予計(jì)算機(jī)的,是計(jì)算機(jī)算法的體現(xiàn)。最好的位置,人都難以保證!但是,計(jì)算機(jī)可以。這正是計(jì)算機(jī)能夠在我們這個(gè)社會迅速發(fā)展的一個(gè)重要原因。當(dāng)然,計(jì)算機(jī)的可以是建立在人的程序的基礎(chǔ)上的。計(jì)算機(jī)的會是依奈于它的速度,人所不能及的速度。上面說過,為什么要把數(shù)組的值賦值為1和-1,而不是0,1或者其它的
32、數(shù)字呢?這里就涉及到數(shù)字的妙用。前面說了,五個(gè)連續(xù)棋子的值相加,如果絕對值是五,則不是五個(gè)棋子同色,應(yīng)該結(jié)束游戲。那么,如果絕對值不是五呢?它就沒有意義了嗎?不是!不但不是,而是它有著更重要的意義。它關(guān)系到我們這個(gè)程序的幾乎全部的算法。下面細(xì)說(數(shù)字是表示絕對值的): 4:表示五個(gè)棋子中有一個(gè)空位置和四個(gè)同色的棋子。3:表示五個(gè)棋子中有兩個(gè)空位置和三個(gè)同色的棋子;也表示五個(gè)棋子中有四個(gè)同色棋子和一個(gè)異色棋子。注意:此時(shí)沒有空位置,可以不考慮。2:表示五個(gè)棋子中有三個(gè)空位置和兩個(gè)同色棋子;也表示五個(gè)棋子中一個(gè)空位置和三個(gè)同色棋子和一個(gè)異色棋子;1、0:由于出現(xiàn)1和0的機(jī)會太少(除了開始
33、的時(shí)候),我們不必多加考慮。但是,因?yàn)閯偛艑Ψ较缕宓膶Ψ奖囟ㄓ幸欢ǖ奈kU(xiǎn),我們只需要在剛剛下棋的附近找一個(gè)空位置下棋就可以了。說明: 1、 上面之所以把空位置這個(gè)詞語放在前面,因?yàn)槲覀冏⒅氐氖峭ㄟ^檢查,出一個(gè)可以下棋的空位置,然后下棋。 2、 為了方便說明,我們在說明用了絕對值。而事實(shí)上正負(fù)還是有區(qū)別的。正表示白棋占多數(shù),負(fù)表示黑棋占多數(shù)。而由于我們的這個(gè)算法是針對于計(jì)算機(jī)的,而我們又讓計(jì)算機(jī)為黑棋,所以我們的后面的算法還是有一定區(qū)別的。我們必須把它們分開,而且這樣規(guī)定:絕對值大表示危險(xiǎn)性大(這是很容易理解的);而當(dāng)出現(xiàn)絕對值相同的正負(fù)兩個(gè)值時(shí),我們必須這樣做,在值為負(fù)(表示黑棋一方)的那個(gè)位
34、置下棋,以保證黑棋一方的優(yōu)先性。 下面,就讓我們在怎樣對程序的擴(kuò)展方法的介紹過程中實(shí)現(xiàn)人對機(jī)游戲。 先把3_1的文件夾復(fù)制一個(gè),改為3_2。以后就用它來擴(kuò)展。 2、 2、 資源編輯 從上面圖中,我們可以看到,工具欄變了。因此我們也可以想到菜單也應(yīng)該變了。它們的修改如下: 修改菜單: 刪除菜單項(xiàng) 開始:ID_START添加菜單項(xiàng) 人對人游戲:ID_PLAYER人對機(jī)游戲:ID_CPMPUTER 修改工具欄: 刪除原來的按扭對應(yīng)ID:ID_START添加兩個(gè)新
35、按扭對應(yīng)ID:ID_PLAYER ID_CPMPUTER 3、 3、 變量和函數(shù) 在view類中添加變量函數(shù)如下: /保存vscomputer時(shí)白棋位置CPoint vspoint;/是人與人游戲?是人與機(jī)游戲?int vscomputer; CPoint bpointcan4,/這個(gè)位置空,它旁邊有四個(gè)黑棋wpointcan4,/這個(gè)位置空,它旁邊有四個(gè)白棋bpointcan3,/這個(gè)位置空,它的旁邊有三個(gè)黑棋wpointcan3,/這個(gè)位置空,它的旁邊有三個(gè)白棋bpointcan2,/這個(gè)位置空,它的旁
36、邊有兩個(gè)黑棋wpointcan2,/這個(gè)位置空,它的旁邊有兩個(gè)白棋bpointcan1;/不是以上情況,這個(gè)位置空 /在得到最大值和方向上尋找落棋點(diǎn)(具體見后面介紹)/其中i、j表示搜索起點(diǎn),n表示方向void searchcandown1(int i,int j,int n);void searchcandown2(int i,int j,int n);void searchcandown3(int i,int j,int n);void searchcandown4(int i,int j,int n);/計(jì)算最大值及方向CPoint maxnum(int a,int b,int
37、 c,int d);/最好落棋點(diǎn)void bestputdown(int i,int j);/計(jì)算機(jī)下棋void computerdown();/在位置point放下棋子void putdown(CPoint point);/人對人菜單afx_msg void OnPlayer();/人對機(jī)菜單afx_msg void OnCpmputer(); 4、 4、 具體實(shí)現(xiàn) 下面,我們朝著程序擴(kuò)充的思路,來實(shí)現(xiàn)我們的游戲。 菜單函數(shù): 添加了菜單項(xiàng),我們必須添加一個(gè)變量vscomputer,賦值為1,
38、并約定:/vscomputer:2表示人對人,1表示人對機(jī) void CMy3_1View:OnPlayer() / TODO: Add your command handler code herevscomputer=2;OnStart(); void CMy3_1View:OnCpmputer() / TODO: Add your command handler code herevscomputer=1;OnStart(); 其中,我們只是添加一個(gè)變量,而仍然利用原來的開始函數(shù)。雖然我們的菜單項(xiàng)已經(jīng)刪除了,但它的函數(shù)還在,我們應(yīng)該加以利用。 人變成
39、計(jì)算機(jī): 下面,我們就必須把游戲雙方中的一方改為計(jì)算機(jī)。我們把黑棋改為計(jì)算機(jī),因?yàn)橐话闱闆r計(jì)算機(jī)比人強(qiáng),應(yīng)讓人先下。當(dāng)然,要是人贏了,就必須讓計(jì)算機(jī)先下了,在將在以后的算法中體現(xiàn)。由于下棋只是在OnLButtonUp(UINT nFlags, CPoint point)函數(shù)中,我們把它改為如下: void CMy3_1View:OnLButtonUp(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call defaultCDC *pDC=GetDC();CDC Dc;
40、if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC"); /顯示棋子/人對機(jī)if(vscomputer=1)if(point.x>30&&point.x<410&&point.y>30&&point.y<410)int px=(point.x-30)/20;int py=(point.y-30)/20;if(colorwhite&&wzqpxpy=0) Dc.SelectObject(m_bmwh
41、ite);pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=1;over(point);colorwhite=false;/保存白棋位置vspoint=point;/計(jì)算機(jī)下棋computerdown(); /人對人if(vscomputer=2)if(point.x>30&&point.x<410&&point.y>30&&point.y<410)int px=(point.x-30)/20;int py=(point.y-30)/
42、20;if(colorwhite&&wzqpxpy=0)Dc.SelectObject(m_bmwhite);pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=1;over(point);colorwhite=false;else if(wzqpxpy=0)Dc.SelectObject(m_bmblack); pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=-1;over(point);color
43、white=true;CView:OnLButtonUp(nFlags, point);由上面可知,我們對人對機(jī)游戲的方法是采用:人下完了之后,檢查是否勝利,是則結(jié)束游戲,重新開始,并改為黑棋(即計(jì)算機(jī))先下;如果人沒有勝利,也改為計(jì)算機(jī)下。而對于計(jì)算機(jī)怎么下棋,我們只是用了一個(gè)函數(shù)computerdown()表示。但是現(xiàn)在我們并不能運(yùn)行程序,因?yàn)橛幸粋€(gè)沒有定義的函數(shù),怎么辦呢? l l 程序擴(kuò)展思想:添加一個(gè)空函數(shù)! 仔細(xì)看程序的話,你還會發(fā)現(xiàn)一個(gè)變量Cpoint vspoint,這是后來添加的它的用
44、處是保存剛才白棋下的位置,有利于黑棋下棋時(shí)的定位。接著,我們的主要問題就是實(shí)現(xiàn)computerdown()函數(shù),讓計(jì)算機(jī)能夠自動下棋! 計(jì)算機(jī)下棋: 計(jì)算機(jī)是怎樣下棋?這就是定位的問題了。即搜索棋盤,找出一個(gè)最佳點(diǎn),放下黑棋。我們實(shí)現(xiàn)的方法是:全盤搜索,并把搜索到的位置,保存在變量。由于有多種情況,我們定義變量如下:CPoint bpointcan4,/這個(gè)位置空,它旁邊有四個(gè)黑棋wpointcan4,/這個(gè)位置空,它旁邊有四個(gè)白棋bpointcan3,/這個(gè)位置空,它的旁邊有三個(gè)黑棋wpointcan3,/這個(gè)位置空,它的旁邊有三個(gè)白棋bpointcan2,/這個(gè)位置空,
45、它的旁邊有兩個(gè)黑棋wpointcan2,/這個(gè)位置空,它的旁邊有兩個(gè)白棋bpointcan1;/不是以上情況,這個(gè)位置空并在搜索之前都賦值為(-1,-1),然后,進(jìn)行搜索,并把相應(yīng)的值保存在相應(yīng)變量里面,而如果前面已經(jīng)對變量賦值,我們依然賦值,用新值代替舊值。注意:我們只保存最后一個(gè)值,這樣的一個(gè)好處是,避免了每次都從左上角開始,并且它的隨機(jī)性比隨機(jī)函數(shù)還隨機(jī)。全盤搜索完之后,由于上面的變量中至少有一個(gè)已經(jīng)被賦值,即不是(-1,-1),我們可以采用多數(shù)優(yōu)先的方法,讓已經(jīng)有多個(gè)同色棋子的位置先下棋。其原理是,如果已經(jīng)有四個(gè)黑棋,計(jì)算機(jī)再下一個(gè)黑棋就贏了;否則,如果人已經(jīng)有四個(gè)白棋,那么計(jì)算機(jī)就必
46、須放下一個(gè)黑棋,阻止白棋下一步贏;如果已經(jīng)有三個(gè)黑棋,再下一個(gè)黑棋,變成四個(gè);否則,如果已經(jīng)有三個(gè)白棋,下一個(gè)黑棋,破壞它;兩個(gè)棋子的同理;否則,在剛才白棋下的地方,順便找一個(gè)位置,下棋。computerdown()函數(shù)如下: /輪到計(jì)算機(jī)下棋void CMy3_1View:computerdown()/把各種情形賦值為如下bpointcan4=(-1,-1);wpointcan4=(-1,-1);bpointcan3=(-1,-1);wpointcan3=(-1,-1);bpointcan2=(-1,-1);wpointcan2=(-1,-1);bpointcan1=(-1,-1)
47、; /搜索最好的落棋點(diǎn)for(int i=0;i<19;i+)for(int j=0;j<19;j+)bestputdown(i,j);/判斷放在哪里/棋多的位置優(yōu)先/黑白一樣多時(shí)黑先 /不是-1就表示已經(jīng)被賦值!if(bpointcan4.x!=-1)putdown(bpointcan4);return;else if(wpointcan4.x!=-1)putdown(wpointcan4);return;else if(bpointcan3.x!=-1)putdown(bpointcan3);return;else if(wpointcan3.x!=-1)putdown(wpo
48、intcan3);return;else if(bpointcan2.x!=-1) putdown(bpointcan2);return;else if(wpointcan2.x!=-1)putdown(wpointcan2);return;else putdown(bpointcan1);return;上面又有兩個(gè)新函數(shù),分別定義為空函數(shù),如下: /搜索最佳位置void bestputdown(int i,int j);/放下黑棋void putdown(CPoint point);現(xiàn)在,我們就必須對上面兩個(gè)空函數(shù)進(jìn)行定義了。 在指定位置下棋: 由于putdown(CPo
49、int point)函數(shù)的原理非常簡單,我們先說明如下: /黑棋下void CMy3_1View:putdown(CPoint point)CDC *pDC=GetDC();CDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC");Dc.SelectObject(m_bmblack); pDC->BitBlt(point.x*20+32,point.y*20+32,160,160,&Dc,0,0,SRCCOPY); wzqpoint.xpoint
50、.y=-1;/由于原來我們檢查是否結(jié)束時(shí)用的是鼠標(biāo)點(diǎn)下的坐標(biāo),而現(xiàn)在/putdown(CPoint point)函數(shù)用的是數(shù)組棋盤的坐標(biāo),所以必須轉(zhuǎn)換CPoint overpoint;overpoint.x=point.x*20+30;overpoint.y=point.y*20+30;over(overpoint);colorwhite=true; 搜索最佳落棋點(diǎn): 現(xiàn)在就剩下void bestputdown(int i,int j)函數(shù)沒有定義了(雖然前面的變量函數(shù)已經(jīng)說明了,當(dāng)時(shí)我們這里是用程序擴(kuò)展的思路進(jìn)行的,故如此說明)。它的實(shí)現(xiàn)原理是:在四個(gè)方向上,各自計(jì)算那
51、個(gè)方向上棋子的狀態(tài),我們的思路是利用原來定義的白棋為1,黑棋為-1,的思想,讓同個(gè)方向上的五個(gè)棋子的值相加,取絕對值并賦值給為這個(gè)方向定義的局部變量numi。 為什么要用五個(gè)棋子的值相加呢?因?yàn)?,如果幾個(gè)棋子是同色的,無論黑白,它的絕對值必然大,而對于幾個(gè)棋子中有黑棋和白棋的,其值必然相加而抵消變小。所以我們可以利用這種方法來尋找旁邊有多個(gè)同色棋子的空位置(前面已經(jīng)具體說明)。在每一個(gè)棋盤位置,計(jì)算以它為起點(diǎn)的四個(gè)方向(橫、豎、撇、捺),再比較這四個(gè)方向中哪個(gè)值最大,然后在這個(gè)方向上尋找落棋點(diǎn)。我們添加函數(shù)如下: /檢查四個(gè)方向,各算出五個(gè)棋子的和并賦值void CMy3_1View:bestputdown(int i,int j) /四個(gè)方向的值int num4;int a,k; / num0 ->a=0; if(i<15)for(k=0;k<5;k+)a=a+wzqi
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 影像管理示教平臺技術(shù)要求
- 油氣管道安裝工技師模擬習(xí)題含答案
- 優(yōu)化住宅物業(yè)管理方案
- 餐飲聯(lián)營品牌授權(quán)合作協(xié)議范本
- 智能家居產(chǎn)品場地適配與必要性論證合同
- 企業(yè)團(tuán)隊(duì)合作課件
- 裝修驗(yàn)收要求方案
- 餐飲品牌加盟權(quán)轉(zhuǎn)讓與經(jīng)營管理協(xié)議
- 營地項(xiàng)目定價(jià)方案
- 高端制造業(yè)勞動合同范本定制與實(shí)施合同
- 糖尿病患者低血糖發(fā)生原因分析品管圈魚骨圖柏拉圖
- 2023年中國人保財(cái)險(xiǎn)全系統(tǒng)聯(lián)合招聘筆試參考題庫附帶答案詳解
- 瓶胚工藝培訓(xùn)
- 脊髓解剖及脊髓損傷
- 地下連續(xù)墻成槽垂直度控制
- 【超星爾雅學(xué)習(xí)通】《老子》《論語》今讀網(wǎng)課章節(jié)答案
- 中國農(nóng)業(yè)銀行筆試題庫(含答案)
- simufact教程基礎(chǔ)部分從Simufact得到支持
- NB-T 10651-2021 風(fēng)電場阻抗特性評估技術(shù)規(guī)范
- 電纜電線出廠檢驗(yàn)報(bào)告參考
- YY/T 0500-2021心血管植入物血管假體管狀血管移植物和血管補(bǔ)片
評論
0/150
提交評論