假期闲来无事,与一位王姓朋友(人美声甜的小仙女)合力制作了一个五子棋小游戏,皆是java初学者,在代码中存在有问题的地方还请各位指出。(内心os:写的代码跟没学过数据结构一样)
主要采用java.awt以及java.swing的GUI常用包,最初框架搭建可参考B站博主 https://www.bilibili.com/video/BV1SJ411x74E?spm_id_from=333.1007.top_right_bar_window_history.content.click 接下来言归正传,看一段接口类设定代码,在Config.java的接口类中定义了棋盘格属性,定义了线条数等初始属性,便于后期进行组件设计
public interface Config {
//配置接口
//定义棋盘属性
int Start_X=40; //起始x轴距离,即第一个x轴
int Start_Y=40; //起始y轴距离,即第一个y轴
int Line_Num=15; //线条数
int Size=40; //线条之间间隔
int Chess_Size=30; //棋子大小
}
在Chess.java中定义了棋子属性,包括其行列坐标,在类中定义了获取其行、列数据的方法,在后期进行悔棋操作时有所使用
public class Chess{
//棋子类
private int row; //行
private int column; //列
public Chess(int row, int column){
//构造方法,分配属性
this.row=row;
this.column=column;
}
public int getRow(){
//获取行
return row;
}
public int getColumn(){
//获取列
return column;
}
}
在RenjuListener.java类中定义了监听器中的操作,类中方法对鼠标事务进行了响应,定义了对胜负的判断方法,框架如下:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
public class RenjuListener extends MouseAdapter implements Config, ActionListener{
Graphics g;
int x=40, y=40; //设置坐标参数
int countClick = 0; //设置鼠标点击计数器
private int stepFlag=0; //记录下棋步数
private Renju renju;
private int ChessNum[][] = new int[Line_Num][Line_Num];
private String com; //获取功能按键命令
private int startFlag = 0; //开始标志,保证每一次要点击开始后才能进入游戏
private ArrayList<Chess> list = new ArrayList<>(); //数组队列,存储Chess类型的对象
/**
* 获取窗体上画笔
* */
public void getGraphics(Graphics g){
this.g = g;
}
/**
* 将获得的数据数组传出
* */
public int[][] getChessNum(){
return ChessNum;
}
/**
* 传入主类
* */
public void getRenju(Renju renju){
this.renju = renju;
}
/**
* 对事务响应的整体框架
* */
@Override
public void actionPerformed(ActionEvent e) {
//详情见后文
}
/**
* 悔棋
* */
public void regret(ArrayList list){
//详情见后文
}
/**
* 确定每次点击过开始后再进入游戏
* */
@Override
public void mouseClicked(MouseEvent e) {
x = e.getX(); //获取点击位置的x坐标
y = e.getY(); //获取点击位置的y坐标
if(startFlag == 1){
//已点击过开始按键,进入了游戏
this.man_VS_man();
}else if(startFlag == 0){
JOptionPane.showMessageDialog(renju,"请点击开始按键进行游戏!");
}
}
/**
* 双人对战
* */
public void man_VS_man(){
//详情见后文
}
/**
* 判断输赢方法
* @param x 棋盘上x个交点的位置
* @param y 棋盘上y个交点的位置
* @return 返回棋子连在一起的个数
* */
public int CheckRow(int x, int y){
int count1=0, count2=0, count3=0, count4=0, count=0;
//判断某行是否连成五子
for (int i = x+1; i < ChessNum.length; i++) { //向左判断
if(ChessNum[i][y] == ChessNum[x][y]){
count1++;
}else{
break;
}
}
for (int i = x; i >=0 ; i--) { //向右判断
if(ChessNum[i][y] == ChessNum[x][y]){
count1++;
}else{
break;
}
}
for (int i = y+1; i < ChessNum.length; i++) { //向上判断
if(ChessNum[x][i] == ChessNum[x][y]){
count2++;
}else{
break;
}
}
for (int i = y; i >=0; i--) { //向下判断
if(ChessNum[x][i] == ChessNum[x][y]){
count2++;
}else{
break;
}
}
int i,j;
for (i = x, j = y; i >=0 && j>=0; i--,j--) { //斜右下判断
if(ChessNum[i][j] == ChessNum[x][y]){
count3++;
}else{
break;
}
}
for (i = x+1, j = y+1; i < ChessNum.length && j< ChessNum.length; i++,j++) { //斜左上判断
if(ChessNum[i][j] == ChessNum[x][y]){
count3++;
}else{
break;
}
}
for (i = x, j = y; i < ChessNum.length && j< ChessNum.length; i--,j++) { //斜右上判断
if(ChessNum[i][j] == ChessNum[x][y]){
count4++;
}else{
break;
}
}
for (i = x+1, j = y-1; i < ChessNum.length && j< ChessNum.length; i++,j--) { //斜左下判断
if(ChessNum[i][j] == ChessNum[x][y]){
count4++;
// System.out.println(count4);
}else{
break;
}
}
if(count1==5 || count2==5 || count3==5 || count4==5){
count =5;
}
return count;
}
/**
* 重置棋盘棋子
* */
public void resetChess(){
//遍历存储棋子信息的数组,重置为0
for(int i=0; i<ChessNum.length ;i++){
for(int j=0; j<ChessNum.length; j++){
ChessNum[i][j]=0;
}
}
countClick=0; //将棋子黑白标志置为零
}
}
public void actionPerformed(ActionEvent e)方法详情,通过instanceof判断发生发生Event的对象是否是按键类型,经由getActionCommand()获取内容后赋值给com,比较按键文字内容与com信息,进行不同操作。
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JButton) {
com = e.getActionCommand();
if ("开始".equals(com)) {
startFlag = 1;
} else if ("重新开始".equals(com)) {
this.resetChess(); //清空棋子,重新开始
renju.repaint();
startFlag = 0;
} else if ("认输".equals(com)) {
if (countClick == 1) {
JOptionPane.showMessageDialog(renju, "白子认输,黑子胜利");
this.resetChess(); //清空棋子,重新开始
renju.repaint();
startFlag = 0;
} else if (countClick == 0) {
JOptionPane.showMessageDialog(renju, "黑子认输,白子胜利");
this.resetChess(); //清空棋子,重新开始
renju.repaint();
startFlag = 0;
}
} else if ("悔棋".equals(com)) {
this.regret(list);
}
}
}
public void regret(ArrayList list)方法详情如下,在list列表中存储着Chess类型的数据,是每一步棋对应的坐标,通过list.get(list.size()-1)获取列表末尾元素信息,将其行列信息赋给chess_row,chess_column,将其对应位置的ChessNum信息置为零(即消去最后一步下棋的信息)后重绘棋子,达成悔棋目的。
public void regret(ArrayList list) {
int chess_row, chess_column; //获取行列
Chess temp = (Chess) list.get(list.size() - 1);
chess_row = temp.getRow();
chess_column = temp.getColumn();
if (list.size() != 0) {
list.remove(list.size() - 1); //移除集合末尾元素(即最后一步的数据)
}
ChessNum[chess_row][chess_column] = 0;
renju.repaint();
this.draw(g);
}
public void man_VS_man()方法详情如下,Graphics2D类提供平面图形绘制,设置为抗锯齿后对棋盘信息进行遍历,当鼠标点击位置的坐标符合一定要求(确保棋子准确落子)后,通过判断countClick的奇偶性,来确定落子颜色,将对应信息存入ChessNum数组中(1为黑子,-1为白子),
public void man_VS_man() {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//遍历整个棋盘的所有位置
for (int i = 0; i < Line_Num; i++) {
for (int j = 0; j < Line_Num; j++) {
int x1 = Start_X + Size * i; //计算棋盘上每个位置的x坐标
int y1 = Start_Y + Size * j; //计算棋盘上每个位置的y坐标
//判断点击位置在棋盘的哪个点的范围
if ((x > x1 - Chess_Size / 3 && x < x1 + Chess_Size / 3) && (y > y1 - Chess_Size / 3 && y < y1 + Chess_Size)) {
//判断点击次数的奇偶
if (ChessNum[i][j] == 0) {
countClick++;
if (countClick == 1) {
g2d.setColor(Color.BLACK);
ChessNum[i][j] = 1; //存储黑子
list.add(new Chess(i, j)); //将黑子坐标存入集合
g2d.fillOval(Start_X + i * Size - Chess_Size / 2, Start_Y + j * Size - Chess_Size / 2, Chess_Size, Chess_Size); //绘制棋子
stepFlag++;
if (this.CheckRow(i, j) == 5) {
JOptionPane.showMessageDialog(renju, "黑子胜利");
this.resetChess(); //清空棋子,重新开始
renju.repaint();
startFlag = 0;
}
} else if (countClick == 2) {
g2d.setColor(Color.WHITE);
ChessNum[i][j] = -1; //存储白子
list.add(new Chess(i, j)); //将白子坐标存入集合
g2d.fillOval(Start_X + i * Size - Chess_Size / 2, Start_Y + j * Size - Chess_Size / 2, Chess_Size, Chess_Size); //绘制棋子
stepFlag++;
if (this.CheckRow(i, j) == 5) {
JOptionPane.showMessageDialog(renju, "白子胜利");
this.resetChess(); //清空棋子,重新开始
renju.repaint();
startFlag = 0;
}
}
}
}
countClick = countClick % 2;
}
}
}
在Renju.java类中完成了对组件的创建与布局,代码详情如下:
import javax.swing.*;
import java.awt.*;
public class Renju extends JPanel implements Config {
private static final long serialVersionUID =1L;
private int[][] chessarray; //存储棋子数据的数组
private RenjuListener listener = new RenjuListener(); //创建监听器
public void init(){
JFrame frame = new JFrame(); //实例化窗体对象
frame.setSize(800,700); //设置窗体大小
frame.setLocationRelativeTo(null); //设置窗体居中
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置关闭时类型为退出程序
frame.setTitle("五子棋");
BorderLayout layout = new BorderLayout(); //设置布局样式
frame.setLayout(layout);
this.centerPanel(frame); //设置棋盘面板
this.eastPanel(frame); //创建功能面板
this.addMouseListener(listener);//设置鼠标监听事件
frame.setVisible(true); //设置为显示窗体,可见
Graphics g = this.getGraphics();//获取画图对象
listener.getGraphics(g); //将画笔传输至监听器
chessarray = listener.getChessNum(); //获取棋子信息——颜色,坐标
listener.getRenju(this);
}
/**
* 创建棋盘面板,居中
* */
public void centerPanel(JFrame jf){
this.setBackground(Color.yellow); //设置面板背景色
jf.add(this,BorderLayout.CENTER); //设置窗体为边框布局,将主面板加入窗体展示,居中
}
/**
* 创建功能面板,居右
* */
public void eastPanel(JFrame jf){
JPanel eastPanel = new JPanel(); //创建窗体
eastPanel.setBackground(Color.blue); //设置背景色
eastPanel.setPreferredSize(new Dimension(160,100));//设置面板大小
jf.add(eastPanel,BorderLayout.EAST);//将面板加入窗体,居右(位东)
String[] ButtonArray={"开始","重新开始","认输","悔棋"};
Dimension buttonsize = new Dimension(100,100);
for(int i=0; i<ButtonArray.length; i++){ //循坏添加按键
JButton button = new JButton(ButtonArray[i]); //设定按键信息
button.setPreferredSize(buttonsize); //设置按键大小
eastPanel.add(button); //将按键添加至面板
button.addActionListener(listener); //添加监听器
}
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d = (Graphics2D) g; //类型转化为Graphic2D,
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
//绘制棋盘
for(int i=0; i<Line_Num; i++){
g2d.drawLine(Start_X, Start_Y+i *Size, Start_X+ (Line_Num - 1) * Size, Start_Y + i * Size); //绘制行
g2d.drawLine(Start_X + i * Size, Start_Y, Start_X + i * Size, Start_Y + (Line_Num-1) * Size); //绘制列
}
//重绘棋子
for(int r=0; r< Line_Num; r++){
for(int c=0; c<Line_Num; c++){
if(chessarray[r][c]!=0){
if(chessarray[r][c]==1){ //落黑子
g2d.setColor(Color.BLACK);
//绘制圆
g2d.fillOval(Start_X + r * Size - Chess_Size/2, Start_Y + c * Size - Chess_Size/2, Chess_Size, Chess_Size);
}
if(chessarray[r][c]==-1){ //落白子
g2d.setColor(Color.WHITE);
g2d.fillOval(Start_X + r * Size - Chess_Size/2, Start_Y + c * Size - Chess_Size/2, Chess_Size, Chess_Size);
}
}
}
}
}
}
上述代码段中运用了基础GUI知识,将组件添加至窗体进行展示,不在过多阐述。
主类代码段如下:
public class Show {
public static void main(String[] args){
Renju r =new Renju();
r.init();
}
}
以上就是本次五子棋制作的整体方式,感谢大家的浏览。(我承认很多东西我是懒得说了,这篇文章写出来就是来玩的,溜了溜了,各位,有缘江湖再见)