五子棋是棋类竞技项目之一。它容易上手,规则简单,趣味性强,有助于增强思维能力,提高智力。
五子棋玩法:五子棋的棋具与围棋通用,双方分别使用黑白两色的棋子,在水平、垂直或各条斜线上先形成五子连线者获胜。
棋子类
棋子只有三种情况:黑棋,白棋,没有棋。所以可以设计成枚举类。
提供一个可以查看棋子信息的功能。但不允许修改棋子。(只提供 getter,不提供 setter) 核心代码如下
public enum Chessmen{
WHITE("🔵"),BLACK("🔴"),DEFAULT("➕");
private Chessmen(String chessmen){
this.chessmen=chessmen;
}
private String chessmen;
public String getChessmen() {
return chessmen;
}
}
棋盘类
棋盘的属性:
- 由棋子构成的二维数组
棋盘的行为:
- 初始化棋盘
- 显示棋盘
- 更新棋盘
说明:
- 初始化棋盘可以通过构造方法实现。
- 显示棋盘只负责将棋盘中的棋子信息输出,单一职责
- 更新棋盘只负责根据指定横坐标,纵坐标,要修改的棋子信息进行修改棋盘,并返回修改结果,单一职责
核心代码如下
/**
* 棋盘类 限定为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;
}
}
玩家类的设计
玩家的属性:
- 昵称
- 等级
- 积分
- 胜局次数
- 密码
玩家的行为:
- 显示玩家的个人信息
- ...
玩家的属性可设计用于
- 信息演示及保存,比如吕布,关羽,后期可用于网络通信及多线程。
- 积分可用于设计礼品兑换
- 等级可用用设计升级规则,比如每胜 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;
}
}
棋局类的设计
棋局的属性:
- 落子顺序标识
- 棋子连线数胜负标识
- 棋盘
- 玩家数组
棋局的行为:
- 游戏界面显示
- 玩家信息登记
- 开始棋局
- 胜负判断
- 是否五子连线
在前面的设计中并没有进行衍生的设计,因为这些对象都是相对客观的存在。但棋局本身是抽象的,毕竟既可以下五子棋,也可以下围棋。但不管下什么棋,棋盘和棋子是一样的,玩家是一样的,但棋局会因为下不同的棋而变得不一样。或者说棋局中的胜负判断方式会变得不一样。那如何设计这个棋局类呢?因为有很多相同的属性和方法,这时候就可以设计成棋局抽象类,它有五子棋子类和围棋子类等;当前的情况也可以设计成五子棋接口,围棋接口。因为 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的面向对象思想 ,首先根据类图,我们可以先完成 玩家类的书写 然后是棋子类,棋盘类。本实验的最难点在于棋局类的实现,怎么判断成功