仅用300行代码实现的智能五子棋人机对战

和最近在做的项目类似,不过我的更高级,mark下稍后研究,这个算法不很好,但是够用了

这款五子棋小游戏是我五六年前在培训学习JAVA的时候花了一整个下午时间即兴写出来的,功能很简单,实现了五子棋的电脑对战,主要就是为了研究下电脑下棋算法,所以 其他的功能和细节都没考虑,比如电脑最后的落子点我是直接用图片标示出来的,最好是另建一个线程来闪烁最后落子点,还有智能等级也是可以调节的,偏重于防 守和进攻都可以修改代码中权值的设定来实现的,有兴趣的朋友可以在此基础上扩展一下。因为没有写谁先下棋的功能,可以模拟下电脑先下子,只需要你在边角上 先下一子就可以了,相比下来,电脑先下的话,电脑赢的几率更大点,一共用到了三张图片,都在附件中。

 



下面是全部的代码,写了一些注释,希望能对你阅读代码起到一点帮助

 

Java代码
  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.net.URL;
  4. import javax.swing.*;
  5. public class GobangGame {
  6. public static void main(String[] args) {
  7. GameF game = new GameF();
  8. game.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  9. game.show();
  10. }
  11. }
  12. class GameF extends JFrame {
  13. public GameF() {
  14. Container contentPane = getContentPane();
  15. final Panel panel = new Panel();
  16. panel.setBackground(new Color(255, 182, 147));
  17. contentPane.setBackground(new Color(255, 182, 147));
  18. contentPane.add(panel);
  19. setSize(560, 560);
  20. setTitle("杨雷的五子棋游戏 版本1.0");
  21. setResizable(false);
  22. panel.setCursor(new Cursor(Cursor.HAND_CURSOR));
  23. JMenuBar menuBar=new JMenuBar();
  24. JMenu menu=new JMenu("选项");
  25. JMenuItem menuStart=new JMenuItem("开始游戏");
  26. menuStart.addActionListener(new ActionListener(){
  27. public void actionPerformed(ActionEvent e){
  28. panel.ResetGame();
  29. panel.repaint();
  30. }
  31. });
  32. JMenuItem menuExit =new JMenuItem("退出");
  33. menuExit.addActionListener(new ActionListener(){
  34. public void actionPerformed(ActionEvent e){
  35. System.exit(0);
  36. }
  37. });
  38. menuBar.add(menu);
  39. menu.add(menuStart);
  40. menu.add(menuExit);
  41. this.setJMenuBar(menuBar);
  42. }
  43. }
  44. class Panel extends JPanel {
  45. private URL blackImgURL = GobangGame.class.getResource("black.gif");
  46. private ImageIcon black=new ImageIcon(blackImgURL);
  47. private URL whiteImgURL = GobangGame.class.getResource("white.gif");
  48. private ImageIcon white=new ImageIcon(whiteImgURL);
  49. private URL currentImgURL = GobangGame.class.getResource("current.gif");
  50. private ImageIcon current=new ImageIcon(currentImgURL);
  51. private int i, j, k, m, n, icount;
  52. private int[][] board = new int [16][16];
  53. private boolean[][][] ptable = new boolean[16][16][672];
  54. private boolean[][][] ctable = new boolean[16][16][672];
  55. private int[][] cgrades = new int[16][16];
  56. private int[][] pgrades = new int[16][16];
  57. private int cgrade,pgrade;
  58. private int[][] win = new int[2][672];
  59. private int oldx,oldy;
  60. private int bout=1;
  61. private int pcount,ccount;
  62. private boolean player,computer,over,pwin,cwin,tie,start;
  63. private int mat,nat,mde,nde;
  64. public Panel(){
  65. addMouseListener(new Xiazi());
  66. this.ResetGame();
  67. }
  68. public void paintComponent(Graphics g) {
  69. super.paintComponent(g);
  70. for (int i = 0; i < 16; i++)
  71. for (int j = 0; j < 16; j++){
  72. g.drawLine(50, 50 + j * 30, 500, 50 + j * 30);
  73. }
  74. for (int i = 0; i < 16; i++)
  75. for (int j = 0; j < 16; j++){
  76. g.drawLine(50 + j * 30, 50, 50 + j * 30, 500);
  77. }
  78. for (int i = 0; i < 16; i++){
  79. String number = Integer.toString(i);
  80. g.drawString(number, 46 + 30 * i, 45);
  81. }
  82. for (int i = 1; i < 16; i++){
  83. String number = Integer.toString(i);
  84. g.drawString(number, 33, 53 + 30 * i);
  85. }
  86. updatePaint(g);
  87. }
  88. class Xiazi extends MouseAdapter{
  89. public void mouseClicked(MouseEvent e){
  90. if(!over)
  91. {
  92. oldx = e.getX();
  93. oldy = e.getY();
  94. mouseClick();
  95. repaint();
  96. }
  97. }
  98. }
  99. // 游戏初始化
  100. public void ResetGame()
  101. {
  102. //初始化棋盘
  103. for(i=0;i<16;i++)
  104. for(j=0;j<16;j++)
  105. {
  106. this.pgrades[i][j] = 0;
  107. this.cgrades[i][j] = 0;
  108. this.board[i][j] = 2;
  109. }
  110. //遍历所有的五连子可能情况的权值
  111. //横
  112. for(i=0;i<16;i++)
  113. for(j=0;j<12;j++){
  114. for(k=0;k<5;k++){
  115. this.ptable[j+k][i][icount] = true;
  116. this.ctable[j+k][i][icount] = true;
  117. }
  118. icount++;
  119. }
  120. //竖
  121. for(i=0;i<16;i++)
  122. for(j=0;j<12;j++){
  123. for(k=0;k<5;k++){
  124. this.ptable[i][j+k][icount] = true;
  125. this.ctable[i][j+k][icount] = true;
  126. }
  127. icount++;
  128. }
  129. //右斜
  130. for(i=0;i<12;i++)
  131. for(j=0;j<12;j++){
  132. for(k=0;k<5;k++){
  133. this.ptable[j+k][i+k][icount] = true;
  134. this.ctable[j+k][i+k][icount] = true;
  135. }
  136. icount++;
  137. }
  138. //左斜
  139. for(i=0;i<12;i++)
  140. for(j=15;j>=4;j--){
  141. for(k=0;k<5;k++){
  142. this.ptable[j-k][i+k][icount] = true;
  143. this.ctable[j-k][i+k][icount] = true;
  144. }
  145. icount++;
  146. }
  147. for(i=0;i<=1;i++) //初始化黑子白子上的每个权值上的连子数
  148. for(j=0;j<672;j++)
  149. this.win[i][j] = 0;
  150. this.player = true;
  151. this.icount = 0;
  152. this.ccount = 0;
  153. this.pcount = 0;
  154. this.start = true;
  155. this.over = false;
  156. this.pwin = false;
  157. this.cwin = false;
  158. this.tie = false;
  159. this.bout=1;
  160. }
  161. public void ComTurn(){ //找出电脑(白子)最佳落子点
  162. for(i=0;i<=15;i++) //遍历棋盘上的所有坐标
  163. for(j=0;j<=15;j++){
  164. this.pgrades[i][j]=0; //该坐标的黑子奖励积分清零
  165. if(this.board[i][j] == 2) //在还没下棋子的地方遍历
  166. for(k=0;k<672;k++) //遍历该棋盘可落子点上的黑子所有权值的连子情况,并给该落子点加上相应奖励分
  167. if(this.ptable[i][j][k]){
  168. switch(this.win[0][k]){
  169. case 1: //一连子
  170. this.pgrades[i][j]+=5;
  171. break;
  172. case 2: //两连子
  173. this.pgrades[i][j]+=50;
  174. break;
  175. case 3: //三连子
  176. this.pgrades[i][j]+=180;
  177. break;
  178. case 4: //四连子
  179. this.pgrades[i][j]+=400;
  180. break;
  181. }
  182. }
  183. this.cgrades[i][j]=0;//该坐标的白子的奖励积分清零
  184. if(this.board[i][j] == 2) //在还没下棋子的地方遍历
  185. for(k=0;k<672;k++) //遍历该棋盘可落子点上的白子所有权值的连子情况,并给该落子点加上相应奖励分
  186. if(this.ctable[i][j][k]){
  187. switch(this.win[1][k]){
  188. case 1: //一连子
  189. this.cgrades[i][j]+=5;
  190. break;
  191. case 2: //两连子
  192. this.cgrades[i][j]+=52;
  193. break;
  194. case 3: //三连子
  195. this.cgrades[i][j]+=100;
  196. break;
  197. case 4: //四连子
  198. this.cgrades[i][j]+=400;
  199. break;
  200. }
  201. }
  202. }
  203. if(this.start){ //开始时白子落子坐标
  204. if(this.board[4][4]==2){
  205. m = 4;
  206. n = 4;
  207. }else{
  208. m = 5;
  209. n = 5;
  210. }
  211. this.start = false;
  212. }else{
  213. for(i=0;i<16;i++)
  214. for(j=0;j<16;j++)
  215. if(this.board[i][j] == 2){ //找出棋盘上可落子点的黑子白子的各自最大权值,找出各自的最佳落子点
  216. if(this.cgrades[i][j]>=this.cgrade){
  217. this.cgrade = this.cgrades[i][j];
  218. this.mat = i;
  219. this.nat = j;
  220. }
  221. if(this.pgrades[i][j]>=this.pgrade){
  222. this.pgrade = this.pgrades[i][j];
  223. this.mde = i;
  224. this.nde = j;
  225. }
  226. }
  227. if(this.cgrade>=this.pgrade){ //如果白子的最佳落子点的权值比黑子的最佳落子点权值大,则电脑的最佳落子点为白子的最佳落子点,否则相反
  228. m = mat;
  229. n = nat;
  230. }else{
  231. m = mde;
  232. n = nde;
  233. }
  234. }
  235. this.cgrade = 0;
  236. this.pgrade = 0;
  237. this.board[m][n] = 1; //电脑下子位置
  238. ccount++;
  239. if((ccount == 50) && (pcount == 50)) //平局判断
  240. {
  241. this.tie = true;
  242. this.over = true;
  243. }
  244. for(i=0;i<672;i++){
  245. if(this.ctable[m][n][i] && this.win[1][i] != 7)
  246. this.win[1][i]++; //给白子的所有五连子可能的加载当前连子数
  247. if(this.ptable[m][n][i]){
  248. this.ptable[m][n][i] = false;
  249. this.win[0][i]=7;
  250. }
  251. }
  252. this.player = true; //该人落子
  253. this.computer = false; //电脑落子结束
  254. }
  255. public void mouseClick(){
  256. if(!this.over)
  257. if(this.player){
  258. if(this.oldx<520 && this.oldy<520) {
  259. int m1=m,n1=n;
  260. m = (oldx-33)/30;
  261. n = (oldy-33)/30;
  262. if(this.board[m][n] == 2){
  263. this.bout++;
  264. this.board[m][n] = 0;
  265. pcount++;
  266. if((ccount == 50) && (pcount == 50)){
  267. this.tie = true;
  268. this.over = true;
  269. }
  270. for(i=0;i<672;i++){
  271. if(this.ptable[m][n][i] && this.win[0][i] != 7)
  272. this.win[0][i]++; //给黑子的所有五连子可能的加载当前连子数
  273. if(this.ctable[m][n][i]){
  274. this.ctable[m][n][i] = false;
  275. this.win[1][i]=7;
  276. }
  277. }
  278. this.player = false;
  279. this.computer = true;
  280. }else{
  281. m=m1;n=n1;
  282. }
  283. }
  284. }
  285. }
  286. public void updatePaint(Graphics g){
  287. if(!this.over){ //如果是轮到电脑下
  288. if(this.computer)
  289. this.ComTurn(); //得到最佳下子点
  290. //遍历当前棋盘上的五连子情况,判断输赢
  291. for(i=0;i<=1;i++)
  292. for(j=0;j<672;j++){
  293. if(this.win[i][j] == 5)
  294. if(i==0){ //人赢
  295. this.pwin = true;
  296. this.over = true; //游戏结束
  297. break;
  298. }else{
  299. this.cwin = true; //电脑赢
  300. this.over = true;
  301. break;
  302. }
  303. if(this.over) //一遇到五连子退出棋盘遍历
  304. break;
  305. }
  306. g.setFont(new Font("华文行楷",0,20));
  307. g.setColor(Color.RED);
  308. //画出当前棋盘所有棋子
  309. for(i=0;i<=15;i++)
  310. for(j=0;j<=15;j++){ //如果board元素值为0,则该坐标处为黑子
  311. if(this.board[i][j] == 0){
  312. g.drawImage(black.getImage(),i*30+31,j*30+31,black.getImage().getWidth(black.getImageObserver())-3,black.getImage().getHeight(black.getImageObserver())-3, black.getImageObserver());
  313. }
  314. //如果board元素值为1,则该坐标处为白子
  315. if(this.board[i][j] == 1){
  316. g.drawImage(white.getImage(),i*30+31,j*30+31,white.getImage().getWidth(white.getImageObserver())-3,white.getImage().getHeight(white.getImageObserver())-3, white.getImageObserver());
  317. }
  318. }
  319. //画出白子(电脑)当前所下子,便于辨认
  320. if(this.board[m][n]!=2)
  321. g.drawImage(current.getImage(),m*30+31,n*30+31,current.getImage().getWidth(current.getImageObserver())-4,current.getImage().getHeight(current.getImageObserver())-4, current.getImageObserver());
  322. //判断输赢情况
  323. //人赢
  324. if(this.pwin)
  325. g.drawString("您太厉害了!再来一次请重新开始游戏..",20,200);
  326. //电脑赢
  327. if(this.cwin)
  328. g.drawString("很遗憾,你输了!再来一次请重新开始游戏..",84,190);
  329. //平局
  330. if(this.tie)
  331. g.drawString("不分胜负!再来一次请重新开始游戏..",80,200);
  332. g.dispose();
  333. }
  334. }
  335. }

如果有兴趣的朋友可以和我一起交流下技术, 我QQ号码: 249930610, MSN: yangsharp@hotmail.com

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值