JAVA版井字棋的設(shè)計(jì)與實(shí)現(xiàn)_第1頁
JAVA版井字棋的設(shè)計(jì)與實(shí)現(xiàn)_第2頁
JAVA版井字棋的設(shè)計(jì)與實(shí)現(xiàn)_第3頁
JAVA版井字棋的設(shè)計(jì)與實(shí)現(xiàn)_第4頁
JAVA版井字棋的設(shè)計(jì)與實(shí)現(xiàn)_第5頁
已閱讀5頁,還剩12頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、優(yōu)秀論文,值得下載!java版井字棋的設(shè)計(jì)與實(shí)現(xiàn)仇賓摘要:井字棋是大家所熟知的一個小游戲,雖然簡單,但其中包含了一些編程的基本技巧和基本算法,本文將在eclipse環(huán)境下用java語言編寫一個可以人人、人機(jī)對弈的井字棋游戲。一 引言井字棋,即棋盤是一個井字,是一種在3x3格子上進(jìn)行的連珠游戲,和五子棋比較類似,由于棋盤一般不畫邊框,格線排成井字而得名。游戲規(guī)則很簡單,游戲雙方一方為“x”,一方為“o”,哪方率先實(shí)現(xiàn)三子相連即為勝者。見圖1:圖1 正在進(jìn)行中的井字棋游戲現(xiàn)在我們來對井字棋游戲的代碼實(shí)現(xiàn)做一個探討,首先介紹人人對弈方式的實(shí)現(xiàn)過程,然后在此基礎(chǔ)上介紹人機(jī)對弈井字棋的實(shí)現(xiàn)。二 人人對弈

2、井字棋的實(shí)現(xiàn)1、難點(diǎn)釋疑人人對弈實(shí)現(xiàn)起來較為簡單,游戲雙方交替在棋盤上落下棋子“x”或“o”即可,最大問題就是如何判定勝負(fù)。從棋盤我們可以看出,獲勝(即任一方出現(xiàn)三連子)一共有8中情況:三連橫、三連豎以及兩個斜對角,如果我們給每個落子點(diǎn)從0到8編號,如圖2所示: 圖2 棋盤落子點(diǎn)編號那么,這8中獲勝情況我們可以用一個二維數(shù)組來表示:static final int win_status = 0, 1, 2,3, 4, 5,6, 7, 8,0, 3, 6,1, 4, 7,2, 5, 8,0, 4, 8,2, 4, 6;這樣,再定義一個一維數(shù)組,每走一步棋就對上面的二維數(shù)組進(jìn)行遍歷從二維數(shù)組中依次

3、取出8種情況放入一維數(shù)組,然后查看一維數(shù)組中的三個棋子是否相同,如果相同可以判定獲勝。2、設(shè)計(jì)實(shí)現(xiàn)第一步:寫一個類繼承自jframe,然后定義幾個必要的變量和常量,如下:public class tic extends jframe jbutton jb = new jbutton9;/ 按鈕數(shù)組構(gòu)成棋盤的8個落子點(diǎn)static final char empty = 32; / 代表空格static int clicknum = 0; / 記錄單擊次數(shù),決定是走x還是走ostatic final int infinity = 100;/ 帶標(biāo)無窮值static final int win =

4、+infinity;/ o獲勝static final int lose = -infinity;/ x獲勝static final int draw = 0;/ 平局/ 獲勝棋盤狀態(tài)static final int win_status = 0, 1, 2,3, 4, 5,6, 7, 8,0, 3, 6,1, 4, 7,2, 5, 8,0, 4, 8,2, 4, 6;第二步:構(gòu)建棋盤在tic類的構(gòu)造方法中,jframe的布局方式設(shè)置為gridlayout,然后每個格子里放置一個按鈕即可,代碼如下:public tic()this.setdefaultcloseoperation(jframe

5、.exit_on_close);this.setsize(400, 400);/棋盤大小this.setlayout(new gridlayout(3,3);this.settitle("井字棋");/ 讓窗口居中顯示dimension screen = toolkit.getdefaulttoolkit().getscreensize();/ 獲取屏幕尺寸封裝到screen中this.setlocation(screen.height - this.getheight()/2, (screen.width - this.getwidth()/2); / 窗口居中/ 把按鈕

6、加入窗體for(int i=0; i<9; i+)jbi = new jbutton("");jbi.setfont(new font("arial",font.bold,30);jbi.addactionlistener(new jbclick();/ 事件監(jiān)聽this.add(jbi);this.setvisible(true);第三步:經(jīng)過前兩步,現(xiàn)在加上main方法,就可以執(zhí)行顯示棋盤了,代碼如下:public static void main(string args) new tic();joptionpane.showmessagedi

7、alog(null,"o代表玩家1,x代表玩家2,玩家1先走","提示",joptionpane.default_option);但是按鈕還沒有事件響應(yīng),現(xiàn)在單擊按鈕沒有任何反應(yīng)。下面我們給按鈕添加事件響應(yīng)。每次單擊按鈕需要完成如下任務(wù):因?yàn)槭莾蓚€玩家進(jìn)行游戲,所以按鈕要輪流顯示“x”和“o”,表示游戲雙方交替走棋。每次單擊走棋后還要判定是否有獲勝方,有則提示哪方獲勝,游戲結(jié)束,無則繼續(xù)走棋。代碼如下:private class jbclick implements actionlistener/ 當(dāng)單擊按鈕時public void actionperf

8、ormed(actionevent e) for(int i=0; i<9; i+)if(e.getsource() = jbi)/ 哪個按鈕被單擊if(+clicknum % 2 = 0)/偶數(shù)次是xjbi.settext("x");elsejbi.settext("o");/奇數(shù)次是ojbi.setenabled(false);/被單擊過的按鈕不可用int gamestate = gamestate(jb);/ 調(diào)方法獲取棋盤狀態(tài)/ 輸出棋局勝負(fù)switch (gamestate) case win:joptionpane.showmessag

9、edialog(null, "o方獲勝", "提示",joptionpane.default_option);break;case lose:joptionpane.showmessagedialog(null, "x方獲勝", "提示",joptionpane.default_option);break;case draw:joptionpane.showmessagedialog(null, "平局", "提示",joptionpane.default_option);b

10、reak;/ 如果結(jié)束,則提示if (gamestate = win | gamestate = lose | gamestate = draw) int over = joptionpane.showconfirmdialog(null, "是否再來一局?", "提示", joptionpane.yes_no_option,joptionpane.question_message);if (over = joptionpane.yes_option) for (int i = 0; i < 9; i+) jbi.settext(" &q

11、uot;);jbi.setenabled(true); else system.exit(0); / 退出游戲 其中代碼:int gamestate = gamestate(jb);表示調(diào)用gamestate方法來判定是否有獲勝方。如何判定呢?首先,遍歷整個棋盤,看棋盤是否已經(jīng)滿,如果未滿,就直接返回,繼續(xù)走棋,如果滿了,則判斷是否符合8種數(shù)組中的一種,符合則分出勝負(fù),不符合則說明平局。代碼如下:public int gamestate(jbutton jb)int result = 1; / 棋局狀態(tài)初始值boolean isfull = true;/ 棋盤是否已滿/ 判斷棋盤是否已滿for

12、 (int pos = 0; pos < 9; pos+) char chess = jbpos.gettext().charat(0);if (empty = chess) isfull = false;return result; / 未滿則返回,繼續(xù)走棋/ 棋局已滿,判定勝負(fù)for(int status:win_status)/遍歷8中棋局獲勝狀態(tài)/得到某個獲勝棋局狀態(tài)的第一個索引的字符char chess = jbstatus0.gettext().charat(0);/ 如果為空,說明此處未下棋子,跳出循環(huán),找下一個狀態(tài)if(chess=empty)continue;int i

13、 ;/不為空,則看其余兩子是否與其相同for(i=1; i<status.length; i+) if(jbstatusi.gettext().charat(0)!=chess)break;/不同則跳出循環(huán) if(i=status.length)/ 三子連線result = chess= 'o' ? win : lose;break;if (result != win & result != lose & isfull) result = draw;/不輸不贏且棋盤滿則為平return result;調(diào)用該方法后,如果返回值result等于1,說明棋局沒有

14、下滿,繼續(xù)走棋;等于win,說明走“o”的一方獲勝;等于lose,說明“x”方獲勝;等于draw,則平局。執(zhí)行過程如圖3所示。圖3 o方獲勝3、 編程技巧盡管很簡單的一個小程序,但是其中卻包含一些編程技巧,比如使用一個二維數(shù)組來記錄棋局獲勝的情況,如果不用數(shù)組,則需要使用8個if語句來描述8種獲勝情況。再比如程序中用到的for each語句,很輕松的做到了用一個一維數(shù)組去遍歷二維數(shù)組。如果使用for語句,那么這個循環(huán)將會很難寫。三 人機(jī)對弈井字棋的實(shí)現(xiàn)1、難點(diǎn)釋疑人機(jī)對弈的難點(diǎn)在于當(dāng)人走一步棋之后,計(jì)算機(jī)如何走下一步,即計(jì)算機(jī)如何找出最合適的位置去走棋。這就需要一定的算法,或者叫做計(jì)算機(jī)的ai

15、。對于井字棋、五子棋等兩方較量的游戲來說,minimax算法(極小極大算法)是最基本也是最常用的。算法的原理不在這里解釋了,我們直接看該算法在井字棋中的應(yīng)用。井字棋中,假設(shè)使用“x”的是人,使用“o”的是計(jì)算機(jī)?!皒”方先走,設(shè)定x方的最大利益為正無窮(程序使用常量+infinity表示),o方的最大利益為負(fù)無窮(程序中使用-infinity表示),即x方和o方走的每步棋都要力圖使自己的利益最大化,而使對方的利益最小化。這樣我們稱x方為max(因?yàn)樗偸亲非蟾蟮闹担琽方為min(它總是追求更小的值),各自都為爭取自己的最大獲益而努力?,F(xiàn)在舉例說明,比如圖4所示的棋局樹:圖4 棋局形成的樹x

16、方先走,有三種選擇,如圖4中第二層所示。假設(shè)x方選擇最左邊的走法,那么o方接下來將有5種走法,o方會選擇最小化的走法,即值為-1的走法,因?yàn)樗淖畲罄媸秦?fù)無窮;同理,x方的另外兩種走法會分別得到o方的最小值1和-2。這樣,對于x方來說,三種走法會導(dǎo)致o方最小化值分別為-1、1、-2,x方的最佳策略則是選擇其中最大的,即第二層中間的走法,因?yàn)樗淖畲罄媸钦裏o窮,這就是極小極大算法的體現(xiàn)x方的選擇總是極大化,o方的選擇總是極小化。對于其中那些值的是如何計(jì)算的,我們舉例說明,比如對于第三層最左邊的棋局,在這種狀態(tài)下,如果把棋局空白處都填上x,則x共有6中3連子情況,即獲勝情況;如果把空白處都填上

17、o,則o共有5種3連子情況,所以結(jié)果是二者相減等于1。在具體走起過程中,max面對min最大獲利中的最小值時,會選擇其中最大的,比如圖4第二層小括號內(nèi)的值都是第三層中能使min最大獲利的最小值,這時候max選擇其中最大的,這對max最為有利,所以max方選擇圖4第二層中間的走法最好。同樣道理,min也會一樣,選擇對自己最有利的,即max有可能獲得的最大值。這時候,min在走棋時會考慮max方占據(jù)哪個位置對max最有利,然后min把這個位置先占了。有點(diǎn)難理解,其實(shí)就是搶先把對對手有利的位置搶占了。簡單說,x方或者max方的走棋時由人來控制的,我們不仔細(xì)說了。對于o方或者min方,它走棋時要考慮哪

18、個位置對x方最有利,然后把該位置占據(jù),即o的最佳走棋就是x的最佳走棋。所以o在走棋之前,先站在x的角度尋找最佳走棋位置。后文中minimax方法就是站在x角度來考慮極小極大算法,找到x的最佳走棋位置,然后由o方來占據(jù)該位置。2、極小極大算法整個算法包括如下幾個部分:首先要有一個評估方法gamestate,對每走一步棋后的棋局進(jìn)行評估,估值為win常量說明x方,即max方獲勝;估值為lose則o方,即min方獲勝;估值draw為平局;估值為inprogress,說明棋未走完;估值為double_link,說明棋局中有兩連子情況然后用一個minimax方法尋找在當(dāng)前棋局狀態(tài)下x方的最佳位置, x方

19、的最佳位置就是當(dāng)x走該位置后,o方所有走法中最小值里的最大值,比如圖4中第二層x 的位置選擇。當(dāng)找到該位置后,由o方來搶先占據(jù)該位置。最后用兩個遞歸方法min和max來遍歷所有的棋局。min方法負(fù)責(zé)找出o方的最小值,比如圖4第二層最左邊的棋局會導(dǎo)致5中o方的走法,min方法就是找出這5種走法中的最小值。同理,max方法負(fù)責(zé)找出x方的最大值,比如圖4第二層三種棋局中的中間棋局。3、代碼實(shí)現(xiàn)在剛才那個人人對弈井字棋的基礎(chǔ)上,我們進(jìn)行一些修改來實(shí)現(xiàn)人機(jī)對弈。首先單擊事件加入調(diào)用方法讓計(jì)算機(jī)走棋的代碼,如下:/ 按鈕的監(jiān)聽事件private class jbclick implements actio

20、nlistener / 當(dāng)單擊按鈕時public void actionperformed(actionevent e) for (int i = 0; i < 9; i+) if (e.getsource() = jbi) jbi.settext("x"); / 被單擊的按鈕走“x”jbi.setenabled(false); /置為不可用int gamestate = gamestate(jb); / 獲取棋盤狀態(tài)/ 如果棋局未結(jié)束,則計(jì)算機(jī)走下一步if (!(gamestate = win | gamestate = lose | gamestate = dra

21、w) int nextpos = getnextmove(jb); / 獲取下一步走棋位置jbnextpos.settext("o"); / 走棋“o”jbnextpos.setenabled(false);gamestate = gamestate(jb); / 獲取最新的棋盤狀態(tài)/ 輸出棋局勝負(fù)switch (gamestate) case win: joptionpane.showmessagedialog(null, "x方獲勝", "提示",joptionpane.default_option); break;case lo

22、se:joptionpane.showmessagedialog(null, "o方獲勝", "提示",joptionpane.default_option);break;case draw: joptionpane.showmessagedialog(null, "平局", "提示",joptionpane.default_option);break;/ 如果結(jié)束,則提示if (gamestate = win | gamestate = lose | gamestate = draw) int over = jo

23、ptionpane.showconfirmdialog(null, "是否再來一局?", "提示", joptionpane.yes_no_option, joptionpane.question_message);if (over = joptionpane.yes_option) / 再來一局for (int i = 0; i < 9; i+) jbi.settext(" ");jbi.setenabled(true); else system.exit(0); / 退出游戲然后,獲取棋局狀態(tài)的方法加入尋找兩連子的代碼,如下

24、:/ 獲取棋盤當(dāng)前狀態(tài)public int gamestate(jbutton jb) int result = inprogress;boolean isfull = true;/ 判斷棋盤是否已滿for (int pos = 0; pos < 9; pos+) char chess = jbpos.gettext().charat(0);if (empty = chess) isfull = false;/ 尋找三連子情況for (int status : win_status) / 遍歷8中棋局獲勝狀態(tài)/ 得到某個獲勝棋局狀態(tài)的第一個索引的字符char chess = jbstat

25、us0.gettext().charat(0);/ 如果為空,說明此處未下棋子,跳出循環(huán),找下一個狀態(tài)if (chess = empty) continue;int i;for (i = 1; i < status.length; i+) / 查看其余兩個字符if (jbstatusi.gettext().charat(0) != chess) / 不與第一個索引字符一致break; / 表明未三子連線,跳出if (i = status.length) / 三子連線result = chess = 'x' ? win : lose;break;/ 尋找兩連子情況if (r

26、esult != win & result != lose) if (isfull) result = draw;/不輸不贏且棋盤滿則為平 else int finds = new int2;/ 存放x或o的兩連子情況for (int status : win_status) char chess = empty;boolean hasempty = false;int count = 0;/ 計(jì)數(shù)for (int i = 0; i < status.length; i+) if (jbstatusi.gettext().charat(0) = empty) hasempty =

27、 true;/ 該處沒有棋子 else if (chess = empty) / 有棋子chess = jbstatusi.gettext().charat(0);if (jbstatusi.gettext().charat(0) = chess) count+;/且棋子相同則加1if (hasempty && count > 1) if (chess = 'x') finds0+; else finds1+;/ 兩連子情況if (finds1 > 0) / o的兩連子result = -double_link; else if (finds0 &g

28、t; 0) / x的兩連子result = double_link;return result;/ 記錄了勝負(fù)平或者兩連子情況o方走棋時,要得到走棋位置,我們用一個方法來獲取該位置,如下:public int getnextmove(jbutton board) int nextpos = minimax(board, 3);return nextpos;上面方法中調(diào)用了極小極大算法minimax,如下:/以'x'的角度來考慮的極小極大算法public int minimax(jbutton board, int depth) int bestmoves = new int9;

29、/存放最佳走棋位置int index = 0;int bestvalue = -infinity;/ 搜索所有空位,試探填上x,然后選其中最小值的for (int pos = 0; pos < 9; pos+) if (boardpos.gettext().charat(0) = empty) boardpos.settext("x");int value = min(board, depth);/ 得到最小值if (value > bestvalue) / 選擇最小值里最大的bestvalue = value;index = 0;bestmovesindex

30、= pos; else if (value = bestvalue) index+;bestmovesindex = pos;boardpos.settext(" ");return bestmovesindex;最后,兩個遞歸方法min和max如下:/對于'o',估值越小對其越有利public int min(jbutton board, int depth) int evalvalue = gamestate(board);boolean isgameover = (evalvalue = win | evalvalue = lose | evalvalue = draw);if (depth = 0 | isgameover) return evalvalue;int bestvalue = infinity;for (int pos = 0; pos < 9; pos+) if (boardpos.gettext().

溫馨提示

  • 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)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論