五子棋可能大家都玩过或者听说过,规则非常简单:双方分别使用黑白两色的棋子,下在棋盘(15*15)直线与横线的交叉点上,先形成5子连线者获胜。
最近,我用Java写了一个五子棋小游戏,现在和大家分享一下。
首先我创建了四个类,分别为:
ChessUI,创建窗口,负责游戏的界面;
ChessPosition,包含两个参数,分别为棋子的x,y坐标;
ChessBoardListener,监听器类,当监听到鼠标的动作时,调用ChessBoard中的方法以做出响应。
ChessBoard,负责管理棋子,包括下棋,悔棋,重新开始,判断输赢等。
首先来实现游戏的界面:
public class ChessUI extends JPanel{
Graphics p;
BufferedImage bgImage;//棋盘的背景图片
AlphaComposite ac; //用以设置透明度
ChessBoard qz = new ChessBoard(this);
public void initUI() {
JFrame frame = new JFrame();
frame.setSize(534,614);//设置窗口大小
frame.setLocationRelativeTo(null);//设置窗口在屏幕居中
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭窗口即结束程序
frame.setTitle("五子棋");//设置
//创建鼠标监听器对象
ChessBoardListener cblisten = new ChessBoardListener(qz);
//创建南边功能面板并添加到窗体的南面
JPanel southPanel = new JPanel();
southPanel.setPreferredSize(new Dimension(45,45));
frame.add(southPanel,BorderLayout.SOUTH);
// //创建西边功能面板并添加到窗体的西边
// JPanel westPanel = new JPanel();
// westPanel.setPreferredSize(new Dimension(45,500));
// frame.add(westPanel,BorderLayout.WEST);
//创建中间绘图区域面板并添加窗体的中间
frame.add(this,BorderLayout.CENTER);
this.setBackground(Color.white);
this.addMouseListener(cblisten);//添加鼠标监听器
//设置一个字符数组来存储图形按钮上的文字
String[] text = {
"重新开始","悔棋","认输","人人对战","人机对战"};
for(int i=0; i<text.length;i++) {
//创建按钮
JButton btn = new JButton(text[i]);
btn.setPreferredSize(new Dimension(90,35));
//添加按钮到南面板
southPanel.add(btn);
btn.addActionListener(cblisten);//给按钮加上动作监听器
}
try {
bgImage = ImageIO.read(new File("C:/Users/background.png"));//给棋盘添加背景图片
} catch (IOException e) {
e.printStackTrace();
}
// ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.1f);//设置透明度
frame.setVisible(true);//设置窗体可见
//获取画布
Graphics2D p = (Graphics2D) this.getGraphics();
p.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
// ((Graphics2D) p).setComposite(ac);//设置透明度
System.out.println(p);//
cblisten.setGraphics(p);
qz.init();
}
public void paint(Graphics g) {
//重绘,每次缩小放大窗体时棋盘和棋子能够再次被画出来
super.paint(g);
g.drawImage(bgImage, 0, 0, 512, 512, this);
if(qz!=null) {
qz.drawChessBorad(g);
qz.drawChess(g);
}
}
public static void main (String[] args) {
ChessUI pl = new ChessUI();
pl.initUI();
}
}
值得注意的是,getGraphics这一步要在setVisible后,要不然会报出空指针异常。
效果如下图所示:
接下来分人人对战,人机对战两部分来讲述。
人人对战的算法相对来说简单一点,所以先来说说人人对战。
分为下棋和判断输赢两部分:
先说下棋:
鼠标在棋盘上点击一下,就在棋盘上画一颗棋子,这里需要对鼠标监听器获取的x,y坐标处理一下,使鼠标点击后棋子画在离获取的坐标最近的格子的正中间,因为我们做不到每一次点击都点在精准的棋盘线交叉点上。
为了使黑白子轮流下棋,我用了一个标志位flag来标志,当flag为1时下的棋为黑色,flag为2时下的棋为白色。一开始初始化flag为1,规定了黑子先下。下完一颗棋子后就反转标志位,就可实现黑白子轮流下棋。
创建一个大小为15*15的二维数组,用来保存棋盘上棋子的值,初始化为0后,下了一颗黑棋就把数组对应的位置的值置为1,下白棋则置为2,同时每下一颗棋子就将其放入ArrayList中,方便后续的悔棋。
public void humanAndhuman(Graphics g, int rowh, int rowl){
if(this.allchess[rowh][rowl]==0){
if(this.flag==1) {
g.setColor(Color.black);
g.fillOval((rowh+1)*gap-radius, (rowl+1)*gap-radius, 2*radius, 2*radius);
this.list.add(new ChessPosition(rowh,rowl));
this.allchess[rowh][rowl] = 1;
this.flag = 2;
}
else if(this.flag==2){
g.setColor(Color.white);
g.fillOval((rowh+1)*gap-radius, (rowl+1)*gap-radius, 2*radius, 2*radius);
this.list.add(new ChessPosition(rowh,rowl));
this.allchess[rowh][rowl] = 2;
this.flag = 1;
}
}
checkWin(rowh,rowl);
}
判断输赢:
每下完一步棋,就要判断一下是否分出胜负。判断胜负的规则非常简单,就是看棋盘上有没有相同颜色的棋子连成5颗或以上,先连成5颗的一方胜利。
所以判断输赢的算法这样来写:
从当前棋子的位置开始,分为四个方向:横向、竖向、左斜、右斜,每个方向都要判断是否有5颗或以上相同颜色的棋子连成一线。举个例子,定义一个变量count来计数,初始化为1(即当前棋子自身),检查横向时,先向左开始检查,遇到相同颜色的棋子时count自加一,直到遇到颜色不同的棋子或者到了棋盘边界,接着从当前棋子位置的右边开始检查,遇到相同颜色的棋子时count自加一,直到遇到颜色不同的棋子或者到了棋盘边界,最后判断count是否大于等于5,若是,获取当前棋子的颜色,若是黑色则弹出“黑方胜利,白方失败”这样的弹窗。
另外,设置一个标志位gameover,初始化为1,意思是允许下棋,当分出胜负后,设置gameover为0,就不能再下棋或者悔棋了。
public void