创建一个窗体来承载五子棋
如何创建一个窗体,以及如何在窗体上添加必要的按钮组件等操作,可以查看我的第一篇文章,里面有详细的讲解,这里就不在重新累述。具体的代码如下:
public class DrawUI{
public void ShowUI(){
MyFrame jf=new MyFrame();//这里原本应该是使用JFrame但,后续重绘中创建了子类MyFrame继承父类JFrame,这里就换成了MyFrame
//设置窗体属性
jf.setSize(500,500);
jf.setLocationRelativeTo(null);
jf.setTitle("五子棋");
jf.setResizable(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout fl=new FlowLayout();//设置窗体格式
jf.setLayout(fl);
JButton jb=new JButton("人人对战");//添加按钮
JButton jb1=new JButton("悔棋");
JButton jb2=new JButton("清空棋盘");
JButton jb3=new JButton("人机对战");
jf.add(jb);
jf.add(jb1);
jf.add(jb2);
jf.add(jb3);
jf.setVisible(true);
Graphics g=jf.getGraphics();//给窗体添加画笔
UIListener ul=new UIListener();
jb.addActionListener(ul);//给按钮添加监听器
jb1.addActionListener(ul);
jb2.addActionListener(ul);
jb3.addActionListener(ul);
jf.addMouseListener(ul);//给窗体添加监听器
ul.gr=g;//将画笔倒给UIlistener中的gr
jf.ul=ul;//为了在Myframe中使用ChessArray,将UIistener中的ul倒给Myframe中的ul
ul.jb11=jb;//将按钮jb倒给ul中的jb11,方便其使用jb
ul.mf=jf;//为了在UIlistener中重绘,将jf倒给其中的mf
}
}
五子棋基本程序编写
绘制棋盘
第一步肯定是要在创建好的窗体上绘制出五子棋的棋盘。思路也很简单,就是画出横竖两条线,然后设置一个固定间隔,不断循环,绘制出一个棋盘。
这里注意,因为如果只是在窗体中绘制出棋盘,那么每次窗体被拖动,刷新后,窗体中的绘制好的棋盘就会被刷新掉,所以需要重绘,确保棋盘永久存在,所以在这里,我们就直接将棋盘绘制在窗体重绘方法中。
public class MyFrame extends JFrame{
//MyFrame子类继承JFrame父类
public void paint(Graphics g) {
//重绘棋盘,确保刷新网页时候,棋盘还在
super.paint(g);
int x1,x2,y1,y2;
int X1,X2,Y1,Y2;
x1=50;x2=450;y1=70;y2=470;
X1=50;X2=450;Y1=70;Y2=470;
for(int i=0; i<21; i++) {
g.drawLine(x1,y1,x2,y1);
y1=y1+20;
}
for(int i=0; i<21; i++) {
g.drawLine(X1,Y1,X1,Y2);
X1=X1+20;
}
}
}
这里也需要注意:因为重绘方法是需要写在MyFrame类中的,由于后续还会有需要重绘的东西,所以后续会在这部分继续扩充需要重绘的内容代码。
绘制棋子(人人对战中棋子黑白交替出现,人机对战中玩家下黑子,电脑下白子)
绘制棋子时候,我们应该能清楚思路。
首先,鼠标点击的位置,这个位置不一定正好是在棋盘的网格交汇处的节点上,我们就要将这个鼠标点击的坐标优化至附近的交汇节点上。原理也很简单,将鼠标点击获取的坐标,计算转化成其在哪两个焦点之间,然后将两个节点之间一分为二,如果坐标在前部分,则优化到前面的节点上,如果在后半部分,则优化到后面的节点上。
其次,将鼠标点击的坐标优化到棋盘网格节点上后,则需要在这个网格节点上绘制棋子即可,但这里我们会发现,程序中绘制的圆形图形的时候,输入的绘制坐标点是绘制出来圆形的外切长方形的左上角的坐标,并非是圆形的圆心坐标。这时同样需要优化,将坐标分别减去它的半径,就能优化在鼠标点击转化过来的网格点上。
最后,我们需要存储已经下过的棋子的信息,方便后续判定输赢,已经棋子重绘时使用。这里为了存储棋子信息,我们就可以设立一个二维数组,二维数组正好对应横竖棋盘的网格,我们可以将黑棋的数组值定为1,白棋的数组值定为2。那么没有棋子的地方的数组值就为默认的0.
具体来看代码:
public class UIListener implements MouseListener ,ActionListener{
//监听器是接口
//后续可能会用到的一些变量
Graphics gr;
int[][] ChessArray=new int[22][22];//黑白棋子数组
int[][] ChessValue=new int[22][22];//AI人机中权值数组
JButton jb11;
int nb=0;
int count=0;
int a,b,x,y,X,Y;
JFrame mf;
HashMap<String,Integer> hm=new HashMap<String,Integer>();//设置AI人机中权值量表
public void mouseClicked(MouseEvent e) {
int z=50,c=20;
Color c1=new Color(0,0,0);
Color c2=new Color(255,255,255);
a=e.getX();b=e.getY();//获得鼠标点击坐标
if(nb==1) {
//确定是否可以下棋
if((a-z)%c>c/2) {
//帮助x轴上点击点优化到坐标格子上
x=(a-z)/c+1;
}else {
x=(a-z)/c;
}
if((b-z)%c>c/2) {
//帮助y轴上点击点优化到坐标格子上
y=(b-z)/c+1;
}else {
y=(b-z)/c;
}
if(ChessArray[x][y]==0) {
//确保棋子只能下在没有棋子的地方
if(zifu.equals("人机对战")) {
gr.setColor(c1);
gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
ChessArray[x][y]=1;
gr.setColor(c2);
AI();
}else if(zifu.equals("人人对战")) {
if(count==0) {
gr.setColor(c1);
count++;
gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
ChessArray[x][y]=1;//黑子数组值为1,方便重绘使用
}else if(count==1) {
gr.setColor(c2);
count--;
gr.fillOval(x*c+z-10, y*c+z-10, 20, 20);
ChessArray[x][y]=2;//白子数组值为21,方便重绘使用
}
}
}else{
System.out.println("该位置已有棋子");
}
}else {
System.out.println("请选择对战类型");
}
}
重绘棋子
在绘制出棋子后,我们同样需要去意识到,拖动窗体或者任何会让窗体刷新的操作后,已经下好的棋子都会消失,那么我们就需要在重绘方法中,同样加入棋子的重绘,也就是正在绘制棋盘这个标题下的代码行中加入重绘棋子的代码。
原理则是,因为我们设置了数组保存棋子,黑棋都赋值了1,白棋都赋值了2,那么我就遍历整个棋盘上上的所有数组,然后把数组等于1和2的都重绘出来即可。
加入了棋子重绘的代码后,整个重绘部分的代码就如下:
public class MyFrame extends JFrame{
//MyFrame子类继承JFrame父类
public UIListener ul;
// int[][] ChessArray;
int z=50,c=20;
public void paint(Graphics g) {
//重绘棋盘,确保刷新网页时候,棋盘还在
super.paint(g);
int x1,x2,y1,y2;
int X1,X2,Y1,Y2;
x1=50;x2=450;y1=70;y2=470;
X1=50;X2=450;Y1=70;Y2=470;
for(int i=0; i<21; i++) {
g.drawLine(x1,y1,x2,y1);
y1=y1+20;
}
for(int i=0; i<21; i++) {
g.drawLine(X1,Y1,X1,Y2);
X1=X1+20;
}
int[][] ChessArray = ul.ChessArray;
for(int i=0;i<ChessArray.length;i++) {
//重绘棋子,网页刷新后,之前下过的棋子还在
for(int j=0;j<ChessArray.length;j++) {
if(ChessArray[i][j]==1) {
g.setColor(Color.black);
g.fillOval(i*c+z-10, j*c+z-10, 20, 20);
}
if(ChessArray[i][j]==2) {
g.setColor(Color.white);
g.fillOval(i*c+z-10, j*c+z-10, 20, 20);
}
}
}
}
}
判定输赢
当棋子都下好了之后,我们就需要在每下一颗棋子后进行输赢的判定。五子棋中,有五颗棋子连在一起即为获胜。因此我们就需要对横竖斜四个大方向上的棋子,进行查找判定。
原理则是因为黑棋的数组值都为1,白棋都为2,那么就在每一次所下棋子的地方为起点,向各个方向查找,每有一个相连的棋子是同色,设置一个变量count,count就加1,当count变为5时候,就证明有五个同色棋子相连,游戏就结束,如果所下棋子是黑棋,黑棋就获胜,如果所下棋子是白棋,白棋就获胜。
下列代码是判定寻找各个方向的同色棋子的方法:
public int Win1(int xx,int yy) {
//判定横向棋子是否有相连的
int count1=0;
for(int i=xx-1;i>=0;i--) {
if(ChessArray[i][yy]==ChessArray[xx][yy]) {
count1++;
}else break;
}
for(int j=xx;j<ChessArray.length;j++) {
if(ChessArray[j][yy]==ChessArray[xx][yy]) {
count1++;
}else break;
}
return count1;
}
public int Win2(int xx,int yy) {
//判定纵向棋子是否有相连的
int count2=0;
for(int n=yy-1;n>=0;n--) {
if(ChessArray[xx][n]==ChessArray[xx][yy]) {
count2++;
}else break;
}
for(int m=yy;m<ChessArray.length;m++) {
if(ChessArray[xx][m]==ChessArray[xx][yy]) {
count2++;
}else break;
}
return count2;
}
public int Win3(int xx,int yy) {
//判定左下右上棋子是否有相连的
int count3=0;
for(int o=xx-1,p=yy+1;o>=0&&p<ChessArray.length;o--,p++) {
if(ChessArray[o][p]==ChessArray[xx][yy]) {
count3++;
}else break;
}
for(int o=xx,p=yy;o<ChessArray.length&&p>=0;o++,p--) {
if(ChessArray[o][p]==ChessArray[xx][yy]) {
count3++;
}else break;
}
return count3;
}
public int Win4(int xx,int yy) {
//判定左上右下棋子是否有相连的


5714





