1.效果图。第一次写有点粗糙。
2.五子棋设计要求
(1)实现界面按钮的基本功能。
(2)实现双人对战和电脑对战(水平太low还没写出来)
3.设计思路
(1)棋盘类设计,黑白棋盘,落子位于交叉点,存储棋子。
(2)棋盘类重绘。
(3)判断输赢。
(4)实现按钮功能。
4.具体代码
(1)为后续可能更改棋盘以及棋子设计要求的方便性,写一个接口定义常量。
public interface Config {
public static final int xo = 30;//起点横坐标
public static final int yo = 30;//起点纵坐标
public static final int rows = 16;//横向线条数
public static final int columns = 16;//纵向线条数
public static final int chesssize = 40;//棋子直径
public static final int size = 50;//单元格大小
}
(2)①主界面棋盘类的设计,以及棋盘和棋子重绘。重绘要先super.paint(g)父类。抗锯齿方法是Graphics2D的方法,强制转换。创建JFrame绘制绘制窗口,继承的JPanel绘制棋盘,实例化一个JPanel对象用来创建按钮,同时给按钮添加鼠标监听器(事件处理类中实现接口ActionListener完成监听)。
棋盘也需要用set方法传递给事件处理类,实现“开始游戏”“悔棋”按钮功能时需要重绘。
重绘方法中也需要重绘棋盘(drawChessTable(g);)以及抗锯齿。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FiveChess extends JPanel implements Config {
private int[][] chess = new int[100][100];
private int count;
//private static FiveChess fi;
static FiveChess fi = new FiveChess();
public static void main(String[] args) {
fi.initUI();
}
public void initUI() {
JFrame frame = new JFrame("五子棋");// JFrame默认边界布局
frame.getContentPane().setBackground(Color.LIGHT_GRAY);
frame.setSize(1000, 860);
frame.setDefaultCloseOperation(3);
frame.setLocationRelativeTo(null);// 居中显示必须设置在窗体大小之后
// frame.setResizable(false);//禁止窗体大小改变
this.setBackground(Color.lightGray);
frame.add(this, BorderLayout.CENTER);
ChessListener ch = new ChessListener();// 创建事件处理类的对象
JPanel eastJPanel = new JPanel();// JPanel默认流式布局
eastJPanel.setPreferredSize(new Dimension(170, 0));
eastJPanel.setBackground(Color.darkGray);
frame.add(eastJPanel, BorderLayout.EAST);
String[] array = { "开始游戏", "悔棋", "认输" };
for (int i = 0; i < array.length; i++) {
JButton jbu = new JButton(array[i]);
jbu.setPreferredSize(new Dimension(150, 75));
jbu.addActionListener(ch);
eastJPanel.add(jbu);
}
String[] itemArray = { "人机对战", "人人对战" };// 设置下拉按钮
JComboBox<String> cbArray = new JComboBox<String>(itemArray);//建下拉框对象
eastJPanel.add(cbArray);
cbArray.setPreferredSize(new Dimension(150, 75));
frame.setVisible(true);
cbArray.addActionListener(ch);
Graphics gr = this.getGraphics();// 从JPanel获取画笔
ch.setChessListener(gr);// 调用事件处理类的方法,将画笔传给事件处理类的画笔对象
ch.setC(chess);// 调用事件处理类的方法,将数组传给事件处理类的数组对象
this.addMouseListener(ch);// 给事件源JPanel添加鼠标监听器
ch.setFiveChess(fi);//调用事件处理类的方法,将棋盘传给事件处理类的棋盘对象
ch.setItemArray(cbArray);
}
public void drawChessTable(Graphics g) {
for (int i = 0; i < Config.rows; i++) {// 棋盘横线
g.drawLine(Config.xo, Config.yo + i * Config.size, Config.xo + (Config.columns - 1) * Config.size, Config.yo + i * Config.size);
}
for (int j = 0; j < Config.columns; j++) {// 竖线
g.drawLine(Config.xo + j * Config.size, Config.yo, Config.xo + j * Config.size,Config.yo + (Config.rows - 1) * Config.size);
}
}
public void paint(Graphics g) {// 继承JFrame,重绘
super.paint(g);
drawChessTable(g);//本类中重绘棋盘
System.out.println("重绘!");
Graphics2D gr ;//重绘抗锯齿
gr = (Graphics2D)g;
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < chess.length; i++) {
for (int j = 0; j < chess.length; j++) {
if (chess[i][j] == 1) {
gr.setColor(Color.BLACK);
gr.fillOval(xo + i * size - chesssize / 2, yo + j * size - chesssize / 2, chesssize, chesssize);
} else if (chess[i][j] == 2) {
gr.setColor(Color.WHITE);
gr.fillOval(xo + i * size - chesssize / 2, yo + j * size - chesssize / 2, chesssize, chesssize);
}
}
}
}
}
③事件处理类中落子设计,同一交点不能下两次。
绘制棋子时继承MouseAdapter类,只需要重写一个方法如 public void mouseReleased(MouseEvent e)(接口需要重写每个方法)。
判断输赢后改变boolean值后,不满足落子条件无法落子。
创建二维数组,给交点赋值记录棋子颜色。
创建数列对象(ArrayList<Point> list = new ArrayList<Point>();)用于保存棋子的交点信息。每画出一颗棋子将point点坐标存入队列。。
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
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.util.ArrayList;
import javax.swing.JOptionPane;
public class ChessListener extends MouseAdapter implements Config, ActionListener {
// 为什么不继承Graphics也可以使用
private Graphics2D gr;// 创建画笔对象
private int x, y, x1, y1, m, n;
private int count = 0;
private int[][] chess;
private FiveChess fc;
private int q = 0;
private boolean game = true;
ArrayList<Point> list = new ArrayList<Point>();//创建数列对象,保存棋子交点数据
private JComboBox<String> cbArray;
ArrayList<Point> list = new ArrayList<Point>();
public void setItemArray(JComboBox<String> cbArray) {
this.cbArray = cbArray;
}
public void setChessListener(Graphics g) {// 实例化对象
gr = (Graphics2D) g;
gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 抗锯齿
}
public void setC(int[][] chess) {
this.chess = chess;
}
public void setFiveChess(FiveChess fc) {
this.fc = fc;
}
public void mouseReleased(MouseEvent e) {// 方法重写
x1 = e.getX();// 点击获得横坐标
y1 = e.getY();// 点击获得纵坐标
for (int j = 0; j < rows; j++) {
for (int i = 0; i < columns; i++) {
x = xo + size * i;//交点x坐标
y = yo + size * j;//交点y坐标
if (x1 > x - size / 3 && x1 < x + size / 3 && y1 > y - size / 3 && y1 < y + size / 3) {
if (chess[i][j] == 0 && q > 0 && game == true) {// 如果选择的位置没有棋子,且点击了开始游戏,且可以落子
if (count == 0) {
chess[i][j] = 1;
Point p = new Point();// 创建数列对象用来保存坐标
p.setLocation(i, j);// 得到坐标
list.add(p);// 存入p点坐标
gr.setColor(Color.BLACK);
gr.fillOval(x - chesssize / 2, y - chesssize / 2, chesssize, chesssize);
count++;
// System.out.println("chess[i][j] = "+chess[i][j]);
} else {
chess[i][j] = 2;
Point p = new Point();
p.setLocation(i, j);
list.add(p);
gr.setColor(Color.WHITE);
gr.fillOval(x - chesssize / 2, y - chesssize / 2, chesssize, chesssize);
count--;
// System.out.println("chess[i][j] = "+chess[i][j]);
}
m = i;
n = j;
this.checkRow(i, j);//调用方法判断输赢
}
}
}
}
System.out.println();
}
public void checkRow(int x, int y) {// 判断输赢
int count1 = 0;
int count2 = 0;
int count3 = 0;
// 判断行赢
for (int i1 = x + 1; i1 < chess.length; i1++) {
if (chess[i1][y] == chess[x][y]) {
count1++;
} else {
break;
}
System.out.println("count1 = " + count1);
}
for (int i2 = x; i2 >= 0; i2--) {
if (chess[i2][y] == chess[x][y]) {
count1++;
} else {
break;
}
System.out.println("count1 =" + count1);
}
if (count1 == 5) {
game = false;
// System.out.println("chess[m][n] ="+chess[m][n]);
if (chess[m][n] == 1) {
JOptionPane.showConfirmDialog(null, "黑棋赢了");
} else {
JOptionPane.showConfirmDialog(null, "白棋赢了");
}
fc.removeMouseListener(this);//由一方胜利或者投降移除鼠标监听器,也可以赋值game = false,不满足条件不能落子
System.out.println("移除鼠标监听器");
}
// 判断列赢
for (int j = y + 1; j < chess.length; j++) {
if (chess[x][j] == chess[x][y]) {
count2++;
} else {
break;
}
System.out.println("count2 =" + count2);
}
for (int j = y; j >= 0; j--) {
if (chess[x][j] == chess[x][y]) {
count2++;
} else {
break;
}
System.out.println("count2 =" + count2);
}
if (count2 > 4) {
game = false;
if (chess[m][n] == 1) {
JOptionPane.showConfirmDialog(null, "黑棋赢了");
} else if (chess[m][n] == 2) {
JOptionPane.showConfirmDialog(null, "白棋赢了");
}
fc.removeMouseListener(this);
}
// 判断斜赢
for (int i = x, j = y; i < rows && j < columns; i++, j++) {
if (chess[i][j] == chess[x + 1][y + 1]) {
count3++;
} else {
break;
}
System.out.println("count3 =" + count3);
}
for (int i = x, j = y; i < rows && j < columns; i--, j--) {
if (chess[i][j] == chess[x - 1][y - 1]) {
count3++;
} else {
break;
}
System.out.println("count3 =" + count3);
}
for (int i = x, j = y; i < rows && j < columns; i++, j--) {
if (chess[i][j] == chess[x + 1][y - 1]) {
count3++;
} else {
break;
}
System.out.println("count3 =" + count3);
}
for (int i = x, j = y; i < rows && j < columns; i--, j++) {
if (chess[i][j] == chess[x - 1][y + 1]) {
count3++;
} else {
break;
}
System.out.println("count3 =" + count3);
}
if (count3 > 4) {
game = false;
if (chess[m][n] == 1) {
JOptionPane.showConfirmDialog(null, "黑棋赢了");
} else if (chess[m][n] == 2) {
JOptionPane.showConfirmDialog(null, "白棋赢了");
}
fc.removeMouseListener(this);
}
}
④实现按钮功能。动作监听器ActionListener接口的方法重写。变量值控制按下按钮以后是否还可以落子。
new一个point对象用来存储从队列中移出的交点数据,把交点chess[x][y]=0;即悔棋。
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("开始游戏")) {
count = 0;
q++;
fc.addMouseListener(this);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
chess[i][j] = 0;
}
}
fc.paint(gr);//结束一局游戏重绘棋盘,开始一局新游戏
game = true;
}
if (e.getActionCommand().equals("认输")) {
if (count == 0) {
JOptionPane.showConfirmDialog(null, "黑棋认输");//直接弹出对话框黑棋认输
}
if (count == 1) {
JOptionPane.showConfirmDialog(null, "白棋认输");
}
game = false;
}
if (e.getActionCommand().equals("悔棋")) {
Point p = new Point();创建对象用来存储队列中取出来的点
p = list.remove(list.size() - 1);赋值p,存储
chess[(int) p.getX()][(int) p.getY()] = 0;该点重绘为无点
fc.paint(gr);重绘棋盘
if (count == 1) {//悔棋后下一颗棋子的颜色判断
count--;
} else if (count == 0) {
count++;
}
}
if(cbArray.getSelectedItem().equals("人人对战")) {
game= true;
}else if(cbArray.getSelectedItem().equals("人机对战")){
JOptionPane.showConfirmDialog(null, "很抱歉!功能正在开发。。。");
}
}