首页 疯狂java实战演义-第1章_控制台五子棋

疯狂java实战演义-第1章_控制台五子棋

举报
开通vip

疯狂java实战演义-第1章_控制台五子棋第1章 控制台五子棋 ·222· Eclipse从入门到精通 ·13· 第1章 控制台五子棋 第1章 控制台五子棋 1.1 引言 控制台五子棋,顾名思义,就是在Java控制台运行的五子棋游戏,需要用户用键盘输入棋子的位置来进行游戏。 由于是在控制台下面运行的程序,所以并没有漂亮的游戏界面,与及鼠标操作等东西,只是在一片黑色控制台环境下进行游戏,游戏的可玩性并不高,似乎这并不是一个完整的游戏。虽然如此,但事实上,一个程序最重要的并不是界面,而是处理各种业务逻辑与数据的方法,只要掌握了核心的方法,掌握基础的知...

疯狂java实战演义-第1章_控制台五子棋
第1章 控制台五子棋 ·222· Eclipse从入门到精通 ·13· 第1章 控制台五子棋 第1章 控制台五子棋 1.1 引言 控制台五子棋,顾名思义,就是在Java控制台运行的五子棋游戏,需要用户用键盘输入棋子的位置来进行游戏。 由于是在控制台下面运行的程序,所以并没有漂亮的游戏界面,与及鼠标操作等东西,只是在一片黑色控制台环境下进行游戏,游戏的可玩性并不高,似乎这并不是一个完整的游戏。虽然如此,但事实上,一个程序最重要的并不是界面,而是处理各种业务逻辑与数据的方法,只要掌握了核心的方法,掌握基础的知识,便更容易学习awt,swing等图形用户界面的编写,万变不离其宗,写起有操作界面的程序也会变得更加容易,更加随心应手。而本章的主要目的让读者掌握与理解Java编程的基础知识,因此,掌握本章五子棋的实现原理,对于学习以后的章节将会非常有帮助。作为本书的第一章 内容 财务内部控制制度的内容财务内部控制制度的内容人员招聘与配置的内容项目成本控制的内容消防安全演练内容 ,我们在本章中将使用最简单的方式来实现一个控制台五子棋游戏。 1.1.1 五子棋介绍 五子棋是起源于中国古代的传统黑白棋种之一。现代五子棋日文称之为“连珠” ,英译为”Renju”,英文称之为”Gobang”或”FIR”(Five in a Row 的缩写 ) ,亦有“连五子”、“五子连” 、“串珠”、“五目”、 “五目碰”、“五格”等多种称谓。五子棋游戏是一个比较大众的棋类游戏,大多数人都会玩这个游戏,五子棋的玩法与规则如下: · 五子棋是两个人之间进行的竞技活动,由于对黑方白方规则不同,黑棋必须先行(本章节设计的游戏,黑棋与白棋的规则一样,但一样由黑棋先下)。 · 五子棋专用盘为 15×15 ,五连子的方向为横、竖、斜。 · 在棋盘上以对局双方均不可能形成五连为和棋。 · 首先形成五连子的一方为赢。 五子棋必须由双方进行游戏,当某一方按照一定规则连成五个棋子的时候,该游戏方就胜利,在本章中,我们并不需要做到对战形式的,我们可以设计一个简单的“电脑”来做我们的对手,当我们下完棋后,这个简单的“电脑”就随便在棋盘中下一个棋,当然,如果想做更强大的“电脑”我们可以编写程序来实现,当我们下棋的时候,这个“电脑”就对我们所下的棋子进行检测,并将棋子下到最恰当的位置。本章主要目的是展现五子棋的实现原理,如果读者有兴趣,可以自行开发强大的“人工智能电脑”来进行游戏。 1.1.2 输入输出约定 玩家必须以(x,y)的方式输入棋盘的坐标,其中,x代表棋坐标,y代表竖坐标。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.1 Chessboard类 要进行五子棋游戏,必须有有一个棋盘,而这个类主要控制棋盘的初始化,输出与及增加新的棋子。这个类包含以下方法: · void initBoard(),这个方法用于初始化棋盘,开始新的游戏时,应该调用此方法,初始化出一个新的空棋盘。 · void printBoard(),此方法用于在控制台输出棋盘,各方每一完一颗棋子后,由于棋盘上棋子的状态有改变,所以必须调用此方法重新输入棋盘。 · void setBoard( int posX , int posY , String chessman ),posX与posY是新下棋子的x与y坐标,,chessman是新下棋子的类型(黑子与白子),每下完一颗棋子后,通过调用此方法把棋子设置到棋盘上。 · String[][] getBoard(),返回棋盘,返回类型是保存棋盘的二维数组。 当我们需要初始化棋盘的时候,可以直接调用Chessboard的initBoard方法,我们需要考虑该方法需要实现的功能:初始化棋盘。由于我们将棋盘看作是一个二维数组,因此initBoard就需要帮我们去创建一个二维数组,创建二维数组可以使用以下代码。 代码清单:code\gobang\src\org\crazyit\gobang\Chessboard.java Object[][] array = new Object[size][size]; for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { array[i][j] = new Object(); } } 以上代码创建一个固定大小(一维与二维大小)的二维数组,再通过嵌套循环为数组中的每一个元素进行赋值。在游戏中如果我们进行了下棋的操作,可以直接改变这个数组的某一个元素值。在创建Chessboard类时,我们就需要发挥面向对象的思维,在我们的程序中,所有看到的或者想的事物,我们都可以将其抽象成具体的某个对象,并赋予一定的属性与行为。在设计对象的过程中,如果有某些事物拿捏不准,不知如何设计属性或者行为,可以将其设计成接口或者抽象类。 Chessboard中提供了一个printBoard的方法用于打印棋盘,在本章中,我们就需要将棋盘数组打印到控制台中,因此该方法可以简单的调用System.out.print去打印相关的字符串。需要注意的是,由于printBoard方法是没有参数的,因此我们需要为Chessboard提供一个二维数组变量,当调用printBoard方法的时候,将对象内的二维数组打印,我们可以将Chessboard看作一个有状态的Java对象,有状态的Java对象可以理解成一个Java对象保存一些与该对象相关的状态属性,如果该对象没有保存与该对象相关的状态属性,那么我们可以将这个对象看成一个无状态的Java对象。 当外部调用Chessboard的setBoard方法时,就可以将某个值设置到Chessboard中的二维数组里,告诉Chessboard玩家或者“电脑”在某个位置下了怎样的棋子。 1.3.2 Chessman类 Chessman类是一个枚举类,此类是构造器私有的,不能直接创建,里面有BLACK与WHITE两个静态属性,代表黑子与白子枚举类,两个表态属性都是Chessman类型的,要获取棋子,则通过这两个属性调用以下的方法获取棋子: · String getChessman(),返回String类型的棋子实例,“●”或者“○”。 如果我们需要得到棋子的字符串(“●”或者“○”),可以使用以下的代码。 代码清单:code\gobang\src\org\crazyit\gobang\Chessman.java Chessman.BLACK.getChessman(); 1.3.3 GobangGame类 GobangGame类是进行游戏的类,Chessboard依赖于此类,此类控制游戏的开始,重玩与结束,并影响Chessboard类。主要包含以下构造器与方法: · GobangGame(),默认无参数构造器。 · GobangGame( Chessboard chessboard ),有参数构造器,以一个Chessboard实例去初始化这个类。 · boolean isValid( String inputStr ),此方法验证控制台的输入字符串是否合法,如果合法,返回true,如果不合法,则返回false,此方法抛出Exception异常。 · void start(),开始游戏。此方法抛出Exception异常。 · boolean isReplay( String chessman ),是否重新开始游戏,如果是,返回true,否则返回false,参数chessman代表黑子或白子。 · int[] computerDo(),计算机随机下棋,由计算机自动设置棋盘,并返回包含新下棋子位置x与y坐标的int[]数组类型。 · boolean isWon( int posX , int posY , String ico ),判断输赢,参数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节介绍过,此类主要是实现棋盘初始始化、输出、与更新,在这节便用代码一步一步地实现各个功能。首先我们需要初始化棋盘的实现,看以下代码片段。 代码清单:code\gobang\src\org\crazyit\gobang\Chessboard.java public void initBoard() { //初始化棋盘数组 board = new String[BOARD_SIZE][BOARD_SIZE]; //把每个元素赋值为“十”,用于控制台输出棋盘 for( int i = 0 ; i < BOARD_SIZE ; i++ ) { for( int j = 0 ; j < BOARD_SIZE ; j++ ) { board[i][j] = "十"; } } } 上面代码中,BOARD_SIZE是代表棋盘的大小,用一个String[][]类型的二维数组来代表棋盘,创建此数组后,通过迭代为为个数组元素的值赋为“十”来初始化棋盘。创建了棋盘数组后,如果需要定位到棋盘的某个位置,只需要得到棋盘数组的一维值与二维值即可,例如处理玩家下棋动作的时候,可以将数组中具体的某个“十”替换成具体的棋子字符串。 1.4.2 输出棋盘 输出棋盘,只需要Chessboard的board属性(二维数组)的每一个值,打印到控制台中。如果可以做到更好的扩展性,我们可以在二维数组中存放棋子对象,而不是简单的字符串,那么存放在二维数组中的每一个棋子对象,都可以实现某个棋子接口或者继承棋子的抽象类,这样可以更好的做到游戏的扩展性。当然,我们在本章为了简单起见,只在该二维数组中存放需要打印的字符串,打印时只需要得到具体的某个二维数组的元素,将其打印即可。 代码清单:code\gobang\src\org\crazyit\gobang\Chessboard.java public void printBoard() { //打印每个数组元素 for( int i = 0 ; i < BOARD_SIZE ; i++ ) { for( int j = 0 ; j < BOARD_SIZE ; j++ ) { //打印后不换行 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(二维数组)返回即可,代码如下。 代码清单:code\gobang\src\org\crazyit\gobang\Chessboard.java /** * 返回棋盘 * @return 返回棋盘 */ public String[][] getBoard() { return this.board; } 到此,棋盘类的几个方法都已经实现,该类的主要功能是创建棋盘、打印棋盘等,实现的过程中涉及了一些Java语言的基本操作,例如嵌套循环、创建二维数组等。在下面的小节中,我们将去实现游戏的核心部分。 1.5 棋子枚举类实现 在某些情况下,一个类的属性是有限而且固定的(在某些情况下),例如本章中的棋子类,它只有两个对象,黑棋和白棋。这种实例有限而且固定的类,在Java里面称为枚举类,枚举类的关健字用enum而不是class,此类中有两个枚举属性BLACK和WHITE,代表黑子与白子,代码实现如下: 代码清单:code\gobang\src\org\crazyit\gobang\Chessman.java public enum Chessman { BLACK("●"),WHITE("○"); private String chessman; /** * 私有构造器 */ private Chessman(String chessman) { this.chessman = chessman; } /** * @return 黑棋或者白棋 */ public String getChessman() { return this.chessman; } } 在上面的代码中,可以看到,枚举类是用enum关键字代替了class关键字,看到此枚举类的构造器的权限修饰符是private,也是表明此类是不可以通过外部创建的,只能在此类的内部创建,这是为了保证此对象只有黑子与白子两种类型。黑体代码是列出枚举值,实际上就是调用私用构造器创建此对象,等同以下代码: public static final Chessman BLACK = new Chessman(“●”); public static final Chessman WHITE = new Chessman(“○”); 由于BLACK与WHITE两个属性是静态的,所以要获取黑子或者白子,可以通过以下代码来获得: Chessman.BLACK.getChessman(); 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是Java IO流中的一个字符包装流,它必须建立在字符流的基础之上。该对象可以从输入流中读取文本,但标准输入:System.in是字节流,所以程序需要使用转换流InputStreamReader将其包装成字符流。所以程序中用于获取键盘的输入采用以下代码创建。 //获取键盘的输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String inputStr = 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 (NumberFormatException e) { chessboard.printBoard(); System.out.println("请以(数字,数字)的格式输入:"); return false; } 当我们调用Integer.parseInt方法将字符串转换成一个Integer类型的时候,如果需要转换的字符串不能转换成某一个数字,该方法将会抛出NumberFormatException异常,我们可以使用catch将该异常捕获,提示玩家需要重新输入棋子坐标。除了判断玩家输入的字符串是否符合我们游戏所要求的格式外,还需要判断玩家输入的坐标范围,即该范围不可小于1并不可大于棋盘数组的最大值,例如棋盘是10乘10,但玩家输入了11,那么将会抛出ArrayIndexOutOfBoundsException异常,因此,x与y的范围只能是大于1与小于N(棋盘的大小),如果超出这个范围,则需要提示玩家重新输入,由以下代码验证。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java //检查输入数值是否在范围之内 if( posX < 0 || posX >= Chessboard.BOARD_SIZE || posY < 0 || posY >= Chessboard.BOARD_SIZE ) { chessboard.printBoard(); System.out.println( "X与Y坐标只能大于等于1,与小于等于" + Chessboard.BOARD_SIZE + ",请重新输入:" ); return false; } 验证了输入坐标的合法性后,还需要验证玩家输入的坐标中是否已经存在棋子,我们通过Chessboard对象中的getBoard方法可以得到棋盘的二维数组,根据玩家输入的坐标得到数组中的元素,再判断元素是否已经是一个棋子(“●”或者“○”),如果该坐标中已经有棋子(元素值为“十”),则提示玩家重新输入,由以下代码验证。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java //检查输入的位置是否已经有棋子 String[][] board = chessboard.getBoard(); if( board[posX][posY] != "十" ) { chessboard.printBoard(); System.out.println( "此位置已经有棋子,请重新输入:" ); return false; } 以上代码中,如果board[i][j]不等于“十”(“十”是棋盘每个位置的默认值),则证明此位置有棋子,需要提示玩家重新输入。这里需要注意的是,如果没有前一个判断(判断输入的坐标是否超过棋盘范围),那么通过棋盘数组获取某个元素时,就会抛出ArrayIndexOutOfBoundsException异常。 1.6.3 判断输赢 判断游戏输赢,需要在玩家输入了坐标并通过了合法性验证后(输入的坐标),再执行输赢的验证,同样地,如果是“电脑”随机生成的坐标,我们同样的需要进行输赢验证,因此,我们已经将判断输赢的行为,独立成一个isWon方法(详细请看1.3.3中的GobangGame类)。 判断输赢在本章的程序中稍微复杂,有两种方法来判断输赢: · 每次下完一颗棋子,就通过程序从横、竖、斜各个方向扫描棋盘,如果在某个方向中,有同种颜色的棋子达到五连子,则此颜色的玩家为赢。如果没有相同颜色的棋子达到五连子,则继续游戏。该判断方法需要遍历整个棋盘,也就是意味着每次下棋后(玩家或者“电脑”)都需要对棋盘进行遍历,这样对程序的性能会造成一定的影响。 · 每次下完一颗棋子,以该棋子为中心,扫描在此棋子所在范围内的横、竖、斜方向,验证加上此棋子有没有形成五连子,如果形成五连子,则下棋子的玩家为赢。此方法与前面的方法比较,因为不需要扫描整个棋盘,所以更加快速,本 章程 公司章程范本下载项目章程下载公司章程下载公司章程下载公司章程下载 序使用的是此方法,该方法的原理如图1.6所示。 图1.6 五连子 在图1.6中可以看出,(0,0),(0,3),(0,6),(3,0),(6,0),(3,7),(7,3),(7,7)这些坐标都是此黑棋能形成五连子的最小或者最大位置的棋子,如果各个方向有足够的空间,就延伸到第五颗棋子,如果没有,就只延伸到边界。所以,只要能计算出任意一颗棋子的这些位置,我们就可以判断游戏的输赢,并且是以该棋子为中心向周围进行遍历。以下是判断输赢的代码实现。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java //直线起点的X坐标 int startX = 0; //直线起点Y坐标 int startY = 0; //直线结束X坐标 int endX = Chessboard.BOARD_SIZE - 1; //直线结束Y坐标 int endY = endX; //同条直线上相邻棋子累积数 int sameCount = 0; int temp = 0; //计算起点的最小X坐标与Y坐标 temp = posX - WIN_COUNT + 1; startX = temp < 0 ? 0 : temp; temp = posY - WIN_COUNT + 1; startY = temp < 0 ? 0 : temp; //计算终点的最大X坐标与Y坐标 temp = posX + WIN_COUNT - 1; endX = temp > Chessboard.BOARD_SIZE - 1 ? Chessboard.BOARD_SIZE - 1 : temp; temp = posY + WIN_COUNT - 1; endY = temp > Chessboard.BOARD_SIZE - 1 ? Chessboard.BOARD_SIZE - 1 : temp; //从左到右方向计算相同相邻棋子的数目 String[][] board = chessboard.getBoard(); for( int i = startY; i < endY; i++) { if( board[posX][i] == ico && board[posX][i+1] == ico ) { sameCount++; } else if( sameCount != WIN_COUNT - 1 ) { sameCount = 0; } } 从上面代码中可以看到,首先是计算出在这颗棋子的直线上(横、竖、斜方向)能达到五连子的最小x、y坐标与最大x、y坐标,然后从最小x、y坐标访问到最大x、y坐标,如果此颜色棋子的相连累积数目达到五连子,则为赢。以上代码只是实现横向遍历判断,竖向遍历与斜向遍历的判断方法与横向遍历的实现基本类似。这里需要注意的是,当遇到一个可以相边的棋子,就需要为sameCount值加1。 1.6.4 计算机随机下棋 我们在前面章节中说到,使用一个简单的方式来产生一个“电脑”与我们进行对战。我们只需要使得到随便的坐标值,并且在该坐标中进行下棋操作,就可以实现“电脑”的下棋,因此,实现这个随机下棋的功能,最主要是产生随机的坐标。我们可以使用Math.random方法来产生0.0到1.0之间的double数组,再使用该值来乘以棋盘的大小,即可产生随机的坐标。 我们使用这个方式来产生随机坐标,因为是随机生成的位置,所以这个计算机是比较“笨”的。如果想让计算机变“聪明”起来,可以加上人工智能“电脑”,该人工智能“电脑”需要分析玩家的所有下棋的位置,并对这些位置的坐标进行相应的分析。以下是随机生成坐标的代码实现。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java //随机生成x坐标,即二维数组具体一维的值 int posX = (int)(Math.random() * ( Chessboard.BOARD_SIZE - 1 ) ); //随机生成y坐标,即二维数组具体二维的值 int posY = (int)(Math.random() * ( Chessboard.BOARD_SIZE - 1 ) ); String[][] board = chessboard.getBoard(); //当棋盘中的位置不是“十”的时候(已经有棋子),则再次生成新的坐标 while( board[posX][posY] != "十" ) { posX = (int)(Math.random() * ( Chessboard.BOARD_SIZE - 1 ) ); posY = (int)(Math.random() * ( Chessboard.BOARD_SIZE - 1 )); } 这里需要注意的是,由于我们使用了while循环,其中循环条件是判断棋盘数组中是否已经存在棋秀,如果已经存在棋子,则需要重新随机生成坐标。那么就会产生这样一种情况,如果整个棋盘中都存在棋子的话,这个while将永远不会跳出,即死循环,所以我们需要判断棋盘中是否所有的位置都有棋子,如果棋盘中已经都存在棋子并且没有输赢的话,就可以提示玩家和棋了,重新开始游戏。 上面代码中,随机生成x与y坐标的过程是先用Math.random()方法获取一个在BOARD_SIZE(棋盘大小)范围之内的x与y正数坐标,如果这个坐标中已经有棋子,则继续使用Math.radom()方法获取坐标,直到这个坐标中没有棋子。 1.6.5 是否重新游戏 实现是否重新开始游戏的功能,这在这方法中,程序的流程是:如果玩家或者电脑赢了,则在控制台输出询问玩家是否重新开始游戏的信息,如果玩家输入”y”字符串,则重新开始游戏,否则直接退出整个程序,实现代码如下。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java /** * 是否重新开始下棋。 * @param chessman "●"为用户,"○"为计算机。 * @return 开始返回true,反则返回false。 */ public boolean isReplay( String chessman ) throws Exception { chessboard.printBoard(); String message = chessman.equals(Chessman.BLACK.getChessman()) ? "恭喜您,您赢了," : "很遗憾,您输了,"; System.out.println( message + "再下一局?(y/n)" ); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); if( br.readLine().equals("y") ) { //开始新一局 return true; } return false; } 1.6.6 游戏过程实现 以下是游戏的流程说明,具体也可以看图1.2: · (1)首先调用Chessboard类型的chessboard属性中的initBoard()与printBoard()方法去初始化与打印棋盘。 · (2)从控制台获取用户的输入。 · (3)再调用本类的isValid()方法去验证玩家输入的合法性,如果输入不合法,返回第2步继续,否则到第4步。 · (4)把玩家下的棋子位置赋值为"●"。 · (5)调用isWon( int posX , int posY , String chessman )判断玩家是否赢了。如果玩家赢了,则调用isReply()方法输出的信息询问玩家是否重新游戏,如果玩家输入y,则返回第1步重新开始。 · (6)调用computerDo()方法随机生成计算机的x,y坐标,并把board[x][y] 赋值为"○"。如果计算机赢了,则调用isReply()方法输出的信息询问玩家是否重新游戏,如果玩家输入y,则返回第1步重新开始,否则返回第2步轮到用户输入。 以下的代码实现以上的流程。 代码清单:code\gobang\src\org\crazyit\gobang\GobangGame.java //true为游戏结束 boolean isOver = false; chessboard.initBoard(); chessboard.printBoard(); //获取键盘的输入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String inputStr = null; //br.readLine:每当键盘输入一行内容按回车键,则输入的内容被br读取到 while( (inputStr = br.readLine()) != null ) { isOver = false; if( !isValid( inputStr ) ) { //如果不合法,要求重新输入,再继续 continue; } //把对应的数组元素赋为"●" String chessman = Chessman.BLACK.getChessman(); chessboard.setBoard( posX , posY , chessman ); //判断用户是否赢了 if( isWon( posX , posY , chessman ) ) { isOver = true; } else { //计算机随机选择位置坐标 int[] computerPosArr = computerDo(); chessman = Chessman.WHITE.getChessman(); chessboard.setBoard( computerPosArr[0] , computerPosArr[1] , chessman ); //判断计算机是否赢了 if( isWon( computerPosArr[0] , computerPosArr[1] , chessman ) ) { isOver = true; } } //如果产生胜者,询问用户是否继续游戏 if( isOver ) { //如果继续,重新初始化棋盘,继续游戏 if( isReplay( chessman ) ) { chessboard.initBoard(); chessboard.printBoard(); continue; } //如果不继续,退出程序 break; } chessboard.printBoard(); System.out.println("请输入您下棋的坐标,应以x,y的格式输入:"); } 以上的代码中,我们使用了一个isOver来标识游戏是否胜利,当游戏胜利时,就询问玩家是否继续,我们可以看到,以上的代码中我们写了多个if语句,其实我们可以使用一些Java的基础知识来解决这些if问题(可以使用设计模式中的策略模式),当然大家也可能觉得这些if没有什么关系,但是,由于这些if的存在,会使得我们程序的可读性变差,在“仿QQ游戏大厅”章节,同样出现了读取字符串关作相应判断的情况,我们在该章节使用了其他方式去解决这些if语句,详细可以看“仿QQ游戏大厅”一章。本章的目的主要介绍一个简单五子棋的实现。 1.7 本章小结 本章主要是介绍开发控制台五子棋的整个过程,体现流程设计与类设计的基本方法,示范了数组的使用,获取用户键盘的输入。使用了分支结构与循环结构的流程控制,还介绍与使用了枚举类。向读者灌输了面向对象的一些基本知识,通过这些基础的知识设计与开发出有趣味性的小游戏,让读者加深对这些基础知识的理解。
本文档为【疯狂java实战演义-第1章_控制台五子棋】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_182672
暂无简介~
格式:doc
大小:201KB
软件:Word
页数:15
分类:互联网
上传时间:2012-03-09
浏览量:22