一、需求分析

1、画一个15x15的棋盘版面

2、功能按钮:开始游戏,悔棋,认输

3、单选按钮:人人对战、人机对战

4、要求:在棋盘上下棋子,棋子必须要在交叉点上;同一个位置上不能有再下棋子;棋子不能消失;判断输赢。

二、设计思路

1、图形界面(棋盘版面)

通过JFrame与JPanel 窗体实现,将JFrame分为2个部分,一部分用于绘制棋盘,另一部分用于放置功能按钮等。 并且添加监听。

(1)创建窗体

public void showUI(){

JFrame frame=new JFrame(); //创建窗体 frameframe.setTitle("五子棋 "); //设置窗体的标题frame.setSize(750,650);//设置大小frame.setResizable(false);//大小不可变frame.setLocationRelativeTo(null);//窗体居中frame.setDefaultCloseOperation(3);//退出时关闭进程cl=new ChessListener(this);// 实例化事件处理类的对象,将棋盘面板作为参数传递过去centerPanel(frame); //在窗体frame上添加中间面板 ---棋盘eastPanel(frame);//窗体frame上添加东边面板 ---功能按钮

frame.setVisible(true);//设置窗体可见cl.setExist(exist);//将棋子数组传入到事件监听类中

}

(2)创建两个面板---中间面板(绘制棋盘)、东边面板(放置功能按钮)

/*** 往窗体上添加中间面板的方法* @param frame 窗体*/

public void centerPanel(JFrame frame){

this.setBackground(Color.ORANGE);

frame.add(this);

}

/*** 往窗体上添加东边面板的方法 ---用于放置功能按钮* @param frame窗体*/

public void eastPanel(JFrame frame){

JPanel epanel=new JPanel();//创建一个面板对象epanel.setBackground(Color.GRAY);//背景颜色设置为grayepanel.setPreferredSize(new Dimension(150,600)); //设置大小epanel.setLayout(new FlowLayout());//默认也是流式布局String[] buttonArray={"开始游戏","悔棋","认输"}; //数组存储 功能按钮命令for(int i=0;i

String[] radioArray={"人人对战","人机对战"};//数组存储 单选按钮命令ButtonGroup bg=new ButtonGroup();//实例化一个按钮组的对象for(int i=0;i

}

epanel.add(radioButton);//在面板上添加单选按钮radioButton.addActionListener(cl);//加监听器}

frame.add(epanel,BorderLayout.EAST);//为窗体(边框布局)添加面板---放置在东侧}

(3)新建一个接口,用于保存棋盘的各种参数(常量)

public interface Config {

public static final int X0=20;

//棋盘起点坐标x0public static final int Y0=30; //棋盘起点坐标y0public static final int ROWS=15; //行数public static final int COLUMNS =15; //列数public static final int CHESS_SIZE=40; //棋子的大小public static final int SIZE=40; //棋盘行与行 列与列之间的距离}

(4)绘制棋盘

/*** 绘制棋盘的方法* @param g 传入画笔*/

public void drawChessTable(Graphics g){

for(int r=0;r

}

for(int c=0;c

}

}

(5)棋子重绘

我们下棋的时候并不希望因为最大化或最小化窗体而导致棋子消失,因为组件具有重绘的方法,不需要我们重绘,但是对于棋子来说,我们必须将棋子重新画出。

这里我们考虑用一个exist[][] 二维数组存储棋子的颜色与位置,值为0时表示没有棋子,值为1时表示黑色棋子,值为-1 时表示该位置有白色的棋子。

/*** 棋子重绘的方法* @param g*/

public void reDrawChess(Graphics g){

Graphics2D g2d=(Graphics2D) g;//转为Graphics2D 后面要为画笔设置颜色g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

}else if(exist[r][c]==-1){ //该位置是白子g2d.setColor(Color.WHITE);

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

}

}

}

}

}(5)重写面板的paint() 方法,完成重绘

因为我们的主类(fiveChess) 继承了JPanel类,在中间面板的方法里,直接向窗体添加的面板就是主类面板。即:

/*** 往窗体上添加中间面板的方法* @param frame 窗体*/

public void centerPanel(JFrame frame){

this.setBackground(Color.ORANGE);

frame.add(this);

}

所以在主类里直接从写JPanel的paint()方法即可:

public void paint(Graphics g){

super.paint(g);

drawChessTable(g);

reDrawChess(g);

}

注:主类的完整代码

package com.xs.chessAI;

import java.awt.BasicStroke;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.Dimension;

import java.awt.FlowLayout;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import javax.swing.ButtonGroup;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JPanel;

import javax.swing.JRadioButton;

/*** 五子棋* @author Administrator**/

public class FiveChess extends JPanel{ //继承JPanel类 使FiveChess作为一个面板private int[][] exist=new int[Config.ROWS][Config.COLUMNS]; //创建一个棋子数组 用于保存棋盘上哪个位置有哪个颜色的棋子private ChessListener cl; //声明事件处理类型的 变量/*** 图像界面的显示方法*/

public void showUI(){

JFrame frame=new JFrame(); //创建窗体 frameframe.setTitle("五子棋 "); //设置窗体的标题frame.setSize(750,650);//设置大小frame.setResizable(false);//大小不可变frame.setLocationRelativeTo(null);//窗体居中frame.setDefaultCloseOperation(3);//退出时关闭进程cl=new ChessListener(this);// 实例化事件处理类的对象,将棋盘面板作为参数传递过去centerPanel(frame); //在窗体frame上添加中间面板 ---棋盘eastPanel(frame);//窗体frame上添加东边面板 ---功能按钮

frame.setVisible(true);//设置窗体可见cl.setExist(exist);//将棋子数组传入到事件监听类中

}

/*** 重写面板重绘的方法*/

public void paint(Graphics g){

super.paint(g);

drawChessTable(g);

reDrawChess(g);

}

/*** 往窗体上添加中间面板* @param frame 窗体*/

public void centerPanel(JFrame frame){

this.setBackground(Color.ORANGE);

frame.add(this);

}

/*** 往窗体上添加东边面板 ---用于放置功能按钮* @param frame窗体*/

public void eastPanel(JFrame frame){

JPanel epanel=new JPanel();//创建一个面板对象epanel.setBackground(Color.GRAY);//背景颜色设置为grayepanel.setPreferredSize(new Dimension(150,600)); //设置大小epanel.setLayout(new FlowLayout());//默认也是流式布局String[] buttonArray={"开始游戏","悔棋","认输"}; //数组存储 功能按钮命令for(int i=0;i

String[] radioArray={"人人对战","人机对战"};//数组存储 单选按钮命令ButtonGroup bg=new ButtonGroup();//实例化一个按钮组的对象for(int i=0;i

}

epanel.add(radioButton);//在面板上添加单选按钮radioButton.addActionListener(cl);//加监听器}

frame.add(epanel,BorderLayout.EAST);//为窗体(边框布局)添加面板---放置在东侧}

/*** 绘制棋盘的方法* @param g 传入画笔*/

public void drawChessTable(Graphics g){

for(int r=0;r

}

for(int c=0;c

}

}

/*** 棋子重绘的方法* @param g*/

public void reDrawChess(Graphics g){

Graphics2D g2d=(Graphics2D) g;//转为Graphics2D 后面要为画笔设置颜色g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

}else if(exist[r][c]==-1){ //该位置是白子g2d.setColor(Color.WHITE);

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

}

}

}

}

}

public static void main(String[] args) {

FiveChess chess=new FiveChess();

chess.showUI();

}

}

二、监听类

功能:根据按钮命令---采取相应的操作

下棋(将棋子放到棋盘上) 人人对战 人机对战(AI算法) 开始游戏(清空棋盘)

悔棋 认输

(1) 下棋功能

鼠标在棋盘上点击时,棋子落在相应的位置

public void mouseClicked(MouseEvent e){//鼠标点击事件的处理方法x=e.getX();//获取点击位置的x坐标y=e.getY();//获取点击位置的y坐标if(mode.equals("人人对战")){ //调用人人对战方法pvp(x,y);

}else if(mode.equals("人机对战")){//调用人机对战的方法pvai(x,y);

}

}

这里mode 是根据按钮命令获取的对战模式,默认为人人对战,mode 的获取命令在在actionPerformed 方法中,后面会提到。

(2)人人对战的方法

/*** 人人对战的方法* @param x* @param y*/

public void pvp(int x,int y){

flag++;//步数+1g=fiveChess.getGraphics();//从棋盘面板类中获取画布g2d=(Graphics2D) g;

g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

exist[r][c]=1;//记录下了黑色棋子r与c的位置g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

count=-1;//下一次点击时 下白色的棋子list.add(new Chess(r,c));//将棋子对象存到数组队列中,保存了棋子的属性 r,cif(win.checkWin()==1){//判断黑色棋子是否赢了JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

fiveChess.removeMouseListener(this);//获胜之后,不允许再在棋盘上下棋子,所以移除鼠标监听return;

}

}else if(count==-1){//放置白子g2d.setColor(Color.WHITE);

exist[r][c]=-1;

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

count=1;//下一步要下黑色棋子list.add(new Chess(r,c));//将棋子对象存到数组队列中,保存了棋子的属性 r,cif(win.checkWin()==-1){//判断白色棋子是否赢了JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

}

}

}

}

}

}

根据获取的坐标,先判断是否在棋盘交点1/3处,根据count 值判断下一步下白棋还是黑棋(count = 1 黑棋 count = -1 白棋),并把位置与颜色传入二维数组中。

(3)人机对战的方法

/*** 人机对战的方法* @param x 人所下棋子的横坐标* @param y 人所下棋子的纵坐标*/

public void pvai(int x,int y){

flag++;//步数+1g=fiveChess.getGraphics();

g2d=(Graphics2D) g;

g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

for(int c=0;c

if(exist[r][c]==0){//判断该位置上是否有棋子if((Math.abs(x-Config.X0-c*Config.SIZE)

exist[r][c]=1;//记录下了黑色棋子的位置g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

list.add(new Chess(r,c));//队列中添加棋子数组对象 存 r cif(win.checkWin()==1){//判断黑色棋子是否胜利JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

ai(g2d);//调用ai下棋的方法if(win.checkWin()==-1){//ai下的是白色棋子,每下完一步,都要判断一次是否获胜JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

}

}

}

}

人下棋时,算法与人人对战时一样,ai下棋,调用ai下棋的方法。每次人下棋或者ai下棋后,都要进行判断输赢。

ai下棋的方法:

/*** ai 下棋的方法* @param g2d*/

public void ai(Graphics2D g2d){

g2d.setColor(Color.WHITE);//设置ai所下棋子的颜色为白色int[][] weightArray=new int[Config.ROWS][Config.COLUMNS];//创建一个存储权值的二维数组/*** 设置每种棋子相连情况下的权值*/

map.put("010", 1);

map.put("0110", 20);

map.put("01110", 50);

map.put("011110", 500);

map.put("0-10", 10);

map.put("0-1-10", 30);

map.put("0-1-1-10", 70);

map.put("0-1-1-1-10", 500);

map.put("-110", 1);

map.put("-1110", 10);

map.put("-11110", 30);

map.put("-111110", 500);

map.put("1-10", 1);

map.put("1-1-10", 10);

map.put("1-1-1-10", 30);

map.put("1-1-1-1-10", 500);

for(int r=0;r

if(exist[r][c]==0){//判断是否是空位String code=countHL(r,c);

Integer weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countVU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countLLU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countLRU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

}

}

}

int max=weightArray[0][0]; //找出最大的权值for(int r=0;r

for(int c=0;c

if(weightArray[r][c]>max){

max=weightArray[r][c];

}

}

}

for(int r=0;r

if(weightArray[r][c]==max&&exist[r][c]==0){//权值最大且是空位exist[r][c]=-1;

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

list.add(new Chess(r,c));

return;

}

}

}

}

AI算法有很多种,这里采用了权值算法,即根据棋子相连的情况,统计每一个空位的权值大小,选出权值最大点 在这个位置下棋。存储棋子相连情况与权值使用HashMap 。

统计权值方法:4种(水平向左统计、垂直向上统计、向上正斜统计( \ ) 、向下反斜统计( / ))

/*** 水平向左方向统计棋子相连的情况* @param r 行* @param c 列* @return*/

private String countHL(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int c1=c-1;c1>=0;c1--){//向左统计if(exist[r][c1]==0){//表示没有棋子的位置if(c1+1==c){//相邻的空位break;

}else{//空位不相连code = exist[r][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r][c1] + code;//记录棋子相连的情况chess = exist[r][c1];//记录第一次的棋子的颜色}else if(chess==exist[r][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 垂直向上统计棋子相连的情况* @param r行* @param c列* @return*/

private String countVU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1;r1>=0;r1--){//向上统计if(exist[r1][c]==0){//表示没有棋子if(r1+1==r){//相邻的空位break;

}else{//不相邻的空位code = exist[r1][c] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c] + code;//记录棋子相连的情况chess = exist[r1][c];//记录第一次的棋子的颜色}else if(chess==exist[r1][c]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 正斜(\) 棋子相连统计* @param r* @param c* @return*/

private String countLLU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1,c1=c-1;r1>=0&&c1>0;r1--,c1--){//向上统计if(exist[r1][c1]==0){//表示没有棋子if(r1+1==r&&c1+1==c){//相邻的空位break;

}else{//不相邻的空位code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c1] + code;//记录棋子相连的情况chess = exist[r1][c1];//记录第一次的棋子的颜色}else if(chess==exist[r1][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 反斜(/) 棋子相连的统计* @param r* @param c* @return*/

private String countLRU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1,c1=c+1;r1>=0&&c1

if(exist[r1][c1]==0){//表示没有棋子if(r1+1==r&&c1-1==c){//相邻的空位break;

}else{

code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c1] + code;//记录棋子相连的情况chess = exist[r1][c1];//记录第一次的棋子的颜色}else if(chess==exist[r1][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

(4)获取命令的方法

这里根据点击的功能按钮 采取不同的功能操作

/*** 点击事件处理方法*/

public void actionPerformed(ActionEvent e){

if(e.getSource() instanceof JRadioButton){//点击单选按钮mode=e.getActionCommand();

}else if(e.getSource() instanceof JButton){//点击其他功能按钮if(e.getActionCommand().equals("开始游戏")){

MouseListener[] mls=fiveChess.getMouseListeners();

if(mls.length>0){//如果还有其他监听 ---移除fiveChess.removeMouseListener(this);

}

reset();//调用棋盘回到初始状态的方法fiveChess.addMouseListener(this);// 为棋盘添加鼠标监听}else if(e.getActionCommand().equals("悔棋")){ //悔棋的算法if(mode.equals("人人对战")){

if(list.size()>1){//存储棋子的数组队列长度大于1Chess chess=list.get(list.size()-1); //获取最后一个被存入数组队列的棋子对象int r=chess.getR(); //分别获取棋子的x与y坐标int c=chess.getC();

exist[r][c]=0;//设置最后一步所下棋子的位置为空list.remove(list.size()-1);//移除队列中最后一个棋子对象fiveChess.repaint();//调用面板重绘的方法if(count==1){//如果悔棋的是黑色方,下一步下棋的还是黑色方count=-1;//如果悔棋的是白色方,下一步下棋的还是白色方}else{

count=1;

}

}

}else if(mode.equals("人机对战")){//人机对战,悔一次棋要退两步,1、电脑所下的棋 2、人所下的棋子if(list.size()>2){//至少下了两步Chess chess=list.get(list.size()-2);//获取倒数第二步的棋子对象int r=chess.getR();

int c=chess.getC();

exist[r][c]=0;//令其为空Chess chessAI=list.get(list.size()-1);//获取最后一步棋子对象r=chessAI.getR();

c=chessAI.getC();

exist[r][c]=0;//令其为空list.remove(list.size()-1);//删除数组队列中后两个对象list.remove(list.size()-1);

fiveChess.repaint();//调用棋子面板的重绘方法}

}

}else if(e.getActionCommand().equals("认输")){//认输的算法if(flag<10){

JOptionPane.showMessageDialog(fiveChess, "总步数小于10,不能认输");

}else{

if(mode.equals("人人对战")){

if(count==1){//本来应该下黑色棋子了 但是点击了认输,所以白色棋子获胜JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

}else if(count==-1){//本来应该下白色棋子了 但是点击了认输,所以黑色棋子获胜JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

}

}else if(mode.equals("人机对战")){//电脑不会认输的JOptionPane.showMessageDialog(fiveChess, "白色棋子AI获得胜利");

}

fiveChess.removeMouseListener(this);//认输后不能在棋盘上上下棋了 所以要移除棋盘上的鼠标监听}

}

}

}

棋盘重置的方法:

/*** 设置棋盘回到初始状态的方法*/

public void reset(){

count=1;//默认黑色棋子先下棋flag=0;//下棋步数重置为0for(int r=0;r

exist[r][c]=0;

}

}

fiveChess.repaint(); //调用棋盘重绘的方法}

即让存储棋子的二维数组每一个点的值都为0;然后重绘棋子面板。

注:监听类完整代码

package com.xs.chessAI;

import java.awt.Color;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.RenderingHints;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.MouseAdapter;

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import java.util.ArrayList;

import java.util.HashMap;

import javax.swing.JButton;

import javax.swing.JOptionPane;

import javax.swing.JRadioButton;

/*** 事件处理类* @author Administrator**/

public class ChessListener extends MouseAdapter implements ActionListener{

private int x,y; //记录点击坐标private Graphics g; //存储画笔private int count=1; //判别人人对战时下一步下黑子还是白子private int[][] exist; //生命棋子数组private Graphics2D g2d; //画笔对象private HashMap map=new HashMap();//创建集合对象,用途是存储每一种棋子相连对应的权值private FiveChess fiveChess; //生命面板类型的变量private WhoWin win;//生命判断输赢类的对象private String mode="人人对战"; //默认的对战模式为人人对战private int flag=0;//记录下棋的步数private ArrayList list=new ArrayList(); //数组队列 存储的Chess类型的对象/*** 构造方法* @param fiveChess*/

public ChessListener(FiveChess fiveChess) {

this.fiveChess=fiveChess;

}

/*** 设置方法,接收数组* @param exist 存储棋盘上棋子的位置*/

public void setExist(int[][] exist){

this.exist=exist;

win=new WhoWin(exist);

}

/*** 点击事件处理方法*/

public void actionPerformed(ActionEvent e){

if(e.getSource() instanceof JRadioButton){//点击单选按钮mode=e.getActionCommand();

}else if(e.getSource() instanceof JButton){//点击其他功能按钮if(e.getActionCommand().equals("开始游戏")){

MouseListener[] mls=fiveChess.getMouseListeners();

if(mls.length>0){//如果还有其他监听 ---移除fiveChess.removeMouseListener(this);

}

reset();//调用棋盘回到初始状态的方法fiveChess.addMouseListener(this);// 为棋盘添加鼠标监听}else if(e.getActionCommand().equals("悔棋")){ //悔棋的算法if(mode.equals("人人对战")){

if(list.size()>1){//存储棋子的数组队列长度大于1Chess chess=list.get(list.size()-1); //获取最后一个被存入数组队列的棋子对象int r=chess.getR(); //分别获取棋子的x与y坐标int c=chess.getC();

exist[r][c]=0;//设置最后一步所下棋子的位置为空list.remove(list.size()-1);//移除队列中最后一个棋子对象fiveChess.repaint();//调用面板重绘的方法if(count==1){//如果悔棋的是黑色方,下一步下棋的还是黑色方count=-1;//如果悔棋的是白色方,下一步下棋的还是白色方}else{

count=1;

}

}

}else if(mode.equals("人机对战")){//人机对战,悔一次棋要退两步,1、电脑所下的棋 2、人所下的棋子if(list.size()>2){//至少下了两步Chess chess=list.get(list.size()-2);//获取倒数第二步的棋子对象int r=chess.getR();

int c=chess.getC();

exist[r][c]=0;//令其为空Chess chessAI=list.get(list.size()-1);//获取最后一步棋子对象r=chessAI.getR();

c=chessAI.getC();

exist[r][c]=0;//令其为空list.remove(list.size()-1);//删除数组队列中后两个对象list.remove(list.size()-1);

fiveChess.repaint();//调用棋子面板的重绘方法}

}

}else if(e.getActionCommand().equals("认输")){//认输的算法if(flag<10){

JOptionPane.showMessageDialog(fiveChess, "总步数小于10,不能认输");

}else{

if(mode.equals("人人对战")){

if(count==1){//本来应该下黑色棋子了 但是点击了认输,所以白色棋子获胜JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

}else if(count==-1){//本来应该下白色棋子了 但是点击了认输,所以黑色棋子获胜JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

}

}else if(mode.equals("人机对战")){//电脑不会认输的JOptionPane.showMessageDialog(fiveChess, "白色棋子AI获得胜利");

}

fiveChess.removeMouseListener(this);//认输后不能在棋盘上上下棋了 所以要移除棋盘上的鼠标监听}

}

}

}

public void mouseClicked(MouseEvent e){//鼠标点击事件的处理方法x=e.getX();//获取点击位置的x坐标y=e.getY();//获取点击位置的y坐标if(mode.equals("人人对战")){ //调用人人对战方法pvp(x,y);

}else if(mode.equals("人机对战")){//调用人机对战的方法pvai(x,y);

}

}

/*** 设置棋盘回到初始状态的方法*/

public void reset(){

count=1;//默认黑色棋子先下棋flag=0;//下棋步数重置为0for(int r=0;r

exist[r][c]=0;

}

}

fiveChess.repaint(); //调用棋盘重绘的方法}

/*** 人人对战的方法* @param x* @param y*/

public void pvp(int x,int y){

flag++;//步数+1g=fiveChess.getGraphics();//从棋盘面板类中获取画布g2d=(Graphics2D) g;

g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

exist[r][c]=1;//记录下了黑色棋子r与c的位置g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

count=-1;//下一次点击时 下白色的棋子list.add(new Chess(r,c));//将棋子对象存到数组队列中,保存了棋子的属性 r,cif(win.checkWin()==1){//判断黑色棋子是否赢了JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

fiveChess.removeMouseListener(this);//获胜之后,不允许再在棋盘上下棋子,所以移除鼠标监听return;

}

}else if(count==-1){//放置白子g2d.setColor(Color.WHITE);

exist[r][c]=-1;

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

count=1;//下一步要下黑色棋子list.add(new Chess(r,c));//将棋子对象存到数组队列中,保存了棋子的属性 r,cif(win.checkWin()==-1){//判断白色棋子是否赢了JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

}

}

}

}

}

}

/*** 人机对战的方法* @param x 人所下棋子的横坐标* @param y 人所下棋子的纵坐标*/

public void pvai(int x,int y){

flag++;//步数+1g=fiveChess.getGraphics();

g2d=(Graphics2D) g;

g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

for(int r=0;r

for(int c=0;c

if(exist[r][c]==0){//判断该位置上是否有棋子if((Math.abs(x-Config.X0-c*Config.SIZE)

exist[r][c]=1;//记录下了黑色棋子的位置g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

list.add(new Chess(r,c));//队列中添加棋子数组对象 存 r cif(win.checkWin()==1){//判断黑色棋子是否胜利JOptionPane.showMessageDialog(fiveChess, "黑色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

ai(g2d);//调用ai下棋的方法if(win.checkWin()==-1){//ai下的是白色棋子,每下完一步,都要判断一次是否获胜JOptionPane.showMessageDialog(fiveChess, "白色棋子获得胜利");

fiveChess.removeMouseListener(this);

return;

}

}

}

}

}

}

/*** ai 下棋的方法* @param g2d*/

public void ai(Graphics2D g2d){

g2d.setColor(Color.WHITE);//设置ai所下棋子的颜色为白色int[][] weightArray=new int[Config.ROWS][Config.COLUMNS];//创建一个存储权值的二维数组/*** 设置每种棋子相连情况下的权值*/

map.put("010", 1);

map.put("0110", 20);

map.put("01110", 50);

map.put("011110", 500);

map.put("0-10", 10);

map.put("0-1-10", 30);

map.put("0-1-1-10", 70);

map.put("0-1-1-1-10", 500);

map.put("-110", 1);

map.put("-1110", 10);

map.put("-11110", 30);

map.put("-111110", 500);

map.put("1-10", 1);

map.put("1-1-10", 10);

map.put("1-1-1-10", 30);

map.put("1-1-1-1-10", 500);

for(int r=0;r

if(exist[r][c]==0){//判断是否是空位String code=countHL(r,c);

Integer weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countVU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countLLU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

code=countLRU(r,c);

weight = map.get(code);//获取棋子相连情况对应的权值if(null != weight){//判断权值不为nullweightArray[r][c] += weight;//累加权值}

}

}

}

int max=weightArray[0][0]; //找出最大的权值for(int r=0;r

for(int c=0;c

if(weightArray[r][c]>max){

max=weightArray[r][c];

}

}

}

for(int r=0;r

if(weightArray[r][c]==max&&exist[r][c]==0){//权值最大且是空位exist[r][c]=-1;

g2d.fillOval(Config.X0+c*Config.SIZE-Config.CHESS_SIZE/2,Config.Y0+r*Config.SIZE-Config.CHESS_SIZE/2 , Config.CHESS_SIZE, Config.CHESS_SIZE);

list.add(new Chess(r,c));

return;

}

}

}

}

/*** 水平向左方向统计棋子相连的情况* @param r 行* @param c 列* @return*/

private String countHL(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int c1=c-1;c1>=0;c1--){//向左统计if(exist[r][c1]==0){//表示没有棋子的位置if(c1+1==c){//相邻的空位break;

}else{//空位不相连code = exist[r][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r][c1] + code;//记录棋子相连的情况chess = exist[r][c1];//记录第一次的棋子的颜色}else if(chess==exist[r][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 垂直向上统计棋子相连的情况* @param r行* @param c列* @return*/

private String countVU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1;r1>=0;r1--){//向上统计if(exist[r1][c]==0){//表示没有棋子if(r1+1==r){//相邻的空位break;

}else{//不相邻的空位code = exist[r1][c] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c] + code;//记录棋子相连的情况chess = exist[r1][c];//记录第一次的棋子的颜色}else if(chess==exist[r1][c]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 正斜(\) 棋子相连统计* @param r* @param c* @return*/

private String countLLU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1,c1=c-1;r1>=0&&c1>0;r1--,c1--){//向上统计if(exist[r1][c1]==0){//表示没有棋子if(r1+1==r&&c1+1==c){//相邻的空位break;

}else{//不相邻的空位code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c1] + code;//记录棋子相连的情况chess = exist[r1][c1];//记录第一次的棋子的颜色}else if(chess==exist[r1][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

/*** 反斜(/) 棋子相连的统计* @param r* @param c* @return*/

private String countLRU(int r,int c){

String code = "0";//默认记录r,c位置的情况int chess = 0;//记录第一次出现的棋子for(int r1=r-1,c1=c+1;r1>=0&&c1

if(exist[r1][c1]==0){//表示没有棋子if(r1+1==r&&c1-1==c){//相邻的空位break;

}else{

code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}else{//表示有棋子if(chess==0){//判断是否是第一次出现棋子code = exist[r1][c1] + code;//记录棋子相连的情况chess = exist[r1][c1];//记录第一次的棋子的颜色}else if(chess==exist[r1][c1]){//表示之后的棋子和第一次的棋子颜色一致code = exist[r1][c1] + code;//记录棋子相连的情况}else{//表示之后的棋子和第一次的棋子颜色不同code = exist[r1][c1] + code;//记录棋子相连的情况break;

}

}

}

return code;

}

}

三、判断输赢类:

我们注意到,人人对战与人机对战时,每下一步棋都要判断一次输赢,所以这里我把判断输赢的方法单独写到一个类里。

输赢判断规则: 同一颜色棋子5子相连 (共4个方向 水平 垂直 正斜(\) 反斜(/))

package com.xs.chessAI;

/**

* 判断输赢类

* @author Administrator

*

*/

public class WhoWin {

private int[][]exist;

public WhoWin(int exist[][]){

this.exist=exist;

}

/**

* 判断输赢的方法

*/

public int checkWin(){

if((rowWin()==1)||(columnWin()==1)||(rightSideWin()==1)||(leftSideWin()==1)){

return 1;

}else if((rowWin()==-1)||(columnWin()==-1)||(rightSideWin()==-1)||(leftSideWin()==-1)){

return -1;

}

return 0;

}

/**

* 以行的方式赢

*/

public int rowWin(){

for(int i=0;i

for(int j=0;j

if(exist[i][j]==-1){

if(exist[i][j+1]==-1&&exist[i][j+2]==-1&&exist[i][j+3]==-1&&exist[i][j+4]==-1){

return -1;

}

}

if(exist[i][j]==1){

if(exist[i][j+1]==1&&exist[i][j+2]==1&&exist[i][j+3]==1&&exist[i][j+4]==1){

return 1;

}

}

}

}

return 0;

}

/**

* 以列的方式赢

*/

public int columnWin(){

for(int i=0;i

for(int j=0;j

if(exist[i][j]==-1){

if(exist[i+1][j]==-1&&exist[i+2][j]==-1&&exist[i+3][j]==-1&&exist[i+4][j]==-1){

return -1;

}

}

if(exist[i][j]==1){

if(exist[i+1][j]==1&&exist[i+2][j]==1&&exist[i+3][j]==1&&exist[i+4][j]==1){

return 1;

}

}

}

}

return 0;

}

/**

* 斜的方式赢

*/

public int rightSideWin(){ //正斜

for(int i=0;i

for(int j=0;j

if(exist[i][j]==-1){

if(exist[i+1][j+1]==-1&&exist[i+2][j+2]==-1&&exist[i+3][j+3]==-1&&exist[i+4][j+4]==-1){

return -1;

}

}

if(exist[i][j]==1){

if(exist[i+1][j+1]==1&&exist[i+2][j+2]==1&&exist[i+3][j+3]==1&&exist[i+4][j+4]==1){

return 1;

}

}

}

}

return 0;

}

public int leftSideWin(){ //反斜

for(int i=4;i

for(int j=0;j

if(exist[i][j]==-1){

if(exist[i-1][j+1]==-1&&exist[i-2][j+2]==-1&&exist[i-3][j+3]==-1&&exist[i-4][j+4]==-1){

return -1;

}

}

if(exist[i][j]==1){

if(exist[i-1][j+1]==1&&exist[i-2][j+2]==1&&exist[i-3][j+3]==1&&exist[i-4][j+4]==1){

return 1;

}

}

}

}

return 0;

}

}

四、棋子类

在悔棋的过程,我们使用队列来实现的,即每下一步棋,我们都要记录这一棋子的位置,添加到队列中,悔棋时,只需删掉这一位置的棋子即可。

故在每次下棋后,应当为队列添加一个棋子对象(记录了行和列),记录这一次下棋的位置,所以这里创建一个棋子类。

package com.xs.chessAI;

/**

* 棋子类---具有棋子行与列的属性

* @author Administrator

*

*/

public class Chess {

private int r;//行

private int c;//列

public Chess(int r,int c){

this.r=r;

this.setC(c);

}

public int getR() {

return r;

}

public void setR(int r) {

this.r = r;

}

public int getC() {

return c;

}

public void setC(int c) {

this.c = c;

}

}

这样,一个简单的五子棋小游戏制作就完成啦。

java五子棋_java实现五子棋相关推荐

  1. java绘制五子棋_java绘制五子棋棋盘

    免费资源网,https://freexyz.cn/ 本文实例为大家分享了java绘制五子棋棋盘的具体代码,供大家参考,具体内容如下 源码: import javax.imageio.ImageIO; ...

  2. java 网络五子棋_Java实现五子棋网络版

    本文实例为大家分享了Java实现五子棋网络版的具体代码,供大家参考,具体内容如下 需求分析: 对于网络五子棋而言,在普通五子棋的基础上需要添加以下功能: 1.拥有服务器端和客户端,用户通过客户端登录服 ...

  3. java实现五子棋_java实现五子棋

    [java]代码库import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Tool ...

  4. java中五子棋_Java简单五子棋的实现

    在经过了几天的学习后,已经可以实现一个简单的五子棋游戏了,下面我就写一下编写程序 的过程和自己在这个过程中的心得体会. 第一步:绘制棋盘和实现落子 具体的过程就不写了,我是绘制了一个15*15的棋盘, ...

  5. java 控制台五子棋_java控制台五子棋

    package frank; import java.io.*; public class App { //棋盘 private String[][] board; //棋盘大小 private st ...

  6. 单机版五子棋java功能_JAVA单机版五子棋怎么写

    展开全部 import java.awt.*; import java.awt.event.*; import java.applet.Applet; import java.awt.Color; p ...

  7. java设计五子棋_JAVA课程设计+五子棋(团队博客)

    JAVA课程设计 利用所学习的JAVA知识设计一个五子棋小游戏 1.团队名称.团队成员介绍(菜鸟三人组) 杨泽斌[组长]:201521123049 网络1512 叶文柠[组员]:20152112305 ...

  8. java五子棋游戏源代码_Java实现五子棋游戏的完整代码

    用Java编写简单的五子棋,供大家参考,具体内容如下 前言 这两天在空闲时间做了个五子棋项目,分享给大家看一下,界面是这样的: 界面很丑我知道,本人虽有几年PS基础,但知识浅薄,审美观不尽人意,做到如 ...

  9. java走棋_Java五子棋实现

    终于开始第一个小项目了哈哈,今天小编来介绍一个如何用Java来实现一个五子棋.那么,我们先来想一想我们的五子棋需要有些什么功能呢???棋盘界面的实现 人人对战.人机对战 开始新游戏.认输.悔棋的操作 ...

最新文章

  1. CouchDB 简单HTTP接口使用说明
  2. ASP编程常用的15个非常有用的代码及用法
  3. POI实现Excel导入Cannot get a text value from a numeric cell
  4. 微信公众号之微信买单
  5. 论文阅读 - Video Swin Transformer
  6. 织梦cms响应式站长导航分类网站模板(自适应手机版)
  7. android 编译faac,faac 移植到android
  8. SQL基础——DDL、DML、DQL、DCL速览
  9. Android自定义之流式布局
  10. LeetCode路径问题
  11. 阶段3 1.Mybatis_11.Mybatis的缓存_1 今日课程安排
  12. letex编辑输出】pdf文件嵌入字体embedded fonts的问题
  13. otf字体转ttf字体
  14. MYSQL--主键 外键
  15. Word自动目录字体过大,如何全选并修改样式
  16. 云服务器  虚拟主机  服务器
  17. 中国第二家傲途格精选酒店开业;德意志酒店集团所有旗舰酒店合入全新总品牌 | 美通企业日报...
  18. css 文字超出三行展示省略号
  19. diy 单片机 自动浇花_单片机自动浇花系统(附原理图、仿真文件、源程序)
  20. 验证性因子分析(CFA)全流程

热门文章

  1. 喜讯|云畅科技被认定为2022年度湖南省移动互联网重点企业
  2. 张勇发全员信:阿里云将分拆上市;ChatGPT官方iOS应用上线,支持中文语音;Bun 0.6发布|极客头条...
  3. 移动叔叔MT6573一键ROOT工具!适应所有android2.2/2.3以上系统
  4. 各个语言的应用场景?
  5. 宝塔控制面板安装禅道开源版教程
  6. 数据库找不到字段报错
  7. nginx-http-flv-module使用鉴权完整版
  8. SecPod:基于虚拟化的安全系统框架
  9. 考研政治——马克思原理之认知规律
  10. POJ 1654 乱搞题?