五子棋系列(二)

五子棋系列博客(总共三篇,从简单功能简单界面到人机对战,以及较美观的登录及对战界面。第三篇博客中有最终实现的界面效果):

JAVA五子棋的实现(一)

 

前面我们已经实现了一个初步的五子棋,已完成的功能如下:

1.一个15*15的五子棋界面;

2.能够在界面上下黑白棋子;

3.需要把棋子下在交叉点上;

4.实现棋子重绘;——作用:当界面大小被改变时能够保持棋盘和棋盘上面的棋子消失;

5.相同位置不能下多个棋子;

6.只有当“开始新游戏”的按钮被点击时,才能开始下棋;

今天我们继续完善这个五子棋,同样的在开始写代码之前我们还是要做一些准备工作

一、要实现哪些功能:

1.判断输赢

2.实现悔棋操作

3.实现认输操作

二、实现这些功能是否需要新增API类——这里的话不需要,我们前面用的那些API类就已经够了

三、各个功能实现的思路

1.判断输赢——出现了五颗连在一起的同颜色的棋子

A.判断的时间:刚落完棋子的时候

B.判断的范围:

一开始我想要遍历整个棋盘,但仔细想想这个想法是不切实际的。整个棋盘太大了,而且相连的棋子情况不好细分。于是我就换了一种思路:既然我们是在落子的时候开始判断,那么显然我们是要围绕我们刚落的这个棋子来进行判断。

如图,每一个点一共有四个方向可能出现五个相连的棋子。与这个棋子是否能决定输赢相关联的棋子每一个方向上有八个,往前四个往后四个。也就是每一个方向我们都要遍历九个棋子的颜色,判断是否有五个相同颜色的棋子,有就说明输赢已经出现。假设我们落下的棋子对应的数组位置为isAvail[i][j]。

同一行上:遍历isAvail[i-4][j]...isAvail[i+4][j];

同一列上:遍历isAvail[i][j-4]...isAvail[i][j+4];

同一斜线上(左上到右下):遍历isAvail[i-4][j-4]...isAvail[i+4][j+4]

同一斜线上(左下到右上):令sum=i+j;遍历isAvail[i-4][sum-(i-4)]...isAvail[i+4][sum-(i+4)];

C.注意事项:

要注意防止数组越界,iaAvail[i][j]中的i,j均不能小于0,大于14;

要注意五个棋子必须是相连的,在我们一次遍历的时候一旦出现了没有棋子或者是其他棋子,必须把前面的计数值清空,重新开始计数;

2.实现悔棋操作

设置一个ChessPosition对象,该对象包含listi,listj两个数据,分别表示我们与棋子对应的数组位置中的i和j。定义一个动态的ChessPosition对象数组ChessPositonList,每当我们落下一颗棋子时,就把这个棋子的数组位置信息添加到对象数组中。每当我们点击悔棋时,我们就把turn相应的数值转换到另一方,并且用remove方法,取出对象数组中最后一步所对应的棋子信息,并且删除这个信息。然后根据这个信息把isAvail数组中对应的位置重新置为0,并且调用GoBangframe对象的重绘方法。

3.实现认输操作

一旦“认输”按钮被点击,先通过turn的数值判断当前是轮到哪一方。如果是白方,就输出“黑方赢”的信息;如果是黑方,就输出“白方赢”的信息。

四、代码部分


 
 
  1. //构建五子棋界面GoBangframe类
  2. import javax.swing.JFrame;
  3. import javax.swing.JPanel;
  4. import javax.swing.JButton;
  5. import javax.swing.JComboBox;
  6. import java.awt.Dimension;
  7. import java.awt.BorderLayout;
  8. import java.awt.Color;
  9. import java.awt.FlowLayout;
  10. import java.awt.Graphics;
  11. import java.awt.event.ActionListener;
  12. import java.awt.event.MouseListener;
  13. import java.util.ArrayList;
  14. public class GoBangframe extends JPanel implements GoBangconfig{
  15. public Graphics g; //定义一支画笔
  16. public int[][] isAvail= new int [ 15][ 15]; //定义一个二维数组来储存棋盘的落子情况
  17. public ArrayList<ChessPosition>ChessPositonList= new ArrayList<ChessPosition>(); //保存每一步的落子情况
  18. public int turn= 1;
  19. //主函数入口
  20. public static void main(String args[]) {
  21. GoBangframe gf= new GoBangframe(); //初始化一个五子棋界面的对象
  22. gf.initUI(); //调用方法进行界面的初始化
  23. }
  24. public void initUI() {
  25. //初始化一个界面,并设置标题大小等属性
  26. JFrame jf= new JFrame();
  27. jf.setTitle( "五子棋");
  28. jf.setSize( 800, 650);
  29. jf.setLocationRelativeTo( null);
  30. jf.setDefaultCloseOperation( 3);
  31. jf.setLayout( new BorderLayout()); //设置顶级容器JFrame为框架布局
  32. Dimension dim1= new Dimension( 150, 0); //设置右半部分的大小
  33. Dimension dim3= new Dimension( 550, 0); //设置左半部分的大小
  34. Dimension dim2= new Dimension( 140, 40); //设置右边按钮组件的大小
  35. //实现左边的界面,把GoBangframe的对象添加到框架布局的中间部分
  36. //已经有一个GoBangframe对象了,表示当前类的对象是this
  37. this.setPreferredSize(dim3); //设置下棋界面的大小
  38. this.setBackground(Color.LIGHT_GRAY); //设置下棋界面的颜色
  39. //这里的话直接把左边的画板添加上去,指明是在框架布局的中间版块
  40. //若放在其他版块会有一些小问题
  41. jf.add( this,BorderLayout.CENTER); //添加到框架布局的中间部分
  42. //实现右边的JPanel容器界面
  43. JPanel jp= new JPanel();
  44. jp.setPreferredSize(dim1); //设置JPanel的大小
  45. jp.setBackground(Color.white); //设置右边的界面颜色为白色
  46. jf.add(jp,BorderLayout.EAST); //添加到框架布局的东边部分
  47. jp.setLayout( new FlowLayout()); //设置JPanel为流式布局
  48. //接下来我们需要把按钮等组件依次加到那个JPanel上面
  49. //设置按钮数组
  50. String[] butname= { "开始新游戏", "悔棋", "认输"};
  51. JButton[] button= new JButton[ 3];
  52. //依次把三个按钮组件加上去
  53. for( int i= 0;i<butname.length;i++) {
  54. button[i]= new JButton(butname[i]);
  55. button[i].setPreferredSize(dim2);
  56. jp.add(button[i]);
  57. }
  58. //按钮监控类
  59. ButtonListener butListen= new ButtonListener( this);
  60. //对每一个按钮都添加状态事件的监听处理机制
  61. for( int i= 0;i<butname.length;i++) {
  62. button[i].addActionListener(butListen); //添加发生操作的监听方法
  63. }
  64. //设置选项按钮
  65. String[] boxname= { "人人对战", "人机对战"};
  66. JComboBox box= new JComboBox(boxname);
  67. jp.add(box);
  68. jf.setVisible( true);
  69. }
  70. //重写重绘方法,这里重写的是第一个大的JPanel的方法
  71. public void paint(Graphics g) {
  72. super.paint(g); //画出白框
  73. //重绘出棋盘
  74. g.setColor(Color.black);
  75. for( int i= 0;i<row;i++) {
  76. g.drawLine(x, y+size*i, x+size*(column- 1), y+size*i);
  77. }
  78. for( int j= 0;j<column;j++) {
  79. g.drawLine(x+size*j, y, x+size*j, y+size*(row- 1));
  80. }
  81. //重绘出棋子
  82. for( int i= 0;i<row;i++) {
  83. for( int j= 0;j<column;j++) {
  84. if(isAvail[i][j]== 1) {
  85. int countx=size*i+ 20;
  86. int county=size*j+ 20;
  87. g.setColor(Color.black);
  88. g.fillOval(countx-size/ 2, county-size/ 2, size, size);
  89. }
  90. else if(isAvail[i][j]== 2) {
  91. int countx=size*i+ 20;
  92. int county=size*j+ 20;
  93. g.setColor(Color.white);
  94. g.fillOval(countx-size/ 2, county-size/ 2, size, size);
  95. }
  96. }
  97. }
  98. }
  99. }

 
 
  1. import java.awt.event.ActionListener;
  2. import java.awt.event.MouseListener;
  3. import java.awt.event.ActionEvent;
  4. import java.awt.Graphics;
  5. import java.awt.Color;
  6. import java.util.ArrayList;
  7. //实现对GoBangframe下棋界面的监听接口处理
  8. public class frameListener implements GoBangconfig,MouseListener{
  9. public GoBangframe gf;
  10. //public int turn;//判断当前轮到谁了,1表示黑方,2表示白方
  11. //动态数组对象的实例化
  12. //public ArrayList<ChessPosition>ChessPositonList=new ArrayList<ChessPosition>();
  13. public void setGraphics(GoBangframe gf) {
  14. this.gf=gf;
  15. }
  16. public void mouseClicked(java.awt.event.MouseEvent e) {
  17. int x=e.getX();
  18. int y=e.getY();
  19. //计算棋子要落在棋盘的哪个交叉点上
  20. int countx=(x/ 40)* 40+ 20;
  21. int county=(y/ 40)* 40+ 20;
  22. Graphics g=gf.getGraphics();
  23. if(gf.isAvail[(countx- 20)/ 40][(county- 20)/ 40]!= 0) {
  24. System.out.println( "此处已经有棋子了,请下在其它地方");
  25. }
  26. else {
  27. //计算棋盘上棋子在数组中相应的位置
  28. int colu=(countx- 20)/ 40;
  29. int ro=(county- 20)/ 40;
  30. if(gf.turn== 1) {
  31. //先获取要落的地方
  32. g.setColor(Color.black);
  33. //落子
  34. g.fillOval(countx-size/ 2, county-size/ 2, size, size);
  35. //设置当前位置已经有棋子了,棋子为黑子
  36. gf.isAvail[colu][ro]= 1;
  37. //把当前所下的棋子位置保存在动态数组中
  38. gf.ChessPositonList.add( new ChessPosition(colu,ro));
  39. gf.turn++;
  40. //判断是否已经出现五科棋子了
  41. //列判断
  42. //首先界定数组范围,防止越界
  43. int imin=colu- 4,imax=colu+ 4;
  44. if(imin< 0) imin= 0;
  45. if(imax> 14) imax= 14;
  46. int count1= 0; //判断相连的棋子数
  47. for( int i=imin;i<=imax;i++) {
  48. if(gf.isAvail[i][ro]== 1) count1++;
  49. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  50. else count1= 0;
  51. if(count1== 5) {
  52. System.out.println( "黑方赢");
  53. return;
  54. }
  55. }
  56. //行判断
  57. //首先界定数组范围,防止越界
  58. int jmin=ro- 4,jmax=ro+ 4;
  59. if(jmin< 0) jmin= 0;
  60. if(jmax> 14) jmax= 14;
  61. int count2= 0; //判断相连的棋子数
  62. for( int j=jmin;j<=jmax;j++) {
  63. if(gf.isAvail[colu][j]== 1) count2++;
  64. else count2= 0;
  65. if(count2== 5) {
  66. System.out.println( "黑方赢");
  67. return;
  68. }
  69. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  70. }
  71. //135度判断
  72. //首先界定数组范围,防止越界
  73. int count3= 0; //判断相连的棋子数
  74. for( int i=- 4;i<= 4;i++) {
  75. if((colu+i>= 0)&&(ro+i>= 0)&&(colu+i<= 14)&&(ro+i<= 14)) {
  76. if(gf.isAvail[colu+i][ro+i]== 1) count3++;
  77. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  78. else count3= 0;
  79. if(count3== 5) {
  80. System.out.println( "黑方赢");
  81. return;
  82. }
  83. }
  84. }
  85. int count4= 0; //判断相连的棋子数
  86. for( int i=- 4;i<= 4;i++) {
  87. if((colu+i>= 0)&&(ro-i>= 0)&&(colu+i<= 14)&&(ro-i<= 14)) {
  88. //System.out.print("count4:"+count4);
  89. if(gf.isAvail[colu+i][ro-i]== 1) count4++;
  90. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  91. else count4= 0;
  92. if(count4== 5) {
  93. System.out.println( "黑方赢");
  94. return;
  95. }
  96. }
  97. }
  98. }
  99. else {
  100. g.setColor(Color.white);
  101. g.fillOval(countx-size/ 2, county-size/ 2, size, size);
  102. //设置当前位置已经有棋子了,棋子为白子
  103. gf.ChessPositonList.add( new ChessPosition(colu,ro));
  104. gf.isAvail[colu][ro]= 2;
  105. gf.turn--;
  106. //列判断
  107. //首先界定数组范围,防止越界
  108. int imin=colu- 4,imax=colu+ 4;
  109. if(imin< 0) imin= 0;
  110. if(imax> 14) imax= 14;
  111. int count1= 0; //判断相连的棋子数
  112. for( int i=imin;i<=imax;i++) {
  113. if(gf.isAvail[i][ro]== 2) count1++;
  114. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  115. else count1= 0;
  116. if(count1== 5) {
  117. System.out.println( "白方赢");
  118. return;
  119. }
  120. }
  121. //行判断
  122. //首先界定数组范围,防止越界
  123. int jmin=ro- 4,jmax=ro+ 4;
  124. if(jmin< 0) jmin= 0;
  125. if(jmax> 14) jmax= 14;
  126. int count2= 0; //判断相连的棋子数
  127. for( int j=jmin;j<=jmax;j++) {
  128. if(gf.isAvail[colu][j]== 2) count2++;
  129. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  130. else count2= 0;
  131. if(count2== 5) {
  132. System.out.println( "白方赢");
  133. return;
  134. }
  135. }
  136. //135度判断
  137. //首先界定数组范围,防止越界
  138. int count3= 0; //判断相连的棋子数
  139. for( int i=- 4;i<= 4;i++) {
  140. if((colu+i>= 0)&&(ro+i>= 0)&&(colu+i<= 14)&&(ro+i<= 14)) {
  141. if(gf.isAvail[colu+i][ro+i]== 2) count3++;
  142. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  143. else count3= 0;
  144. if(count3== 5) {
  145. System.out.println( "白方赢");
  146. return;
  147. }
  148. }
  149. }
  150. int count4= 0; //判断相连的棋子数
  151. for( int i=- 4;i<= 4;i++) {
  152. if((colu+i>= 0)&&(ro-i>= 0)&&(colu+i<= 14)&&(ro-i<= 14)) {
  153. if(gf.isAvail[colu+i][ro-i]== 2) count4++;
  154. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  155. else count4= 0;
  156. if(count4== 5) {
  157. System.out.println( "白方赢");
  158. return;
  159. }
  160. }
  161. }
  162. }
  163. }
  164. }
  165. // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V
  166. public void mousePressed(java.awt.event.MouseEvent e) {
  167. }
  168. // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V
  169. public void mouseReleased(java.awt.event.MouseEvent e) {
  170. }
  171. // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V
  172. public void mouseEntered(java.awt.event.MouseEvent e) {
  173. }
  174. // Method descriptor #8 (Ljava/awt/event/MouseEvent;)V
  175. public void mouseExited(java.awt.event.MouseEvent e) {
  176. }
  177. }

 
 
  1. //设置按钮监听方法ButttonLitener类
  2. import java.awt.event.ActionListener;
  3. import java.awt.Color;
  4. import java.awt.event.ActionEvent;
  5. import javax.swing.JFrame;
  6. //实现对JPanel的监听接口处理
  7. public class ButtonListener implements GoBangconfig,ActionListener{
  8. public GoBangframe gf;
  9. public ButtonListener(GoBangframe gf) {
  10. this.gf=gf; //获取左半部分的画板
  11. }
  12. //当界面发生操作时进行处理
  13. public void actionPerformed(ActionEvent e) {
  14. //获取当前被点击按钮的内容,判断是不是"开始新游戏"这个按钮
  15. if(e.getActionCommand().equals( "开始新游戏")) {
  16. //如果是开始新游戏的按钮,再为左半部分设置监听方法
  17. frameListener fl= new frameListener();
  18. fl.setGraphics(gf); //获取画笔对象
  19. gf.addMouseListener(fl);
  20. }
  21. //判断当前点击的按钮是不是悔棋
  22. else if(e.getActionCommand().equals( "悔棋")) {
  23. if(gf.ChessPositonList.size()> 1) {
  24. //把棋子数组相应的位置置为0;
  25. ChessPosition l= new ChessPosition();
  26. //获取最后一个棋子的对象信息
  27. l=gf.ChessPositonList.remove(gf.ChessPositonList.size()- 1);
  28. //把相应的数组位置置为0
  29. gf.isAvail[l.Listi][l.Listj]= 0;
  30. //把玩家还原为上一步的玩家
  31. if(gf.turn== 1) gf.turn++;
  32. else gf.turn--;
  33. //直接调用gf的重绘方法,重绘方法的画笔应该是在棋盘页面还没生成的时候就要获取
  34. //调用repaint会自动调用paint方法,而且不用给参数
  35. gf.repaint();
  36. //gf.paint(gf.getGraphics());
  37. }
  38. else {
  39. System.out.println( "不能悔棋!");
  40. }
  41. }
  42. else if(e.getActionCommand().equals( "认输")) {
  43. if(gf.turn== 1) System.out.println( "白方赢");
  44. else System.out.println( "黑方赢");
  45. }
  46. }
  47. }

 
 
  1. //定义与棋盘数据相关的接口,保存棋盘的起点,格子大小,行数列数等信息
  2. public interface GoBangconfig {
  3. int x= 20,y= 20,size= 40,row= 15,column= 15;
  4. }

 
 
  1. //新建一个棋子类ChessPosition保存每一步棋子所在的位置
  2. public class ChessPosition {
  3. public int Listi,Listj;
  4. public ChessPosition() {
  5. }
  6. public ChessPosition(int Listi,int Listj) {
  7. this.Listi=Listi;
  8. this.Listj=Listj;
  9. }
  10. }

五、总结

1.这里在判断是否已经出现五颗相连的同颜色棋子时,不要用else if 语句。因为if..else if语句的执行情况是:一旦if条件满足,它就会直接进入if里面,不会再去判断else if,这样子的话即使棋子已经五颗了它也不会输出“黑方赢”的信息


 
 
  1. for( int i=imin;i<=imax;i++) {
  2. if(gf.isAvail[i][ro]== 1) count1++;
  3. //如果出现了其他棋子,或者是没有棋子时,就重新开始计数
  4. else count1= 0;
  5. if(count1== 5) {
  6. System.out.println( "黑方赢");
  7. return;
  8. }
  9. }

2.不要过多地创建对象。如果在写代码的时候我们需要用到当前这个对象,就直接用this来指,不要再重新定义一个对象。

3.连个重绘方法。

repaint():没有参数,会自动调用paint

paint(Graphics):有画笔参数

六、实现界面

至此,我们已经初步实现了五子棋项目判断输赢、悔棋和认输的功能。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值