五子棋人机对战设计与实践
这款五子棋小游戏是我五六年前在培训学习JAVA的时候花了一整个下午时间即兴写出来的,功能很简单,实现了五子棋的电脑对战,主要就是为了研究下电脑下棋算法,所以其他的功能和细节都没考虑,比如电脑最后的落子点我是直接用图片标示出来的,最好是另建一个线程来闪烁最后落子点,还有智能等级也是可以调节的,偏重于防守和进攻都可以修改代码中权值的设定来实现的,有兴趣的朋友可以在此基础上扩展一下。因为没有写谁先下棋的功能,可以模拟下电脑先下子,只需要你在边角上先下一子就可以了,相比下来,电脑先下的话,电脑赢的几率更大点,一共用到了三张图片,都在附件中。
下面是全部的代码,写了一些注释,希望能对你阅读代码起到一点帮助
Java代码
- import java.awt.*;
- import java.awt.event.*;
- import java.net.URL;
- import javax.swing.*;
- public class GobangGame {
- public static void main(String[] args) {
- GameF game = new GameF();
- game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- game.show();
- }
- }
- class GameF extends JFrame {
- public GameF() {
- Container contentPane = getContentPane();
- final Panel panel = new Panel();
- panel.setBackground(new Color(255, 182, 147));
- contentPane.setBackground(new Color(255, 182, 147));
- contentPane.add(panel);
- setSize(560, 560);
- setTitle("杨雷的五子棋游戏 版本1.0");
- setResizable(false);
- panel.setCursor(new Cursor(Cursor.HAND_CURSOR));
- JMenuBar menuBar=new JMenuBar();
- JMenu menu=new JMenu("选项");
- JMenuItem menuStart=new JMenuItem("开始游戏");
- menuStart.addActionListener(new ActionListener(){
- public void actionPerformed(ActionEvent e){
- panel.ResetGame();
- panel.repaint();
- }
- });
- JMenuItem menuExit =new JMenuItem("退出");
- menuExit.addActionListener(new ActionListener(){
- public void actionPerformed(ActionEvent e){
- System.exit(0);
- }
- });
- menuBar.add(menu);
- menu.add(menuStart);
- menu.add(menuExit);
- this.setJMenuBar(menuBar);
- }
- }
- class Panel extends JPanel {
- private URL blackImgURL = GobangGame.class.getResource("black.gif");
- private ImageIcon black=new ImageIcon(blackImgURL);
- private URL whiteImgURL = GobangGame.class.getResource("white.gif");
- private ImageIcon white=new ImageIcon(whiteImgURL);
- private URL currentImgURL = GobangGame.class.getResource("current.gif");
- private ImageIcon current=new ImageIcon(currentImgURL);
- private int i, j, k, m, n, icount;
- private int[][] board = new int [16][16];
- private boolean[][][] ptable = new boolean[16][16][672];
- private boolean[][][] ctable = new boolean[16][16][672];
- private int[][] cgrades = new int[16][16];
- private int[][] pgrades = new int[16][16];
- private int cgrade,pgrade;
- private int[][] win = new int[2][672];
- private int oldx,oldy;
- private int bout=1;
- private int pcount,ccount;
- private boolean player,computer,over,pwin,cwin,tie,start;
- private int mat,nat,mde,nde;
- public Panel(){
- addMouseListener(new Xiazi());
- this.ResetGame();
- }
- public void paintComponent(Graphics g) {
- super.paintComponent(g);
- for (int i = 0; i < 16; i++)
- for (int j = 0; j < 16; j++){
- g.drawLine(50, 50 + j * 30, 500, 50 + j * 30);
- }
- for (int i = 0; i < 16; i++)
- for (int j = 0; j < 16; j++){
- g.drawLine(50 + j * 30, 50, 50 + j * 30, 500);
- }
- for (int i = 0; i < 16; i++){
- String number = Integer.toString(i);
- g.drawString(number, 46 + 30 * i, 45);
- }
- for (int i = 1; i < 16; i++){
- String number = Integer.toString(i);
- g.drawString(number, 33, 53 + 30 * i);
- }
- updatePaint(g);
- }
- class Xiazi extends MouseAdapter{
- public void mouseClicked(MouseEvent e){
- if(!over)
- {
- oldx = e.getX();
- oldy = e.getY();
- mouseClick();
- repaint();
- }
- }
- }
- // 游戏初始化
- public void ResetGame()
- {
- //初始化棋盘
- for(i=0;i<16;i++)
- for(j=0;j<16;j++)
- {
- this.pgrades[i][j] = 0;
- this.cgrades[i][j] = 0;
- this.board[i][j] = 2;
- }
- //遍历所有的五连子可能情况的权值
- //横
- for(i=0;i<16;i++)
- for(j=0;j<12;j++){
- for(k=0;k<5;k++){
- this.ptable[j+k][i][icount] = true;
- this.ctable[j+k][i][icount] = true;
- }
- icount++;
- }
- //竖
- for(i=0;i<16;i++)
- for(j=0;j<12;j++){
- for(k=0;k<5;k++){
- this.ptable[i][j+k][icount] = true;
- this.ctable[i][j+k][icount] = true;
- }
- icount++;
- }
- //右斜
- for(i=0;i<12;i++)
- for(j=0;j<12;j++){
- for(k=0;k<5;k++){
- this.ptable[j+k][i+k][icount] = true;
- this.ctable[j+k][i+k][icount] = true;
- }
- icount++;
- }
- //左斜
- for(i=0;i<12;i++)
- for(j=15;j>=4;j--){
- for(k=0;k<5;k++){
- this.ptable[j-k][i+k][icount] = true;
- this.ctable[j-k][i+k][icount] = true;
- }
- icount++;
- }
- for(i=0;i<=1;i++) //初始化黑子白子上的每个权值上的连子数
- for(j=0;j<672;j++)
- this.win[i][j] = 0;
- this.player = true;
- this.icount = 0;
- this.ccount = 0;
- this.pcount = 0;
- this.start = true;
- this.over = false;
- this.pwin = false;
- this.cwin = false;
- this.tie = false;
- this.bout=1;
- }
- public void ComTurn(){ //找出电脑(白子)最佳落子点
- for(i=0;i<=15;i++) //遍历棋盘上的所有坐标
- for(j=0;j<=15;j++){
- this.pgrades[i][j]=0; //该坐标的黑子奖励积分清零
- if(this.board[i][j] == 2) //在还没下棋子的地方遍历
- for(k=0;k<672;k++) //遍历该棋盘可落子点上的黑子所有权值的连子情况,并给该落子点加上相应奖励分
- if(this.ptable[i][j][k]){
- switch(this.win[0][k]){
- case 1: //一连子
- this.pgrades[i][j]+=5;
- break;
- case 2: //两连子
- this.pgrades[i][j]+=50;
- break;
- case 3: //三连子
- this.pgrades[i][j]+=180;
- break;
- case 4: //四连子
- this.pgrades[i][j]+=400;
- break;
- }
- }
- this.cgrades[i][j]=0;//该坐标的白子的奖励积分清零
- if(this.board[i][j] == 2) //在还没下棋子的地方遍历
- for(k=0;k<672;k++) //遍历该棋盘可落子点上的白子所有权值的连子情况,并给该落子点加上相应奖励分
- if(this.ctable[i][j][k]){
- switch(this.win[1][k]){
- case 1: //一连子
- this.cgrades[i][j]+=5;
- break;
- case 2: //两连子
- this.cgrades[i][j]+=52;
- break;
- case 3: //三连子
- this.cgrades[i][j]+=100;
- break;
- case 4: //四连子
- this.cgrades[i][j]+=400;
- break;
- }
- }
- }
- if(this.start){ //开始时白子落子坐标
- if(this.board[4][4]==2){
- m = 4;
- n = 4;
- }else{
- m = 5;
- n = 5;
- }
- this.start = false;
- }else{
- for(i=0;i<16;i++)
- for(j=0;j<16;j++)
- if(this.board[i][j] == 2){ //找出棋盘上可落子点的黑子白子的各自最大权值,找出各自的最佳落子点
- if(this.cgrades[i][j]>=this.cgrade){
- this.cgrade = this.cgrades[i][j];
- this.mat = i;
- this.nat = j;
- }
- if(this.pgrades[i][j]>=this.pgrade){
- this.pgrade = this.pgrades[i][j];
- this.mde = i;
- this.nde = j;
- }
- }
- if(this.cgrade>=this.pgrade){ //如果白子的最佳落子点的权值比黑子的最佳落子点权值大,则电脑的最佳落子点为白子的最佳落子点,否则相反
- m = mat;
- n = nat;
- }else{
- m = mde;
- n = nde;
- }
- }
- this.cgrade = 0;
- this.pgrade = 0;
- this.board[m][n] = 1; //电脑下子位置
- ccount++;
- if((ccount == 50) && (pcount == 50)) //平局判断
- {
- this.tie = true;
- this.over = true;
- }
- for(i=0;i<672;i++){
- if(this.ctable[m][n][i] && this.win[1][i] != 7)
- this.win[1][i]++; //给白子的所有五连子可能的加载当前连子数
- if(this.ptable[m][n][i]){
- this.ptable[m][n][i] = false;
- this.win[0][i]=7;
- }
- }
- this.player = true; //该人落子
- this.computer = false; //电脑落子结束
- }
- public void mouseClick(){
- if(!this.over)
- if(this.player){
- if(this.oldx<520 && this.oldy<520) {
- int m1=m,n1=n;
- m = (oldx-33)/30;
- n = (oldy-33)/30;
- if(this.board[m][n] == 2){
- this.bout++;
- this.board[m][n] = 0;
- pcount++;
- if((ccount == 50) && (pcount == 50)){
- this.tie = true;
- this.over = true;
- }
- for(i=0;i<672;i++){
- if(this.ptable[m][n][i] && this.win[0][i] != 7)
- this.win[0][i]++; //给黑子的所有五连子可能的加载当前连子数
- if(this.ctable[m][n][i]){
- this.ctable[m][n][i] = false;
- this.win[1][i]=7;
- }
- }
- this.player = false;
- this.computer = true;
- }else{
- m=m1;n=n1;
- }
- }
- }
- }
- public void updatePaint(Graphics g){
- if(!this.over){ //如果是轮到电脑下
- if(this.computer)
- this.ComTurn(); //得到最佳下子点
- //遍历当前棋盘上的五连子情况,判断输赢
- for(i=0;i<=1;i++)
- for(j=0;j<672;j++){
- if(this.win[i][j] == 5)
- if(i==0){ //人赢
- this.pwin = true;
- this.over = true; //游戏结束
- break;
- }else{
- this.cwin = true; //电脑赢
- this.over = true;
- break;
- }
- if(this.over) //一遇到五连子退出棋盘遍历
- break;
- }
- g.setFont(new Font("华文行楷",0,20));
- g.setColor(Color.RED);
- //画出当前棋盘所有棋子
- for(i=0;i<=15;i++)
- for(j=0;j<=15;j++){ //如果board元素值为0,则该坐标处为黑子
- if(this.board[i][j] == 0){
- g.drawImage(black.getImage(),i*30+31,j*30+31,black.getImage().getWidth(black.getImageObserver())-3,black.getImage().getHeight(black.getImageObserver())-3, black.getImageObserver());
- }
- //如果board元素值为1,则该坐标处为白子
- if(this.board[i][j] == 1){
- g.drawImage(white.getImage(),i*30+31,j*30+31,white.getImage().getWidth(white.getImageObserver())-3,white.getImage().getHeight(white.getImageObserver())-3, white.getImageObserver());
- }
- }
- //画出白子(电脑)当前所下子,便于辨认
- if(this.board[m][n]!=2)
- g.drawImage(current.getImage(),m*30+31,n*30+31,current.getImage().getWidth(current.getImageObserver())-4,current.getImage().getHeight(current.getImageObserver())-4, current.getImageObserver());
- //判断输赢情况
- //人赢
- if(this.pwin)
- g.drawString("您太厉害了!再来一次请重新开始游戏..",20,200);
- //电脑赢
- if(this.cwin)
- g.drawString("很遗憾,你输了!再来一次请重新开始游戏..",84,190);
- //平局
- if(this.tie)
- g.drawString("不分胜负!再来一次请重新开始游戏..",80,200);
- g.dispose();
- }
- }
- }