五子棋总结(附代码)
今天终于完成了关于自己的第一个小游戏,觉得挺有成就感的,所以想总结一下这几天我做五子棋的心得:
第一,要完成一个五子棋游戏,首先要有一些知识准备,1.swing和awt一些窗口还有一些容器,主要是做一个五子棋游戏界面,2.通过鼠标监听器在五子棋盘界面上画出一个五子棋盘,3.在五子棋盘上画出棋子,4.实实现棋盘上棋子的重绘,4.实现一些按钮的功能
第二,分析游戏的设计过程,例如这个游戏的重心应该是在五子棋盘上下棋的过程,这也是人机对战的重心
第三,在分析的基础上,我们的下一步就是要设计在屋子棋盘上下棋的算法,因为是人机对战所以显示黑子根据鼠标点击的位置在棋盘上下一颗棋子,然后机器的白子应该在哪呢,这就是这个游戏的重中之重,也是算法最难的地方,电脑要根据棋盘棋子的分布情况然后要算出那个空格的重要性然后在下棋,所以要对每个空格赋权值,那么最大全职的那个点就是电脑要下的地方,所限要付一个权值表.
x1 = e.getX();
y1 = e.getY();
for (int i = 0; i < Config.ROWS; i++)
for (int j = 0; j < Config.COLUMNS; j++) {
// 得到点(i,j)的坐标值(x,y)
int x = Config.XO + Config.SIZE * i;
int y = Config.YO + Config.SIZE * j;
// 寻找离鼠标释放处最近的交叉点
if (x1 > x - Config.SIZE / 3 && x1 < x + Config.SIZE / 3
&& y1 > y - Config.SIZE / 3 && y1 < y + Config.SIZE / 3) {
// 如果该点还没有放过棋子
if( canplay==true){
if (ChessUI.chesses[i][j] == 0) {
// 如果count等于0则画黑棋
if (ChessUI.count == 0) {
// 画黑棋的对象
javax.swing.ImageIcon imgB = new javax.swing.ImageIcon(
this.getClass().getResource("b.png"));
g.drawImage(imgB.getImage(), x - Config.CHESS_SIZE
/ 2, y - Config.CHESS_SIZE / 2,
Config.CHESS_SIZE, Config.CHESS_SIZE, null);
ChessUI.chesses[i][j] = 1;
ChessUI.count = 1;
ch.check(i, j);
}
// 如果是count等于1则画白棋
if (ChessUI.count == 1) {
/**
* 评分规则 遍历棋盘上的所有点
*/
//
score = new int[Config.ROWS][Config.COLUMNS];
Count count = new Count();
int[] arr = new int[8];
for (int m = 0; m < Config.ROWS; m++) {
for (int n = 0; n < Config.COLUMNS; n++) {
if (ChessUI.chesses[m][n] == 0) {
score[m][n] = count.bCountColumn(m, n)
+ count.bCountRow(m, n)
+ count.bCountLU(m, n)
+ count.bCountRU(m, n)
+ count.wCountColumn(n, n)
+ count.wCountRow(m, n)
+ count.wCountRU(m, n)
+ count.wCountLU(m, n);
}
}
}
/**
* 找出分数最高的空点
*/
int tempi = 0, tempj = 0;
int temp = score[tempi][tempj];
for (int m = 0; m < Config.ROWS; m++) {
for (int n = 0; n < Config.COLUMNS; n++) {
if (temp < score[m][n]) {
temp = score[m][n];
tempi = m;
tempj = n;
}
}
}
// 此时的tempi,tempj是棋盘上的真实坐标
int a = Config.XO + Config.SIZE * tempi;
int b = Config.YO + Config.SIZE * tempj;
javax.swing.ImageIcon imgW = new javax.swing.ImageIcon(
this.getClass().getResource("w.png"));
g.drawImage(imgW.getImage(), a - Config.CHESS_SIZE
/ 2, b - Config.CHESS_SIZE / 2,
Config.CHESS_SIZE, Config.CHESS_SIZE, null);
ChessUI.chesses[tempi][tempj] = -1;
ChessUI.count = 0;
// 判断白棋是否已经赢啦
ch.check(tempi, tempj);
}
}
}
}
第四,以上提到了在下白字的时候在空格处找最大权值,要找最大值,先要对棋盘进行赋权值,赋权值的标准就是该处空格
附近妻子连起来的个数,可分为活四连,死四连....,根据自己设定的标准进行赋权值.
public int wScore(int cnt, int flag) {
int score = 0;
if (cnt >= 4 ) {//四连以上
score = 1000000;
} else if (cnt == 3) {//
if (flag == 2) {// 活三连
score = 210000;
} else if (flag == 1) {// 半活三连
score = 50000;
} else {// 死三连
score = 500;
}
} else if (cnt == 2) {//
if (flag == 2) {// 活二连
score = 55000;
} else if (flag == 1) {// 半活二连
score = 20000;
} else {// 死二连
score = 400;
}
} else if (cnt == 1) {
if (flag == 2) {// 活一连
score = 15000;
} else if (flag == 1) {// 单活一连
score = 10000;
} else {// 死一连
score = 300;
}
} else {// 空格
score = 400;
}
return score;
}
/**
* 根据连在一起黑棋子个数的多少给空点打分
*/
public int bScore(int cnt, int flag) {
int score = 0;
if (cnt >= 4 ) {// 四连以上
score = 900000;
} else if (cnt == 3) {//
if (flag == 2) {// 活三连
score = 200000;
System.out.println(flag);
} else if (flag == 1) {// 单活三连
score = 50000;
} else {// 死三连
score = 500;
}
} else if (cnt == 2) {
if (flag == 2) {// 活二连
score = 50000;
} else if (flag == 1) {// 单活二连
score = 20000;
} else {// 死二连
score = 400;
}
} else if (cnt == 1) {
if (flag == 2) {// 活一连
score = 15000;
} else if (flag == 1) {// 单活一连
score = 10000;
} else {// 死一连
score = 300;
}
} else {
score = 300;//空格
}
return score;
}
}
第五,以上提到找到棋盘空格附近棋子连起来的个数,那我们就要进行循环遍历查找空格附近八个方向同色棋子的个数(其中要分出来是活连还是死连,他们的区别就是活连最后的棋子后是两个空格,死连后面都是不同色的棋子,可以用一个flag标记).
/**
* 检查空点的各个方向上黑子的个数
*/
// 检查该空交点的横向有几颗黑棋连在一起
public int bCountRow(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
// 向右数
for (int i = x + 1; i < ChessUI.chesses.length; i++) {
if (ChessUI.chesses[i][y] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][y] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1; i >= 0; i--) {
if (ChessUI.chesses[i][y] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][y] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.bScore(cnt, flag);
return score ;
}
// 检查该空交点的竖直方向有几颗黑棋连在一起
public int bCountColumn(int x, int y) {
int[] count = new int[2];
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
count[0]=cnt;
count[1]=flag;
for (int j = y + 1; j < ChessUI.chesses.length; j++) {
if (ChessUI.chesses[x][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[x][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int j = y - 1; j >= 0; j--) {
if (ChessUI.chesses[x][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[x][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.bScore(cnt, flag);
return score ;
}
// 检查该空交点的右斜方向有几颗黑棋连在一起
public int bCountRU(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
for (int i = x + 1, j = y - 1; i < ChessUI.chesses.length && j >= 0; i++, j--) {
if (ChessUI.chesses[i][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1, j = y + 1; i >= 0 && j < ChessUI.chesses.length; i--, j++) {
if (ChessUI.chesses[i][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.bScore(cnt, flag);
return score ;
}
// 检查该空交点的左斜方向有几颗黑棋连在一起
public int bCountLU(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
for (int i = x + 1, j = y + 1; i < ChessUI.chesses.length
&& j < ChessUI.chesses.length; i++, j++) {
if (ChessUI.chesses[i][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
if (ChessUI.chesses[i][j] == 1)// 是否为黑棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.bScore(cnt, flag);
return score ;
}
/**
* 检查遍历到的空点的各个方向上白子的个数
*/
// 检查该空交点的横向有几颗白棋连在一起
public int wCountRow(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
// 向右数
for (int i = x + 1; i < ChessUI.chesses.length; i++) {
if (ChessUI.chesses[i][y] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][y] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1; i >= 0; i--) {
if (ChessUI.chesses[i][y] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][y] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.wScore(cnt, flag);
return score ;
}
// 检查该空交点的竖直方向有几颗白棋连在一起
public int wCountColumn(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
for (int j = y + 1; j < ChessUI.chesses.length; j++) {
if (ChessUI.chesses[x][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[x][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int j = y - 1; j >= 0; j--) {
if (ChessUI.chesses[x][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[x][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.wScore(cnt, flag);
return score ;
}
// 检查该空交点的右斜方向有几颗白棋连在一起
public int wCountRU(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
for (int i = x + 1, j = y - 1; i < ChessUI.chesses.length && j >= 0; i++, j--) {
if (ChessUI.chesses[i][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1, j = y + 1; i >= 0 && j < ChessUI.chesses.length; i--, j++) {
if (ChessUI.chesses[i][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.wScore(cnt, flag);
return score ;
}
// 检查该空交点的左斜方向有几颗白棋连在一起
public int wCountLU(int x, int y) {
int cnt = 0;// 计算一行中连在一起的棋子数
int flag = 0;
for (int i = x + 1, j = y + 1; i < ChessUI.chesses.length
&& j < ChessUI.chesses.length; i++, j++) {
if (ChessUI.chesses[i][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
if (ChessUI.chesses[i][j] == -1)// 如果是白棋
cnt++;
else if (ChessUI.chesses[i][j] == 0) {// 如果是空点
flag++;
break;
} else
break;
}
qz=new quanzhi(cnt, flag);
int score = qz.wScore(cnt, flag);
return score ;
}
}
第六 ,在做完下棋这一部最重要的步骤后我们还要进行一些其他的操作,例如要对棋盘上的棋子进行判断输赢的步骤,就是要盘算连起来的同色棋子是不是超过5个.大于或者等于就赢了.
/**
* 判断输赢的方法
*/
// 检查横向是否有五个连在一起的同种棋子
private int checkRow(int x, int y) {
int sum = 0;
for (int i = x + 1; i < Config.ROWS; i++) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[i][y])
sum++;
else
break;
}
for (int i = x; i >= 0; i--) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[i][y])
sum++;
else
break;
}
return sum;
}
// 检查横向是否有五个连在一起的同种棋子
private int checkColumn(int x, int y) {
int sum = 0;
for (int j = y + 1; j < Config.COLUMNS; j++) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[x][j])
sum++;
else
break;
}
for (int j = y; j >= 0; j--) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[x][j])
sum++;
else
break;
}
return sum;
}
// 检查右下方向是否有五个连在一起的同种棋子
private int checkRD(int x, int y) {
int sum = 0;
for (int i = x + 1, j = y + 1; i < Config.ROWS && j < Config.COLUMNS; i++, j++) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[i][j])
sum++;
else
break;
}
for (int i = x, j = y; i >= 0 && j >= 0; i--, j--) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[i][j])
sum++;
else
break;
}
return sum;
}
// 检查左下向是否有五个连在一起的同种棋子
private int checkLD(int x, int y) {
int sum = 0;
for (int i = x - 1, j = y + 1; i >= 0 && j < Config.COLUMNS; i--, j++) {
if (ChessUI.chesses[x][y] == ChessUI.chesses[i][j])
sum++;
else
break;
}
for (int i = x, j = y; i < Config.ROWS && j >= 0; i++, j--) {
if (ChessUI.chesses[x][y] ==ChessUI.chesses[i][j])
sum++;
else
break;
}
return sum;
}
//判断输赢
public void check(int x,int y){
if (checkLD(x, y) >= 5) {
if (ChessUI.count==1) {
DrawListener.canplay=false;
JOptionPane.showMessageDialog(null, "黑棋棋赢了");
} else {
JOptionPane.showMessageDialog(null, "白棋赢了");
}
}
if ( checkRD(x, y)>= 5) {
DrawListener.canplay=false;
if (ChessUI.count==1) {
JOptionPane.showMessageDialog(null, "黑棋棋赢了");
} else {
JOptionPane.showMessageDialog(null, "白棋赢了");
}
}
if (checkRow(x, y) >= 5) {
DrawListener.canplay=false;
if (ChessUI.count==1) {
JOptionPane.showMessageDialog(null, "黑棋棋赢了");
} else {
JOptionPane.showMessageDialog(null, "白棋赢了");
}
}
if (checkColumn(x, y)>= 5) {
DrawListener.canplay=false;
if (ChessUI.count==1) {
JOptionPane.showMessageDialog(null, "黑棋棋赢了");
} else {
JOptionPane.showMessageDialog(null, "白棋赢了");
}
第七,以上步骤后的基础肯定是要有一个棋盘窗口作为界面,所以还要建立一个棋盘界面,还要画好棋盘格子,就是一个
JFrame窗口,还要加上鼠标监听器.
public void init() {
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
this.setTitle("五子棋");
this.setSize(new Dimension(803, 630));
this.setLocation((width - 803) / 2, (height - 630) / 2);
this.setResizable(false);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(3);
this.setVisible(true);
g = this.getGraphics();
DrawListener dl = new DrawListener(g);
this.addMouseListener(dl);
}
// 重写paint方法
public void paint(Graphics g) {
//
super.paint(g);
// 在棋盘上绘制棋盘
javax.swing.ImageIcon img = new javax.swing.ImageIcon(this.getClass()
.getResource("ganzhe.jpg"));
g.drawImage(img.getImage(), 3, 25, 800, 600, null);
// drawchess(g);
drawChess(g);
}
public void drawchess(Graphics g) {
// 画横线
for (int i = 0; i < Config.ROWS; i++) {
g.setColor(Color.BLUE);
g.drawLine(Config.XO, Config.SIZE * i + Config.YO, Config.XO
+ (Config.COLUMNS - 1) * Config.SIZE, Config.SIZE * i
+ Config.YO);
}
// 画竖线
for (int j = 0; j < Config.COLUMNS; j++) {
g.setColor(Color.blue);
g.drawLine(Config.SIZE * j + Config.XO, Config.YO, Config.SIZE * j
+ Config.XO, Config.YO + (Config.ROWS - 1) * Config.SIZE);
}
}
第八,在做完以上步骤偶就可以下棋了,但是当我们在棋盘上下了一些棋子后,然后最小化后就会发现当我们再次打开时棋盘上的妻子会不见所以还要对棋盘上的棋子进行重绘操作,这是很重要的,也是很容易忽略的.
public void drawChess(Graphics g) {
javax.swing.ImageIcon imgB = new javax.swing.ImageIcon(this.getClass()
.getResource("b.png"));
javax.swing.ImageIcon imgW = new javax.swing.ImageIcon(this.getClass()
.getResource("w.png"));
for (int i = 0; i < Config.ROWS; i++) {
for (int j = 0; j < Config.COLUMNS; j++) {
int x = Config.XO + Config.SIZE * i;
int y = Config.YO + Config.SIZE * j;
if (chesses[i][j] != 0) {
if (chesses[i][j] == 1) {
g.drawImage(imgB.getImage(), x - Config.CHESS_SIZE / 2,
y - Config.CHESS_SIZE / 2, Config.CHESS_SIZE,
Config.CHESS_SIZE, null);
} else if (chesses[i][j] == -1) {
g.drawImage(imgW.getImage(), x - Config.CHESS_SIZE / 2,
y - Config.CHESS_SIZE / 2, Config.CHESS_SIZE,
Config.CHESS_SIZE, null);
}
}
第九,最后做的就是完善五子棋界面加上一些按钮功能,比如说认输,开始.......,这样一个屋子其游戏就做完了,如果思路清楚的话,一步步慢慢来其实五子棋并不难,主要难点就是设计好的五子棋算法,怎么复权制,还有就是在编写程序代码的时候,出现错误是很正常的,我们要做的就是慢慢调试,要细心,这就是我做完五子棋的体会和一些总结.
// 实现退出按钮的功能
if (e.getX() > 713 && e.getX() < 753 && e.getY() > 434
&& e.getY() < 476) {
int result = JOptionPane.showConfirmDialog(null, "你确定退出小鱼儿的五子棋吗?");
if (result == 0) {
System.exit(0);
}
}
// 实现关于按钮的功能
if (e.getX() > 245 && e.getX() < 320 && e.getY() > 540
&& e.getY() < 569) {
JOptionPane.showMessageDialog(null, "这个五子棋游戏是由余靖精心制作的,厉害吧!"
+ "五子棋是一种两人对弈的纯策略型棋类游戏");
}
// 实现认输按钮的功能
if (e.getX() > 607 && e.getX() < 700 && e.getY() > 544
&& e.getY() < 570) {
int result = JOptionPane.showConfirmDialog(null, "确定要认输吗?");
if (result == 0) {
canplay=false;
if (ChessUI.count == 0) {
JOptionPane.showMessageDialog(null, "黑方已经认输!游戏结束!");
} else {
JOptionPane.showMessageDialog(null, "白方已经认输!游戏结束!");
}
}
}
// 实现设置按钮功能
if (e.getX() > 491 && e.getX() < 564 && e.getY() > 542
&& e.getY() < 570) {
JOptionPane.showMessageDialog(null, "设置按钮的功能暂时还没设置好!呵呵,正在努力中!!");
}
// 实现开始游戏按钮功能
if (e.getX() > 365 && e.getX() < 449 && e.getY() > 540
&& e.getY() < 570) {
int result = JOptionPane.showConfirmDialog(null, "确定要重新开始游戏吗?");
if (result == 0) {
canplay=true;
for (int a = 0; a < Config.COLUMNS; a++) {
for (int b = 0; b < Config.ROWS; b++) {
ChessUI.chesses[a][b] = 0;
}
}
ChessUI.chessframe.repaint();
}
}
// 实现重玩功能
if (e.getX() > 717 && e.getX() < 755 && e.getY() > 299
&& e.getY() < 338) {
int result = JOptionPane.showConfirmDialog(null, "确定要重玩五子棋游戏吗?");
if (result == 0) {
canplay=true;
for (int a = 0; a < Config.COLUMNS; a++) {
for (int b = 0; b < Config.ROWS; b++) {
ChessUI.chesses[a][b] = 0;
}
}
ChessUI.chessframe.repaint();
}
JOptionPane.showMessageDialog(null, "再接再厉!!!呵呵");
}
// 实现悔棋按钮功能
if (e.getX() > 710 && e.getX() < 763 && e.getY() > 380
&& e.getY() < 413) {
int result = JOptionPane.showConfirmDialog(null,
"确认要悔棋吗?悔棋是不道德的!!!");
if (result == 0) {
JOptionPane.showMessageDialog(null, "其实悔棋这个按钮的功能我还没实现,正在考虑中");
}
其实我这个五子棋还加上了一个登录界面,要先通过登录才能进入游戏界面,
登录界面见截图:
上一篇: NT完全入侵教程(新手篇)
下一篇: 网页打开后自动执行木马