疯狂java实战演义第1章 控制台五子棋Word文档下载推荐.docx
《疯狂java实战演义第1章 控制台五子棋Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《疯狂java实战演义第1章 控制台五子棋Word文档下载推荐.docx(19页珍藏版)》请在冰豆网上搜索。
x与y的值必须是1到N(棋盘的大小)的正数。
系统询问玩家是否继续游戏时,玩家输入y是代表继续,输入其它则代表退出游戏。
“●”代表黑子,“○”代表白子。
当玩家以(x,y)的形式输入下棋的坐标后,游戏中就可以根据玩家所下的坐标,再去将棋子放置到棋盘中。
我们可以将棋盘看作一个二维数组,填充着棋盘式的标志(“十”),玩家下棋后,将棋子替换原来的标志,最后再执行输入。
由于本章是在控制台中进行打印,因此只需要使用System.out.println来进行打印即可,如果需要实现有界面的五子棋游戏,例如使用swing或者awt,可以使用相应的方法,将二维数组“画”到界面中。
因此,不管是使用swing、awt或者其他界面技术,五子棋的实现原理几乎大同小异。
1.2了解游戏流程描述
在开发五子棋之前,我们先了解一下游戏的整个游戏流程,了解游戏的流程,有助于我们在开发的过程中可以清晰的掌握程序结构,对于实现功能有莫大的帮助,五子棋的具体流程如图1.1所示。
图1.1五子棋游戏流程
1.2.1玩家输入坐标
游戏开始,系统在控制台中打印出棋盘,玩家根据这个棋盘,选定下棋的位置坐标后,在控制台中输入相应的坐标,系统读取玩家所输入的坐标并进行相应的分析,如果玩家所下的棋使得玩家游戏胜利,则系统询问是否继续游戏。
系统读取了玩家输入的坐标后,除了判断游戏是否胜利外,还需要判断玩家输入的坐标中是否已经存在了相应的棋子,如果存在的话,需要再次提示玩家,重新输入。
1.2.2“电脑”下棋
玩家输入了坐标,系统判断玩家没有游戏胜利后,就应该轮到“电脑”下棋,在本章的开头中我们已经讲到,本章可以实现一个简单的电脑来进行游戏,只需要随便的产生棋盘坐标,就可以让“电脑”在相应的坐标中下棋。
如果电脑随机产生的坐标中已经存在棋子,那么我们可以重新随机产生坐标,直到产生的坐标上没有存在棋子为止。
当“电脑”下完棋后,就可以使用同样的判断方式(判断是否五子相连)来判断“电脑”所下的棋子是否已经使得游戏胜利,如果游戏胜利,同样地去提示玩家,电脑已经胜利了。
在本章我们并不需要实现强大的人工智能“电脑”,只需要简单的随机产生坐标即可。
1.3创建游戏的各个对象
这里设计三个类来完成游戏的功能,棋盘类(Chessboard),游戏类(GobangGame)与棋子类(Chessman)(枚举类),类的关系如图1.2所示,从图中可以看出,Chessboard依赖于GobangGame,gobangGame的改变,会影响到Chessboard状态的改变,而Chessman与GobangGame是一个聚合关系。
下面一一介绍。
图1.2五子棋类图
1.3.1Chessboard类
要进行五子棋游戏,必须有有一个棋盘,而这个类主要控制棋盘的初始化,输出与及增加新的棋子。
这个类包含以下方法:
❑voidinitBoard(),这个方法用于初始化棋盘,开始新的游戏时,应该调用此方法,初始化出一个新的空棋盘。
❑voidprintBoard(),此方法用于在控制台输出棋盘,各方每一完一颗棋子后,由于棋盘上棋子的状态有改变,所以必须调用此方法重新输入棋盘。
❑voidsetBoard(intposX,intposY,Stringchessman),posX与posY是新下棋子的x与y坐标,,chessman是新下棋子的类型(黑子与白子),每下完一颗棋子后,通过调用此方法把棋子设置到棋盘上。
❑String[][]getBoard(),返回棋盘,返回类型是保存棋盘的二维数组。
当我们需要初始化棋盘的时候,可以直接调用Chessboard的initBoard方法,我们需要考虑该方法需要实现的功能:
初始化棋盘。
由于我们将棋盘看作是一个二维数组,因此initBoard就需要帮我们去创建一个二维数组,创建二维数组可以使用以下代码。
代码清单:
code\gobang\src\org\crazyit\gobang\Chessboard.java
Object[][]array=newObject[size][size];
for(inti=0;
i<
array.length;
i++){
for(intj=0;
j<
array[i].length;
j++){
array[i][j]=newObject();
}
}
以上代码创建一个固定大小(一维与二维大小)的二维数组,再通过嵌套循环为数组中的每一个元素进行赋值。
在游戏中如果我们进行了下棋的操作,可以直接改变这个数组的某一个元素值。
在创建Chessboard类时,我们就需要发挥面向对象的思维,在我们的程序中,所有看到的或者想的事物,我们都可以将其抽象成具体的某个对象,并赋予一定的属性与行为。
在设计对象的过程中,如果有某些事物拿捏不准,不知如何设计属性或者行为,可以将其设计成接口或者抽象类。
Chessboard中提供了一个printBoard的方法用于打印棋盘,在本章中,我们就需要将棋盘数组打印到控制台中,因此该方法可以简单的调用System.out.print去打印相关的字符串。
需要注意的是,由于printBoard方法是没有参数的,因此我们需要为Chessboard提供一个二维数组变量,当调用printBoard方法的时候,将对象内的二维数组打印,我们可以将Chessboard看作一个有状态的Java对象,有状态的Java对象可以理解成一个Java对象保存一些与该对象相关的状态属性,如果该对象没有保存与该对象相关的状态属性,那么我们可以将这个对象看成一个无状态的Java对象。
当外部调用Chessboard的setBoard方法时,就可以将某个值设置到Chessboard中的二维数组里,告诉Chessboard玩家或者“电脑”在某个位置下了怎样的棋子。
1.3.2Chessman类
Chessman类是一个枚举类,此类是构造器私有的,不能直接创建,里面有BLACK与WHITE两个静态属性,代表黑子与白子枚举类,两个表态属性都是Chessman类型的,要获取棋子,则通过这两个属性调用以下的方法获取棋子:
❑StringgetChessman(),返回String类型的棋子实例,“●”或者“○”。
如果我们需要得到棋子的字符串(“●”或者“○”),可以使用以下的代码。
code\gobang\src\org\crazyit\gobang\Chessman.java
Chessman.BLACK.getChessman();
1.3.3GobangGame类
GobangGame类是进行游戏的类,Chessboard依赖于此类,此类控制游戏的开始,重玩与结束,并影响Chessboard类。
主要包含以下构造器与方法:
❑GobangGame(),默认无参数构造器。
❑GobangGame(Chessboardchessboard),有参数构造器,以一个Chessboard实例去初始化这个类。
❑booleanisValid(StringinputStr),此方法验证控制台的输入字符串是否合法,如果合法,返回true,如果不合法,则返回false,此方法抛出Exception异常。
❑voidstart(),开始游戏。
此方法抛出Exception异常。
❑booleanisReplay(Stringchessman),是否重新开始游戏,如果是,返回true,否则返回false,参数chessman代表黑子或白子。
❑int[]computerDo(),计算机随机下棋,由计算机自动设置棋盘,并返回包含新下棋子位置x与y坐标的int[]数组类型。
❑booleanisWon(intposX,intposY,Stringico),判断输赢,参数posX与posY代表新下棋子的x与y坐标,ico代表新下的棋子类型,如果赢了,返回true,否则返回false。
GobangGame是我们五子棋游戏的主体类,游戏里面所有的处理都在该类中实现。
GobangGame中的isValid方法用于验证控制台的输入,玩家主要在控制台输入下棋的坐标,下棋的坐标的字符串形式为:
x,y,我们需要对字符串进行处理得到x和y的值,如果玩家输入的字符串不符合系统要求,则isValid方法返回false,只有当该方法返回true的时候,才会去修改Chessboard的二维数组。
GobangGame中提供了一个start方法,用于游戏的开始,我们需要考虑游戏开始的行为,例如需要初始化棋盘(调用Chessboard的init方法),需要开始从控制台读取玩家的输入信息、打印棋盘,验证控制台输入的信息等,这些功能我们将在下面的章节中加以描述。
当轮到“电脑”下棋的时候,我们需要随机生成电脑的下棋坐标,GobangGame中的computerDo方法用于随机产生坐标。
判断一局游戏是否胜利,可以调用GobangGame的isWon方法,该方法判断游戏是否胜利,是五子棋中最主要的方法,五子棋是否可以相连的所有逻辑,都会在该方法中实现。
isWon方法会在每次下棋后(玩家下棋或者“电脑”下棋)调用。
到此,游戏中的三个对象已经设计完成,这三个对象中已经定义好了各种方法,并在前面章节中详细描述了各个方法的作用,在下面章节中我们将开始对这三个对象所定义的方法进行实现。
当然,如果需要做到更好的程序解耦,我们可以使用一些设计模式,例如将游戏规则写成一个具体的算法,可以使用策略模式,如果需要产生出不同的棋子(将控制台换成其他界面),可以编写棋子工厂等。
但是本章主要目的是展现一个最简单的五子棋,因此本章中并不涉及任何具体的设计模式。
1.4棋盘类实现
在此类中,主要是用一个String[][]类型的二维数组board去保存棋盘,board[i][j]代表棋盘的某个位置(i代表x坐标,j代表y坐标),如果此位置没有棋子,默认值为“十”,如果有棋子,board[i][j]的值为“●”或者“○”。
用一个不可改变的常量BOARD_SIZE来表示棋盘的大小,所以保存这个棋盘的是一个BOARD_SIZE*BOARD_SIZE的二维数组。
图1.3描述了为什么需要使用一个二维数组来代表一个棋盘,如果把棋盘的一列当做一个数组,那么N列的棋盘就是一个二维数组,用数组能很好的存储与表现这个棋盘。
图1.3棋盘与数组的关系
1.4.1初始化棋盘
在1.3节介绍过,此类主要是实现棋盘初始始化、输出、与更新,在这节便用代码一步一步地实现各个功能。
首先我们需要初始化棋盘的实现,看以下代码片段。
publicvoidinitBoard(){
//初始化棋盘数组
board=newString[BOARD_SIZE][BOARD_SIZE];
//把每个元素赋值为“十”,用于控制台输出棋盘
for(inti=0;
BOARD_SIZE;
i++){
for(intj=0;
j++){
board[i][j]="
十"
;
}
}
上面代码中,BOARD_SIZE是代表棋盘的大小,用一个String[][]类型的二维数组来代表棋盘,创建此数组后,通过迭代为为个数组元素的值赋为“十”来初始化棋盘。
创建了棋盘数组后,如果需要定位到棋盘的某个位置,只需要得到棋盘数组的一维值与二维值即可,例如处理玩家下棋动作的时候,可以将数组中具体的某个“十”替换成具体的棋子字符串。
1.4.2输出棋盘
输出棋盘,只需要Chessboard的board属性(二维数组)的每一个值,打印到控制台中。
如果可以做到更好的扩展性,我们可以在二维数组中存放棋子对象,而不是简单的字符串,那么存放在二维数组中的每一个棋子对象,都可以实现某个棋子接口或者继承棋子的抽象类,这样可以更好的做到游戏的扩展性。
当然,我们在本章为了简单起见,只在该二维数组中存放需要打印的字符串,打印时只需要得到具体的某个二维数组的元素,将其打印即可。
publicvoidprintBoard(){
//打印每个数组元素
for(inti=0;
//打印后不换行
System.out.print(board[i][j]);
//每打印完一行数组元素就换行一次
System.out.print("
\n"
);
棋盘的输出与棋盘的初始化相类似,都是要遍历保存棋盘的数组,只不过是每遍历到一个元素都要输出来,注意到这里的输出方法用的是System.out.print()而不是常用的System.out.println(),这里因为System.out.println()方法是输出后自动换行的,如果使用此方法,便达不到我们需要的效果,棋盘的输出效果如图1.4。
图1.4控制台五子棋的棋盘
打印出来的效果,就好像在控制台中出现了一个棋盘。
1.4.3获取棋盘
在Chessboard中提供了一个getBoard的方法,用于返回本对象的棋盘二维数组,该方法一般在游戏类GobangGame中调用,游戏类得到棋盘的二维数组,可以用于判断棋盘中的某一个位置是否有棋子或者计算游戏是否胜利。
getBoard方法只需要将本对象中的board(二维数组)返回即可,代码如下。
/**
*返回棋盘
*@return返回棋盘
*/
publicString[][]getBoard(){
returnthis.board;
到此,棋盘类的几个方法都已经实现,该类的主要功能是创建棋盘、打印棋盘等,实现的过程中涉及了一些Java语言的基本操作,例如嵌套循环、创建二维数组等。
在下面的小节中,我们将去实现游戏的核心部分。
1.5棋子枚举类实现
在某些情况下,一个类的属性是有限而且固定的(在某些情况下),例如本章中的棋子类,它只有两个对象,黑棋和白棋。
这种实例有限而且固定的类,在Java里面称为枚举类,枚举类的关健字用enum而不是class,此类中有两个枚举属性BLACK和WHITE,代表黑子与白子,代码实现如下:
publicenumChessman{
BLACK("
●"
),WHITE("
○"
privateStringchessman;
/**
*私有构造器
*/
privateChessman(Stringchessman){
this.chessman=chessman;
*@return黑棋或者白棋
publicStringgetChessman(){
returnthis.chessman;
在上面的代码中,可以看到,枚举类是用enum关键字代替了class关键字,看到此枚举类的构造器的权限修饰符是private,也是表明此类是不可以通过外部创建的,只能在此类的内部创建,这是为了保证此对象只有黑子与白子两种类型。
黑体代码是列出枚举值,实际上就是调用私用构造器创建此对象,等同以下代码:
publicstaticfinalChessmanBLACK=newChessman(“●”);
publicstaticfinalChessmanWHITE=newChessman(“○”);
由于BLACK与WHITE两个属性是静态的,所以要获取黑子或者白子,可以通过以下代码来获得:
Chessman.WHITE.getChessman();
在控制台中,我们可以使用这种方式来确定棋子的字符串,如果我们需要在swing或者其他界面中展示一个棋子,可能需要为具体的某个棋子保存相应的棋子图片,在本章中,由于棋子只是普通的两个字符串,因此可以直接写成枚举对象即可。
如果你希望你的程能有更好的扩展性,笔者建议可以根据情况建立棋子接口,并提供白棋与黑棋的实现类,我们在棋盘二维数组中存放的只是某个接口,而不是具体的类,这样,提高了程序的可扩展性,在本小节的开头提到:
在某些情况下,一个类中的属性有限并且是固定的。
但是在我们开发的实际情况中(特别是做企业应用),随着业务的不断变化,类的不可变几乎是不可能的。
举个例子,如果需要将本章中的五子棋迁移到swing界面中,那么该棋子枚举类就不得不更改了。
虽然本章是为了做一个较为简单的五子棋,但更多的想向大家展现面向对象的思维。
1.6游戏类实现
本章中的游戏类是GogangGame,在该类中,主要控制游戏的开始,重新开始与结束,验证玩家输入的合法性,判断游戏的输与赢,调用棋盘类来初始化棋盘,打印棋盘,使用棋子类去设置棋盘等。
此类中有四个属性,两个int类型的posX与poxY,用来存储玩家现在输入的x与y坐标(x和y坐标是指玩家输入的数字对应棋盘数组中的一维值与二维值),一个默认值为5的int类型常量WIN_COUNT,游戏胜利需要连接的棋子达到的连子数目,由于是五子棋,因此只需要5个棋子相连,游戏就胜利。
还有一个Chessboard类型的变量chessboard,用来表示棋盘,游戏中就只用到一个棋盘,该对象可以使用初始化棋盘、打印棋盘、获得棋盘(数组)等方法。
1.6.1使用BufferedReader获取键盘输入
BufferedReader是JavaIO流中的一个字符包装流,它必须建立在字符流的基础之上。
该对象可以从输入流中读取文本,但标准输入:
System.in是字节流,所以程序需要使用转换流InputStreamReader将其包装成字符流。
所以程序中用于获取键盘的输入采用以下代码创建。
//获取键盘的输入
BufferedReaderbr=newBufferedReader(newInputStreamReader(System.in));
StringinputStr=null;
//br.readLine:
每当键盘输入一行内容按回车键,则输入的内容被br读取到
while((inputStr=br.readLine())!
=null){
*处理键盘输入
BufferedReader中有一个readLine()方法,此方法总是读取下一行的输入字符串,如果没有下一行,则返回null。
当得到玩家输入的字符后,我们可以进这些字符进行验证,验证完后,如果字符串符合系统要求,可以在验证处使用continue跳出本次循环。
如果需要读取输入,我们就需要为这些输入作出不同的判断,例如,玩家输入了y(继续游戏),那么我们就需要判断玩家输入了y后程序所需要执行哪些操作,因此,这样会为while循环体中增加许多的if语句,这些if语句会影响程序的可读性,如果需要将这些if语句去掉,我们可以将每个if中的代码抽取出来,作为具体的一个处理类。
这样做不仅减少while循环体中的代码,而且可以使得程序更加清晰,程序的耦合度更低,while循环体中只负责读取玩家输入的字符串,而具体的处理则不必由该方法来执行。
由于本章中的代码与动作相对较少,因此并不涉及如何实现以上所说的处理模式,更深入的可以查看“仿QQ游戏大厅”一章。
1.6.2验证玩家输入字符串的合法性
根据引言中提到的输入约定,玩家在控制台输入的字符串必须是以(x,y)的方式输入,还需要验证输入的字符串是否能转换为数字,是否超越棋盘的边界(小于等于1,大于等于棋盘数组的长度),并且需要判断该位置是否已经存在棋子,具体判断流程如图1.5所示。
图1.5验证流程
首先,x与y必须是一个数字,由以下代码验证。
code\gobang\src\org\crazyit\gobang\GobangGame.java
//将用户输入的字符串以逗号(,)作为分隔,分隔成两个字符串
String[]posStrArr=inputStr.split("
"
try{
posX=Integer.parseInt(posStrArr[0])-1;
posY=Integer.parseInt(posStrArr[1])-1;
}catch(NumberFormatExceptione){
chessboard.printBoard();
Sy