前段时间忙于考试,一直没时间写Java学习笔记。现在继续踏上Java学习的路程。前面学了界面、监听、继承等等知识,现在就用这些知识来实现自己的第一个Java项目。
五子棋的一些要求如下:
(1)点击开始新游戏的时候才能在棋盘上下棋;
(2)游戏结束后棋盘不能下棋;
(3)实现悔棋功能;
(4)实现认输功能;
(5)对战模式选择;
首先来思考几个问题:
1.实现五子棋所需的API类要哪些?
JFrame
BorderLayout //这是一个布置容器的边框布局,它可以对容器组件进行安排,并调整其大小,使其符合下列五个区 域:北、南、东、西、中。每个区域最多只能包含一个组件,并通过相应的常量进行标识:NORTH
、SOUTH
、EAST
、WEST
、CENTER
。当使用边框布局将一个组件添加到容器中时,要使用这五个常量之一。
JPanel 面板容器组件类
MouseAdapter | MouseListener
MouseEvent
Graphics | Graphics2D
RenderingHints //设置那个抗锯齿的,不然棋子看起来很糙,设置这个可以让棋子看起来更光滑。
Color
数组
2.界面实现
(1).重绘 //棋盘始终要显示在界面上,跟之前重绘的道理一样。
(2).继承
(3).swing界面
3.下棋子
(1).事件监听机制
(2).画图
(3).重绘
以上就是简单废话一下,下面我会具体说明如何实现。
一、棋盘界面实现:
玩过五子棋的都应该知道这个界面就是网格线,也就是画线呗。那么划线需要一个起点和终点。
那么确定一下棋盘应该有的属性:
(1)棋盘的大小,就是行ROW,和列COL;
(2)网格线的宽度SIZE,这样能让棋盘更好看些;
(3)棋子半径的大小
(4)第一个画线的起点坐标(X0,Y0)
为了方便修改我们的棋盘,我们把这些属性定义成一个接口,可供修改。
贴下代码:
public interface Config {
public static final int X0 = 70;
public static final int Y0 =50;
public static final int ROWS = 12;
public static final int COLUMS = 12;
public static final int CHESS_SIZE =40;
public static final int SIZE = 50;
}
interface是关键字。
下面开始将棋盘的界面完善,用到JFrame,设置一下这个窗体的属性:大小、组件布局、窗体名字、添加面板、在面板里面添加按钮组件和下拉框组件。定义一个棋盘界面的类,让它继承JPanel类,或者JFrame,但这里继承JPanel好一些,因为我们主要用到的是JPanel。
public void Chessinit()
{
JFrame frame = new JFrame("五子棋");
frame.setSize(800, 700);
frame.setLayout(new BorderLayout());
}
这样就初始化了一个棋盘界面了。
然后添加JPanel组件:
JPanel eastPane = new JPanel();
eastPane.setPreferredSize(new Dimension(150,0));
为啥不用设置高度,因为选用了BorderLayout布局,不需要设置高度了。
然后就是添加一些按钮,比如开始新游戏、悔棋、认输等等,然后就是对战模式的选择。
添加按钮:
JButton JBstart = new JButton("开始新游戏");
JBstart.setPreferredSize(new Dimension(100,100));
eastPane.add(JBstart);
JButton JBback = new JButton("悔棋");
JBback.setPreferredSize(new Dimension(100,100));
eastPane.add(JBback);
JButton JBgiveup = new JButton("认输");
JBgiveup.setPreferredSize(new Dimension(100,100));
eastPane.add(JBgiveup);
String []type ={"人人对战","人机对战"};
JComboBox<String>cItem = new JComboBox<>(type);
cItem.setPreferredSize(new Dimension(100,50));
eastPane.add(cItem);
下拉框那个要用到JComboBox,type是一个String数组,存储了你给的选项,这样写可以直接添加整个数组,而不用一项一项的添加。
完事之后就把面板添加到窗体去吧。
代码就一句:frame.add(eastPane, BorderLayout.EAST);
第二个参数表示面板放置在东边,也就是棋盘的右侧。
下面开始重绘棋盘,重写下paint方法或者自己定义一个方法都可以。
代码如下:
public void paint(Graphics g)
{
super.paint(g);
Graphics2D G = (Graphics2D)g;//g本身就是一个对象了。
G.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for(int i=0;i<Config.ROWS;i++)
{
g.drawLine(Config.X0, Config.Y0+Config.SIZE*i, Config.X0+(Config.COLUMS-1)*Config.SIZE, Config.Y0+Config.SIZE*i);
}
for(int j=0;j<Config.COLUMS;j++)
{
g.drawLine(Config.X0+Config.SIZE*j, Config.Y0, Config.X0+Config.SIZE*j, Config.Y0+(Config.ROWS-1)*Config.SIZE);
}
//棋子重绘:
for(int i=0;i<Chess.length;i++)
{
for(int j=0;j<Chess.length;j++)
{
int x = Config.X0 + Config.SIZE*i;
int y = Config.Y0 + Config.SIZE*j;
if(Chess[i][j]==1)
{
g.setColor(Color.black);//
Chess[i][j]=1;
g.fillOval(x-Config.CHESS_SIZE/2,y-Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE);
}
else if(Chess[i][j]==-1)
{
g.setColor(Color.white);
Chess[i][j]=-1;
g.fillOval(x-Config.CHESS_SIZE/2,y-Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE);
}
}
}
G.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);是设置那个抗锯齿,让棋子看起来更光滑。
二、添加监听
首先要创建一个监听器类,当你拿到动作信息后该干什么。想一下,当鼠标点击的时候是不是要在棋盘上画一个棋子呢?所以就要用到鼠标器适配类(这个包含了所有监听鼠标动作的方法),然后就是按钮动作信息捕捉,ActionListener提供了方法,所以还要继承接口。注意这里哦,前面那个是类,后面那个是接口,继承关键字分别为:extends、implements。继承接口后务必记得重写它的方法,不然会报错的。
1.先写鼠标点击后,在点击位置画棋子:
画棋子我们需要拿到画笔类,画笔在哪呢?窗体上啊!所以我们可以传一个窗体过来再取,或者在界面类那边传一个画笔类对象过来。你需要用到的东西需要定义属性来接受。怎么接受?用构造方法呗。我们还需要用到棋盘,因为要判断哪个位置是否下棋。
代码如下:
public class ChessListener extends MouseAdapter implements ActionListener {
private ChessUI gm;
private Graphics2D g;
private int chess[][] = new int[Config.ROWS][Config.COLUMS];
public ChessListener(ChessUI G, int[][] chess) {
this.gm = G;
this.chess = chess;
} }
拿到需要的东西后,就开始画棋子啦。
你可以选择在鼠标被按下或者释放后画,看自己。
如何让棋子在线的交叉点处画出来呢?先get到交叉点的坐标,然后以它为中心,一定的半径画出。
for (int j = 0; j < Config.ROWS; j++) {
for (int i = 0; i < Config.COLUMS; i++) { //遍历整个棋盘,寻找可以画棋子的位置
int x = Config.X0 + Config.SIZE * i;//对每个点都进行一个放大处理
int y = Config.Y0 + Config.SIZE * j;
if (x1 > x - Config.SIZE / 3 && x1 < x + Config.SIZE / 3 && y1 > y - Config.SIZE / 3
&& y1 < y + Config.SIZE / 3) {//x1,y1是当前鼠标点击位置的坐标:x1=e.getX()....
if (chess[i][j] == 0) { //0表示当前位置没有棋子
if (count == 0) {//count=0表示画白棋子,1画黑棋子
g.setColor(Color.white);// 设置画棋子的颜色
g.fillOval(x - Config.CHESS_SIZE / 2, y - Config.CHESS_SIZE / 2, Config.CHESS_SIZE,
Config.CHESS_SIZE);
Point ch = new Point(i,j,count);//这先不管,悔棋用到的。
chessList[chessCount++]=ch;//这也是悔棋用到的
count = 1;//设置为1,表示下一次画的是黑棋
chess[i][j] = -1;//该位置设置为-1,表示该位置画了白棋,设置为1表示画了黑棋。
if (Judge(i, j)) {//这是判断输赢的,每下一个棋子都要判断一次,也先不管。
// System.out.println("黑棋赢了!");
String msg = String.format("恭喜,%s赢啦~", "白棋 win!");//弹出框
JOptionPane.showMessageDialog(gm, msg);
gameover = 1;//游戏是否接受的标志
}
} else if (count==1){//画黑棋,跟白棋一样的。
g.setColor(Color.black);
g.fillOval(x - Config.CHESS_SIZE / 2, y - Config.CHESS_SIZE / 2, Config.CHESS_SIZE,
Config.CHESS_SIZE);
Point ch = new Point(i,j,count);
chessList[chessCount++]=ch;
count = 0;
chess[i][j] = 1;
if (Judge(i, j)) {
// System.out.println("白棋赢了!");
String msg = String.format("恭喜,%s赢啦~", "黑棋 win!");
JOptionPane.showMessageDialog(gm, msg);
gameover = 1;
}
}
return;
}
}
}
}
}
下棋的写完了。该写下监听了。
2.给按钮添加监听:
重写监听方法:
当点击按钮的时候该干嘛:这里也顺带把三个功能如何实现一并说了。
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JComboBox) {instanceof关键字主要功能是判断这个事件源是不是它的
JComboBox<String> comboBox = (JComboBox) e.getSource();// 拿到对象
fontName = comboBox.getSelectedItem().toString();// 拿到下拉菜单被选择的内容,因为我们要确定它选择了哪种对战模式
}
if (e.getActionCommand() == "开始新游戏") {//开始新游戏的话把棋盘数组重置一下,系统重绘的时候会扫一遍这个数组,都为0,系统就不会重绘棋子了。
for (int i = 0; i < chess.length; i++) {
for (int j = 0; j < chess.length; j++) {
chess[i][j] = 0;
}
}
gameover = 0;//要把状态都恢复到初始
// System.out.println("okok " + gameover);
gm.repaint();//系统重绘棋盘
count = 1;//第一步先画黑棋(就是黑棋先行)
} else if (e.getActionCommand() == "悔棋") {
this.GoBack();//Goback函数的就是定义一个类专门去保存你画的棋子的信息:坐标、颜色。具体代码在最尾部贴出,当点击悔棋的时候就取
上一次保存的棋子的位置取出来还原为未下棋子的状态,然后再重绘一下棋盘即可。
} else if (e.getActionCommand() == "认输") {//通过判断当前是谁刚下完棋,点击认输后就是它赢,比如当前白棋刚下完,到黑棋下,但它认输,所以是白棋赢;
if (count == 0) {
String msg = String.format("恭喜,%s赢啦~", "黑棋 win!");
JOptionPane.showMessageDialog(gm, msg);
} else if (count == 1) {
String msg = String.format("恭喜,%s赢啦~", "白棋 win!");
JOptionPane.showMessageDialog(gm, msg);
}
}
}
写完后就添加监听到按钮去吧,这些组件都共用一个监听:
在你的棋盘界面添加如下代码:
ChessListener L = new ChessListener(this,Chess);//共用一个监听即可。
JBstart.addActionListener(L);
JBback.addActionListener(L);
JBgiveup.addActionListener(L);
cItem.addActionListener(L);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
frame.add(this);
frame.setVisible(true);
this.addMouseListener(L);
然后完整的棋盘界面代码如下:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.AncestorListener;
public class ChessUI extends JPanel{
public static void main(String args[])
{
ChessUI ch = new ChessUI();
ch.Chessinit();
}
private int Chess[][] = new int[Config.ROWS][Config.COLUMS];
public void Chessinit()
{
JFrame frame = new JFrame("五子棋");
frame.setSize(800, 700);
frame.setLayout(new BorderLayout());
JPanel eastPane = new JPanel();
/*添加一系列按钮并设置他们的属性*/
eastPane.setPreferredSize(new Dimension(150,0));
JButton JBstart = new JButton("开始新游戏");
JBstart.setPreferredSize(new Dimension(100,100));
eastPane.add(JBstart);
JButton JBback = new JButton("悔棋");
JBback.setPreferredSize(new Dimension(100,100));
eastPane.add(JBback);
JButton JBgiveup = new JButton("认输");
JBgiveup.setPreferredSize(new Dimension(100,100));
eastPane.add(JBgiveup);
String []type ={"人人对战","人机对战"};
JComboBox<String>cItem = new JComboBox<>(type);
cItem.setPreferredSize(new Dimension(100,50));
eastPane.add(cItem);
eastPane.setBackground(Color.orange);
/*棋盘的监听*/
ChessListener L = new ChessListener(this,Chess);//共用一个监听即可。
JBstart.addActionListener(L);
JBback.addActionListener(L);
JBgiveup.addActionListener(L);
cItem.addActionListener(L);
frame.add(eastPane, BorderLayout.EAST);//添加面板
frame.setBackground(Color.orange);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
frame.add(this);
frame.setVisible(true);
this.addMouseListener(L);
}
/*重绘棋盘*/
public void paint(Graphics g)
{
super.paint(g);
Graphics2D G = (Graphics2D)g;//g本身就是一个对象了。
G.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for(int i=0;i<Config.ROWS;i++)
{
g.drawLine(Config.X0, Config.Y0+Config.SIZE*i, Config.X0+(Config.COLUMS-1)*Config.SIZE, Config.Y0+Config.SIZE*i);
}
for(int j=0;j<Config.COLUMS;j++)
{
g.drawLine(Config.X0+Config.SIZE*j, Config.Y0, Config.X0+Config.SIZE*j, Config.Y0+(Config.ROWS-1)*Config.SIZE);
}
/*棋子重绘*/
for(int i=0;i<Chess.length;i++)
{
for(int j=0;j<Chess.length;j++)
{
int x = Config.X0 + Config.SIZE*i;
int y = Config.Y0 + Config.SIZE*j;
if(Chess[i][j]==1)
{
g.setColor(Color.black);//设置下一次画棋子的颜色
Chess[i][j]=1;
g.fillOval(x-Config.CHESS_SIZE/2,y-Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE);
}
else if(Chess[i][j]==-1)
{
g.setColor(Color.white);
Chess[i][j]=-1;
g.fillOval(x-Config.CHESS_SIZE/2,y-Config.CHESS_SIZE/2,Config.CHESS_SIZE,Config.CHESS_SIZE);
}
}
}
}
}
这里就得到一个完整的棋盘界面了。
3.判断输赢:
思路就是每个方向都统计一下同色的棋子是否够五个。比较简单的方法就是循环遍历了。
private boolean Judge(int xx, int yy) {
int count = 1;
for (int i = xx - 1; i >= 0; i--)// 横向向左找
{
if (chess[i][yy] == chess[xx][yy]) {
count++;
// System.out.println("(Left)Count:"+count);
} else
break;
}
for (int i = xx + 1; i < chess.length; i++)// 横向向右找
{
if (chess[i][yy] == chess[xx][yy]) {
count++;
// System.out.println("Count:"+count);
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
for (int j = yy - 1; j >= 0; j--)// 纵向向上
{
if (chess[xx][j] == chess[xx][yy]) {
count++;
// System.out.println("(UP)Count:"+count);
} else
break;
}
for (int j = yy + 1; j < chess.length; j++)// 纵向向下
{
if (chess[xx][j] == chess[xx][yy]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
for (int i = xx + 1, j = yy - 1; j >= 0 && i < chess.length; i++, j--)// 右下
{
if (chess[i][j] == chess[xx][yy]) {
count++;
} else
break;
}
for (int i = xx - 1, j = yy + 1; i >= 0 && j < chess.length; i--, j++)// 左上
{
if (chess[i][j] == chess[xx][yy]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
for (int i = xx + 1, j = yy + 1; j < chess.length && i < chess.length; i++, j++)// 右上
{
if (chess[i][j] == chess[xx][yy]) {
count++;
} else
break;
}
for (int i = xx - 1, j = yy - 1; i >= 0 && j >= 0; i--, j--)// 左下
{
if (chess[i][j] == chess[xx][yy]) {
count++;
} else
break;
}
if (count >= 5) {
return true;
} else {
count = 1;
}
// System.out.println("Count:"+count);
return false;
}
4.悔棋:
思路我在前面说了一下,就是像画板重绘那样,保存你画的每一个棋子即可。
Point类:
public class Point {
private int x;//索引x
private int y;
private int count=0;//表示当前棋子的颜色
public Point(int x,int y,int count)
{
this.x=x;
this.y=y;
this.count=count;
}
public int getx()
{
return x;
}
public int gety()
{
return y;
}
}
监听类中实现GoBack()函数:
public void GoBack()
{
if(chessCount==0)//chessCount用来记录当前下的棋子个数
return ;
if(chessCount>0){
xIndex=chessList[chessCount-1].getx();
yIndex=chessList[chessCount-1].gety();
chess[xIndex][yIndex]=0;
}
chessList[chessCount-1]=null;
chessCount--;
gm.repaint();//重绘棋盘
}
三、接下来是实现人人对战和人机对战。
1.人人对战:
人人对战比较简单,将上面我贴出来画旗子的那个代码封装成一个pvp方法,在mousePressed里面调用就可以了。
2.人机对战(我的人机有点蠢,后期找时间优化一下)
电脑下棋可以用很多种方法实现,比如权值法、博弈树、机器学习。
但只是简单入门,就用一下权值法。
先来科普一下棋盘可能的情况:
活连:当你下完某课棋子的时候,从它的左或右看过去,没有被异色的棋子挡住就是活连,比如连续的三个黑棋,左右两边都还能下黑棋。
如图:
对于黑棋来说,它目前是活三连,白棋是活二连,最多只可能是活四连。
然后是眠连,眠连跟活连的区别就是眠连有一边被异色棋子堵住了。
如图:
当前黑棋是眠四连。
眠连和活连都是最大只有四。那么给他们定义一下权值,权值大的位置优先被选择下棋。
活一连:10 眠一连:1
活二连:80 眠二连:50
活三连:100 眠三连:100
活四连:3000 眠四连:2500
为什么最后两个权值设那么大?为了保证其它情况的权值总和加起来不大于四连,否则四连就没有意义了。权值如何设置看自己,但要保证满足我说的这个条件即可。
如何将棋子的情况跟它的权值映射起来呢?可以考虑选用map这个数据结构,我下面列出了一些可能的情况,0表示空位,1表示黑棋,-1表示白棋。要想你的AI更智能,就尽可能的考虑更多的情况。
代码如下:
private static HashMap<String,Integer> map = new HashMap<String,Integer>();
static{
map.put("010",10);//活一连(黑)
map.put("0-10", 10);//活一连(白)
map.put("01010", 80);//活二连(黑)
map.put("0110", 80);//活二连(黑)
map.put("0-10-10", 80);//活二连(白)
map.put("0-1-10", 80);//活二连(白)
map.put("01110", 100);//活三连(黑)
map.put("0-1-1-10", 100);//活三连(白)
map.put("011110", 3000);//活四连(黑)
map.put("0-1-1-1-10", 3000);//活四连(白)
map.put("1-1", 1);//眠一连(黑)
map.put("-11", 1);//眠一连(白)
map.put("011-1", 50);//眠二连(黑)
map.put("-1110", 50);//眠二连(黑)
map.put("0-1-11", 50);//眠二连(白)
map.put("1-1-10", 50);//眠二连(白)
map.put("0111-1", 100);//眠三连(黑)
map.put("-11110", 100);//眠三连(黑)
map.put("0-1-1-11", 100);//眠三连(白)
map.put("1-1-1-10", 100);//眠三连(白)
map.put("01111-1", 250);//眠四连(黑)
map.put("-111110", 250);//眠四连(黑)
map.put("1-1-1-1-10", 2500);//眠四连(白)
map.put("0-1-1-1-11", 2500);//眠四连(白)
}
由于我的棋盘是人先行,所以计算权值就不用考虑那么多了。计算权值就是当人下完一颗棋子后,这个方法就要开始计算当前棋盘棋子的情况有哪些,比如活一连啊活二连的情况有多少,然后计算活一连的权值总和并存储到数组里面去,计算权值的时候会对每一个空位都进行计算。如何统计当前棋盘有哪些连,跟判断输赢类似,也是分方向,每个方向都去找,找完了就计算一次那个方向的权值。代码如下:(hin长!),希望有大佬帮我优化一下,记得考虑边界,不然会发生越界的哦。
public void Search()
{
for(int r=0;r<chess.length;r++)
{
for(int c=0;c<chess.length;c++)//遍历整个棋盘
{
if(chess[r][c]==0)//该位置没有棋子
{
int num=0;//空位出现的次数
String code="0";//棋子相连的情况
int chesscount=0;//记录当前出现的棋子
for(int c1=c-1;c1>=0;c1--)//向左
{
if(chess[r][c1]==0)//空位
{
if(c==c1+1) break;
else if(num==0)
{
code+=chess[r][c1];
num++;
}
else if(num==1)
{
if(chess[r][c1]==chess[r][c1+1]) break;
code+=chess[r][c1];
num++;
}
else if(num==3)
{
if(chess[r][c1]==chess[r][c1+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[r][c1];
code+=chess[r][c1];
}
else if(chess[r][c1]==chesscount)
{
code+=chess[r][c1];
}
else
{
code+=chess[r][c1];
break;
}
}
}
Integer value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*----------------------------------------------*/
//向右
for(int c1=c+1;c1<chess.length-1;c1++)
{
if(chess[r][c1]==0)
{
if(c==c1-1) break;
else if(num==0)
{
code+=chess[r][c1];
num++;
}
else if(num==1)
{
if(chess[r][c1]==chess[r][c1+1]) break;
code+=chess[r][c1];
num++;
}
else if(num==3)
{
if(chess[r][c1]==chess[r][c1+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[r][c1];
code+=chess[r][c1];
}
else if(chess[r][c1]==chesscount)
{
code+=chess[r][c1];
}
else
{
code+=chess[r][c1];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*----------------------------------------------*/
//up
for(int r1=r-1;r1>=0;r1--)
{
if(chess[r1][c]==0)
{
if(r==r1+1) break;
else if(num==0)
{
code+=chess[r1][c];
num++;
}
else if(num==1)
{
if(chess[r1][c]==chess[r1+1][c]) break;
code+=chess[r1][c];
num++;
}
else if(num==3)
{
if(chess[r1][c]==chess[r1+1][c]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[r1][c];
code+=chess[r1][c];
}
else if(chess[r1][c]==chesscount)
{
code+=chess[r1][c];
}
else
{
code+=chess[r1][c];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*----------------------------------------------*/
//down
for(int r1=r+1;r1<chess.length-1;r1++)
{
if(chess[r1][c]==0)
{
if(r==r1-1) break;
else if(num==0)
{
code+=chess[r1][c];
num++;
}
else if(num==1)
{
if(chess[r1][c]==chess[r1+1][c]) break;
code+=chess[r1][c];
num++;
}
else if(num==3)
{
if(chess[r1][c]==chess[r1+1][c]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[r1][c];
code+=chess[r1][c];
}
else if(chess[r1][c]==chesscount)
{
code+=chess[r1][c];
}
else
{
code+=chess[r1][c];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*------for (int i = xx + 1, j = yy - 1; j >= 0 && i < chess.length; i++, j--)// 右下----------------------------------------*/
//右下
for(int i=r+1,j=c-1;j>=0&&i<chess.length-1;i++,j--)
{
if(chess[i][j]==0)
{
if(r==i-1 && c==j+1) break;
else if(num==0)
{
code+=chess[i][j];
num++;
}
else if(num==1)
{
if(chess[i][j]==chess[i+1][j+1]) break;
code+=chess[i][j];
num++;
}
else if(num==3)
{
if(chess[i][j]==chess[i+1][j+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[i][j];
code+=chess[i][j];
}
else if(chess[i][j]==chesscount)
{
code+=chess[i][j];
}
else
{
code+=chess[i][j];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*------------------------------------*/
/*左上*/
for(int i=r-1,j=c+1;i>=0&&j<chess.length-1;j++,i--)
{
if(chess[i][j]==0)
{
if(r==i+1 && c==j-1) break;
else if(num==0)
{
code+=chess[i][j];
num++;
}
else if(num==1)
{
if(chess[i][j]==chess[i+1][j+1]) break;
code+=chess[i][j];
num++;
}
else if(num==3)
{
if(chess[i][j]==chess[i+1][j+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[i][j];
code+=chess[i][j];
}
else if(chess[i][j]==chesscount)
{
code+=chess[i][j];
}
else
{
code+=chess[i][j];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*------------------------------------*/
/*右上*/
for(int i=r+1,j=c+1;i<chess.length-1&&j<chess.length-1;j++,i++)
{
if(chess[i][j]==0)
{
if(r==i-1 && c==j-1) break;
else if(num==0)
{
code+=chess[i][j];
num++;
}
else if(num==1)
{
if(chess[i][j]==chess[i+1][j+1]) break;
code+=chess[i][j];
num++;
}
else if(num==3)
{
if(chess[i][j]==chess[i+1][j+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[i][j];
code+=chess[i][j];
}
else if(chess[i][j]==chesscount)
{
code+=chess[i][j];
}
else
{
code+=chess[i][j];
break;
}
}
}
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
/*------------------------------------*/
/*左下*/
for(int i=r-1,j=c-1;i>=0&&j>=0;j--,i--)
{
if(chess[i][j]==0)
{
if(r==i+1 && c==j+1) break;
else if(num==0)
{
code+=chess[i][j];
num++;
}
else if(num==1)
{
if(chess[i][j]==chess[i+1][j+1]) break;
code+=chess[i][j];
num++;
}
else if(num==3)
{
if(chess[i][j]==chess[i+1][j+1]) break;
}
}
else//有棋子
{
if(chesscount==0)//第一次出现棋子
{
chesscount=chess[i][j];
code+=chess[i][j];
}
else if(chess[i][j]==chesscount)
{
code+=chess[i][j];
}
else
{
code+=chess[i][j];
break;
}
}
}
//计算weight
value = map.get(code);
if(value!=null){//判断value是否不为null
weightArray[r][c]+=value;//在对应空位累加权值
}
}/*---if---*/
}/*第一个for*/
}/*第二个for*/
}
电脑画棋子的代码补一下吧:
public void pve_draw()
{
this.Search();
int max=-1;
int xIndex=0;
int yIndex=0;
for(int i=0;i<weightArray.length;i++)
{
for(int j=0;j<weightArray.length;j++)
{
if(weightArray[i][j]>max)//找到第一个最大的
{
max=weightArray[i][j];
xIndex=i;
yIndex=j;
}
}
}
/*-------------TEST--------------*/
for(int i=0;i<weightArray.length;i++)
{
for(int j=0;j<weightArray.length;j++)
{
System.out.print(weightArray[i][j]+" ");
}
System.out.println();
}
System.out.println("所选最大权值位置为:"+xIndex+" "+yIndex+"最大权值:"+max);
/*-------------TEST--------------*/
if(chess[xIndex][yIndex]==0){g = (Graphics2D) gm.getGraphics();g.setColor(Color.white);g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);g.fillOval(Config.X0+xIndex*Config.SIZE- Config.CHESS_SIZE / 2, Config.Y0+yIndex*Config.SIZE - Config.CHESS_SIZE / 2, Config.CHESS_SIZE,Config.CHESS_SIZE);Point ch = new Point(xIndex,yIndex,count);//保存电脑画的棋子,悔棋要用的chessList[chessCount++]=ch;chess[xIndex][yIndex] = -1;weightArray[xIndex][yIndex]=0;//用了这个点清空if (Judge(xIndex, yIndex)) {String msg = String.format("恭喜,%s赢啦~", "白棋 win!");JOptionPane.showMessageDialog(gm, msg);gameover = 1;}}}
核心的东西都已经写完了,把人人对战和人机对战封装成两个方法,根据选择的对战模式进行相应的调用即可。
下面附一个完整的代码:https://paste.ubuntu.com/p/dTJx6MQ49y/
运行方法:新建一个Java工程,分别建四个类,把对应的代码放入,运行即可。
声明:emmm这五子棋有个小小的bug,就是电脑有的时候会下少一次棋子,应该是计算权值的时候出错了,希望有大神能解决这个bug。我本人也在努力的解决这个buging。有机会会回来完善这个博客的。如有不懂的欢迎下方留言,我们一起讨论。
贴个图: