java開發(fā)實現(xiàn)五子棋游戲
本文實例為大家分享了java實現(xiàn)五子棋游戲的具體代碼,供大家參考,具體內(nèi)容如下
此游戲具有雙人對戰(zhàn)功能和人機對戰(zhàn)功能
一、游戲界面的實現(xiàn)
一個游戲首先從設(shè)計界面開始
1、首先創(chuàng)建一個類,作用是通過對窗體組件的一些設(shè)置來實現(xiàn)簡單游戲界面
public void gameUI(){ //窗體組件 MyFrame jf = new MyFrame(); jf.setSize(900, 800); jf.setTitle("冷丁-五子棋"); //居中顯示 jf.setLocationRelativeTo(null); //設(shè)置退出進程 jf.setDefaultCloseOperation(3); //流式布局管理器 FlowLayout flow=new FlowLayout(); jf.setLayout(flow); //邊框布局 BorderLayout border=new BorderLayout(); jf.setLayout(border); //面板: JPanel 默認(rèn)流式布局 JPanel eastPanel=new JPanel(); Dimension dm=new Dimension(100,0); //除了JFrame,其他組件設(shè)置大小都是該方法 eastPanel.setPreferredSize(dm); jf.add(eastPanel,BorderLayout.EAST); }
2、加入功能按鈕
JButton jbu=new JButton("開始"); eastPanel.add(jbu,BorderLayout.EAST); JButton jbu2=new JButton("悔棋"); eastPanel.add(jbu2,BorderLayout.EAST); JButton jbu3=new JButton("認(rèn)輸"); eastPanel.add(jbu3,BorderLayout.EAST); JButton jbu4=new JButton("人機對戰(zhàn)"); eastPanel.add(jbu4,BorderLayout.EAST); //設(shè)置窗體可見 jf.setVisible(true);
每個按鈕添加ActionListner監(jiān)聽
jbu.addActionListener(mouse); jbu2.addActionListener(mouse); jbu3.addActionListener(mouse); jbu4.addActionListener(mouse);
在GameMouse類中對過對actionPerformed方法的重新來判斷當(dāng)前是哪個按鈕按下,并實現(xiàn)對應(yīng)的功能
public void actionPerformed(ActionEvent e) { //獲取按鈕上的內(nèi)容 String name=e.getActionCommand(); System.out.println("點擊按鈕:"+name); if(name.equals("開始")){ jf.removeMouseListener(this); jf.addMouseListener(this); startflag=false; flag=false; gameOver=false; for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ arrChess[i][j]=0; } } jf.paint(gr); }else if(name.equals("悔棋")){ arrChess[nowx][nowy]=0; jf.paint(gr); this.flag=!this.flag; } else if(name.equals("認(rèn)輸")){ if(this.flag){ gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("黑方獲勝!",300,300); }else{ gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("白方獲勝!",300,300); } }else if(name.equals("人機對戰(zhàn)")){ jf.removeMouseListener(this); jf.addMouseListener(this); startflag=false; flag=false; gameOver=false; for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ arrChess[i][j]=0; } } this.aiflag=true; jf.paint(gr); } }
3、游戲效果是通過畫筆畫在組件上實現(xiàn)的,獲取畫筆:圖形顯示在哪個組件上,畫筆就從該組件上獲取。
注意從窗體上獲取畫筆對象,一定要在窗體顯示可見之后
Graphics g = jf.getGraphics();
4、當(dāng)我們點擊按鈕時,應(yīng)該實現(xiàn)相應(yīng)的功能,為了實現(xiàn)人機交互,需要添加監(jiān)聽器
監(jiān)聽器
a.事件源:當(dāng)前動作所發(fā)生的組件
b.確定監(jiān)聽器方法:鼠標(biāo)監(jiān)聽器方法
c.綁定處理類
給窗口添加鼠標(biāo)監(jiān)聽器方法
注意:由于接口不能創(chuàng)建對象,重新定義類繼承(實現(xiàn))接口,重寫接口中的抽象方法
GameMouse mouse = new GameMouse(); mouse.setJf(jf); jbu.addActionListener(mouse); jbu2.addActionListener(mouse); jbu3.addActionListener(mouse); jbu4.addActionListener(mouse); mouse.setGr(g); //把GameMouse類中的數(shù)組傳遞給MyFrame類 jf.setArrChess(mouse.getArrChess());
二、完成自定義窗口類
單純使用JFrame存在一個問題,就是當(dāng)我們移動窗口或窗口產(chǎn)生改變時,之前畫在窗口組件上的畫面就消失不見了
原因是每當(dāng)窗口組件受到影響或發(fā)生改變時,都會自動的調(diào)用其paint函數(shù),這時候畫面就會被刷新,之前畫的東西就不見了
為了解決這個問題,我們需要自定義一個自己的窗口類MyFrame,繼承JFrame,通過重寫JFrame的paint方法來解決此問題
###自定義窗口類MyFrame
public class MyFrame extends JFrame implements Config{ private int[][] arrChess; public void setArrChess(int[][] arrChess){ this.arrChess = arrChess; } //重寫繪制組件的方法 //super 表示當(dāng)前類的父類對象 //this 表示本類對象 public void paint(Graphics g){ //1.繪制組件 super.paint(g); System.out.println("繪制自定義窗體類!"); //2.繪制棋盤,棋子 g.setColor(Color.black); for(int i=0;i<LINE;i++){ g.drawLine(X0, Y0+i*SIZE, (LINE-1)*SIZE+X0, Y0+i*SIZE); g.drawLine(X0+i*SIZE, Y0, X0+i*SIZE, (LINE-1)*SIZE+Y0); } //遍歷arrChess for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ if(arrChess[i][j]==1){ g.setColor(Color.black); g.fillOval(i*SIZE+25, j*SIZE+25, 50, 50); } else if(arrChess[i][j]==2){ g.setColor(Color.white); g.fillOval(i*SIZE+25, j*SIZE+25, 50, 50); } } } } }
這里通過循環(huán)和drawLine方法繪制出棋盤
arrChess是一個二維數(shù)組存放了當(dāng)前的棋局情況,后面會詳細(xì)說明。此類中通過遍歷此數(shù)組每次重新畫一遍棋局就可以維持游戲畫面的正確。
三、游戲功能實現(xiàn)
1、首先要將棋子黑白交替的畫在棋盤的交點上
首先mouseClicked類傳進的參數(shù)為MouseEvent e
調(diào)用e.getX();e.getY();方法可以得到鼠標(biāo)點擊的位置坐標(biāo)
由于玩家在玩游戲時點擊的位置不一定正好是棋盤交點的位置,但是我們可以簡單的計算出鼠標(biāo)點擊位置最近的焦點,只需要計算出這個位置最近的
交點是第幾行和第幾列就可以了
用當(dāng)前坐標(biāo)x-原點X0 對棋子的大小SIZE取余,如果這個值大于棋子大小SIZE/2 則判斷橫坐標(biāo)xx=(x-X0)/SIZE+1
否則判斷橫坐標(biāo)xx=(x-X0)/SIZE
y坐標(biāo)同理 代碼如下
public void mouseClicked(MouseEvent e){ System.out.println(this.aiflag); this.flag=!flag; System.out.println("點擊"); //獲取當(dāng)前坐標(biāo) int x = e.getX(); int y = e.getY(); //計算交點值 if((x-X0)%SIZE > SIZE/2 ){ xx = (x-X0)/SIZE+1; }else{ xx = (x-X0)/SIZE; } if((y-Y0)%SIZE > SIZE/2 ){ yy = (y-Y0)/SIZE+1; }else{ yy = (y-Y0)/SIZE; } }
2、交替畫黑白棋
交替畫黑白棋只需要加入一個flag標(biāo)志,每次判斷這個標(biāo)志
如果flag為true則畫黑棋,將數(shù)組賦值為1
如果flag為false則畫白棋,將數(shù)組賦值為2
每次畫完棋后,將flag取反
代碼如下
if (arrChess[xx][yy] == 0) { if (this.flag == true) arrChess[xx][yy] = 1; else arrChess[xx][yy] = 2; if (this.flag) gr.setColor(Color.black); else gr.setColor(Color.white); gr.fillOval(xx * SIZE + 25, yy * SIZE + 25, 50, 50); } else { this.flag = !this.flag; }
3、判斷輸贏方法的實現(xiàn)
判斷輸贏通過遍歷數(shù)組實現(xiàn),在上述講解中我們知道,整個棋局的情況都存儲在二維數(shù)組arrChess[][]中
若arrChess[3][5]=1,就說明第三行第五列這個位置上為黑棋
所以通過遍歷這個數(shù)組我們就可以掌握整個棋局從而對輸贏做出判斷
五子棋在橫著、豎著、斜著三個方向任意一個方向上連成五個相同顏色的棋子都可以獲勝
這里將三個方向分為四種情況判斷:
第一種情況:棋子橫向連珠的數(shù)目,也就是一個棋子向左找,向右找,和自己顏色相同的數(shù)目和
第二種情況:棋子縱向連珠的數(shù)目,一個棋子向上找和向下找,和自己顏色相同的數(shù)目和
第三種情況:棋子正對角線連珠的數(shù)目,一個棋子向右上方找、左下方找和自己顏色相同的數(shù)目和
第四種情況:棋子反對角線連珠的數(shù)目,一個棋子向左上方找、右下方找和自己顏色相同的數(shù)目和
第一種情況:棋子橫向連珠的數(shù)目,也就是一個棋子向左找,向右找,和自己顏色相同的數(shù)目和
代碼如下
int sum = 0; for (int i = xx; i > 0; i--) { if (arrChess[i][yy] == 1) sum++; else if (arrChess[i][yy] == 2||arrChess[i][yy]==0) break; } for (int i = xx + 1; i < 15; i++) { if (arrChess[i][yy] == 1) sum++; else if (arrChess[i][yy] == 2||arrChess[i][yy]==0) break; } if (sum >= 5) { System.out.println("black win!"); gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("黑方獲勝!",300,300); gameOver = true; }
第二種情況:棋子縱向連珠的數(shù)目,一個棋子向上找和向下找,和自己顏色相同的數(shù)目和
代碼如下
sum = 0; for (int i = yy; i > 0; i--) { if (arrChess[xx][i] == 1) sum++; else if (arrChess[xx][i] == 2||arrChess[xx][i] == 0) break; } for (int i = yy + 1; i < 15; i++) { if (arrChess[xx][i] == 1) sum++; else if (arrChess[xx][i] == 2||arrChess[xx][i] == 0) break; } if (sum >= 5) { System.out.println("black win!"); gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("黑方獲勝!",300,300); gameOver = true; }
第三種情況:棋子正對角線連珠的數(shù)目,一個棋子向右上方找、左下方找和自己顏色相同的數(shù)目和
if (!gameOver) { sum = 1; int temp = min(5, xx); temp = min(temp, yy); for (int i = 1; i < temp; i++) { if (arrChess[xx - i][yy - i] == 1) sum++; else if (arrChess[xx - i][yy - i] == 2||arrChess[xx - i][yy - i] == 0) break; } temp = min(5, 15 - xx); temp = min(temp, 15 - yy); for (int i = 1; i < temp; i++) { if (arrChess[xx + i][yy + i] == 1) sum++; else if (arrChess[xx + i][yy + i] == 2||arrChess[xx + i][yy + i] == 0) break; } if (sum >= 5) { System.out.println("black win!"); gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("黑方獲勝!",300,300); gameOver = true; } }
第四種情況:棋子反對角線連珠的數(shù)目,一個棋子向左上方找、右下方找和自己顏色相同的數(shù)目和
if (!gameOver) { sum = 1; int temp = min(5, 15 - xx); temp = min(temp, yy); for (int i = 1; i < temp; i++) { if (arrChess[xx + i][yy - i] == 1) sum++; else if (arrChess[xx + i][yy - i] == 2||arrChess[xx + i][yy - i] == 0) break; } temp = min(5, xx); temp = min(temp, 15 - yy); for (int i = 1; i < temp; i++) { if (arrChess[xx - i][yy + i] == 1) sum++; else if (arrChess[xx - i][yy + i] == 2||arrChess[xx - i][yy + i] == 0) break; } if (sum >= 5) { System.out.println("black win!"); gr.setColor(Color.red); gr.setFont(new Font("TimesRoman", Font.BOLD, 50)); gr.drawString("黑方獲勝!",300,300); gameOver = true; } } }
上面展示了黑棋獲勝的判斷方法 白棋獲勝的判斷方法同理
4、AI電腦下棋 實現(xiàn)人機對戰(zhàn)
人機對戰(zhàn)使用權(quán)值算法來實現(xiàn),通過對棋局情況的判斷,自己設(shè)定好每種情況權(quán)值的大小
電腦以防守為主
權(quán)值設(shè)置如下
public void setHm() { this.hm.put("1",40); this.hm.put("11",400); this.hm.put("111",4000); this.hm.put("1111",6000); this.hm.put("12",40); this.hm.put("112",400); this.hm.put("1112",4000); this.hm.put("11112",6000); this.hm.put("2",20); this.hm.put("22",200); this.hm.put("222",2000); this.hm.put("2222",8000); this.hm.put("21",10); this.hm.put("221",100); this.hm.put("2221",1000); this.hm.put("22221",2000); }
簡單解釋一下 1112代表三個黑棋一個白棋 權(quán)值為4000
而11112代表四個黑棋和一個白棋 這種情況顯然更危險,權(quán)值設(shè)置為6000
人機下棋的思路主要是,遍歷棋盤上每個空位置周圍的棋局情況
因為機器可以選擇任何一個空位置下棋
每個空位置周圍的棋局情況一定對應(yīng)已經(jīng)設(shè)置好的權(quán)值中的一個,如果一個位置周圍有多個權(quán)值對應(yīng)
則把他們權(quán)值相加綜合判斷
當(dāng)確定好每個空位置的權(quán)值后,選擇權(quán)值最大的也就是最危險的位置下棋,已實現(xiàn)防守
ai算法的時候和使用到了前面判斷輸贏的方法,總體原理和之前判斷輸贏相同
只不過多了一步計算權(quán)值
權(quán)值表存儲在HashMap中,遍歷的過程將每個空位周圍有棋子的情況用字符串存儲起來
最后在通過從HashMap中查找對應(yīng)的權(quán)值來找到該位置的權(quán)值 Integer value=hm.get(s);
實現(xiàn)代碼如下
public void ai(){ for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ chessValue[i][j]=0; } } this.setHm(); for (int i = 0; i <15 ; i++) { for(int j=0;j<15;j++){ //向下 if(arrChess[i][j]==0){ String s=""; int c=0; for(int k=j+1;k<15;k++){ if(arrChess[i][k]==0){ break; }else{ if(c==0){ c=arrChess[i][k]; s+=arrChess[i][k]; }else if(arrChess[i][k]==c){ s+=arrChess[i][k]; }else{ s+=arrChess[i][k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //向上 if(arrChess[i][j]==0){ String s=""; int c=0; for(int k=j-1;k>0;k--){ if(arrChess[i][k]==0){ break; }else{ if(c==0){ c=arrChess[i][k]; s+=arrChess[i][k]; }else if(arrChess[i][k]==c){ s+=arrChess[i][k]; }else{ s+=arrChess[i][k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //向左 if(arrChess[i][j]==0){ String s=""; int c=0; for(int k=i-1;k>0;k--){ if(arrChess[k][j]==0){ break; }else{ if(c==0){ c=arrChess[k][j]; s+=arrChess[k][j]; }else if(arrChess[k][j]==c){ s+=arrChess[k][j]; }else{ s+=arrChess[k][j]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //向右 if(arrChess[i][j]==0){ String s=""; int c=0; for(int k=i+1;k<15;k++){ if(arrChess[k][j]==0){ break; }else{ if(c==0){ c=arrChess[k][j]; s+=arrChess[k][j]; }else if(arrChess[k][j]==c){ s+=arrChess[k][j]; }else{ s+=arrChess[k][j]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //對角線1-- if(arrChess[i][j]==0){ int temp=100; temp=min(temp,i); temp=min(temp,j); String s=""; int c=0; for(int k=1;k<=temp;k++){ if(arrChess[i-k][j-k]==0){ break; }else{ if(c==0){ c=arrChess[i-k][j-k]; s+=arrChess[i-k][j-k]; }else if(arrChess[i-k][j-k]==c){ s+=arrChess[i-k][j-k]; }else{ s+=arrChess[i-k][j-k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //對角線2 -- ++ if(arrChess[i][j]==0){ int temp=100; temp=min(temp,14-i); temp=min(temp,14-j); String s=""; int c=0; for(int k=1;k<=temp;k++){ if(arrChess[i+k][j+k]==0){ break; }else{ if(c==0){ c=arrChess[i+k][j+k]; s+=arrChess[i+k][j+k]; }else if(arrChess[i+k][j+k]==c){ s+=arrChess[i+k][j+k]; }else{ s+=arrChess[i+k][j+k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //對角線2 -- ++ +- if(arrChess[i][j]==0){ int temp=100; temp=min(temp,14-i); temp=min(temp,j); String s=""; int c=0; for(int k=1;k<=temp;k++){ if(arrChess[i+k][j-k]==0){ break; }else{ if(c==0){ c=arrChess[i+k][j-k]; s+=arrChess[i+k][j-k]; }else if(arrChess[i+k][j-k]==c){ s+=arrChess[i+k][j-k]; }else{ s+=arrChess[i+k][j-k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } //對角線2 -- ++ +- -+ if(arrChess[i][j]==0){ int temp=100; temp=min(temp,i); temp=min(temp,14-j); String s=""; int c=0; for(int k=1;k<=temp;k++){ if(arrChess[i-k][j+k]==0){ break; }else{ if(c==0){ c=arrChess[i-k][j+k]; s+=arrChess[i-k][j+k]; }else if(arrChess[i-k][j+k]==c){ s+=arrChess[i-k][j+k]; }else{ s+=arrChess[i-k][j+k]; break; } } } Integer value=hm.get(s); if(value!=null){ chessValue[i][j]+=value; } } } } int maxx=-1; int mx=0,my=0; for(int i=0;i<15;i++){ for(int j=0;j<15;j++){ if(chessValue[i][j]>maxx){ maxx=chessValue[i][j]; mx=i; my=j; } } } gr.setColor(Color.white); gr.fillOval(mx*SIZE+25, my*SIZE+25, 50, 50); arrChess[mx][my]=2; Judge2(mx,my); } }
游戲過程如下
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Postman實現(xiàn)傳List<String>集合
這篇文章主要介紹了Postman實現(xiàn)傳List<String>集合方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08SpringBoot2.0解決Long型數(shù)據(jù)轉(zhuǎn)換成json格式時丟失精度問題
這篇文章主要介紹了SpringBoot2.0解決Long型數(shù)據(jù)轉(zhuǎn)換成json格式時丟失精度問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06深入理解 Java、Kotlin、Go 的線程和協(xié)程
這篇文章主要介紹了深入理解 Java、Kotlin、Go 的線程和協(xié)程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12java圖片滑動驗證(登錄驗證)原理與實現(xiàn)方法詳解
這篇文章主要介紹了java圖片滑動驗證(登錄驗證)原理與實現(xiàn)方法,結(jié)合實例形式詳細(xì)分析了java圖片滑動登錄驗證的相關(guān)原理、實現(xiàn)方法與操作技巧,需要的朋友可以參考下2019-09-09