功能分析
界面绘制
界面分为棋盘区域和按键面板
- 整体界面为1500×1000
- 棋盘区域背景色为浅灰色,棋盘为21×21大小间隔45个像素点,放置在左侧
- 按键面板背景色为白色,大小为500×1000,共放置5个按键“开始游戏,悔棋,认输,回放,退出游戏”
下棋策略
- 棋子放置在棋盘线的交汇点上
- 黑白交替下棋
胜负判定
- 每下颗棋子后进行胜负判定
- 输出胜负信息
棋局录制与回放
- 回放下棋过程的每一步,并间隔一段时间
悔棋
- 撤销最新下的棋子
功能实现
界面绘制与更新
- 继承JFrame类,重写paint方法。在paint方法中绘制棋盘
棋盘从(50,50)到(950,950)像素坐标;间隔45个像素坐标;一共21根线
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.black);
for (int i = 0; i <= 20; i++) {
//画横线
g.drawLine(50, 50 + i*45, 950, 50 + i*45);
//画竖线
g.drawLine(50 + i*45, 50, 50 + i*45, 950);
}
}
- 棋子绘制与更新
棋子分为白色和黑色两种,如何使其看起来立体,需要控制同心圆颜色的变化
黑色棋子RGB值变化:0~200;
白色棋子RGB值变化:205~255;
public static void drawpiece (int x, int y, int color){
for (int i = 0; i < 40; i++) {
if (color == 1){
//黑色
g.setColor(new Color(i*5, i*5, i*5));
}
else if (color == 2){
//白色
g.setColor(new Color(205+i,205+i ,205+i));
}
g.fillOval(x + i/2, y + i/2, 40-i, 40-i);
}
}
棋子更新使用update方法绘制棋子,在画面更新或者回放时调用
本案例使用列表储存坐标点使其包含时间顺序
回放模式加入500ms的停顿,回放会更加清晰每一子的情况
@Override
public void update(Graphics g) {
super.update(g);
for (int i = 0; i < Pieces.num; i++) {
if (Gobang_Listerner.name.equals("回放")){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Gobang_Listerner.drawpiece(Gobang_Listerner.pieces[i].x - 20, Gobang_Listerner.pieces[i].y - 20, Gobang_Listerner.pieces[i].color);
}
}
胜负判定策略
每下一个棋子都应该进行胜负判定。策略如图所示,寻找一条直线上的五个棋子。
实现的过程需要一个二维数组,将其初始化为全0;黑色棋子存为1;白色棋子存为2;
回放
回放的功能主要是把棋子的时间顺序记录下来,然后按照时间先后重新绘制在棋盘上
悔棋
删除最新下的一颗棋子;需要注意的问题是,除了在棋子列表,还需要修改二维数组以及棋子颜色
if (Pieces.num > 0) {
color_flag = !color_flag;
Piece_x.remove(Pieces.num - 1);
Piece_y.remove(Pieces.num - 1);
piece_coordinates[x/45 - 1][y/45 - 1] = 0;
Pieces.num--;
jf.update(g);
}
else {
System.err.println("不能撤销");
}
项目优化
界面重构时棋盘闪烁问题解决方案
在更新界面的时候我们通常会使用repaint方法,但是repaint方法会把paint方法重新调用,这使得棋盘和棋子都重新绘制了一遍,因此造成了画面闪烁。
解决办法如下:将棋盘绘制与棋子绘制进行分开,当程序需要对界面上的棋子进行重绘的时候调用update方法而并非使用repaint。
整体展示
界面展示
代码
- GobangUI
package Gobang;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
public class GobangUI extends JFrame{
public static void main(String[] args) {
GobangUI go = new GobangUI();
go.initUI();
}
public void initUI() {
JFrame jf = this;
jf.setTitle("五子棋");
jf.setSize(1500, 1000);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setBackground(Color.LIGHT_GRAY);
Gobang_Listerner gl = new Gobang_Listerner();
JPanel bt_panel = new JPanel();
bt_panel.setBackground(Color.WHITE);
init_bt_panel(bt_panel, gl);
jf.add(bt_panel, BorderLayout.EAST);
jf.setVisible(true);
gl.setJf(jf);
jf.addMouseListener(gl);
jf.addMouseMotionListener(gl);
Gobang_Listerner.g = jf.getGraphics();
}
public void init_bt_panel(JPanel bt_panel, ActionListener actionListener) {
Dimension dimension = new Dimension(500, 1000);
bt_panel.setPreferredSize(dimension);
bt_panel.setLayout(new FlowLayout());
String[] strs = {"开始游戏","悔棋","认输","回放"
,"退出游戏"};
Dimension bt_dimension = new Dimension(250, 45);
for (String str : strs) {
JButton btn = new JButton(str);
btn.setBackground(Color.WHITE);
btn.setPreferredSize(bt_dimension);
btn.addActionListener(actionListener);
bt_panel.add(btn, BorderLayout.CENTER);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.black);
for (int i = 0; i <= 20; i++) {
//画横线
g.drawLine(50, 50 + i*45, 950, 50 + i*45);
//画竖线
g.drawLine(50 + i*45, 50, 50 + i*45, 950);
}
}
@Override
public void update(Graphics g) {
super.update(g);
for (int i = 0; i < Pieces.num; i++) {
if (Gobang_Listerner.name.equals("回放")){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Gobang_Listener.drawpiece(Gobang_Listener.pieces[i].x - 20, Gobang_Listener.pieces[i].y - 20, Gobang_Listener.pieces[i].color);
}
}
}
- Gobang_Listener
package Gobang;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class Gobang_Listener implements MouseListener, ActionListener, MouseMotionListener {
JFrame jf;
static Graphics g;
boolean repetition_flag = false;
boolean color_flag = false;
int color = 0;
static String name = "a";
int x, y;
ArrayList<Integer> Piece_x = new ArrayList<>();
ArrayList<Integer> Piece_y = new ArrayList<>();
int[][] piece_coordinates = new int[21][21];
static Pieces[] pieces = new Pieces[441];
public Gobang_Listener(){
for (int i = 0; i < 441; i++) {
pieces[i] = new Pieces();
}
}
public void setJf(JFrame jf) {
this.jf = jf;
}
public boolean Victory(int x, int y, int color){
boolean victory = false;
int index_x = x/45 - 1;
int index_y = y/45 - 1;
int i = 1;
int num = 1;
boolean disconnect_L = false;
boolean disconnect_R = false;
boolean disconnect_U = false;
boolean disconnect_D = false;
//左-右
while (i < 5) {
if (index_x - i >= 0 && disconnect_L == false) {
if (piece_coordinates[index_x - i][index_y] == color) {
num++;
}
else {
disconnect_L = true;
}
}
if (index_x + i <= 20 && disconnect_R == false) {
if (piece_coordinates[index_x + i][index_y] == color) {
num++;
}
else {
disconnect_R = true;
}
}
if (num == 5) {
victory = true;
break;
} else {
i++;
}
}
//上-下
if (!victory) {
i = 1;
num = 1;
while (i < 5) {
if (index_y - i >= 0 && !disconnect_U) {
if (piece_coordinates[index_x][index_y - i] == color) {
num++;
}
else {
disconnect_U = true;
}
}
if (index_y + i <= 20 && !disconnect_D) {
if (piece_coordinates[index_x][index_y + i] == color) {
num++;
}
else {
disconnect_D = true;
}
}
if (num == 5) {
victory = true;
break;
} else {
i++;
}
}
}
//左下-右上
if (!victory) {
i = 1;
num = 1;
disconnect_L = false;
disconnect_R = false;
while (i < 5) {
if (index_x - i >= 0 && index_y + i <= 20 && !disconnect_L) {
if (piece_coordinates[index_x - i][index_y + i] == color) {
num++;
}
else {
disconnect_L = true;
}
}
if (index_x + i <= 20 && index_y - i >= 0 && !disconnect_R) {
if (piece_coordinates[index_x + i][index_y - i] == color) {
num++;
}
else{
disconnect_R = true;
}
}
if (num == 5) {
victory = true;
break;
} else {
i++;
}
}
}
//左上-右下
if (!victory) {
i = 1;
num = 1;
disconnect_L = false;
disconnect_R = false;
while (i < 5) {
if (index_x - i >= 0 && index_y - i >= 0 && !disconnect_L) {
if (piece_coordinates[index_x - i][index_y - i] == color) {
num++;
}
else {
disconnect_L = true;
}
}
if (index_x + i <= 20 && index_y + i <= 20 && !disconnect_R) {
if (piece_coordinates[index_x + i][index_y + i] == color) {
num++;
}
else{
disconnect_R = true;
}
}
if (num == 5) {
victory = true;
break;
} else {
i++;
}
}
}
return victory;
}
@Override
public void mouseClicked(MouseEvent e) {
x = e.getX();
y = e.getY();
if (x >= 50 && x <= 950 && y >= 50 && y <= 950){
for (int i = 50; i < 950; i = i+45) {
if (x >= i && x <= i + 45){
if (x - i > i + 45 - x){
x = i + 45;
}
else {
x = i;
}
}
if (y > i && y < i + 45){
if (y - i > i + 45 - y){
y = i + 45;
}
else {
y = i;
}
}
}
if (Piece_x.isEmpty()) {
repetition_flag = false;
}
else{
for (int i = 0; i < Piece_x.size(); i++) {
if (Piece_x.get(i) == x/45 && Piece_y.get(i) == y/45){
repetition_flag = true;
break;
}
if (Piece_x.get(i) != x/45 && Piece_y.get(i) != y/45){
repetition_flag = false;
}
}
}
if (repetition_flag){
System.err.println("重复下子");
}
else {
color_flag = !color_flag;
if (color_flag){
color = 1;
}
else {
color = 2;
}
Piece_x.add(x/45);
Piece_y.add(y/45);
piece_coordinates[x/45 - 1][y/45 - 1] = color;
drawpiece(x-20, y-20, color);
boolean victor = Victory(x, y, color);
System.out.println(victor);
if (victor){
if (color == 1){
JOptionPane.showMessageDialog(null, "恭喜黑子胜利");
}
else if (color == 2){
JOptionPane.showMessageDialog(null, "恭喜白子胜利");
}
}
pieces[Pieces.num].x = x;
pieces[Pieces.num].y = y;
pieces[Pieces.num].color = color;
Pieces.num++;
}
}
else {
System.err.println("请在棋盘内下棋子!");
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
jf.update(g);
}
@Override
public void mouseExited(MouseEvent e) {
}
public static void drawpiece (int x, int y, int color){
for (int i = 0; i < 40; i++) {
if (color == 1){
//黑色
g.setColor(new Color(i*5, i*5, i*5));
}
else if (color == 2){
//白色
g.setColor(new Color(205+i,205+i ,205+i));
}
g.fillOval(x + i/2, y + i/2, 40-i, 40-i);
}
}
@Override
public void actionPerformed(ActionEvent e) {
name = e.getActionCommand();
switch (name) {
case ("开始游戏") -> {
color_flag = false;
Piece_x.clear();
Piece_y.clear();
Pieces.num = 0;
for (int i = 0; i < 21; i++) {
for (int j = 0; j < 21; j++){
piece_coordinates[i][j] = 0;
}
}
jf.update(g);
}
case ("退出游戏") -> {
System.out.println("退出游戏");
color_flag = false;
Piece_x.clear();
Piece_y.clear();
Pieces.num = 0;
for (int i = 0; i < 21; i++) {
for (int j = 0; j < 21; j++){
piece_coordinates[i][j] = 0;
}
}
jf.setVisible(false);
}
case ("悔棋") -> {
if (Pieces.num > 0) {
color_flag = !color_flag;
Piece_x.remove(Pieces.num - 1);
Piece_y.remove(Pieces.num - 1);
piece_coordinates[x/45 - 1][y/45 - 1] = 0;
Pieces.num--;
jf.update(g);
}
else {
System.err.println("不能撤销");
}
}
case ("回放") -> {
jf.update(g);
name = "a";
}
case ("认输") -> {
if (color == 1) {
JOptionPane.showMessageDialog(null, "恭喜黑子胜利");
} else if (color == 2) {
JOptionPane.showMessageDialog(null, "恭喜白子胜利");
}
}
}
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
}
}
- Pieces
package Gobang;
public class Pieces {
int x;
int y;
int color;
static int num = 0;
}