Java初学者练习之五子棋游戏教程(附源码)

1.图形化界面的创建

1.1创建JFrame窗体容器

         1)JFrame窗体需要设置基本的大小、布局、默认的关闭方式,以及最重要的设置可见。

1.2在JFrame上添加组件,用来绘制棋盘棋子和游戏操作。

        1)棋盘棋子的绘制:自定义一个类去继承JPanel,把绘制棋盘和棋子的方法重写进入paint()方法里,这样当窗体发生变化(放大、缩小、移动等操作时,棋盘棋子不会消失,棋局得以保存)。

        2)悔棋、认输等操作通过JButton按钮添加鼠标监听来实现。

2.关键点的实现(使用六个类实现背后逻辑,UI类,DrawChessBoard类,GameMouse类,QiZi类,Location类以及AI类)。

2.1在棋盘点击的位置绘制棋子

        1)给棋盘添加鼠标监听,获取点击位置的坐标

        UI类负责初始化1中的图形化界面,并给DrawChessBoard类添加GameMouse类的监听。这样在鼠标点击棋盘时通过重写GameMouse类的mouseClicked(),就可以获取鼠标在棋盘上点击的像素坐标。

        2)坐标转化成二位数组中的坐标

       保存棋盘上所有棋子位置用到QiZi类中的int[][] memory二维数组,这样把像素坐标转化为二维数组中的坐标,并附上棋子颜色对应的值,就可以保存棋盘上所有棋子的位置。

        3)在棋盘上画出棋子

        在DrawChessBoard类paint()方法中遍历QiZi类中的int[][] memory二维数组非零值,就可以在相应位置调用画笔方法画出黑白棋子。

2.2判断输赢

        1)下完棋子后,将下棋位置保存到QiZi类的int[][] memory二维数组中后,就可以以该点为中心计算其四个方向上连续棋子的数目,达到五个则通过JOptionPane类生成弹窗确定赢家。

2.2悔棋功能和提示最后落子位置功能的实现

        1)每次成功下一颗棋子,就可以创建一个保存了棋子坐标的Location对象,并将该对象添加到 QiZi类的ArrayList或者Stack容器当中,当鼠标点击悔棋Button后,清除QiZi类的int[][] memory二维数组相应位置的数值(将之改为0即可),然后棋盘重绘棋子,就可以完成悔棋的效果。

        2)同时可以找到容器中最后落子的位置,并在棋盘相应的坐标出画出最后落子提示。

2.3开始、认输的实现

        1)开始游戏,即重置游戏,将棋子类的相应属性清零即可,比如int[][] memory二维数组(即棋谱),owener=1(重置为白色),以及清楚棋盘上面的棋子。

        2)认输就可以判断当前QiZi.owner的值,来判断输的一方并给出提示即可。

2.4AI的实现

        1)默认AI为黑方的情况下,需要在白色方落子之后调用AI下黑色棋子,所以在需要在GameMouse中下白棋的if分支中调用AI方法

        2)AI的厉害与否取决于其设计,在这里提供一个思路:设置一个棋型对照表,给不同棋型赋值(如1111,代表白子四连,权重较高),轮到AI时,可以根据该表计算棋盘上每一个空位在八个方向总的权重大小,在权重最大处落子即可。棋型对照表中不同棋的权重设置,可以通过python等分析大量棋局来获取,以此来训练AI,当权重设置越合理,AI就越强。

3其他功能

        下子的动画效果音效等可以通过开辟不同的线程来实现,而网络对战则可增加网络通信相关模块即可。

4源码

package wuziqi925;

import javax.swing.*;
import java.awt.*;

public class GameUI {
    public static void main(String[] args) {
        GameUI gameUI=new GameUI();
        gameUI.showUI();
    }

    public void showUI(){
        //创建棋子对象
        QiZi qizi=new QiZi();
        //获取窗体
        JFrame jFrame=new JFrame();
        jFrame.setSize(1000,795);
        jFrame.setDefaultCloseOperation(3);
        jFrame.setLocationRelativeTo(null);
        jFrame.setLayout(null);
        jFrame.setTitle("五子棋");
        jFrame.setResizable(false);

        //窗体添加棋盘面板
        DrawChessBoard chessBoard=new DrawChessBoard(qizi);
        jFrame.add(chessBoard);
        chessBoard.setSize(760,760);
        chessBoard.setBackground(Color.ORANGE);


        //测试JFrame框架像素大小,Insets[top=32,left=3,bottom=3,right=3]
        //System.out.println(jFrame.getInsets());

        //窗体添加选项面板,用来画棋盘
        JPanel bp=new JPanel();
        bp.setSize(236,760);
        bp.setBackground(Color.lightGray);
        bp.setLocation(760,0);
        bp.setLayout(null);
        jFrame.add(bp);


        //选项面板添加按钮
        JButton start=new JButton("开始");
        start.setBackground(Color.white);
        start.setFont(new Font("华文行楷",Font.BOLD,20));
        start.setBounds(40,350,150,50);

        JButton quit=new JButton("认输");
        quit.setBackground(Color.white);
        quit.setFont(new Font("华文行楷",Font.BOLD,20));
        quit.setBounds(40,440,150,50);

        JButton undo=new JButton("悔棋");
        undo.setBackground(Color.white);
        undo.setFont(new Font("华文行楷",Font.BOLD,20));
        undo.setBounds(40,530,150,50);

        bp.add(start);
        bp.add(quit);
        bp.add(undo);

        //选择模式选项
        ButtonGroup bg=new ButtonGroup();
        JRadioButton rrdz=new JRadioButton("玩家对战");
        JRadioButton rjdz=new JRadioButton("人机对战");
        rrdz.setSize(120,30);
        rrdz.setLocation(55,60);
        rrdz.setFont(new Font("华文行楷",Font.BOLD,20));
        rrdz.setVisible(true);
        rjdz.setSize(120,30);
        rjdz.setLocation(55,90);
        rjdz.setFont(new Font("华文行楷",Font.BOLD,20));
        rjdz.setVisible(true);
        bg.add(rjdz);
        bg.add(rrdz);
        bp.add(rjdz);
        bp.add(rrdz);
        bp.setVisible(true);

        //设置窗体可见
        jFrame.setVisible(true);


        AI ai=new AI(qizi,chessBoard);
        //获取棋盘的鼠标监听和画笔并将该画笔添加给鼠标
        Graphics g1=chessBoard.getGraphics();
        GameMouse gameMouse=new GameMouse(qizi,chessBoard,ai);

        chessBoard.addMouseListener(gameMouse);
        start.addActionListener(gameMouse);
        quit.addActionListener(gameMouse);
        undo.addActionListener(gameMouse);
        rrdz.addActionListener(gameMouse);
        rjdz.addActionListener(gameMouse);
    }
}

package wuziqi925;

import javax.swing.*;
import java.awt.*;


public class DrawChessBoard extends JPanel {
    QiZi qiZi;
    private static int LINE_NUM = 15;
    private static int MARGIN_WIDTH = 30;
    public static int CELL_SIZE = 50;

    public DrawChessBoard(QiZi qiZi) {
        this.qiZi = qiZi;
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //画棋盘
        for (int i = 0; i < LINE_NUM; i++) {
            g.drawLine(MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE, MARGIN_WIDTH + i * CELL_SIZE);
            g.drawLine(MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH, MARGIN_WIDTH + i * CELL_SIZE, MARGIN_WIDTH + (LINE_NUM - 1) * CELL_SIZE);
        }
        //画棋盘上的点
        g.fillOval(CELL_SIZE*3+22,CELL_SIZE*3+22,16,16);
        g.fillOval(CELL_SIZE*11+22,CELL_SIZE*3+22,16,16);
        g.fillOval(CELL_SIZE*3+22,CELL_SIZE*11+22,16,16);
        g.fillOval(CELL_SIZE*11+22,CELL_SIZE*11+22,16,16);
        //画棋子
        int[][] a = qiZi.memory;
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[i].length; j++) {
                if (a[i][j] == 1)    {
                    g.setColor(Color.white);
                    g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46);
                } else if (a[i][j] == 2) {
                    g.setColor(Color.black);
                    g.fillOval(CELL_SIZE * i + 7, CELL_SIZE * j + 7, 46, 46);
                }
            }
        }
        //画出最后一步棋子的位置上的十字架
        if (qiZi.arr.size() > 0) {
            Graphics2D g1=(Graphics2D)g;
            g1.setColor(Color.red);
            g1.setStroke(new BasicStroke(3.0f));

            Location l = qiZi.arr.get(qiZi.arr.size() - 1);
            g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH - 8, CELL_SIZE * l.y + MARGIN_WIDTH, CELL_SIZE * l.x + MARGIN_WIDTH + 8, CELL_SIZE * l.y + MARGIN_WIDTH);
            g1.drawLine(CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH - 8, CELL_SIZE * l.x + MARGIN_WIDTH, CELL_SIZE * l.y + MARGIN_WIDTH + 8);
        }
    }
}

package wuziqi925;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class GameMouse implements MouseListener, ActionListener {
    QiZi qizi;
    DrawChessBoard drawChessBoard;
    AI ai;

    public GameMouse() {
    }

    public GameMouse( QiZi qiZi, DrawChessBoard drawChessBoard,AI ai) {
        this.qizi = qiZi;
        this.drawChessBoard=drawChessBoard;
        this.ai=ai;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        int cellSize= DrawChessBoard.CELL_SIZE;
        int x=e.getX();
        int xx=0;
        int y=e.getY();
        int yy=0;

        //在点击位置画棋子,并将下子位置转换成棋子在二维数组中的坐标
        if (qizi.owner==1) {
            if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) {
                xx=(x - 30) / cellSize + 1;
                yy=(y - 30) / cellSize + 1;
            } else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) {
                xx=(x - 30) / cellSize + 1;
                yy=(y - 30) / cellSize;
            } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) {
                xx=(x - 30) / cellSize;
                yy=(y - 30) / cellSize + 1;
            } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) {
                xx=(x - 30) / cellSize;
                yy=(y - 30) / cellSize;
            }
            if(qizi.memory[xx][yy]==0) {//判断无子
                qizi.x=xx;
                qizi.y=yy;
                qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子并将棋子放入容器
                Location location=new Location(qizi);//记录刚下的棋子位置顺序
                qizi.arr.add(location);
                drawChessBoard.repaint();//绘制刚下的棋子
                qizi.judgeWinner();//判断输赢

                qizi.owner=2;//交换棋权
            }
            if (ai.state){
                ai.initiateAI();
            }

        }else {
            qizi.owner=2;
            if ((x - 30) % cellSize > 25 & (y - 30) % cellSize > 25) {
                xx=(x - 30) / cellSize + 1;
                yy=(y - 30) / cellSize + 1;
            } else if ((x - 30) % cellSize > 25 & (y - 30) % cellSize < 25) {
                xx=(x - 30) / cellSize + 1;
                yy=(y - 30) / cellSize;
            } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize > 25) {
                xx=(x - 30) / cellSize;
                yy=(y - 30) / cellSize + 1;
            } else if ((x - 30) % cellSize < 25 & (y - 30) % cellSize < 25) {
                xx=(x - 30) / cellSize;
                yy=(y - 30) / cellSize;
            }
            if(qizi.memory[xx][yy]==0) {
                qizi.x=xx;
                qizi.y=yy;
                qizi.memory[qizi.x][qizi.y] = qizi.owner;//下棋子并将棋子放入容器
                Location location=new Location(qizi);//记录刚下的棋子位置顺序
                qizi.arr.add(location);
                drawChessBoard.repaint();//绘制刚下的棋子
                qizi.judgeWinner();//判断输赢

                qizi.owner=1;//交换棋权
            }
        }
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        int cellSize= DrawChessBoard.CELL_SIZE;
        String s=e.getActionCommand();
        JOptionPane tc=new JOptionPane();

        //悔棋功能的实现
        if (s.equals("悔棋")){
            if (qizi.arr.size()>0) {
                Location l = qizi.arr.get(qizi.arr.size() - 1);
                qizi.memory[l.x][l.y] = 0;
                qizi.arr.remove(qizi.arr.size() - 1);

                if (qizi.owner == 1) {
                    qizi.owner = 2;
                } else if (qizi.owner == 2) {
                    qizi.owner = 1;
                }
            }else {
                tc.showMessageDialog(null,"无棋可毁,请下棋!");
            }
            //刷新棋盘
            drawChessBoard.repaint();
        }

        if (s.equals("开始")){
            qizi.owner=1;
            qizi.memory=new int[15][15];
            qizi.arr.clear();
            qizi.win=false;
            drawChessBoard.repaint();
        }

        if(s.equals("认输")){
            int whiteCount=0;
            int blackCount=0;
            for (int i = 0; i < qizi.memory.length; i++) {
                for (int j = 0; j < qizi.memory[i].length; j++) {
                    if (qizi.memory[i][j]==1){
                        whiteCount++;
                    }else if (qizi.memory[i][j]==2){
                        blackCount++;
                    }
                }
            }
            if (whiteCount==blackCount){
                tc.showMessageDialog(null,qizi.owner==1 ?"黑方投降,白方胜!":"白方投降,黑方胜!");
            }else if(whiteCount>blackCount){
                tc.showMessageDialog(null,"黑方投降,白方胜!");
            }else {
                tc.showMessageDialog(null,"白方投降,黑方胜!");
            }
        }
        if (s.equals("人机对战")){
            ai.state=true;

        }
        if (s.equals("玩家对战")){
            ai.state=false;

        }
    }

    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }


}
package wuziqi925;

import javax.swing.*;
import java.util.ArrayList;

public class QiZi {
    int x;
    int y;
    int owner=1;//1 代表白色,2代表黑色
    int[][] memory;
    ArrayList arr;
    boolean win=false;

    public QiZi() {
        memory=new int[15][15];
        arr=new ArrayList<>(50);
    }

    public void judgeWinner() {
        JOptionPane tc=new JOptionPane();
        int count1=0;
        int count2=0;
        int count3=0;
        int count4=0;

        //竖直方向检测
        for (int i = y-1; i >-1 ; i--) {
            if (memory[x][i]==owner){
                count1++;
            }else {
                break;
            }
        }
        for (int i = y+1; i <15; i++) {
            if (memory[x][i]==owner){
                count1++;
            }else {
                break;
            }
        }
        if (count1 > 3){
            tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
            win=true;
            return;
        }

        //水平方向检测
        for (int i = x-1; i >-1 ; i--) {
            if (memory[i][y]==owner){
                count2++;
            }else {
                break;
            }
        }
        for (int i = x+1; i <15; i++) {
            if (memory[i][y]==owner){
                count2++;
            }else {
                break;
            }
        }
        if (count2 > 3){
            tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
            win=true;
            return;
        }

        //在\方向上检测
        int yy=y;
        for (int i = x+1; i <15; i++) {
            if(yy==14){
                break;
            }
            yy++;
            if (memory[i][yy]==owner){
                count3++;
            }else {
                break;
            }
        }
        yy=y;
        for (int i = x-1; i >-1; i--) {
            if (yy==0){
                break;
            }
            yy--;
            if (memory[i][yy]==owner){
                count3++;
            }else {
                break;
            }

        }
        if (count3 > 3){
            tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
            win=true;
            return;
        }

        //在/方向上检测
        yy=y;
        for (int i = x+1; i <15; i++) {
            if(yy==0){
                break;
            }
            yy--;
            if (memory[i][yy]==owner){
                count4++;
            }else {
                break;
            }
        }
        yy=y;
        for (int i = x-1; i >-1; i--) {
            if(yy==14){
                break;
            }
            yy++;
            if (memory[i][yy]==owner){
                count4++;
            }else {
                break;
            }
        }
        if (count4 > 3){
            tc.showMessageDialog(null,owner==1?"白方胜":"黑方胜");
            win=true;
            return;
        }
    }
}

package wuziqi925;

public class Location {
    QiZi qiZi;
    int x;
    int y;

    public Location(QiZi qiZi) {
        //记录棋谱
        x=qiZi.x;
        y=qiZi.y;
    }

    public Location(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

package wuziqi925;

import java.util.*;

public class AI {
    boolean state=false;//true为on false为off
    QiZi qiZi;
    //存储棋型权值
    private HashMap playValueTable=new HashMap<>();
    //存储每个可下点的权重大小
    private HashMap locationsAndValues=new HashMap<>();
    DrawChessBoard drawChessBoard;
    int AIDO=0;

    public AI(QiZi qiZi,DrawChessBoard drawChessBoard){
        this.drawChessBoard=drawChessBoard;
        this.qiZi=qiZi;
        //1代表该方向为白方棋子(玩家),2代表该方向为黑方棋子(AI)
        playValueTable.put("22221",100);
        playValueTable.put("2222",100);
        playValueTable.put("11112",99);
        playValueTable.put("1111",0);

        playValueTable.put("2221",40);
        playValueTable.put("222",45);
        playValueTable.put("1112",35);
        playValueTable.put("111",46);

        playValueTable.put("22",25);
        playValueTable.put("221",20);
        playValueTable.put("11",15);
        playValueTable.put("112",10);

        playValueTable.put("21",5);
        playValueTable.put("2",10);
        playValueTable.put("1",8);
        playValueTable.put("12",2);
    }

    public void initiateAI(){
        if (qiZi.win){
            return;
        }
        int chessValue=0;
        //遍历棋盘找到空点
        for (int i = 0; i  locationsAndValues进行排序找到最大值
        Set> set=locationsAndValues.entrySet();
        List> list=new ArrayList<>(set);
        list.sort(new Comparator>() {
            @Override
            public int compare(Map.Entry o1, Map.Entry o2) {
                return o2.getValue()-o1.getValue();
            }
        });
        //排序完毕取最大//获取最大权重值对应的空点坐标
        Map.Entry maxSet=list.get(0);
        Location toDo=maxSet.getKey();

        qiZi.x= toDo.x;
        qiZi.y= toDo.y;
        qiZi.memory[qiZi.x][qiZi.y]=qiZi.owner;
        Location location=new Location(qiZi);//记录刚下的棋子位置顺序
        qiZi.arr.add(location);
        drawChessBoard.repaint();//绘制刚下的棋子
        qiZi.judgeWinner();//判断输赢
        qiZi.owner=1;//交换棋权
        System.out.println(++AIDO);
        locationsAndValues.clear();
    }

    private int countValue(int i,int j) {
        int totalValue=0;
        StringBuilder s1=new StringBuilder();
        StringBuilder s2=new StringBuilder();
        StringBuilder s3=new StringBuilder();
        StringBuilder s4=new StringBuilder();
        StringBuilder s5=new StringBuilder();
        StringBuilder s6=new StringBuilder();
        StringBuilder s7=new StringBuilder();
        StringBuilder s8=new StringBuilder();
        //八个方向去去判定
        //North
        for (int k = j-1; k >-1 ; k--) {
            if (qiZi.memory[i][k]==1){
                s1.append(1);
            }else if (qiZi.memory[i][k]==2) {
                s1.append(2);
            }else {
                break;
            }
        }
        int count1=playValueTable.get(s1.toString())==null?0:playValueTable.get(s1.toString());
        totalValue+=count1;

        //South
        for (int k = j+1; k <15; k++) {
            if (qiZi.memory[i][k]==1){
                s2.append(1);
            }else if (qiZi.memory[i][k]==2) {
                s2.append(2);
            }else {
                break;
            }
        }
        int count2=playValueTable.get(s2.toString())==null?0:playValueTable.get(s2.toString());
        totalValue+=count2;

        //West
        for (int k = i-1; k >-1 ; k--) {
            if (qiZi.memory[k][j]==1){
                s3.append(1);
            }else if (qiZi.memory[k][j]==2) {
                s3.append(2);
            }else {
                break;
            }
        }
        int count3=playValueTable.get(s3.toString())==null?0:playValueTable.get(s3.toString());
        totalValue+=count3;

        //East
        for (int k = i+1; k <15; k++) {
            if (qiZi.memory[k][j]==1){
                s4.append(1);
            }else if (qiZi.memory[k][j]==2) {
                s4.append(2);
            }else {
                break;
            }
        }
        int count4=playValueTable.get(s4.toString())==null?0:playValueTable.get(s4.toString());
        totalValue+=count4;

        //SE
        int yy=j;
        for (int k = i+1; k < 15; k++) {
            if(yy==14){
                break;
            }
            yy++;
            if (qiZi.memory[k][yy]==1){
                s5.append(1);
            }else if (qiZi.memory[k][yy]==2) {
                s5.append(2);
            }else {
                break;
            }
        }
        int count5=playValueTable.get(s5.toString())==null?0:playValueTable.get(s5.toString());
        totalValue+=count5;

        //NW
        yy=j;
        for (int k = i-1; k >-1; k--) {
            if(yy==0){
                break;
            }
            yy--;
            if (qiZi.memory[k][yy]==1){
                s6.append(1);
            }else if (qiZi.memory[k][yy]==2) {
                s6.append(2);
            }else {
                break;
            }
        }
        int count6=playValueTable.get(s6.toString())==null?0:playValueTable.get(s6.toString());
        totalValue+=count6;

        //NE
        yy=j;
        for (int k = i+1; k <15; k++) {
            if(yy==0){
                break;
            }
            yy--;
            if (qiZi.memory[k][yy]==1){
                s7.append(1);
            }else if (qiZi.memory[k][yy]==2) {
                s7.append(2);
            }else {
                break;
            }
        }
        int count7=playValueTable.get(s7.toString())==null?0:playValueTable.get(s7.toString());
        totalValue+=count6;
        //SW
        yy=j;
        for (int k = i-1; k >-1; k--) {
            if(yy==14){
                break;
            }
            yy++;
            if (qiZi.memory[k][yy]==1){
                s8.append(1);
            }else if (qiZi.memory[k][yy]==2) {
                s8.append(2);
            }else {
                break;
            }
        }
        int count8=playValueTable.get(s8.toString())==null?0:playValueTable.get(s8.toString());
        totalValue+=count8;
        return totalValue;
    }
}

你可能感兴趣的:(java,游戏)