如何使用Java实现五子棋

一.基本功能实现

1.创建棋盘:

package gobang;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

/**
 * Board类 控制棋盘
 * 继承自JPanel
 * 负责棋盘的绘制和棋局的记录
 */
public class Board extends JPanel {
	
	private static final long serialVersionUID = 1L;
	
	private Chess[][] state;	// 记录棋局

	/**
	 * 构造器 初始化state 设置尺寸大小
	 */
	public Board () {
		state = new Chess[Tool.ROWNUM][Tool.COLNUM];
		setSize(Tool.BOARDSIZE, Tool.BOARDSIZE);
	}
	
	/**
	 * 绘制棋盘背景
	 */
	@Override
	protected void paintComponent(Graphics g) {   
        g.drawImage(Tool.board, 0, 0, getSize().width, getSize().height, this);  
    } 
	
	/**
	 * 绘制棋盘
	 */
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		
		// 绘制棋盘
		Graphics2D g2 = (Graphics2D) g;
		g2.setStroke(new BasicStroke(2.0f));
		// 绘制中心和四角五个点
		int size = Tool.BOARDPOINTSIZE;
		g2.fillOval(Tool.BOARDSIZE / 2 - 5, Tool.BOARDSIZE / 2 - 5, 10, 10);
		g2.fillOval(Tool.MARGIN + Tool.CELL * 3 - size / 2, Tool.MARGIN + Tool.CELL * 3 - size / 2, size, size);
		g2.fillOval(Tool.MARGIN + Tool.CELL * 3 - size / 2, Tool.BOARDSIZE - Tool.MARGIN - Tool.CELL * 3 - size / 2, size, size);
		g2.fillOval(Tool.BOARDSIZE - Tool.MARGIN - Tool.CELL * 3 - size / 2, Tool.MARGIN + Tool.CELL * 3 - size / 2, size, size);
		g2.fillOval(Tool.BOARDSIZE - Tool.MARGIN - Tool.CELL * 3 - size / 2, Tool.BOARDSIZE - Tool.MARGIN - Tool.CELL * 3 - size / 2, size, size);
		// 绘制横竖15条线
		for (int i = 0; i < Tool.ROWNUM; i++) 
			g2.drawLine(Tool.MARGIN, Tool.MARGIN + i * Tool.CELL, Tool.BOARDSIZE - Tool.MARGIN, Tool.MARGIN + i * Tool.CELL);
		for (int i = 0; i < Tool.COLNUM; i++)
			g2.drawLine(Tool.MARGIN + i * Tool.CELL, Tool.MARGIN, Tool.MARGIN + i * Tool.CELL, Tool.BOARDSIZE - Tool.MARGIN);
			
		// 绘制棋子
		for (int i = 0; i < Tool.ROWNUM; i++) {
			for (int j = 0; j < Tool.COLNUM; j++) {
				if (state[i][j] != null) {
					state[i][j].draw(g, i, j);
				}
			}
		}
		
		// 显示标记
		Tool.show(g);
	}
	
	/**
	 * 初始化状态数组 重新绘制棋盘
	 */
	public void restart () {
		state = new Chess[Tool.ROWNUM][Tool.COLNUM];
		repaint();
	}
	
	/**
	 * 落子后更新状态数组 重新绘制棋盘
	 * @param i 棋子横坐标
	 * @param j 棋子纵坐标
	 * @param color 棋子颜色
	 * @return 返回是否成功
	 */
	public Boolean setChess (int i, int j, Color color) {
		// 如果当前位置不为空 落子失败
		if (state[i][j] != null)
			return false;
		state[i][j] = new Chess(color);
		repaint();
		return true;
	}

	/**
	 * 获取状态数组
	 */
	public Chess[][] getState() {
		return state;
	}
}

2.棋子设置:

package gobang;

import java.awt.Color;
import java.awt.Graphics;

/**
 * Chess类 代表棋子
 * 负责记录颜色和绘制自己
 */
public class Chess {
	
	private Color color; // 棋子颜色
	
	/**
	 * 构造器
	 * @param color 棋子颜色
	 */
	public Chess (Color color) {
		this.color = color;
	}

	/**
	 * 获取该棋子颜色
	 */
	public Color getColor () {
		return color;
	}
	
	/**
	 * 设置该棋子颜色
	 * @param color 颜色
	 */
	public void setColor(Color color) {
		this.color = color;
	}
	
	/**
	 * 绘制自己
	 * @param g 画笔
	 * @param i 横坐标
	 * @param j 纵坐标
	 */
	public void draw(Graphics g, int i, int j) {
		if (color.equals(Color.BLACK))
			g.drawImage(Tool.blackChess, Tool.CELL * j - Tool.CHESSSIZE / 2 + Tool.MARGIN, Tool.CELL * i - Tool.CELL / 2 + Tool.MARGIN, Tool.CHESSSIZE, Tool.CHESSSIZE, null);
		else
			g.drawImage(Tool.whiteChess, Tool.CELL * j - Tool.CHESSSIZE / 2 + Tool.MARGIN, Tool.CELL * i - Tool.CELL / 2 + Tool.MARGIN, Tool.CHESSSIZE, Tool.CHESSSIZE, null);
	}
}

3.游戏页面设置以及图像管理:

package gobang;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * GamePanel类 代表游戏面板
 * 负责控制游戏及显示状态
 */
public class GamePanel extends JPanel {

	private static final long serialVersionUID = 1L;

	private JLabel title; // 游戏标题
	
	JButton start;	  // 开始游戏按钮
	JButton setting;  // 游戏设置按钮
	JButton exit;     // 退出游戏按钮
	JButton withdraw; // 悔棋按钮
	JButton defeat;   // 认输按钮
	
	private JLabel step;       // 步数标签
	private JLabel image;      // 图标
	private JLabel statusText; // 状态标签
	
	private JPanel controlPanel; // 控制面板 负责5个按钮
	private JPanel statusPanel;  // 状态面板 负责3个标签
	
	private ImageIcon blackIcon; // 黑棋图标
	private ImageIcon whiteIcon; // 白棋图标
	
	/**
	 * 构造器
	 * 设置显示位置 背景透明 初始化组件等
	 */
	public GamePanel () {
		setPreferredSize(Tool.PANELDIMENSION); // 显示位置
		setOpaque(false);					   // 背景透明
		
		// 初始化各个组件 设置内容 字体 鼠标监听器 图标等
		
		title = new JLabel();
		title.setIcon(new ImageIcon(Tool.gobang.getScaledInstance(Tool.TITLEWIDTH, Tool.TTITLEHEIGHT, Image.SCALE_AREA_AVERAGING)));
		
		start = new JButton("开始游戏");
		Tool.initComponent(start, Tool.getFont(Tool.fontkai, Font.PLAIN, 32));
		Tool.addMListener(start, Tool.LARGEBUTTONWIDTH, Tool.LARGEBUTTONHEIGHT);
		
		setting = new JButton("游戏设置");
		Tool.initComponent(setting, Tool.getFont(Tool.fontkai, Font.PLAIN, 32));
		Tool.addMListener(setting, Tool.LARGEBUTTONWIDTH,Tool.LARGEBUTTONHEIGHT);
		
		exit = new JButton("退出游戏");
		Tool.initComponent(exit, Tool.getFont(Tool.fontkai, Font.PLAIN, 32));
		Tool.addMListener(exit, Tool.LARGEBUTTONWIDTH, Tool.LARGEBUTTONHEIGHT);
		
		withdraw = new JButton("悔棋");
		Tool.initComponent(withdraw, Tool.getFont(Tool.fontkai, Font.PLAIN, 24));
		withdraw.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseEntered(MouseEvent arg0) {	
				if (withdraw.isEnabled())
					Tool.setEntered(withdraw, Tool.SMALLBUTTONWIDTH, Tool.SMALLBUTTONHEIGHT);
				super.mouseEntered(arg0);
			}
			
			@Override
			public void mouseExited(MouseEvent arg0) {	
				if (withdraw.isEnabled()) {
					Tool.setExited(withdraw);
				}
				super.mouseExited(arg0);
			}
		});
		
		defeat = new JButton("认输");
		Tool.initComponent(defeat, Tool.getFont(Tool.fontkai, Font.PLAIN, 24));
		Tool.addMListener(defeat, Tool.SMALLBUTTONWIDTH, Tool.SMALLBUTTONHEIGHT);
		
		step = new JLabel();
		step.setFont(Tool.getFont(Tool.fontwei, Font.PLAIN, 16));
		
		image = new JLabel();
		
		statusText = new JLabel();
		statusText.setFont(Tool.getFont(Tool.fontwei, Font.PLAIN, 16));
		
		controlPanel = new JPanel();
		statusPanel = new JPanel();
		
		blackIcon = new ImageIcon(Tool.blackChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT));
		whiteIcon = new ImageIcon(Tool.whiteChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT));
		
		// 设置边界布局
		setLayout(new BorderLayout());
		add(controlPanel, BorderLayout.CENTER);
		add(statusPanel, BorderLayout.SOUTH);
		
		// controlPanel设置网格袋布局
		controlPanel.setLayout(new GridBagLayout());	
		JPanel empty = new JPanel();
		empty.setOpaque(false);
		controlPanel.add(empty, new GBC(0, 0).setInsetsUpandDown(15));
		controlPanel.add(title, new GBC(0, 1, 1, 6).setInsetsUpandDown(30));
		controlPanel.add(start, new GBC(0, 8).setInsetsUpandDown(30));
		controlPanel.add(setting, new GBC(0, 9).setInsetsUpandDown(30));
		controlPanel.add(exit, new GBC(0, 10).setInsetsUpandDown(30));
		controlPanel.add(withdraw, new GBC(0, 11).setInsetsUpandDown(5));
		controlPanel.add(defeat, new GBC(0, 12).setInsetsUpandDown(15));
		controlPanel.add(empty, new GBC(0, 13).setInsetsUpandDown(5));
		controlPanel.setOpaque(false);
		
		// statusPanel布局
		statusPanel.setPreferredSize(new Dimension(0, 30));
		statusPanel.add(image);
		statusPanel.add(statusText);
		statusPanel.add(step);
		statusPanel.setOpaque(false);
	}
	
	/**
	 * 更新状态栏UI和悔棋状态
	 * @param i 步数
	 */
	public void updateUI(int i) {
		switch (i % 2) {
			case 1 : statusText.setText("黑棋"); image.setIcon(blackIcon); break;
			case 0 : statusText.setText("白棋"); image.setIcon(whiteIcon); break;
		}
		step.setText("第 " + i + " 步");
		// 若一步未下 则不能悔棋
		if (i == 1 || i == 2)
			withdraw.setEnabled(false);
		else
			withdraw.setEnabled(true);
	}
	
	/**
	 * 游戏面板背景
	 */
	@Override
	protected void paintComponent(Graphics g) {   
        g.drawImage(Tool.background, 0, 0, getSize().width, getSize().height, this);  
    } 
}

4.网格重新布局:

package gobang;

import java.awt.GridBagConstraints;
import java.awt.Insets;

/**
 * 重新设计网格袋布局
 */
public class GBC extends GridBagConstraints {
	
	private static final long serialVersionUID = 1L;

	public GBC (int gridx, int gridy) {
		this.gridx = gridx;
		this.gridy = gridy;
	}
	
	public GBC (int gridx, int gridy, int gridwidth, int gridheight) {
		this.gridx = gridx;
		this.gridy = gridy;
		this.gridwidth = gridwidth;
		this.gridheight = gridheight;
	}
	
	public GBC setAnchor (int anchor) {
		this.anchor = anchor;
		return this;
	}
	
	public GBC setFill (int fill) {
		this.fill = fill;
		return this;
	}
	
	public GBC setWeight (double weightx, double weighty) {
		this.weightx = weightx;
		this.weighty = weighty;
		return this;
	}
	
	public GBC setInsets (int distance) {
		this.insets = new Insets(distance, distance, distance, distance);
		return this;
	}
	
	public GBC setInsetsUpandDown (int distance) {
		this.insets = new Insets(distance, distance / 2, distance, distance / 2);
		return this;
	}
	
	public GBC setIpad (int ipadx, int ipady) {
		this.ipadx = ipadx;
		this.ipady = ipady;
		return this;
	}
}

5.控制各个类:

package gobang;

import java.awt.BorderLayout;

import javax.swing.JFrame;

/**
 * Gobang类 项目的入口
 * 控制各个类
 */
public class Gobang extends JFrame {
	
	private static final long serialVersionUID = 1L;
	
	private Board board;			 // 棋盘
	private GamePanel panel;    	 // 游戏面板
	private GobangListener listener; // 监听器

	/**
	 * 构造器 初始化各个类 设置监听器
	 */
	public Gobang () {
		new Tool(this);			  // 初始化静态工具类
		board = new Board();	  // 初始化棋盘
		panel = new GamePanel();  // 初始化面板
		
		// 设置状态
		setTitle("gobang");
		setSize(Tool.GAMEWIDTH, Tool.GAMEHEIGHT);
		setResizable(false);
		setLocation(Tool.GAMELOCATION);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		// 设置布局
		setLayout(new BorderLayout());
		add(board, BorderLayout.CENTER);
		add(panel, BorderLayout.EAST);

		// 设置监听器
		listener = new GobangListener(this);
		addMouseListener(listener);
	}	
	
	/**
	 * 返回棋盘
	 */
	public Board getBoard() {
		return board;
	}

	/**
	 * 返回面板
	 */
	public GamePanel getPanel() {
		return panel;
	}
	
	/**
	 * 返回监听器
	 */
	public GobangListener getListener() {
		return listener;
	}
	
	/**
	 * 主函数 项目入口
	 */
	public static void main(String[] args) {	
		new Gobang();
	}
}

二.对弈实现:

1.检测游戏状态:

package gobang;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;

/**
 * GobangListener类 
 * 各按钮等监听器的设置
 * 各弹出面板的控制 
 * 游戏状态的检测
 */
public class GobangListener extends MouseAdapter {
	
	private int step;					// 步数
	private boolean enable; 			// 棋盘是否可以下棋
	
	private Gobang gobang;  			// Gobang实例
	private WinFrame winFrame;			// 胜利frame
	private SettingFrame settingFrame;  // 设置frame
	private AI ai;                      // ai实例
	
	private Cursor whiteChessCursor;	// 白棋鼠标
	private Cursor blackChessCursor;    // 黑棋鼠标
	 
	private Record last;				// 玩家上一步
	private ArrayList<Record> record;   // 棋局记录

	/**
	 * 构造器 初始化和设置监听器
	 * @param gobang Gobang类实例
	 */
	public GobangListener (Gobang gobang) {
		// 初始化
		
		this.gobang = gobang;
		winFrame = new WinFrame();
		settingFrame = new SettingFrame();
		ai = new AI(Color.WHITE);
		record = new ArrayList<>();
		
		step = 1;
		enable = true;
		
		Toolkit tk = Toolkit.getDefaultToolkit(); 
		whiteChessCursor = tk.createCustomCursor(Tool.whiteChess, new Point(10, 10), "norm");
		blackChessCursor = tk.createCustomCursor(Tool.blackChess, new Point(10, 10), "norm");
				
		// 设置监听器
		
		winFrame.exit.addActionListener((event) -> {
			// 退出游戏
			System.exit(0);
		});
		
		winFrame.again.addActionListener((event) -> {
			// 再来一盘
			gobang.setEnabled(true);
			restart();
			winFrame.setVisible(false);
		});
		
		winFrame.addWindowListener(new WindowAdapter() {
			// 窗口关闭后复盘 显示下棋顺序
			@Override
			public void windowClosing(WindowEvent e) {  
	              gobang.setEnabled(true);
	              gobang.getPanel().withdraw.setEnabled(false);  // 此时不能悔棋
	              enable = false;			// 此时不能下棋
	              Tool.reviewflag = true;   // 复盘 显示下棋顺序      
	         }            
		});
		
		gobang.getPanel().exit.addActionListener((event) -> {
			// 退出游戏
			System.exit(0);
		});
		
		gobang.getPanel().start.addActionListener((event) -> {
			// 开始游戏
			restart();
		});
		
		gobang.getPanel().setting.addActionListener((event) -> {
			// 设置 弹出设置frame
			gobang.setEnabled(false);
			settingFrame.setVisible(true);
		});
		
		gobang.getPanel().withdraw.addActionListener((event) -> {
			// 悔棋
			if (Tool.model == Tool.PVP) { // 人人模式移除last
				step--;
				record.remove(last);
			}
			else {						  // 人机模式移除last和ai的last
				step = step - 2; 
				record.remove(ai.getLast());
				record.remove(last);
				gobang.getBoard().getState()[ai.getLast().getI()][ai.getLast().getJ()] = null;
			}
			int i = last.getI();
			int j = last.getJ();
			// 更新上一步
			if (record.size() == 0)
				last = null;
			else		
			last = record.get(record.size() - 1);      
			gobang.getBoard().getState()[i][j] = null;
			updateUI();	
			
			gobang.getPanel().withdraw.setEnabled(false); // 设置悔棋按钮不可用
			gobang.getPanel().withdraw.setIcon(null);
			gobang.getPanel().withdraw.setForeground(Color.BLACK);
		});
		
		gobang.getPanel().defeat.addActionListener((event) -> {
			// 认输 弹出胜利frame
			gobang.setEnabled(false);
			winFrame.setWinner(step);
			winFrame.setVisible(true);
		});
		
		settingFrame.certain.addActionListener((event) -> {
			// 确认设置 
			gobang.setEnabled(true);
			saveSetting();  // 保存设置
			settingFrame.setVisible(false);
		});
		
		settingFrame.cancel.addActionListener((event) -> {
			// 取消设置
			gobang.setEnabled(true);
			// 恢复设置前的状态
			settingFrame.setModel(Tool.model);
			settingFrame.setFirst(Tool.first);
			settingFrame.setShow(Tool.show);
			settingFrame.setVisible(false);
			if (Tool.model == Tool.PVE) {  // 若为人机模式 则可改变先手
				settingFrame.firstP.setEnabled(true); 
				settingFrame.firstE.setEnabled(true); 
			}
			else {						   // 若为人人模式 则不能改变先手
				settingFrame.firstP.setEnabled(false); 
				settingFrame.firstE.setEnabled(false); 
			}
		});
		
		settingFrame.addWindowListener(new WindowAdapter() {
			// 设置窗口关闭 与确认设置相同
			public void windowClosing(WindowEvent e) {  
	              gobang.setEnabled(true);
	              saveSetting();
	              settingFrame.setVisible(false);
	         }            
		});
		
		// 更新UI
		updateUI();
	}

	/**
	 * 保存设置
	 */
	private void saveSetting() {
		int m = Tool.model;
		Tool.model = settingFrame.getModel();
		int f = Tool.first;
		Tool.first = settingFrame.getFirst();
		Tool.show = settingFrame.getShow();
		// 如果游戏模式或人机模式下先手改变 则重开游戏
		if (Tool.model != m || (Tool.model == Tool.PVE && Tool.first != f)) {
			restart();
		}
	}
	
	/**
	 * ai应对玩家下在(i,j)位置的棋
	 * @param i 横坐标
	 * @param j 纵坐标
	 */
	private void playwithAI (int i, int j) {
		Record r = ai.play(gobang.getBoard().getState(), i, j);
		record.add(r); 	// 记录这一步棋
		gobang.getBoard().setChess(r.getI(), r.getJ(), r.getColor());
		step++;
		updateUI();
		check(r.getI(), r.getJ()); // 检查是否5子相连
	}
	
	/**
	 * 重写mouseReleased方法
	 */
	@Override
	public void mouseReleased (MouseEvent e) { 
		// 不能控制棋盘 直接返回
		if (!enable)
			return ;
		
		// 获取当前点位置对应棋盘的横纵坐标
		int i = Tool.geti(e.getY());
		int j = Tool.getj(e.getX());
		// 如不在棋盘范围内 则直接返回
		if (i < 0 || j < 0 || i >= Tool.ROWNUM || j>= Tool.COLNUM)
			return ;
		
		Color c = step % 2 == 1 ? Color.BLACK : Color.WHITE;	// 棋子颜色通过步数奇偶判断	
		boolean flag = gobang.getBoard().setChess(i, j, c);     // 落子是否有效
		
		if (flag) {
			last = new Record(i, j); // 玩家上一步
			last.setColor(c);
			record.add(last);
			step++;
			updateUI();
			
			if (!check(i, j) && Tool.model == Tool.PVE) // 如果游戏继续并且是PVE模式 
				playwithAI(i, j);
		}
	}

	/**
	 * 检查游戏是否结束
	 * @param i 横坐标
	 * @param j 纵坐标
	 * @return 若胜利或平局 则返回true 游戏继续则返回false
	 */
	private boolean check(int i, int j) {
		if(checkWin(i, j) || step == Tool.ROWNUM * Tool.COLNUM + 1) {
			// 胜利或者棋盘已满(平局) 则弹出胜利frame
			gobang.setEnabled(false);
			winFrame.setWinner(step);
			winFrame.setVisible(true);
			return true;
		} 
		
		return false;
	}
	
	/**
	 * 更新UI 设置鼠标图标和游戏面板状态
	 */
	private void updateUI () {
		if (step % 2 == 1) 
			gobang.getBoard().setCursor(blackChessCursor); 
		else 
			gobang.getBoard().setCursor(whiteChessCursor); 
		
		gobang.getPanel().updateUI(step);
	}

	/**
	 * 重新开始
	 */
	private void restart() {
		// 复位
		step = 1;
		enable = true;
		Tool.reviewflag = false;
		gobang.getBoard().restart();
		record.clear();
		
		if (Tool.model == Tool.PVE && Tool.first == Tool.FIRSTE) {	
			// PVE并且ai先手 
			ai.setColor(Color.BLACK);
			// 第一步正中央
			Record r = new Record(Tool.ROWNUM / 2, Tool.COLNUM / 2);
			r.setColor(Color.BLACK);
			record.add(r);
			step++;		
			gobang.getBoard().setChess(Tool.ROWNUM / 2, Tool.COLNUM / 2, Color.BLACK);
			ai.setFirst(gobang.getBoard().getState());
		} else if (Tool.model == Tool.PVE && Tool.first == Tool.FIRSTP) {
			// PVE并且玩家先手
			ai.setColor(Color.WHITE);
		}
		updateUI();
	}
	
	/**
	 * 返回棋局记录
	 */
	public ArrayList<Record> getRecord() {
		return record;
	}
	
	/**
	 * 检查是否获胜 判断(i,j)四个方向上是否有五子相连
	 * @param i 横坐标
	 * @param j 纵坐标
	 * @return 若胜利返回true 否则返回false
	 */
	private Boolean checkWin (int i, int j) {
		int x;
		int y;
		int count;
		Chess[][] state = gobang.getBoard().getState();
		Color c = state[i][j].getColor();
		
		// 竖向
		count = 1;
		x = i - 1;
		y = j;
		while (x >= 0 && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x--;
		}
		x = i + 1;
		y = j;
		while (x < Tool.ROWNUM && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x++;
		}
		if (count >= 5)
			return true;
		
		// 横向
		count = 1;
		x = i;
		y = j - 1;
		while (y >= 0 && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			y--;
		}
		x = i;
		y = j + 1;
		while (y < Tool.COLNUM && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			y++;
		}
		if (count >= 5)
			return true;
		
		// 右上-左下
		count = 1;
		x = i - 1;
		y = j + 1;
		while (x >= 0 && y < Tool.COLNUM && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x--;
			y++;
		}
		x = i + 1;
		y = j - 1;
		while (x < Tool.ROWNUM && y >= 0 && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x++;
			y--;
		}
		if (count >= 5)
			return true;
		
		// 左上-右下
		count = 1;
		x = i - 1;
		y = j - 1;
		while (x >= 0 && y >= 0 && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x--;
			y--;
		}
		x = i + 1;
		y = j + 1;
		while (x < Tool.ROWNUM && y < Tool.COLNUM && state[x][y] != null && state[x][y].getColor().equals(c)) {
			count++;
			x++;
			y++;
		}
		if (count >= 5)
			return true;
		return false;
	}
}

2.记录棋子:

package gobang;

import java.awt.Color;

/**
 * Record类 记录一个棋子的颜色和位置
 */
class Record {
	
	private Color color; // 颜色
	private int i;		 // 横坐标
	private int j;       // 纵坐标
	
	/**
	 * 构造器
	 * @param color 颜色
	 */
	public Record (Color color) {
		this.color = color;
	}

	/**
	 * 构造器
	 * @param i 横坐标
	 * @param j 纵坐标
	 */
	public Record (int i, int j) {
		this.i = i;
		this.j = j;
	}
	
	/**
	 * 获取横坐标
	 */
	public int getI() {
		return i;
	}

	/**
	 * 设置横坐标
	 * @param i 横坐标
	 */
	public void setI(int i) {
		this.i = i;
	}

	/**
	 * 获取纵坐标
	 */
	public int getJ() {
		return j;
	}

	/**
	 * 设置纵坐标
	 * @param j 纵坐标
	 */
	public void setJ(int j) {
		this.j = j;
	}

	/**
	 * 获取颜色
	 */
	public Color getColor() {
		return color;
	}
	
	/**
	 * 设置颜色
	 * @param color 颜色
	 */
	public void setColor(Color color) {
		this.color = color;
	}
}

3.对弈设置:

package gobang;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;

import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;

/**
 * SettingFrame类 设置frame
 * 点击游戏设置时弹出
 */
public class SettingFrame extends JFrame {
	
	private static final long serialVersionUID = 1L;
	
	private JLabel model;			// 游戏模式
	private JRadioButton PVE;   	// 人机
	private JRadioButton PVP;  	 	// 人人
	
	private JLabel first;      	 	// 先手
	JRadioButton firstP;       	 	// 玩家先手
	JRadioButton firstE;      		// 电脑先手
	
	private JLabel show;			// 显示方式
	private JRadioButton showlast;  // 只标记上一步
	private JRadioButton showall;   // 标记所有棋子(1, 2, 3...)
	
	JButton certain;				// 确认键
	JButton cancel;                 // 取消键
	
	/**
	 * 构造器 初始化
	 */
	public SettingFrame () {
		// 设置属性
		setTitle("setting");
		setSize(Tool.SETTINWIDTH, Tool.SETTINGHEIGHT);
		setLocation(Tool.SETTINGLOCATION);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		
		// 设置背景
		JPanel panel = new JPanel() {
			private static final long serialVersionUID = 1L;

			@Override
			protected void paintComponent(Graphics g) {   
		        g.drawImage(Tool.settingframe, 0, 0, getSize().width, getSize().height, this);  
			}	
	    } ;
		panel.setOpaque(false);
	    
		// 模式选项
		model = new JLabel("模式");
		Tool.initComponent(model, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		PVE = new JRadioButton("人机对战");
		Tool.initComponent(PVE, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		PVP = new JRadioButton("人人对战");
		Tool.initComponent(PVP, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		ButtonGroup bgmodel = new ButtonGroup();
		PVE.setSelected(true);
		bgmodel.add(PVE);
		bgmodel.add(PVP);
		
		// 先手选项
		first = new JLabel("先手");
		Tool.initComponent(first, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		firstP = new JRadioButton("玩家先行");
		Tool.initComponent(firstP, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		firstE = new JRadioButton("电脑先行");
		Tool.initComponent(firstE, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		ButtonGroup bgfirst = new ButtonGroup();
		firstP.setSelected(true);
		bgfirst.add(firstP);
		bgfirst.add(firstE);
		
		// 显示选项
		show = new JLabel("显示");
		Tool.initComponent(show, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		showlast = new JRadioButton("标记一步");
		Tool.initComponent(showlast, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		showall = new JRadioButton("标记所有");
		Tool.initComponent(showall, Tool.getFont(Tool.fontwei, Font.PLAIN, 24));
		ButtonGroup bgshow = new ButtonGroup();
		showlast.setSelected(true);
		bgshow.add(showlast);
		bgshow.add(showall);
		
		// 确认按钮
		certain = new JButton("确定");
		Tool.initComponent(certain, Tool.getFont(Tool.fontkai, Font.PLAIN, 20));
		certain.setIcon(new ImageIcon(Tool.blackChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT)));
		
		// 取消按钮
		cancel = new JButton("取消");
		Tool.initComponent(cancel, Tool.getFont(Tool.fontkai, Font.PLAIN, 20));
		cancel.setIcon(new ImageIcon(Tool.whiteChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT)));
		
		// 设置布局
		
		add(panel);
		
		panel.setLayout(new GridBagLayout());
		panel.add(model, new GBC(0, 0).setInsets(5));
		panel.add(PVE, new GBC(1, 0).setInsets(5));
		panel.add(PVP, new GBC(2, 0).setInsets(5));
		panel.add(first, new GBC(0, 1).setInsets(5));
		panel.add(firstP, new GBC(1, 1).setInsets(5));
		panel.add(firstE, new GBC(2, 1).setInsets(5));
		panel.add(show, new GBC(0, 2).setInsets(5));
		panel.add(showlast, new GBC(1, 2).setInsets(5));
		panel.add(showall, new GBC(2, 2).setInsets(5));
		panel.add(certain, new GBC(1, 3).setInsets(5));
		panel.add(cancel, new GBC(2, 3).setInsets(5));
		
		// 选择PVP时禁用先手选项
		PVP.addActionListener((event) -> {
			firstP.setEnabled(false);
			firstE.setEnabled(false);
		});
		// 选择PVE时启用先手选项
		PVE.addActionListener((event) -> {
			firstP.setEnabled(true);
			firstE.setEnabled(true);
		});
	}

	/**
	 * 获取模式
	 */
	public int getModel() {
		return PVP.isSelected() ? Tool.PVP : Tool.PVE;
	}
	
	/**
	 * 获取先手
	 * @return
	 */
	public int getFirst() {
		return firstP.isSelected() ? Tool.FIRSTP : Tool.FIRSTE;
	}
	
	/**
	 * 获取显示
	 * @return
	 */
	public int getShow() {
		return showall.isSelected() ? Tool.SHOWALL : Tool.SHOWLAST;
	}
	
	/**
	 * 设置模式
	 * @param i 模式
	 */
	public void setModel(int i) {
		switch (i) {
			case Tool.PVE : PVE.setSelected(true); break;
			case Tool.PVP : PVP.setSelected(true); break;
		}
	}
	
	/**
	 * 设置先手
	 * @param i 先手
	 */
	public void setFirst(int i) {
		switch (i) {
			case Tool.FIRSTP : firstP.setSelected(true); break;
			case Tool.FIRSTE : firstE.setSelected(true); break;
		}
	}
	
	/**
	 * 设置显示
	 * @param i 显示
	 */
	public void setShow(int i) {
		switch (i) {
			case Tool.SHOWLAST : showlast.setSelected(true); break;
			case Tool.SHOWALL  : showall.setSelected(true);  break;
		}
	}
}

4.AI对弈的实现:

package gobang;

import java.awt.Color;
import java.util.HashMap;

/**
 * AI类  权值法实现电脑下棋
 */
public class AI {

	private HashMap<Integer, Integer> weightmap; 	// 权值哈希表
	
	private int[][] weightE;  // 存储电脑权值
	private int[][] weightP;  // 存储玩家权值
		
	private Color colorE;	  // 电脑棋子颜色
	private Color colorP;	  // 玩家棋子颜色
	
	private Chess[][] state;  // 棋盘状态
	private Record last;	  // 记录电脑上一步棋
	
	/**
	 * 构造器
	 * @param c 电脑的棋子颜色
	 */
	public AI (Color c) {
		setColor(c);
		weightmap = new HashMap<>();
		weightE = new int[Tool.ROWNUM][Tool.COLNUM];
		weightP = new int[Tool.ROWNUM][Tool.COLNUM];
			
		// 哈希表
		// key的第一个数字代表某一方向上连子的数量 范围 1 - 5
		// key的第二个数字代表该方向上两端可延伸的方向数量 范围 0 - 2
		// value为所赋权值
		weightmap.put(10, 0);
		weightmap.put(11, 5);
		weightmap.put(12, 8);
		weightmap.put(20, 0);
		weightmap.put(21, 15);
        weightmap.put(22, 18);
        weightmap.put(30, 0);
        weightmap.put(31, 30);
        weightmap.put(32, 100);
        weightmap.put(40, 0);
        weightmap.put(41, 100);
        weightmap.put(42, 200);
        weightmap.put(50, 1000);
        weightmap.put(51, 1000);
        weightmap.put(52, 1000);
	}
	
	/**
	 * 电脑先行 第一步下在棋盘中央
	 * @param state 棋局状态 棋子的二维数组
	 */
	public void setFirst (Chess[][] state) {
		this.state = state;
		int i = Tool.ROWNUM / 2;
		int j = Tool.COLNUM / 2;
		updateWeight(i, j);
		Record r = new Record(colorE);
		r.setI(i);
		r.setJ(j);
		last = r;		
	}
	
	/**
	 * 玩家先行
	 * @param state 棋局状态
	 * @param x 玩家上一步的横坐标
	 * @param y 玩家上一步的纵坐标
	 * @return 记录电脑下棋的位置和颜色
	 */
	public Record play (Chess[][] state, int x, int y) {
		this.state = state;
		// 更新权值表
		updateWeight(x, y);
		Record r = new Record(colorE);
		
		int max = 0;		// 记录电脑或玩家的最大权值
		int opposite = 0;   // 记录最大权值所在位置对手的权值
		// 遍历两个权值二维数组 找到最大权值
		// 如果权值相等 选择对手权值较大的点
		for (int i = 0; i < Tool.ROWNUM; i++) {
			for (int j = 0; j < Tool.COLNUM; j++) {
				if (weightP[i][j] > max) {
					max = weightP[i][j];
					r.setI(i);
					r.setJ(j);
					opposite = weightE[i][j];
				} else if (weightP[i][j] == max && weightE[i][j] > opposite) {
					max = weightP[i][j];
					r.setI(i);
					r.setJ(j);
					opposite = weightE[i][j];
				}
				if (weightE[i][j] > max) {
					max = weightE[i][j];
					r.setI(i);
					r.setJ(j);
					opposite = weightP[i][j];
				} else if (weightE[i][j] == max && weightP[i][j] > opposite) {
					max = weightE[i][j];
					r.setI(i);
					r.setJ(j);
					opposite = weightP[i][j];
				}
			}
		}
		
		// 记录电脑的上一步棋
		last = r;
		// 更新权值表
		updateWeight(r.getI(), r.getJ());
		
		return r;
	}
	
	/**
	 * 更新权值表
	 * @param i 上一步棋的横坐标
	 * @param j 上一步棋的纵坐标
	 */
	private void updateWeight (int i, int j) {	
		// 遍历棋盘的每一个位置 探测该点权值
		for (int p = 0; p < Tool.ROWNUM; p++) {
			for (int q = 0; q < Tool.COLNUM; q++) {
				if (state[p][q] != null)
					continue;
				weightE[p][q] = getWeight(p, q, colorE);
				weightP[p][q] = getWeight(p, q, colorP);
			}
		}
		
		// 有棋子的点权值置为0
		weightE[i][j] = 0;
		weightP[i][j] = 0;
	}
	
	/**
	 * 探测(i,j)位置的权值大小
	 * @param i 横坐标
	 * @param j 纵坐标
	 * @param c 该点颜色
	 * @return 返回4个方向权值大小之和作为该点的权值
	 */
	private int getWeight (int i, int j, Color c) {
		int weight = 0;
		int count; // 某一方向上连子数量 关键字的第一位
		int p;
		int q;
		int flag;  // 某一方向上可延伸的方向数 关键字第二位
		
		// 竖向
		flag = 0;
		count = 1;
		p = i - 1;
		q = j;
		while (p >= 0 && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p--;
		}
		// 如果上方没有棋子 则上方可以延伸 flag+1
		if (p >= 0 && state[p][q] == null)
			flag++;
		p = i + 1;
		q = j;
		while (p < Tool.ROWNUM && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p++;
		}
		// 如果下方没有棋子 则下方可以延伸 flag+1
		if (p < Tool.ROWNUM && state[p][q] == null)
			flag++;
		
		// 连子数超过5 按5计算
		if (count > 5)
			count = 5;
		// 从哈希表中取出权值
		weight += weightmap.get(count * 10 + flag);
		
		// 其他方向以此类推
		
		// 横向
		flag = 0;
		count = 1;
		p = i;
		q = j - 1;
		while (q >= 0 && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			q--;
		}
		if (q >= 0 && state[p][q] == null)
			flag++;
		p = i;
		q = j + 1;
		while (q < Tool.COLNUM && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			q++;
		}
		if (q < Tool.COLNUM && state[p][q] == null)
			flag++;
		if (count > 5)
			count = 5;
		weight += weightmap.get(count * 10 + flag);
		
		// 右上-左下
		flag = 0;
		count = 1;
		p = i - 1;
		q = j + 1;
		while (p >= 0 && q < Tool.COLNUM && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p--;
			q++;
		}
		if (p >= 0 && q < Tool.COLNUM && state[p][q] == null)
			flag++;
		p = i + 1;
		q = j - 1;
		while (p < Tool.ROWNUM && q >= 0 && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p++;
			q--;
		}
		if (p < Tool.ROWNUM && q >= 0 && state[p][q] == null)
			flag++;
		if (count > 5)
			count = 5;
		weight += weightmap.get(count * 10 + flag);
	
		// 左上-右下
		flag = 0;
		count = 1;
		p = i - 1;
		q = j - 1;
		while (p >= 0 && q >= 0 && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p--;
			q--;
		}
		if (p >= 0 && q >= 0 && state[p][q] == null)
			flag++;
		p = i + 1;
		q = j + 1;
		while (p < Tool.ROWNUM && q < Tool.COLNUM && state[p][q] != null && state[p][q].getColor().equals(c)) {
			count++;
			p++;
			q++;
		}
		if (p < Tool.ROWNUM && q < Tool.COLNUM && state[p][q] == null)
			flag++;
		if (count > 5)
			count = 5;
		weight += weightmap.get(count * 10 + flag);
		
		return weight;
	}
	
	/**
	 * 返回电脑的上一步
	 */
	public Record getLast() {
		return last;
	}

	/**
	 * 设置电脑棋子颜色与玩家棋子颜色
	 * @param c 电脑棋子颜色
	 */
	public void setColor(Color c) {
		colorE = c;
		if (c.equals(Color.WHITE))
			colorP = Color.BLACK;
		else
			colorP = Color.WHITE;
	}
}

5.结果判断:

package gobang;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.Image;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

/**
 * WinFrame类 设置frame
 * 获胜或认输时弹出
 */
public class WinFrame extends JFrame {
	
	private static final long serialVersionUID = 1L;
	
	JButton again;          // 再来一盘
	JButton exit;           // 退出游戏
	private JLabel winner;  // 获胜者
	
	private static String whitewin = "白棋获胜!";
	private static String blackwin = "黑棋获胜!";
	private static String draw = "平局!";
	
	/**
	 * 构造器 初始化
	 */
	public WinFrame () {
		// 设置属性
		setTitle("win");
		setSize(Tool.WINWIDTH, Tool.WINHEIGHT);
		setLocation(Tool.WINLOCATION);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		// 设置背景
		JPanel panel = new JPanel() {
			private static final long serialVersionUID = 1L;

			@Override
			protected void paintComponent(Graphics g) {   
		        g.drawImage(Tool.winframe, 0, 0, getSize().width, getSize().height, this);  
			}	
	    } ;
		panel.setOpaque(false);
	    
		// 胜利者
		winner = new JLabel();
		winner.setFont(Tool.getFont(Tool.fontkai, Font.BOLD, 36));
		
		// 再来一盘
		again = new JButton("再来一盘");	
		Tool.initComponent(again, Tool.getFont(Tool.fontkai, Font.PLAIN, 20));
		again.setIcon(new ImageIcon(Tool.blackChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT)));
		
		// 退出游戏
		exit = new JButton("退出游戏");
		Tool.initComponent(exit, Tool.getFont(Tool.fontkai, Font.PLAIN, 20));
		exit.setIcon(new ImageIcon(Tool.whiteChess.getScaledInstance(Tool.ICONSIZE, Tool.ICONSIZE, Image.SCALE_DEFAULT)));
		
		// 设置布局
		
		add(panel);
		
		panel.setLayout(new GridBagLayout());
		panel.add(winner, new GBC(0, 0, 2, 1).setInsets(15));
		panel.add(again, new GBC(0, 1).setInsets(10));
		panel.add(exit, new GBC(1, 1).setInsets(10));
	}
	
	/**
	 * 设置胜利者
	 * @param step 步数
	 */
	public void setWinner (int step) {
		// 根据步数奇偶性判断胜利者
		if (step % 2 == 1)
			winner.setText(whitewin);
		else
			winner.setText(blackwin);
	}
	
	/**
	 * 平局时
	 */
	public void setDraw() {
		winner.setText(draw);
	}
}

三.界面:

1.图像设置:

package gobang;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.SwingConstants;

/**
 * Tool类 静态工具类
 * 只包含静态域和静态方法
 */
public class Tool {
	
	static Gobang game;  	// Gobang实例
	
	static final int ROWNUM = 15;			// 行数
	static final int COLNUM = 15;   		// 列数
	static final int BOARDSIZE = 750;  		// 棋盘大小
	static final int MARGIN = 25; 			// 棋盘边距
	static final int CELL = 50;				// 每个格子大小
	static final int CHESSSIZE = 45;	    // 棋子图片大小
	static final int BOARDPOINTSIZE = 10;   // 棋盘点大小(4个角+中央点)
	static final int ICONSIZE = 15;			// 图标大小(按钮旁边的黑白棋子)
	static final int LARGEBUTTONWIDTH = 155;// 大按钮的宽(开始游戏...)
	static final int LARGEBUTTONHEIGHT = 36;// 大按钮的高
	static final int SMALLBUTTONWIDTH = 75; // 小按钮的宽(悔棋...)
	static final int SMALLBUTTONHEIGHT = 24;// 小按钮的高
	static final int GAMEWIDTH = 950;		// 主窗口的宽
	static final int GAMEHEIGHT = 800;      // 主窗口的高
	static final Dimension PANELDIMENSION = new Dimension(180, 0); 	// 游戏面板显示位置
	static final Point GAMELOCATION = new Point(500, 100);          // 主窗口显示位置
	static final Point WINLOCATION = new Point(710, 420);			// 胜利窗口显示位置
	static final Point SETTINGLOCATION = new Point(710, 420);		// 设置窗口显示位置
	static final int SETTINWIDTH = 400;		// 设置窗口宽
	static final int SETTINGHEIGHT = 200;   // 设置窗口高
	static final int WINWIDTH = 300;		// 胜利窗口宽
	static final int WINHEIGHT = 180;       // 胜利窗口高
	static final int TITLEWIDTH = 100;      // 标题图片宽
	static final int TTITLEHEIGHT = 300;	// 标题图片高
	static final int RECTSIZE = 10;			// 方形标记尺寸
	static final int NUMOFFSETX = -5;		// 数字标记x向偏移量
	static final int NUMOFFSETY = 5;		// 数字标记y向偏移量
	static final int FONTSIZE = 20;         // 数字标记字号
	
	static final int PVE = 0;		// PVE定义为0
	static final int PVP = 1;       // PVP定义为1
	static int model;				// 模式变量
	static final int FIRSTP = 0;	// 玩家先手定义为0
	static final int FIRSTE = 1;    // 电脑先手定义为1
	static int first;               // 先手变量
	static final int SHOWLAST = 0;  // 只显示上一步定义为0
	static final int SHOWALL = 1;   // 显示所有定义为1
	static int show;				// 显示变量
	static boolean reviewflag;		// 复盘标志
	
	static String fontwei = "res\\STXINWEI.TTF";  // 华文新魏字体路径
	static String fontkai = "res\\STXINGKA.TTF";  // 华文行楷字体路径
	
	static Image blackChess = new ImageIcon("res\\black.png").getImage();  			// 黑棋图片
	static Image whiteChess = new ImageIcon("res\\white.png").getImage();  			// 白棋图片
	static Image select = new ImageIcon("res\\select.png").getImage();	   			// 按钮被选中图片
	static Image board = new ImageIcon("res\\board.jpg").getImage();	  			// 棋盘背景
	static Image settingframe = new ImageIcon("res\\settingframe.png").getImage();  // 设置背景
	static Image winframe = new ImageIcon("res\\winframe.png").getImage();			// 获胜背景
	static Image background = new ImageIcon("res\\background.png").getImage();		// 面板背景
	static Image gobang = new ImageIcon("res\\gobang.png").getImage();				// 五子棋标题图片
	
	/**
	 * 构造器
	 * @param gobang Gobang类实例
	 */
	public Tool(Gobang gobang) {
		game = gobang;
	}
	
	/**
	 * 初始化组件 设置背景透明 无边框 字体
	 * @param x 组件
	 * @param f 字体
	 */
	static void initComponent (JComponent x, Font f) {
		x.setBackground(new Color(255, 255, 255));
		x.setFont(f);
		x.setOpaque(false);
		x.setBorder(BorderFactory.createLineBorder(Color.BLACK, 0));
	}
	
	/**
	 * 设置按钮被选中效果
	 * @param b 按钮
	 * @param width 图片宽
	 * @param height 图片高
	 */
	static void setEntered (JButton b, int width, int height) {
		b.setIcon(new ImageIcon(Tool.select.getScaledInstance(width, height, Image.SCALE_DEFAULT)));
		b.setHorizontalTextPosition(SwingConstants.CENTER);
		b.setForeground(Color.WHITE);  // 字体颜色
	}
	
	/**
	 * 设置按钮未选中效果
	 * @param b 按钮
	 */
	static void setExited (JButton b) {
		b.setIcon(null);
		b.setForeground(Color.BLACK);  // 字体颜色
	}

	/**
	 * 为按钮添加鼠标监听器 实现选中效果
	 * @param b 按钮
	 * @param width 图片宽
	 * @param height 图片高
	 */
	static void addMListener(JButton b, int width, int height) {
		b.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseEntered(MouseEvent arg0) {	
				Tool.setEntered(b, width, height);
				super.mouseEntered(arg0);
			}
			
			@Override
			public void mouseExited(MouseEvent arg0) {	
				Tool.setExited(b);
				super.mouseExited(arg0);
			}
		});
	}
	
	/**
	 * 获取字体
	 * @param path 字体路径
	 * @param type 字体类型(plain,bold...)
	 * @param size 字体大小
	 * @return 生成字体
	 */
	static Font getFont(String path, int type, int size) {
		Font font = null;
		File file = new File(path);
		try {
			font = Font.createFont(Font.TRUETYPE_FONT, file);
			font = font.deriveFont(type, size);
		} catch (FontFormatException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return font;
	}
	
	/**
	 * 仅为上一步棋添加方形标记
	 * @param g 画笔
	 * @param r 记录
	 */
	private static void showlast (Graphics2D g, ArrayList<Record> r) {		
		Record last = r.get(r.size()-1);
		if (last == null)
			return ;
		
		int x = last.getI();
		int y = last.getJ();
		Color color = last.getColor().equals(Color.WHITE) ? Color.DARK_GRAY : Color.LIGHT_GRAY;
		g.setColor(color);
		g.setStroke(new BasicStroke(2.0f));
		g.drawRect(CELL * y - RECTSIZE / 2 + MARGIN, CELL * x - RECTSIZE / 2 + MARGIN, RECTSIZE, RECTSIZE);
	}

	/**
	 * 为所有棋子添加步数标记
	 * @param g 画笔
	 * @param r 记录
	 */
	private static void showall(Graphics2D g, ArrayList<Record> r) {
		int i = 1;
		g.setFont(getFont(fontwei, Font.BOLD, FONTSIZE));
		int x;
		int y;
		for (Record item : r) {
			Color c = item.getColor().equals(Color.WHITE) ? Color.DARK_GRAY : Color.LIGHT_GRAY;
			g.setColor(c);
			if (i > 99) 	// 三位数
				x = CELL * item.getJ() + MARGIN + NUMOFFSETX * 3;
			else if (i > 9) // 两位数
				x = CELL * item.getJ() + MARGIN + NUMOFFSETX * 2;
			else 			// 一位数
				x = CELL * item.getJ() + MARGIN + NUMOFFSETX * 1;
			
			y = CELL * item.getI() + MARGIN + NUMOFFSETY;
			g.drawString(String.valueOf(i++), x, y);
		}
	}
	
	/**
	 * 显示标记
	 * @param g 画笔
	 */
	static void show (Graphics g) {
		if (game.getListener() == null)
			return ;
		ArrayList<Record> r = game.getListener().getRecord();
		if (r == null || r.size() == 0)
			return ;
		
		Graphics2D g2 = (Graphics2D) g;
		if (show == 1 || reviewflag)  // 当选项显示所有被选中或游戏结束复盘时
			showall(g2, r);
		else
			showlast(g2, r);
		// 立即更新UI
		game.getBoard().updateUI(); 
	}

	/**
	 * 通过点的横坐标计算对应棋子的列数
	 * @param x 横坐标
	 * @return 列
	 */
	static int getj (int x) {
		return (int) Math.round(1.0 * (x - MARGIN) / CELL);
	}

	/**
	 * 通过点点纵坐标计算对应棋子的行数
	 * @param y 纵坐标
	 * @return 行
	 */
	static int geti (int y) {
		return (int) Math.round(1.0 * (y - MARGIN * 2) / CELL);
	}
}

运行结果截图:

简单 五子棋的客户端部分程序 //chessClient.java:客户端主程序。 //userPad.java:客户端的界面。 //chessPad.java:棋盘的绘制。 //chessServer.java:服务器端。 //可同时容纳50个人同时在线下棋,聊天。 import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.util.*; class clientThread extends Thread{ chessClient chessclient; clientThread(chessClient chessclient){ this.chessclient=chessclient; } public void acceptMessage(String recMessage){ if(recMessage.startsWith("/userlist ")){ StringTokenizer userToken=new StringTokenizer(recMessage," "); int userNumber=0; chessclient.userpad.userList.removeAll(); chessclient.inputpad.userChoice.removeAll(); chessclient.inputpad.userChoice.addItem("所有人"); while(userToken.hasMoreTokens()){ String user=(String)userToken.nextToken(" "); if(userNumber>0 && !user.startsWith("[inchess]")){ chessclient.userpad.userList.add(user); chessclient.inputpad.userChoice.addItem(user); } userNumber++; } chessclient.inputpad.userChoice.select("所有人"); } else if(recMessage.startsWith("/yourname ")){ chessclient.chessClientName=recMessage.substring(10); chessclient.setTitle("Java五子棋客户端 "+"用户名:"+chessclient.chessClientName); } else if(recMessage.equals("/reject")){ try{ chessclient.chesspad.statusText.setText("不能加入游戏"); chessclient.controlpad.cancelGameButton.setEnabled(false); chessclient.controlpad.joinGameButton.setEnabled(true); chessclient.controlpad.creatGameButton.setEnabled(true); } catch(Exception ef){ chessclient.chatpad.chatLineArea.setText("chessclient.chesspad.chessSocket.close无法关闭"); } chessclient.controlpad.joinGameButton.setEnabled(true); } else if(recMessage.startsWith("/peer ")){ chessclient.chesspad.chessPeerName=recMessage.substring(6); if(chessclient.isServer){ chessclient.chesspad.chessColor=1; chessclient.chesspad.isMouseEnabled=true; chessclient.chesspad.statusText.setText("请黑棋下子"); } else if(chessclient.isClient){ chessclient.chesspad.chessColor=-1; chessclient.chesspad.statusText.setText("已加入游戏,等待对方下子..."); } } else if(recMessage.equals("/youwin")){ chessclient.isOnChess=false; chessclient.chesspad.chessVictory(chessclient.chesspad.chessColor); chessclient.chesspad.statusText.setText("对方退出,请点放弃游戏退出连接"); chessclient.chesspad.isMouseEnabled=false; } else if(recMessage.equals("/OK")){ chessclient.chesspad.statusText.setText("创建游戏成功,等待别人加入..."); } else if(recMessage.equals("/error")){ chessclient.chatpad.chatLineArea.append("传输错误:请退出程序,重新加入 \n"); } else{ chessclient.chatpad.chatLineArea.append(recMessage+"\n"); chessclient.chatpad.chatLineArea.setCaretPosition( chessclient.chatpad.chatLineArea.getText().length()); } } public void run(){ String message=""; try{ while(true){ message=chessclient.in.readUTF(); acceptMessage(message); } } catch(IOException es){ } } } public class chessClient extends Frame implements ActionListener,KeyListener{ userPad userpad=new userPad(); chatPad chatpad=new chatPad(); controlPad controlpad=new controlPad(); chessPad chesspad=new chessPad(); inputPad inputpad=new inputPad(); Socket chatSocket; DataInputStream in; DataOutputStream out; String chessClientName=null; String host=null; int port=4331; boolean isOnChat=false; //在聊天? boolean isOnChess=false; //在下棋? boolean isGameConnected=false; //下棋的客户端连接? boolean isServer=false; //如果是下棋的主机 boolean isClient=false; //如果是下棋的客户端 Panel southPanel=new Panel(); Panel northPanel=new Panel(); Panel centerPanel=new Panel(); Panel westPanel=new Panel(); Panel eastPanel=new Panel(); chessClient( ){ super("Java五子棋客户端"); setLayout(new BorderLayout()); host=controlpad.inputIP.getText(); westPanel.setLayout(new BorderLayout()); westPanel.add(userpad,BorderLayout.NORTH); westPanel.add(chatpad,BorderLayout.CENTER); westPanel.setBackground(Color.pink); inputpad.inputwords.addKeyListener(this); chesspad.host=controlpad.inputIP.getText(); centerPanel.add(chesspad,BorderLayout.CENTER); centerPanel.add(inputpad,BorderLayout.SOUTH); centerPanel.setBackground(Color.pink); controlpad.connectButton.addActionListener(this); controlpad.creatGameButton.addActionListener(this); controlpad.joinGameButton.addActionListener(this); controlpad.cancelGameButton.addActionListener(this); controlpad.exitGameButton.addActionListener(this); controlpad.creatGameButton.setEnabled(false); controlpad.joinGameButton.setEnabled(false); controlpad.cancelGameButton.setEnabled(false); southPanel.add(controlpad,BorderLayout.CENTER); southPanel.setBackground(Color.pink); addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ if(isOnChat){ try{ chatSocket.close(); }catch(Exception ed){ } } if(isOnChess || isGameConnected){ try{ chesspad.chessSocket.close(); }catch(Exception ee){ } } System.exit(0); } public void windowActivated(WindowEvent ea){ } }); add(westPanel,BorderLayout.WEST); add(centerPanel,BorderLayout.CENTER); add(southPanel,BorderLayout.SOUTH); pack(); setSize(670,548); setVisible(true); setResizable(false); validate(); } public boolean connectServer(String serverIP,int serverPort) throws Exception{ try{ chatSocket=new Socket(serverIP,serverPort); in=new DataInputStream(chatSocket.getInputStream()); out=new DataOutputStream(chatSocket.getOutputStream()); clientThread clientthread=new clientThread(this); clientthread.start(); isOnChat=true; return true; } catch(IOException ex){ chatpad.chatLineArea.setText("chessClient:connectServer:无法连接,建议重新启动程序 \n"); } return false; } public void actionPerformed(ActionEvent e){ if(e.getSource()==controlpad.connectButton){ host=chesspad.host=controlpad.inputIP.getText(); try{ if(connectServer(host,port)){ chatpad.chatLineArea.setText(""); controlpad.connectButton.setEnabled(false); controlpad.creatGameButton.setEnabled(true); controlpad.joinGameButton.setEnabled(true); chesspad.statusText.setText("连接成功,请创建游戏或加入游戏"); } } catch(Exception ei){ chatpad.chatLineArea.setText("controlpad.connectButton:无法连接,建议重新启动程序 \n"); } } if(e.getSource()==controlpad.exitGameButton){ if(isOnChat){ try{ chatSocket.close(); } catch(Exception ed){ } } if(isOnChess || isGameConnected){ try{ chesspad.chessSocket.close(); } catch(Exception ee){ } } System.exit(0); } if(e.getSource()==controlpad.joinGameButton){ String selectedUser=userpad.userList.getSelectedItem(); if(selectedUser==null || selectedUser.startsWith("[inchess]") || selectedUser.equals(chessClientName)){ chesspad.statusText.setText("必须先选定一个有效用户"); } else{ try{ if(!isGameConnected){ if(chesspad.connectServer(chesspad.host,chesspad.port)){ isGameConnected=true; isOnChess=true; isClient=true; controlpad.creatGameButton.setEnabled(false); controlpad.joinGameButton.setEnabled(false); controlpad.cancelGameButton.setEnabled(true); chesspad.chessthread.sendMessage("/joingame "+userpad.userList.getSelectedItem()+" "+chessClientName); } }else{ isOnChess=true; isClient=true; controlpad.creatGameButton.setEnabled(false); controlpad.joinGameButton.setEnabled(false); controlpad.cancelGameButton.setEnabled(true); chesspad.chessthread.sendMessage("/joingame "+userpad.userList.getSelectedItem()+" "+chessClientName); } } catch(Exception ee){ isGameConnected=false; isOnChess=false; isClient=false; controlpad.creatGameButton.setEnabled(true); controlpad.joinGameButton.setEnabled(true); controlpad.cancelGameButton.setEnabled(false); chatpad.chatLineArea.setText("chesspad.connectServer无法连接 \n"+ee); } } } if(e.getSource()==controlpad.creatGameButton){ try{ if(!isGameConnected){ if(chesspad.connectServer(chesspad.host,chesspad.port)){ isGameConnected=true; isOnChess=true; isServer=true; controlpad.creatGameButton.setEnabled(false); controlpad.joinGameButton.setEnabled(false); controlpad.cancelGameButton.setEnabled(true); chesspad.chessthread.sendMessage("/creatgame "+"[inchess]"+chessClientName); } }else{ isOnChess=true; isServer=true; controlpad.creatGameButton.setEnabled(false); controlpad.joinGameButton.setEnabled(false); controlpad.cancelGameButton.setEnabled(true); chesspad.chessthread.sendMessage("/creatgame "+"[inchess]"+chessClientName); } } catch(Exception ec){ isGameConnected=false; isOnChess=false; isServer=false; controlpad.creatGameButton.setEnabled(true); controlpad.joinGameButton.setEnabled(true); controlpad.cancelGameButton.setEnabled(false); ec.printStackTrace(); chatpad.chatLineArea.setText("chesspad.connectServer无法连接 \n"+ec); } } if(e.getSource()==controlpad.cancelGameButton){ if(isOnChess){ chesspad.chessthread.sendMessage("/giveup "+chessClientName); chesspad.chessVictory(-1*chesspad.chessColor); controlpad.creatGameButton.setEnabled(true); controlpad.joinGameButton.setEnabled(true); controlpad.cancelGameButton.setEnabled(false); chesspad.statusText.setText("请建立游戏或者加入游戏"); } if(!isOnChess){ controlpad.creatGameButton.setEnabled(true); controlpad.joinGameButton.setEnabled(true); controlpad.cancelGameButton.setEnabled(false); chesspad.statusText.setText("请建立游戏或者加入游戏"); } isClient=isServer=false; } } public void keyPressed(KeyEvent e){ TextField inputwords=(TextField)e.getSource(); if(e.getKeyCode()==KeyEvent.VK_ENTER){ if(inputpad.userChoice.getSelectedItem().equals("所有人")){ try{ out.writeUTF(inputwords.getText()); inputwords.setText(""); } catch(Exception ea){ chatpad.chatLineArea.setText("chessClient:KeyPressed无法连接,建议重新连接 \n"); userpad.userList.removeAll(); inputpad.userChoice.removeAll(); inputwords.setText(""); controlpad.connectButton.setEnabled(true); } } else{ try{ out.writeUTF("/"+inputpad.userChoice.getSelectedItem()+" "+inputwords.getText()); inputwords.setText(""); } catch(Exception ea){ chatpad.chatLineArea.setText("chessClient:KeyPressed无法连接,建议重新连接 \n"); userpad.userList.removeAll(); inputpad.userChoice.removeAll(); inputwords.setText(""); controlpad.connectButton.setEnabled(true); } } } } public void keyTyped(KeyEvent e){} public void keyReleased(KeyEvent e){} public static void main(String args[]){ chessClient chessClient=new chessClient(); } }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值