摘要:由于二維數組中代表黑棋,代表白棋,,的數字集合即可代表棋盤上的棋子情況,可以對整個棋盤數組進行遍歷,得到每一個可以下的位置的八個方向的棋子情況。
java基本入門之后,迎來第一個挑戰——五子棋設計
寒假的時候,靠著看java手冊,實現了雙人對戰并判斷輸贏的功能。但是一直卡在了人機對戰上面。
之后隨著學習的深入,終于實現。
以下詳細的敘述一下整體的設計過程:
首先是五子棋窗口界面的設計,畫窗體,加按鈕,這些都比較基礎,主要是要重寫重繪的方法,否則每次改變窗體都會使其變化。
package wuziqi; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class FiveChessUI extends JPanel implements Config { /** *五子棋界面 */ private static final long serialVersionUID = 1L; private static int[][] chesses=new int[ROWS][COLUMNS]; public static void main(String[] args) { FiveChessUI fcUI = new FiveChessUI(); fcUI.initUI(); } public void initUI () { JFrame jf = new JFrame(); jf.setTitle("五子棋v0.5 by xzw"); jf.setSize(700,650); jf.setLocationRelativeTo(null); jf.add(this,BorderLayout.CENTER); this.setBackground(Color.ORANGE); jf.setDefaultCloseOperation(3); this.setLayout(new FlowLayout()); JPanel jp2 = new JPanel(); jp2.setPreferredSize(new Dimension(100, 0)); jp2.setBackground(Color.CYAN); // JButton jbuStart = new JButton("開始"); // jbuStart.setPreferredSize(new Dimension(70, 70)); JButton jbuReg = new JButton("悔棋"); jbuReg.setPreferredSize(new Dimension(70, 50)); JButton jbuR = new JButton("重來"); jbuR.setPreferredSize(new Dimension(70, 50)); JButton jbup2p = new JButton("雙人"); jbup2p.setPreferredSize(new Dimension(70, 70)); JButton jbup2c = new JButton("人機"); jbup2c.setPreferredSize(new Dimension(70, 70)); JLabel la2 = new JLabel("當前執子:black"); jp2.add(jbup2p); jp2.add(jbup2c); jp2.add(jbuReg); jp2.add(jbuR); jp2.add(la2); jf.add(jp2,BorderLayout.EAST); jf.setVisible(true); Graphics g = this.getGraphics(); ChessListener e = new ChessListener(g,chesses,this); jbup2p.addActionListener(e); jbup2c.addActionListener(e); jbuReg.addActionListener(e); jbuR.addActionListener(e); } /** * 重寫重繪方法 */ public void paint(Graphics g){ //調用父類的重繪窗體 super.paint(g); //重繪窗體的同時繪制棋盤和棋子 drawChessTable(g); drawChesses(g); } //畫棋盤 public void drawChessTable (Graphics g){ g.setColor(Color.BLACK); for (int i=0;i其次是界面鼠標監聽器的設計,窗體被分為2個界面,左邊的界面作為棋盤,右邊的界面用作放按鈕,模式,悔棋,重來等按鈕都放在上面。
在做這里的時候意識到自己還不會傳參,因此要在不同類里對同一個對象進行修改就會很困難。于是我向請教學會了傳參的方法,即構造函數傳參法。在一個類的構造函數的參數里添加需要傳遞的參數,并聲明this.var=var(var即為參數)然后在另一個類里調用這個類的方法時,聲明對象時加入要傳的參數,即實現了參數傳遞。
通過繼承mouseAdapter類,重寫該類的mouseReleased的方法,實現鼠標點擊后畫一個棋子。(重寫方法時要求名稱相同,參數相同,返回值相同)同時,要想做到在鼠標點擊的附近的交叉點上畫棋子,需要先計算出鼠標點擊處的行列坐標。
定義一與棋盤相同大小的二維數組,用來存放棋子,每在一個交叉點下一顆棋子,就在數組相應位置將其置為1或2,便于之后的判斷輸贏以及人機算法的實現。另加一個變量判斷所下棋子的黑白。
public ChessListener(Graphics g, int[][] chesses,JPanel jp) { this.g = g; this.chesses = chesses; this.jp=jp; a = new Check(chesses); } int flag=0; int r=0,c=0; public void mouseReleased(MouseEvent e) { // 得到鼠標事件發生的時候光標的位置 int x = e.getX(); int y = e.getY(); r = (x - X0 + CHESS_SIZE / 2) / CHESS_SIZE; c = (y - Y0 + CHESS_SIZE / 2) / CHESS_SIZE; if (chesses[r][c] == 0) { if (count == 0) { chesses[r][c] = 1; g.setColor(Color.BLACK); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count++; } else if (count == 1) { chesses[r][c] = 2; g.setColor(Color.WHITE); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count--; } } if (a.validateChess(r, c)) { if (count == 0) { JOptionPane.showMessageDialog(null, "WhiteWin"); jp.removeMouseListener(this); } else { JOptionPane.showMessageDialog(null, "BlackWin"); jp.removeMouseListener(this); } } }然后就是五子棋判斷輸贏的方法了。原理很簡單,只要對每次下的子的四個方向判斷是否有5個子連在一起即可。只要中間有一個顏色不同,立即break。
幾天后發現,這里的判斷輸贏的方法有些不合理,斜向的判斷方法有問題。有時會出現斜向4個棋子就被判贏了,經過
觀察發現,斜向的兩個方向我把合在了一起。所以就會出現4+1=5,判斷贏的情況。
改進后代碼如下:package wuziqi; public class Check { private int[][] chesses; public Check(int[][] chesses) { this.chesses = chesses; } public boolean validateChess(int x, int y) { boolean state = false; if (checkRow(x, y) == 5||checkColumn(x,y)==5||checkDiagonal1(x,y)==5||checkDiagonal2(x,y)==5) state = true; // System.out.print(chesses.length); return state; } public int checkRow(int x,int y) { int count=0; for (int i=x+1;i=0;i--) {//go left if (chesses[i][y]==chesses[x][y]) { count++; } else break; } return count; } public int checkColumn(int x,int y) { int count=0; for (int i=y+1;i =0;i--) {//go up if (chesses[x][i]==chesses[x][y]) { count++; } else break; } return count; } public int checkDiagonal1(int x,int y) { int count=1; for(inti=1;x+i =0&&y-i>=0;i++) {//go left up if (chesses[x-i][y-i]==chesses[x][y]) { count++; } else break; } return count; } public int checkDiagonal2(int x,int y) { int count=1; for (int i=1;x+i =0;i++) {//go right up if (chesses[x+i][y-i]==chesses[x][y]) { count++; } else break; } for (int i=1;x-i>=0&&y+i 接下來,就是花費我時間最長的人機算法了。首先要讓電腦實現下棋,而且是在相對正常的位置落子,即要通過判斷棋盤上棋子的形勢。由于二維數組中1代表黑棋,2代表白棋,1,2的數字集合即可代表棋盤上的棋子情況,可以對整個棋盤數組進行遍歷,得到每一個可以下的位置的八個方向的棋子情況。因此可以讓每一種情況對應一種權值,權值越大,說明該位置越危險,最后只需找到權值最大的那個位置,落子,即可。
string來代表棋子情況,數組存權值,建立一個同時存string和int的的hashmap。并預存好每個情況對應的權值。
權值修改:(使電腦更智能)
public ChessAI(int chesses[][], int chessValue[][]) { this.chesses = chesses; this.chessValue = chessValue; //black hm.put("1", 11); hm.put("11", 110); hm.put("111", 1200); hm.put("1111", 11000); hm.put("12", 11); hm.put("112", 110); hm.put("1112", 1100); hm.put("11112", 11000); hm.put("21", 11); hm.put("211", 110); hm.put("2111", 1100); hm.put("21111", 11000); //white hm.put("2", 10); hm.put("22", 100); hm.put("222", 1100); hm.put("2222", 10000); hm.put("221", 100); hm.put("2221", 1000); hm.put("22221", 10000); hm.put("122", 100); hm.put("1222", 1000); hm.put("12222", 10000); } `` 每個位置,八個方向遍歷,存入權值數組。public void AI() { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLUMNS; j++) { if (chesses[i][j] == 0) { String code = ""; color = 0; // 向東 for (int k = i + 1; k < ROWS; k++) { if (chesses[k][j] == 0) {// 為空跳出循環 break; } else { if (color == 0) {// 用color記住開始的地方棋子的顏色 color = chesses[k][j]; code += chesses[k][j]; } else if (chesses[k][j] == color) {// 顏色相同 code += chesses[k][j]; } else {// 顏色不同 code += chesses[k][j]; break; } } } // System.out.print(code); Integer value = hm.get(code); if (value != null) { chessValue[i][j] += value; } // 向西 code = ""; color = 0// south-east code = ""; color = 0; for (int p = 1; i + p < ROWS && j + p < COLUMNS; p++) { if (chesses[i + p][j + p] == 0) { break; } else { if (color == 0) {// 開始的地方棋子的顏色 color = chesses[i + p][j + p]; code += chesses[i + p][j + p]; } else if (chesses[i + p][j + p] == color) { code += chesses[i + p][j + p]; } else { code += chesses[i + p][j + p]; break; } } } value = hm.get(code); if (value != null) { chessValue[i][j] += value; }以及接下來ai監聽器的設計,先由玩家自己下黑子之后,判斷輸贏。再調用ai算法,獲取權值最大的點,然后在該點畫白子,下完之后立即清空權值數組。以便于下次下子時,重新統計。
public void mouseReleased(MouseEvent e) { int x = e.getX(); int y = e.getY(); r = (x - X0 + CHESS_SIZE / 2) / CHESS_SIZE; c = (y - Y0 + CHESS_SIZE / 2) / CHESS_SIZE; if (chesses[r][c] == 0 && count == 0) { chesses[r][c] = 1; g.setColor(Color.BLACK); g.fillOval(X0 + r * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count++; } if (a.validateChess(r, c)) { JOptionPane.showMessageDialog(null, "BlackWin"); jp.removeMouseListener(this); } else { ai.AI(); r1 = ai.getr(chessValue); c1 = ai.getc(chessValue); if (chesses[r1][c1] == 0 && count == 1) { chesses[r1][c1] = 2; g.setColor(Color.WHITE); g.fillOval(X0 + r1 * CHESS_SIZE - CHESS_SIZE / 2, Y0 + c1 * CHESS_SIZE - CHESS_SIZE / 2, CHESS_SIZE, CHESS_SIZE); count--; } if (a.validateChess(r1, c1)) { JOptionPane.showMessageDialog(null, "WhiteWin"); jp.removeMouseListener(this); } } // 電腦下完后清空 for (int i1 = 0; i1 < ROWS; i1++) { for (int j1 = 0; j1 < COLUMNS; j1++) { chessValue[i1][j1] = 0; } } }
至此,五子棋項目設計完成。但實際下的結果電腦有時候下的子還是不太科學,希望之后還能改善讓難度更高些。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70313.html
摘要:簡言之,機器學習是內功,而數據挖掘則是機器學習的一種用途。但本質上是我在學習機器學習方面的實戰項目,所以我想辦法利用機器學習的方面的算法實現。 BetaMeow的起源 前段時間AlphaGo和李世石廣受關注,作為人工智能的腦殘粉,看完比賽后激動不已,因為有一定的機器學習的基礎,便打算擼一個棋類的AI,但我還算有點自知之明,圍棋AI,甚至google打算做得通用AI是做不出的了,所以打算...
摘要:五子棋游戲博客官網示例實現源碼之前一直在用,前幾天看了下的官方文檔,寫了個加強對的理解,歡迎指正。五子棋游戲該模塊實現了五子棋和井字游戲兩個游戲。五子棋游戲只記錄了最近步的數據,步以前的數據不會記錄,故悔棋只可悔步以內的棋。 五子棋游戲 + 博客 demo + React官網示例實現 github 源碼:https://github.com/moshang-xc/react-demo ...
閱讀 2715·2021-11-22 13:52
閱讀 1184·2021-10-14 09:43
閱讀 3640·2019-08-30 15:56
閱讀 2952·2019-08-30 13:22
閱讀 3269·2019-08-30 13:10
閱讀 1563·2019-08-26 13:45
閱讀 1102·2019-08-26 11:47
閱讀 2789·2019-08-23 18:13