java实现五子棋游戏

527 阅读3分钟

五子棋是棋类竞技项目之一。它容易上手,规则简单,趣味性强,有助于增强思维能力,提高智力。

五子棋玩法:五子棋的棋具与围棋通用,双方分别使用黑白两色的棋子,在水平、垂直或各条斜线上先形成五子连线者获胜。

image.png

棋子类

image.png 棋子只有三种情况:黑棋,白棋,没有棋。所以可以设计成枚举类。

提供一个可以查看棋子信息的功能。但不允许修改棋子。(只提供 getter,不提供 setter) 核心代码如下

public enum Chessmen{ 
WHITE("🔵"),BLACK("🔴"),DEFAULT("➕");
private Chessmen(String chessmen){ 
this.chessmen=chessmen; 
} 
private String chessmen;
public String getChessmen() {
return chessmen;
} 
}

棋盘类

image.png 棋盘的属性:

  • 由棋子构成的二维数组

棋盘的行为:

  • 初始化棋盘
  • 显示棋盘
  • 更新棋盘

说明:

  • 初始化棋盘可以通过构造方法实现。
  • 显示棋盘只负责将棋盘中的棋子信息输出,单一职责
  • 更新棋盘只负责根据指定横坐标,纵坐标,要修改的棋子信息进行修改棋盘,并返回修改结果,单一职责

核心代码如下

/** 
* 棋盘类 限定为16 X 16
* 属性:棋子数组
* 方法:初始化棋盘
* 方法:展示棋盘
* 方法: 修改棋盘  */
public class ChessBoard {
  private Chessmen[][] chesses;

  //初始化棋盘
  public ChessBoard() {
      initBoard(16);
  }
  //创建 16 X 16 棋盘
  private void initBoard(int size) {
      chesses = new Chessmen[size+8][size+8];
      for (int i = 4; i < size+4; i++) {
          for (int j = 4; j < size+4; j++) {
              chesses[i][j] = Chessmen.DEFAULT;
           }
       }
  }
  /**
   *  显示棋盘
   */
  public void showBoard() {
      System.out.println("   壹贰叁肆伍陆柒捌玖拾屲亗岌岄岪峘");
      for (int i = 4; i < chesses.length-4; i++) {
          System.out.print(i-3+(i<13?"  ":" "));
          for (int j = 4; j < chesses[i].length-4; j++) {
              System.out.print(chesses[i][j].getChessmen());
           }
           System.out.println();
       }
  }
  /**
   * 更新棋盘信息
   * @param x  横坐标
   * @param y  纵坐标
   * @param chess  棋子
   * @return 是否修改成功
   */
  public boolean changeBoard(int x, int y,Chessmen chess) {
      boolean flag=true;
      if( chesses[x+3][y+3]==Chessmen.DEFAULT){
          chesses[x+3][y+3]=chess;
      }else{
          flag=false;
      }
      return flag;
  }

  //提供棋盘上所有棋子信息
  public Chessmen[][] getChesses() {
      return chesses;
  }

}

玩家类的设计

image.png

玩家的属性:

  • 昵称
  • 等级
  • 积分
  • 胜局次数
  • 密码

玩家的行为:

  • 显示玩家的个人信息
  • ...

玩家的属性可设计用于

  • 信息演示及保存,比如吕布,关羽,后期可用于网络通信及多线程。
  • 积分可用于设计礼品兑换
  • 等级可用用设计升级规则,比如每胜 5 局升一级 负 5 局降一级 上不封顶,最低一级
  • 统计胜局次数后期可用于排行榜之类
  • 密码可用于登录

显示玩家的个人信息可以通过重写 Object 类的 toString() 方法来实现。 核心代码如下


/**
*  玩家类
*/
public class Player {
  //昵称
  private String name;
  //等级
  private int level;
  //积分
  private int score;
  //胜局次数
  private int winTimes;
  //密码
  private String password;
   /**
   *  显示玩家基本信息
   *  @return
   */
  @Override
  public String toString() {
      return "玩家基本信息: [姓名:" + name + ", 等级=" + level + "级, 积分=" + score + "分, 胜局:" + winTimes + "次]";
  }

  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  public int getLevel() {
      return level;
  }

  public void setLevel(int level) {
      this.level = level;
  }

  public int getWinTimes() {
      return winTimes;
  }

  public void setWinTimes(int winTimes) {
      this.winTimes = winTimes;
  }

  public int getScore() {
      return score;
  }

  public void setScore(int score) {
      this.score = score;
  }

  public String getPassword() {
      return password;
  }

  public void setPassword(String password) {
      this.password = password;
  }

}

棋局类的设计

image.png

棋局的属性:

  • 落子顺序标识
  • 棋子连线数胜负标识
  • 棋盘
  • 玩家数组

棋局的行为:

  • 游戏界面显示
  • 玩家信息登记
  • 开始棋局
  • 胜负判断
  • 是否五子连线

在前面的设计中并没有进行衍生的设计,因为这些对象都是相对客观的存在。但棋局本身是抽象的,毕竟既可以下五子棋,也可以下围棋。但不管下什么棋,棋盘和棋子是一样的,玩家是一样的,但棋局会因为下不同的棋而变得不一样。或者说棋局中的胜负判断方式会变得不一样。那如何设计这个棋局类呢?因为有很多相同的属性和方法,这时候就可以设计成棋局抽象类,它有五子棋子类和围棋子类等;当前的情况也可以设计成五子棋接口,围棋接口。因为 Java 中的单根继承特性,通常会选择接口的方式,这样可以更灵活更方便地进行扩展。

本次我们选择接口的方式,在棋局的行为中,只有胜负判断和是否五子连线两个功能和其它棋类游戏是不一致的,所以就声明一个 FiveInLineInterface 五子棋接口,定义这两个功能,需要棋局实现类重写接口中的这两个方法实现功能。

/**
* 五子棋接口

*/
public interface FiveInLineInterface {
   /**
    * 胜负判断入口,产生四个一维数组
    * @param x  横坐标
    * @param y  纵坐标
    * @return
    */
    boolean isWin(int x,int y);
    /**
     *  五子连续判断
     * @param chessmen 棋子周边的一维数组
     * @return  是否存在连续五个棋子一样
     */
    boolean isFiveInLine(Chessmen[] chessmen);
}

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/**
*  棋局类
* @version 1.2
*/
public class ChessGame implements FiveInLineInterface{
    private Scanner sc = new Scanner(System.in);
    //落子顺序
    private boolean initiative = true;
    //棋子连线数
    private int pointCount=0;
    //棋盘
    private ChessBoard board=new ChessBoard();
    //本局对战玩家信息
    private Player[] players=new Player[2];
    /**
    * 初始界面
    */
    public void showMain(){
        System.out.println("************************************");
        System.out.println("------------游戏说明-----------------");
        System.out.println("------1.落子方式为指定坐标-----------");
        System.out.println("------2.数字与数字之前用,号隔开------");
        System.out.println("------3.合法输入格式:5,5-------------");
        System.out.println("------4.无禁手规则-----------------");
        System.out.println("------5.五子连线获胜----------------");
        System.out.println("************************************");
    }

     /**
      * 初始化玩家信息
      */
      public void initPlayer(){
        showMain();
        //默认角色数组
         String [] roles = {"吕布","关羽","张飞","典韦","许储","赵云","马超","黄忠","孙策","周瑜"};

        Player p1 =  new Player();
        Player p2 =  new Player();
        int a=(int)(Math.random()*10);
        int b=0;
        p1.setName(roles[a]);
         do{
             b=(int)(Math.random()*10);
             p2.setName(roles[b]);
         }while(p1.getName().equals(p2.getName()));
         players[0]=p1; players[1]=p2;
         System.out.println("进行随机分配角色。。。 \n 分配玩家 A 角色:"+p1.getName()+" \n 分配玩家 B 角色:"+p2.getName());
         System.out.println("---------------------------------");
         initiative=a<b;
         System.out.println(p1+"\n"+p2);
         System.out.println("开始猜先。。。\n"+(initiative?p2.getName():p1.getName())+"获得先手");
         System.out.println("---------------------------------");
         System.out.print("任意键回车开始对弈");
         sc.nextLine();
    }

    /**
     * 开始下棋
     */
    public void play(){
        initPlayer();
        Chessmen chess=null;
        int x=0;int y=0;
        Player tempPlayer;
        do{
            board.showBoard();
            x=0; y=0;
            System.out.print((initiative?players[1].getName():players[0].getName())+"请输入要下的位置:");
            if(initiative){
                chess=Chessmen.BLACK;
            }else{
                chess=Chessmen.WHITE;
            }
            while(true){
                String point = sc.next();
                if(point.matches("\\d+,\\d+")){
                    String [] ps =point.split(",");
                    x=Integer.parseInt(ps[0]);
                    y=Integer.parseInt(ps[1]);
                    if(x>16||x<0||y>16||y<0){
                        System.out.print("请控制坐标在棋盘内");
                           continue;
                      }
                    break;
                }else{
                    System.out.print("请按要求输入落子坐标");
                }
            }
            //在指定位置落子
            if(board.changeBoard(x,y,chess)){
                //落子成功,切换先手
                initiative=!initiative;
                //判断胜负
                if(isWin(x,y)){
                    board.showBoard();
                    if(initiative){
                      tempPlayer=players[0];
                    }else{
                      tempPlayer=players[1];
                    }
                    tempPlayer.setWinTimes(tempPlayer.getWinTimes()+1);
                    tempPlayer.setWinTimes(tempPlayer.getScore()+5);
                    System.out.println(tempPlayer);
                    System.out.print(tempPlayer.getName()+"胜利,胜局加1 ,是否继续对战(按0继续,其它结束)");
                    if(!"0".equals(sc.next())){
                        System.out.println("游戏结束");
                        break;
                    }else{
                         //重新加载棋盘, 换先
                         System.out.println("双方交换棋子换先,开始对战");
                         board=new ChessBoard();
                    }
                }
            }else{
                  System.out.println("此处已经存在棋子");
            }
        }while(true);
    }

    /**
     * 判断是否胜利 ,需要注意坐标需要加3
     * @param x 棋子横坐标
     * @param y 棋子纵坐标
     * @return
     */
     @Override
    public boolean isWin(int x,int y) {
        x=x+3;
        y=y+3;
        Chessmen[][] chesses= board.getChesses();
        Chessmen [] up={chesses[x][y-4],chesses[x][y-3],chesses[x][y-2],chesses[x][y-1],chesses[x][y],chesses[x][y+1],chesses[x][y+2],chesses[x][y+3],chesses[x][y+4]};
        Chessmen [] down={chesses[x-4][y],chesses[x-3][y],chesses[x-2][y],chesses[x-1][y],chesses[x][y],chesses[x+1][y],chesses[x+2][y],chesses[x+3][y],chesses[x+4][y]};
        Chessmen [] left={chesses[x-4][y-4],chesses[x-3][y-3],chesses[x-2][y-2],chesses[x-1][y-1],chesses[x][y],chesses[x+1][y+1],chesses[x+2][y+2],chesses[x+3][y+3],chesses[x+4][y+4]};
        Chessmen [] right={chesses[x-4][y+4],chesses[x-3][y+3],chesses[x-2][y+2],chesses[x-1][y+1],chesses[x][y],chesses[x+1][y-1],chesses[x+2][y-2],chesses[x+3][y-3],chesses[x+4][y-4]};
        return isFiveInLine(up)||isFiveInLine(down)||isFiveInLine(left)||isFiveInLine(right);
    }

    /**
     * 判断一维数组中是否有五子连线
     * @param line 需要判断是数组
     * @return
     */
     @Override
    public boolean isFiveInLine(Chessmen[] line) {
        String flag=Chessmen.BLACK.getChessmen();
        if(initiative){
            flag=Chessmen.WHITE.getChessmen();
        }
        boolean result=false;
        for (int i = 0; i < line.length; i++) {
            if(line[i]!=null){
                if(!flag.equals(line[i].getChessmen())){
                    //如果没有连续,重新计数
                    pointCount=0;
                    continue;
                }
                pointCount++;
                if(pointCount==5){
                    result=true;
                    break;
                }
            }
        }
        return result;
    }
}

总结:完成本次实验需要使用Java的面向对象思想 ,首先根据类图,我们可以先完成 玩家类的书写 然后是棋子类,棋盘类。本实验的最难点在于棋局类的实现,怎么判断成功