JavaSE:实现象棋游戏
文章目录
- 1. 每日一言
- 2. 游戏内容介绍
- 3. 代码介绍
- 4. 全部代码
- 4.1 MainFream
- 4.2 GamePanel
- 4.3 ChessFactory
- 4.4 Bing
- 4.5 Boss
- 4.6 Che
- 4.7 Chess
- 4.8 Ma
- 4.9 Pao
- 4.10 Shi
- 4.11 Xiang
- 结语
1. 每日一言
Every cloud has a silver lining.
天无绝人之路。
2. 游戏内容介绍
象棋是一种中国传统的棋类游戏,也是世界上最古老、最普及的棋类之一。象棋使用一个棋盘,上面有64个方格,棋盘被分成红棋和黑棋两方,每方有16个棋子。
棋子分别为:帅(将)、仕(士)、相(象)、马、车、炮和兵(卒)。每个棋子都有特定的移动方式和规则。帅/将是最重要的棋子,每方的目标就是要将对方的帅/将困住,使其无法移动。
象棋的游戏目标是要将对方的帅/将逼到无路可走的境地,即将军,并且无论如何对方都无法避免被将军,这就是将军、绝杀的最佳状态,获胜方即为胜利。
本游戏实现了"自弈"功能
游戏截图:
3. 代码介绍
该程序使用的GUI为 swing,听说已经过时了。他给我的感受就是,十分难用,而且还丑,也可能是因为我只了解一点点吧。
- MainFream 该类是用来管理游戏的总框架,也是程序的入口
- 其中定义了一个简单的主界面窗口,包含了一个游戏面板和一些按钮,读者可以根据需要进行扩展和实现相应的功能。
- GamePanel 该类用来控制游戏面板
- 主要功能包括:
创建棋子并将棋子放到数组中
通过点击事件,判断棋子的选择、移动和吃子操作
绘制棋盘和棋子的图像
其中的creatChesses()方法用于创建棋子并将棋子保存到数组中。每个棋子都有一个名称、阵营和网格坐标。棋子的名称、阵营和网格坐标通过数组和循环进行设置。
MouseAdapter类用于处理鼠标点击事件。在点击事件中,通过获取鼠标点击的坐标,判断是否选中了棋子。根据不同的情况,进行重新选择、移动或吃子操作。在操作完成后,刷新棋盘。
paint()方法用于绘制棋盘和棋子的图像。首先通过图片路径获取图片对象,然后使用drawImage()方法将图片绘制到面板上。最后调用drawChesses()方法画出棋子的图像。如果有棋子被选中,通过drawRect()方法在棋子周围画出一个矩形框。
- 主要功能包括:
- ChessFactory 顾名思义,该类用来快速生成棋子
- 这段代码是一个棋子工厂类。它的作用是根据输入的棋子名称、玩家和位置信息来创建对应的棋子对象。棋子对象根据不同的名称会有不同的移动规则。如果输入的名称是"boss",则创建一个Boss棋子对象
- Bing 该类管理兵这个棋子
- Boss 该类管理将/帅这个棋子
- Che 该类管理车这个棋子
- Chess 该类为抽象类用来辅助生成棋子以及判断棋子的前进方式
- Ma 该类管理马这个棋子
- Pao 该类管理炮这个棋子
- Shi 该类管理士这个棋子
- Xiang 该类管理象这个棋子
具体可以看代码中的注释。
4. 全部代码
4.1 MainFream
package com.code.main;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;//主界面
public class MainFream extends JFrame implements ActionListener {public MainFream() {//设置窗口大小setSize(480,480);
// setSize(580,500);//设置窗口居中setLocationRelativeTo(null);//设置点击关闭按钮同时结束虚拟机setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置布局管理员setLayout(new BorderLayout());//将游戏面板添加到窗口中GamePanel gp = new GamePanel();add(gp, BorderLayout.CENTER);//添加按钮面板JPanel btnPanel = new JPanel(new GridLayout(4,1));//add(btnPanel,BorderLayout.EAST);add(btnPanel,BorderLayout.EAST);JLabel hintLabel = new JLabel("红方走");btnPanel.add(hintLabel);gp.setHintLabel(hintLabel);
// JButton btnHuiQi = new JButton("悔棋");
// btnHuiQi.setActionCommand("huiqi");
// btnHuiQi.addActionListener(this);
// btnPanel.add(btnHuiQi);
// JButton btnSave = new JButton("保存棋谱");
// btnSave.setActionCommand("baocun");
// btnSave.addActionListener(this);
// btnPanel.add(btnSave);
// JButton btnImport = new JButton("导入棋谱");
// btnImport.setActionCommand("daoru");
// btnImport.addActionListener(this);
// btnPanel.add(btnImport);
// JButton btnQiuHe = new JButton("求和");
// btnQiuHe.setActionCommand("qiuhe");
// btnQiuHe.addActionListener(this);
// btnPanel.add(btnQiuHe);
// JButton btnRenShu= new JButton("认输");
// btnRenShu.setActionCommand("renshu");
// btnRenShu.addActionListener(this);
// btnPanel.add(btnRenShu);//设置窗口可见,建议放在后面setVisible(true);}public static void main(String[] args) {//JFrame frm = new JFrame();//JFrame默认是一个看不见的窗口new MainFream();//匿名对象}@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("按钮被点击了");//获取按钮的ActionCommandString cmd = e.getActionCommand();switch (cmd) {case "huiqi":System.out.println("huiqi");break;case "baocun":System.out.println("baocun");break;case "daoru":System.out.println("daoru");break;case "qiuhe":System.out.println("qiuhe");break;case "renshu":System.out.println("renshu");break;}}
}
4.2 GamePanel
package com.code.main;import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.Arrays;//游戏面板
public class GamePanel extends JPanel {private Chess[] chesses = new Chess[32];//保存所有棋子private Chess selectedChess;//当前选中的棋子//记住当前阵营private int curPlayer = 0;//红方先手private JLabel hintLabel;public void setHintLabel(JLabel hintLabel) {this.hintLabel = hintLabel;}public GamePanel() {creatChesses();/* 如何操作棋子1 点击棋盘2 如何获取棋子对象(如何判断点击的地方是否有棋子)3 如何区分选择,重新选择,移动,吃子棋盘规则1 红方不能操作黑方的棋子2 一方走完,另一方才能走*///添加点击事件addMouseListener(new MouseAdapter() {@Overridepublic void mouseClicked(MouseEvent e) {//通过e可以获得鼠标点击的坐标Point p = Chess.getPointFromXY(e.getX(),e.getY());if(selectedChess == null) {//没选过棋子selectedChess = getChessByP(p);if(selectedChess != null && selectedChess.getPlayer() != curPlayer) {//选了对面的棋子selectedChess = null;hintLabel.setText("<html> 不能选择对方的棋子<br/> " + (curPlayer == 0 ? "红方走" : "黑方走") + "</html>");}}else {//重新选择,移动,吃子Chess c = getChessByP(p);if(c != null) {//第n次点击的时候有棋子//重新选择,吃子if(c.getPlayer() == selectedChess.getPlayer()) {//重新选择System.out.println("重新选择");selectedChess = c;}else {//吃子System.out.println("吃子");if(selectedChess.isAbleMove(p,GamePanel.this)) {/* 从数组中删除被吃掉的棋子修改要移动的棋子*/chesses[c.getIndex()] = null;if(c.getIndex() == 4) {hintLabel.setText("红方获胜");
// while (true) {
// try {
// Thread.sleep(1000);
// } catch (InterruptedException ex) {
// throw new RuntimeException(ex);
// }
// }} else if (c.getIndex() == 20) {hintLabel.setText("黑方获胜");
// while (true) {
// try {
// Thread.sleep(1000);
// } catch (InterruptedException ex) {
// throw new RuntimeException(ex);
// }
// }} else {selectedChess.setP(p);overMyTurn();}}}}else {//第n次点击的时候没有棋子,点的是空地//移动System.out.println("移动");if(selectedChess.isAbleMove(p,GamePanel.this)) {selectedChess.setP(p);overMyTurn();}}}//System.out.println("点击的棋子对象为:" + selectedChess);//刷新棋盘,即重新执行paint方法repaint();}});}//一方走完了,换成另一方走private void overMyTurn() {curPlayer = curPlayer == 0 ? 1 : 0;//将选择的棋子放下selectedChess = null;hintLabel.setText(curPlayer == 0 ? "红方走" : "黑方走");}//查找棋子对象public Chess getChessByP(Point p) {for (Chess item : chesses) {if(item != null && item.getP().equals(p)) {return item;}}return null;}//创建棋子并将棋子放到数组中private void creatChesses() {String[] names = {"che","ma","xiang","shi","boss","shi","xiang","ma","che","pao","pao","bing","bing","bing","bing","bing"};int[] xs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 2, 8, 1, 3, 5, 7, 9};
// for (int i = 0; i < names.length; i++) {
Chess c = new Chess(names[i],1,ps[i]);
c.setName(names[i]);
c.setP(ps[i]);//指定棋子的网格坐标
c.setPlayer(1);//设置阵营
// Chess c = ChessFactory.create(names[i],0,xs[i]);
//
// c.reserve();
// c.setIndex(i+16);
// chesses[c.getIndex()] = c;
// }for (int i = 0; i < names.length; i++) {Chess c = ChessFactory.create(names[i],1,xs[i]);
// c.setName(names[i]);
// c.setP(ps[i]);//指定棋子的网格坐标
// c.setPlayer(0);//设置阵营c.setIndex(i);chesses[i] = c;//将棋子保存到数组中//System.out.println(c);}for (int i = 0; i < names.length; i++) {
// Chess c = new Chess(names[i],1,ps[i]);
// c.setName(names[i]);
// c.setP(ps[i]);//指定棋子的网格坐标
// c.setPlayer(1);//设置阵营Chess c = ChessFactory.create(names[i],0,xs[i]);c.reserve();c.setIndex(i+16);chesses[c.getIndex()] = c;}}//画棋子private void drawChesses(Graphics g) {for (Chess item:chesses) {if(item != null) {item.draw(g,this);}}}@Overridepublic void paint(Graphics g) {//super.paint(g);//清除原来的痕迹/* paint 方法是JPanel的绘制面板内容的方法Graphics:绘制类常用方法g.drawImage:画图片g.drawChars:画文字g.drawLine:画直线drawOval:画园或椭圆如何在Jpanel画一张图11 准备图片路径File.separator:路径分隔符2 通过图片路径得到图片对象3 使用g.drawImage方法将图片绘制到面板上*///1.准备图片路径String bgPath = "pic" + File.separator + "qipan.jpg";//2 通过图片路径得到图片对象/* Toolkit.getDefaultToolkit():获取Toolkit的实例createImage():创建图片getImage():获取图片*/Image bgImg = Toolkit.getDefaultToolkit().getImage(bgPath);//3 使用g.drawImage方法将图片绘制到面板上g.drawImage(bgImg,0,0,this);/*img:要汇总的图片对象x:坐标x,在编程中坐标都是从左上角开始,往右是正数y:坐标y,往下是正数observer:图片观察者,写JPanel对象即可*///如何画棋子/*String Path = "pic" + File.separator + "che0.png";Image img = Toolkit.getDefaultToolkit().getImage(Path);//g.drawImage(che0Img,0,0,this);g.drawImage(img,5,5,30,30,this);*/drawChesses(g);//画棋子if(selectedChess != null) {selectedChess.drawRect(g);}}}
4.3 ChessFactory
package com.code.main;//生成棋子
public class ChessFactory {private ChessFactory() {}public static Chess create(String name,int player,int px) {//定制棋子移动的规则if("boss".equals(name)) {return new Boss(player,px);}else if("shi".equals(name)){return new Shi(player,px);}else if("xiang".equals(name)){return new Xiang(player,px);}else if("ma".equals(name)){return new Ma(player,px);}else if("che".equals(name)){return new Che(player,px);}else if("pao".equals(name)){return new Pao(player,px);}else if("bing".equals(name)){return new Bing(player,px);}return null;}}
4.4 Bing
package com.code.main;import com.code.main.Chess;
import com.code.main.GamePanel;import java.awt.*;public class Bing extends Chess {public Bing(int player, Point p) {super("bing",player,p);}public Bing(int player, int px) {this(player, new Point(px,4));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {if(line(tp) < 2 || getStep(tp) > 1) {//不是走直线 || 走的步数大于一return false;}if(isOverRiver(p)) {//过河 ,不往前后return !isBack(tp);} else {//没过河,只能前进return isForward(tp);}}
}
4.5 Boss
package com.code.main;import java.awt.*;public class Boss extends Chess{public Boss(int player, Point p) {super("boss",player,p);}public Boss(int player, int px) {this(player, new Point(px,1));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {return line(tp) > 1 && isHome(tp) && getStep(tp) == 1;}
}
4.6 Che
package com.code.main;import java.awt.*;public class Che extends Chess{public Che(int player, Point p) {super("che", player, p);}public Che(int player, int px) {this(player, new Point(px,1));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {//只能横竖走,并且与目的地之间没有棋子阻挡return line(tp) > 1 && getCount(tp,gamePanel) == 0;}
}
4.7 Chess
package com.code.main;import javax.swing.*;
import java.awt.*;
import java.io.File;public abstract class Chess {//棋子大小private static final int SIZE = 30;//棋盘外边距private static final int MARGIN = 20;//棋子间距private static final int SPACE = 40;//棋子名字private String name;//后缀private String suffix = ".png";//棋子阵营protected int player;//绘制时的坐标private int x,y;//棋子的网格坐标protected Point p;//棋子的网格坐标,初始位置,不可改变private Point initP;//保存每个棋子的索引位置private int index;public void setIndex(int index) {this.index = index;}public int getIndex() {return index;}public int isUpOrDown() {//上面和下面的棋子//1.上//2.下if(initP.y < 6 ) {//上面return 1;}else if(initP.y > 5) {//下面return 2;}else {return 0;}}//判断棋子是否在王宫范围内public boolean isHome(Point tp) {if(tp.x < 4 || tp.x > 6) {return false;}int upOrDown = isUpOrDown();if(upOrDown == 1) {//上if(tp.y > 3 || tp.y < 1) {return false;}} else if (upOrDown == 2) {//下if(tp.y > 10 || tp.y < 8) {return false;}}return true;}//判断棋子是否走直线//1.走斜线//2.y直线//3.x直线//0. x轴日字//-1 y轴日字//-2.都不是public int line(Point tp) {if(p.y == tp.y) {//X轴直线return 3;}else if(p.x == tp.x) {//Y轴直线return 2;}else if(Math.abs(p.x - tp.x) == Math.abs(p.y - tp.y)) {//走斜线return 1;} else {//日字if(Math.abs(p.x - tp.x) == 2 && Math.abs(p.y - tp.y) == 1) {//xreturn 0;} else if (Math.abs(p.x - tp.x) == 1 && Math.abs(p.y - tp.y) == 2) {//yreturn -1;}}return -2;}//计算棋子走的步数,起点到终点的步数public int getStep(Point tp) {int line = line(tp);if(line == 3) {//xreturn Math.abs(p.x - tp.x);}else if(line == 2 || line == 1) {//y,正斜线return Math.abs(p.y - tp.y);}return 0;}//判断象或马是否蹩脚,原理是判断蹩脚点是否存在棋子public boolean isBieJiao(Point tp,GamePanel gamePanel) {Point center = new Point();//中心点if("xiang".equals(name)) {//象center.x = (p.x + tp.x) / 2;center.y = (p.y + tp.y) / 2;//System.out.println(gamePanel.getChessByP(center));return gamePanel.getChessByP(center) != null;}else if("ma".equals(name)) {//马int line = line(tp);if(line == 0) {//xcenter.x = (p.x + tp.x) / 2;center.y = p.y;return gamePanel.getChessByP(center) != null;}else if(line == -1) {//ycenter.y = (p.y + tp.y) / 2;center.x = p.x;return gamePanel.getChessByP(center) != null;}}return true;}//判断目标点是否过河public boolean isOverRiver(Point tp) {int upOrDown = isUpOrDown();if(upOrDown == 1) {//上if(tp.y < 6) {return false;}} else if (upOrDown == 2) {//下if(tp.y > 5) {return false;}}return true;}//判断棋子是否能移动到指定的位置public abstract boolean isAbleMove(Point tp,GamePanel gamePanel);//计算起点到目标点之间的棋子数量,不计算起点的棋子public int getCount(Point tp,GamePanel gamePanel) {int start = 0;int end = 0;int count = 0;//统计棋子数量int line = line(tp);Point np = new Point();if(line == 2) {//ynp.x = tp.x;if(tp.y > p.y) {//从上往下start = p.y + 1;end = tp.y;} else {//从下往上start = tp.y + 1;end = p.y;}for(int i = start; i < end; i++) {np.y = i;if(gamePanel.getChessByP(np) != null) {count++;}}} else if (line == 3) {//xnp.y = tp.y;if(tp.x > p.x) {//从左往右start = p.x + 1;end = tp.x;} else {//从右往左start = tp.x + 1;end = p.x;}for(int i = start; i < end; i++) {np.x = i;if(gamePanel.getChessByP(np) != null) {count++;}}}return count;}//是否前进public boolean isForward(Point tp) {int upOrDown = isUpOrDown();if(upOrDown == 1) {//上if(tp.y > p.y) {return true;}} else if (upOrDown == 2) {//下if(tp.y < p.y) {return true;}}return false;}//是否后退public boolean isBack(Point tp) {int upOrDown = isUpOrDown();if(upOrDown == 1) {//上if(tp.y < p.y) {return true;}} else if (upOrDown == 2) {//下if(tp.y > p.y) {return true;}}return false;}//棋子的绘制方法public void draw(Graphics g, JPanel panel) {String path = "pic" + File.separator + name + player +suffix;Image img = Toolkit.getDefaultToolkit().getImage(path);g.drawImage(img,x,y,SIZE,SIZE,panel);}//为选中的棋子画一个边框public void drawRect(Graphics g) {g.drawRect(x,y,SIZE,SIZE);}//计算xy的绘制坐标public void calXY() {this.x = MARGIN - SIZE / 2 + SPACE * (p.x - 1);this.y = MARGIN - SIZE / 2 + SPACE * (p.y - 1);}public void setP(Point p) {this.p = (Point) p.clone();if(initP == null) {initP = this.p;}calXY();}public int getPlayer() {return player;}public Point getP() {return p;}//根据xy坐标计算网格坐标对象public static Point getPointFromXY(int x,int y) {Point p = new Point();p.x = (x - MARGIN + SIZE / 2) / SPACE + 1;p.y = (y - MARGIN + SIZE / 2) / SPACE + 1;if (p.x < 1 || p.x > 9 || p.y < 1 || p.y > 10) {return null;}return p;}//反转网格坐标public void reserve() {p.x = 10 - p.x;p.y = 11 - p.y;initP = p;calXY();}public void setName(String name) {this.name = name;}public void setPlayer(int player) {this.player = player;}public void Point(int x,int y) {this.x = x;this.y = y;}public Chess(String name, int player, Point p) {this.name = name;this.player = player;setP(p);}public Chess() {}
}
4.8 Ma
package com.code.main;import java.awt.*;public class Ma extends Chess{public Ma(int player,Point p) {super("ma",player,p);}public Ma(int player, int px) {this(player, new Point(px,1));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {return (line(tp) == 0 || line(tp) == -1) && !isBieJiao(tp,gamePanel);}
}
4.9 Pao
package com.code.main;import com.code.main.Chess;
import com.code.main.GamePanel;import java.awt.*;public class Pao extends Chess {public Pao(int player, Point p) {super("pao",player,p);}public Pao(int player, int px) {this(player, new Point(px,3));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {Chess c = gamePanel.getChessByP(tp);if(c != null) {//有棋子if(c.getPlayer() != this.player) {//吃子return line(tp) > 1 && getCount(tp, gamePanel) == 1;}} else {//移动return line(tp) > 1 && getCount(tp, gamePanel) == 0;}return false;}
}
4.10 Shi
package com.code.main;import java.awt.*;public class Shi extends Chess{public Shi(int player, Point p) {super("shi", player, p);}public Shi(int player, int px) {this(player, new Point(px,1));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {return line(tp) == 1 && isHome(tp) && getStep(tp) == 1;}
}
4.11 Xiang
package com.code.main;import java.awt.*;public class Xiang extends Chess{public Xiang(int player, Point p) {super("xiang",player,p);}public Xiang(int player, int px) {this(player, new Point(px,1));}@Overridepublic boolean isAbleMove(Point tp, GamePanel gamePanel) {return line(tp) == 1 && getStep(tp) == 2 && !isBieJiao(tp,gamePanel) && !isOverRiver(tp);}
}
结语
本文到这里就结束啦~