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();//匿名对象
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击了");
//获取按钮的ActionCommand
String 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() {
@Override
public 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);
}
}
}
@Override
public void paint(Graphics g) {
//super.paint(g);//清除原来的痕迹
/* paint 方法是JPanel的绘制面板内容的方法
Graphics:绘制类
常用方法
g.drawImage:画图片
g.drawChars:画文字
g.drawLine:画直线
drawOval:画园或椭圆
如何在Jpanel画一张图1
1 准备图片路径
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));
}
@Override
public 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));
}
@Override
public 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));
}
@Override
public 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) {
//x
return 0;
} else if (Math.abs(p.x - tp.x) == 1 && Math.abs(p.y - tp.y) == 2) {
//y
return -1;
}
}
return -2;
}
//计算棋子走的步数,起点到终点的步数
public int getStep(Point tp) {
int line = line(tp);
if(line == 3) {
//x
return 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) {
//x
center.x = (p.x + tp.x) / 2;
center.y = p.y;
return gamePanel.getChessByP(center) != null;
}else if(line == -1) {
//y
center.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) {
//y
np.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) {
//x
np.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));
}
@Override
public 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));
}
@Override
public 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));
}
@Override
public 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));
}
@Override
public boolean isAbleMove(Point tp, GamePanel gamePanel) {
return line(tp) == 1 && getStep(tp) == 2 && !isBieJiao(tp,gamePanel) && !isOverRiver(tp);
}
}