~这些年,我们一起学过的java~06~我的五子棋学习之旅~

       隔了一两个月再来蓝杰继续学习java,赶脚有点小吃力,从8月6号开始,也学了四天的五子棋了,赶脚学的东西越来越多之后就容易乱,所以,今天就上来花点时间整理整理自己的思路,希望自己学了任何东西之后都能学会整理总结,现在也是在慢慢地在这个过程中找到适合自己的表达。

      基本上这里的代码都是手工再重新敲了一遍,想让自己熟悉熟悉,特别是在木有eclipse帮忙自动报错修正的前提下,所以,里面应该还是有不少的问题,希望大家看的可以犀利地指出,帮助我成长……大恩大德,小女不甚感激……

好啦,废话不多说,开始整理咯!

      做任何游戏啊什么其他的东西,最开始都需要做一个界面,由于重点是在五子棋的功能实现,暂时不需要特别精致一个界面,所以,五子棋的界面,就大致写了个简单的框架,以后功能一个个都实现了,就开始着手完善自己的界面:

//先写一个普通方法供主函数调用

public void play(){
/
/因为定义的类里面已经继承了JFrame,所以,直接使用this即可;

this.setTitle("明艳牌五子棋");//这是调用设置标题的方法,注意因为参数类型是spring,所以自己要加""

 this.setSize(850,700);//这是调用了JFrame里面的设置窗体大小的方法

this.setResizable(false);//这个时候最好设置窗体的大小不可调,这个方法的参数是布尔型
this.setDefaultCloseOperation(3);//这个方法 的设置是因为调试程序是会开很多个console,但是会经常忘                                                                          记关闭,导致开了很多而占用了空间

   //还需要加this.setvisible(true);这个是设置这个窗体能否被看得见,但是写在这个位置有可能后面的代码写了也看不见,所以,最好放在后面再写

       //根据自己的喜好也可以设置背景图片,但是注意要放在窗体的底层,并且底层的上面的层设置透明,而我们底层放了图片后,还要在上面画棋盘,对,接下来就是画棋盘,但是画棋盘之前一定要把画布啊画笔之类的那个包导入进来,这也意味着要  Graphics g=this.getGraphics();  做了这一步之后就可以开始画棋盘啦,注意要把画笔传入画棋盘的那个方法里面。

           //首先要定义一个画棋盘的方法,得传入画笔的参数
public void drawChessTable2(Graphics g){
//先画棋盘横线(先画竖线也行,看个人习惯吧)
//这里有个很重要的一步就是定义一个常量类以便修改,如果只是单单设置几个数值,那么以后想修改大小,就很辛苦,得一个一个地改,效率低而且容易出错,于是定义几个常量,之后若要修改,只须改定义时候的常量就可以了,一劳永逸。

public class Config{
public static final int X0=50;//表格左上角起点的x值
public static final int Y0=50;//表格左上角起点的y值
public static final int Rows=13;//横向线的条数
public static final int Columns=13;//纵向线的条数
public static final int ChessSize=40;//棋子的直径
public static int chess[][]=new int[1
3][13];//定义一个二维数组用来保存棋子的位置
public static final int Size=50;//单元格的大小

}

//这里的static的作用在于 每个对象都共享该属性的值  可以通过类名. 来调用

 //这里的final作用在于表示可以直接进行使用;

//说了那么久要画棋盘,咋还不开始画,再吊人胃口拖出去斩了!嘿嘿,好啦,准备充分了,我们才可以开始画棋盘哟……

for(int i=0;i<Config.Rows;i++)
g.drawLine(Config.X0, Config.Y0+i*Config.Size, Config.X0+(Config.Columns-1)*Config.Size, Config.Y0+i*Config.Size);
//上面用循环语句来画一条一条的横线,g.drawLine(直线的起点的横坐标,起点的纵坐标,直线终点的横坐标,终点的纵坐标);就是在调用画笔去画直线
//画棋盘竖线
for(int j=0;j<Config.Columns;j++)
g.drawLine(Config.X0+j*Config.Size, Config.Y0, Config.X0+j*Config.Size,Config.Y0+(Config.Rows-1)*Config.Size );
//画横线竖线的方法差不多,就是细节地方注意下下,找准起点终点的横纵坐标很重要!
  }


       //画完棋盘之后就要找到落棋点并画上棋子,而我们都知道鼠标单击一下落一个子,那么就需要加入一个鼠标监听器里监听鼠标的动作,在MouseListener这个接口中有很多方法,比如单击,释放,退出等等,我们要找到public void mousePressed(MouseEvent e) {}这个方法,因为这个方法是点了还木有释放时执行的,而恰好符合我们落子的情况。

//这里记得另开一个类来实现MouseListener这个接口,下方的ActionListener 是用作其他功能,下文会详细解释

public class MyListener implements MouseListener,ActionListener{

      //我们先找落棋点吧

public void mousePressed(MouseEvent e) {
int x=e.getX(); 
int y=e.getY();//先得通过MouseEvent e来获取点击时的横纵坐标
int row;   
int clo;     //定义下棋盘线上的横纵交叉点

if(x%Config.Size<Config.Size/2){
row=x/Config.Size;
// System.out.println("row1="+row); 这行输出语句仅用来帮助检测获取的交叉点是否正确
}else{
row=(x/Config.Size)+1;
//System.out.println("row2="+row); 这行输出语句仅用来帮助检测获取的交叉点是否正确
}
//这是先做横向位置的交叉点处理
if(y%Config.Size<Config.Size/2){
clo=y/Config.Size;
//System.out.println("clo1="+clo);这行输出语句仅用来帮助检测获取的交叉点是否正确
}else{
clo=(y/Config.Size+1);
//System.out.println("clo2="+clo);这行输出语句仅用来帮助检测获取的交叉点是否正确
}
//这是做向位置的交叉点处理这里的计算处理主要是为了防止游戏玩家点不到刚刚好交叉点的位置,所以这样可以让玩家在交叉点附近点击依然可以使棋子落在交叉点上

        //画棋子之前先得做点判断,到底是出黑棋还是白棋呢……根据游戏规则,当然是一人一下,所以我们定义一个值来帮助我们识别到底是该出什么颜色的棋,这里我们定义int flag=1;让他的初始值为1,之后用条件语句来判断1的时候出黑棋,然后自动flag++;让值为2的时候出白棋,出完白棋后再flag--

//当然,为了防止棋子在棋盘外面的部分出现,我们需要做些条件限制
if(row>=1 && row<=Config.Rows && clo>=1 && clo<=Config.Columns){
//下面这个if判断是为了避免重复一个地方落子
if(Config.chess[rows][clos]==0){
  if(flag==1 ){
g.setColor(Color.black);//这里在调用设置画笔颜色的方法
g.fillOval(row*Config.Size-Config.ChessSize/2,clo*Config.Size-Config.ChessSize/2,Config.ChessSize, Config.ChessSize);
//棋子就是个圆形,所以,我们用g.fillOval(棋子落下的横坐标,棋子落下的纵坐标,椭圆的长轴,椭圆的短轴);因为圆形长短轴大小一样,所以都填Config.ChessSize
Config.chess[rows][clos]=1;  //1代表黑棋,0就代表没有落子
flag++;//注意此时flag值为2
  }else if(flag==2){
g.setColor(Color.white);//和前面一样调用设置颜色的方法
g.fillOval(row*Config.Size-Config.ChessSize/2, clo*Config.Size-Config.ChessSize/2, Config.ChessSize, Config.ChessSize);
Config.chess[rows][clos]=2;//2代表白棋,0代表没有落子
flag--;
}

  }

}

  //能够完成让画笔在准确的地方画棋子之后,就可以进行判断输赢了,因为五子棋的连续五个子有可能在8个方向上,所以我们得进行分类,横向,纵向和斜向,为了便于之后的修改和查看,我分别写了三个方法,这样也便于调试程序,及时知道是哪个方法出了问题

public void hengxiang(int row ,int clo){

//上面一行设置了参数,是为了将棋盘的交叉点坐标传入方法中

count=1;

int rows=row-1;

int clos=clo-1; //这两个变量的定义是因为row和clo都代表交叉点在第几行第几列,而放入二维数组中就得减一,如果不进行此操作,之后得多次写这个减一,为了一劳永逸,就干脆在这里多定义两个量

for(int i=rows;i<Config.Columns-1;i++){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i+1][clos]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

//以上部分是按黑棋落子方向从右到左的方向依次落五子则赢得胜利,下面是白旗部分,很类似,只需改动一点即可

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i+1][clos]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

//我们还需要写横方向上从左到右连续五子的胜利,只要公用count,即使最后落子在五子的正中间,也能判断输赢

  }

for(int i=rows;i>0;i--){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i-1][clos]){

count++;

}else{

break;

}

}

if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i-1][clos]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

}

}

//这个方法仅仅写了横向的输赢判断,接下来是纵向的输赢判断,因为算法原理太过简单,我也不太好说明,相信大家都能看得懂……

public void zongxiang(int row,int clo){

int count=1;

int rows=row-1;

int clos=clo-1;//跟之前一样再定义一遍

for(int i=clos;i<Config.Rows-1;i++){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[rows][clos+1]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[rows[i+1]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

//跟之前横向方法的写法类似,这个只写了从下到上的落子输赢,还得写从上到下的那部分…… }

for(int i=clos;i>0;i--){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[rows][clos-1]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[rows[i-1]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

}

//于是,横向纵向都已经实现输赢的判断啦,但是斜的如何判断呢?原理其实很类似的,看看我的代码就知道啦……

public void xiexiang(int row, int clo){

int count=1;

int rows=row-1,clos=clo-1;

for(int i=rows,j=clos;i<Config.Columns-1 && j<Config.Rows-1;i++,j++){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i][j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i[j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

//斜的方向比较复杂需要写4个循环语句……

for(int i=rows,j=clos;i<Config.Columns-1 && j>0 ;i++,j--){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i][j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i[j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

for(int i=rows,j=clos;i>0 && j>0 ;i--,j--){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i][j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i[j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

for(int i=rows,j=clos;i>0&& j<Config.Rows-1 ;i++,j--){

if(Config.chess[rows][clos]==1){

if(Config.chess[rows][clos]==Config.chess[i][j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==1 && count==5){

System.out.println("恭喜黑棋赢得胜利!!!");

}

if(Config.chess[rows][clos]==2){

if(Config.chess[rows][clos]==Config.chess[i[j]){

count++;

}else{

break;

}

}

 if(Config.chess[rows][clos]==2 && count==5){

System.out.println("恭喜白棋赢得胜利!!!");

}

}

//这部分写在监听器里,然后得把监听器加到主函数里面去,这个过程大家都懂吧,我就不赘述了

//到这里为止,五子棋的大部分功能就实现了,记得调用这些方法就可以了,我就在这里写调用的方法了,还有悔棋和重绘的功能,先实现重绘吧,首先为什么要重绘呢?当我运行自己的程序的时候会发现缩小程序再打开的时候之前的棋子都不见了,就像还没有下过似的,所以我们得把之前的棋子都保存下来,所谓重绘就是当缩小或进行其他移动改变的时候,不影响棋盘,也就是重新再画过,所以,我们得将之前下过的棋子的颜色,横纵坐标,以及交叉点的值都储存起来,然后再重新画一遍,所以这里也要用到之前的二维数组

//先将要储存的量放在一个类里面

public class Save{

public int row,clo,flag,x,y;

public Save(int x,int y,int row,int clo,int flag){

this.x=x;

this.y=y;

this.row=row;

this.clo=clo;

this.flag=flag;

}

}

//然后主函数所在的那个类再写个重绘的方法

public void paint(Graphics g){

super.paint(g);//子类调用父类的方法时记得关键字是super

//接下来一样和之前的流程要画棋盘的横竖线

for(int i=0;i<Config.Rows;i++){g.drawLine(Config.X0, Config.Y0+i*Config.Size, Config.X0+(Config.Columns-1)*Config.Size, Config.Y0+i*Config.Size);}

for(int j=0;j<Config.Columns;j++){
g.drawLine(Config.X0+j*Config.Size, Config.Y0, Config.X0+j*Config.Size,Config.Y0+(Config.Rows-1)*Config.Size );
}

}

//重绘完棋盘就要重绘棋子啦,这里要补充下,我们得在真正落子的地方,也就是判断出黑棋还是白棋的那个if判断语句中需要将棋子的信息存在数组里,所以,之前要初始化下系统自带的数组,

ArrayList<Save>   array=new  ArrayList<Save>(); //当然,这个是写在监听器的最开始部分

记得在落子的那个地方加上

int x=row*Config.Size-Config.ChessSize/2;

int y=clo*Config.Size-Config.ChessSize/2;

  Save save=new Save(x,y,rows,clos,1);

//这里的x,y其实应该要定义下,是之前g.fillOval那个方法的横纵坐标的值,对于重绘是很有必要滴!然后接下来就是将这个save存入数组中,点击一次,就存一次

array.add(save);

//再回到上面那个public void paint(Graphics g)的方法中,因为之前存了棋子,所以,现在就可以重绘啦!

public void paint(Graphics g){

super.paint(g);

//画棋盘的横竖线

for(int i=0;i<Config.Rows;i++){g.drawLine(Config.X0, Config.Y0+i*Config.Size, Config.X0+(Config.Columns-1)*Config.Size, Config.Y0+i*Config.Size);}

for(int j=0;j<Config.Columns;j++){
g.drawLine(Config.X0+j*Config.Size, Config.Y0, Config.X0+j*Config.Size,Config.Y0+(Config.Rows-1)*Config.Size );
}

}

//重绘棋子

try{

for(i=0;i<my.array.size();i++){

Save chess=my.array.get(i);//my在这里是因为我设置的监听器的名称是my

if(chess.flag==1){

g.getColor(Color.black);

g.fillOval(chess x,chess y,Config.ChessSize,Config.ChessSize);

}else if(chess.flag==2){

g.getColor(Color.white);

g.fillOval(chess x,chess y,Config.ChessSize,Config.ChessSize);

}

}

}catch(Exception e){//这个是捕捉异常的情况处理机制

}

//要实现悔棋的功能的话,当然要现在制作界面的时候添加一个JButton,然后内容写上悔棋,再在这个JButton上装个动作监听器,就可以啦,我在这里就把ActionListener接口要实现的方法打出来

public void actionPerformed(ActionEvent e){

if(e.getActionCommand().equals("悔棋")){

Save chess=this.array.get(array.size()-1);//这里需要把最后一个落子取出来才能调用里面的内容

Config.chess[rows][clos]=0;//这里必须得让它归零,不然悔棋之后那个点就不能再下子了

array.remove(array.size()-1);//通过调用系统数组内部的方法将这个棋子去掉

//然后再重绘一遍

frame.repaint();//frame就是在监听器里new的一个JFrame

}

}

//到这里,五子棋的基本功能都可以实现啦,我还加了图片和音乐,谁先执子等功能,期待更多的东西出现!已经很晚了,明天要学习新的内容,好期待呀,剩下的功能实现下次有时间就附上来!欢迎各位大神指点呀!!








}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值