1.五子棋功能
1.黑白轮流下棋 – 逻辑控制
2.下棋的范围 – 限制,棋子不能被覆盖
3.棋子最好下在网格中心
4.悔棋
5.悔棋双方计数功能
6.判断输赢
7.存读档
8.录制回放
9.AI下棋算法
2.项目开发流程
1.设计棋盘
1.棋盘画在窗体上,15*15网格,绘制直线画出。
2.SIZE:网格间距大小,Row,Column行列值,X,Y左上角的坐标。
3.拖动窗体不会消失,在重绘函数中画出(继承JFrame 重写paint方法,窗体的paint方法 – 在窗体改变大小初始化时自动调用刷新。
public void paint(Graphics g){ //重绘传递画笔参数,可用
super.paint(g); //注意 必须先调用父类绘制窗体
//绘制图片
g.drawImage(image, X, Y,COLUMN*SIZE, ROW*SIZE, null);
this.setIconImage(new ImageIcon("Image//login.png").getImage()); //给窗体加图标
for(int i=0; i<=ROW; i++){
g.drawLine(X+SIZE*i, Y, X+SIZE*i, Y+ROW*SIZE);
}
for(int i=0; i<=COLUMN; i++){ //重回功能里面写的函数,拖动窗体不会消失
g.drawLine(X, Y+SIZE*i, X+COLUMN*SIZE, Y+SIZE*i);
}
2.黑白轮流下棋
1.每次点击可以绘制一个实心的圆,g.fillOval();参数是圆的外切矩形的左上角 宽 高
–在窗体上添加鼠标监听器: 鼠标拖动移动监听器
@Override
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if(x<X||x>X+COLUMN*SIZE||y<Y||y>Y+ROW*SIZE){ //限制下棋范围
return;
}
ChessX = x;
ChessY = y;
ChessX = (ChessX-X+SIZE/2)/SIZE; //找出该坐标位于第几格,为X轴坐标
ChessY = (ChessY-Y+SIZE/2)/SIZE;
if(chessrecord[ChessX][ChessY] != 0) //当前棋盘位置有棋子,无法下棋
return;
// e.getClickCount(); 获取鼠标点击次数
if(flag == true) { //设置boolean变量改变状态
g.setColor(Color.BLACK);
flag = false;
countblack++;
g.fillOval(ChessX*SIZE-SIZE/2+X, ChessY*SIZE-SIZE/2+Y, SIZE, SIZE); //还原坐标
chesscolor = 1;
chessrecord[ChessX][ChessY] = chesscolor; //棋盘棋子(X,Y)坐标缩小为格数
//创建棋子对象,棋子对象坐标,个数,颜色,保存下来
index++; //-1++=0; 此时下标0中存棋子
Chess chess = new Chess(index, chesscolor, ChessX, ChessY);
// System.out.println("ChesX"+x1+"ChessY"+y1+"index"+index);
chesssaving[index] = chess; //将棋子对象存储到棋子保存数组中,index++
// gobangjf.repaint();
}
else{
g.setColor(Color.white);
flag = true;
countwhite++;
g.fillOval(ChessX*SIZE-SIZE/2+X, ChessY*SIZE-SIZE/2+Y, SIZE, SIZE); //还原坐标
chesscolor = 2;
chessrecord[ChessX][ChessY] = chesscolor; //X轴坐标为棋盘列数,Y轴坐标为棋盘行数
// gobangjf.repaint();
//创建棋子对象,棋子对象坐标,个数,颜色,保存下来
index++;
Chess chess = new Chess(index, chesscolor, ChessX, ChessY);
chesssaving[index] = chess;
}
}
3.重绘棋子
保证窗体大小改变时,棋子会消失,因此需要重绘棋子,我们需要保存棋子的数据 :行列值,只有黑白之分,存在矩阵中,依据棋盘格子交叉数生成一个二维数组。
int[][] chessrecord = new int[ROW+1][CLOUM +1]; //存储棋子
chesscolor 0:空 1:黑 2:白
// 初始化数组
int[][] chessrecord = new int[ROW+1][CLOUM +1];
// 每下一颗棋子 就存入
chesscolor = 1 或者 2
chessrecord[chessX][chessY]=chesscolor;
int[][] chessrecord = gl.getchessrecord(); //重绘棋子
// System.out.println(chessrecord.toString()); 非list接口定义的数组,无tostring功能
for(int i=0; i<chessrecord.length; i++){ //棋盘记录数组行为X轴Y轴 行数,列为列数
for(int j=0; j<chessrecord[i].length; j++){
//X轴Y轴行列数转换为数组坐标,转置
// System.out.print(chessrecord[j][i]+ " "); //println每输出一次换一次行,打开UI即调用重绘函数
if(chessrecord[i][j]==1){
g.setColor(Color.BLACK);
g.fillOval(i*SIZE-SIZE/2+X, j*SIZE-SIZE/2+Y, SIZE, SIZE); //依据当前棋盘记录中的行列值重绘
}
else if(chessrecord[i][j]==2){
g.setColor(Color.WHITE);
g.fillOval(i*SIZE-SIZE/2+X, j*SIZE-SIZE/2+Y, SIZE, SIZE);
}
}
// System.out.println(); //sout+Enter打印语句快捷键, println为换行语句
}
4.悔棋
存储棋子为棋子对象: 可用于悔棋,存档和读档,根据棋子顺序来存储棋子对象形成一个顺序数组。
Chess[] chesssaving = new Chess[(ROW+1)*(COLUMN+1)]; //棋盘保存数组保存棋子
悔棋实现方法:
1.取出当前的栈顶(最后存入的这颗棋子) 棋子对象
2.根据棋子的行列值 去 到 chessrecord中删除
3.再将当前的棋子一维数组中棋子对象删除
if(text.equals("悔棋")) { //悔棋按钮必须传入窗体监听器
//chesssaving下标首先为0
if(index<0){ //下标小于0,返回
return;
}
Chess curchess = chesssaving[index]; //取出最后一颗棋子对象,下标为index
int curChessX = curchess.ChessX;
int curChessY = curchess.ChessY;
chessrecord[curChessX][curChessY] = 0; //在棋盘记录数组中重绘棋子,当前位置棋子为空
chesssaving[index] =null; //删除棋盘保存数组悔棋对象
index--; //新数组棋子个数减1
gobangjf.repaint();
}
5.判断输赢
规则:在横向,纵向,左斜,右斜四个方向找到大于或等于同色五子连棋
实现方法:每次下棋从当前下的这个棋子向八个小方向遍历
横向(向左 与 向右)
纵向(向上 与 向下)
右斜(左下方(x-- y++) 与 右上方(x++ y–))
左斜 (右下方(x++ y++) 与 左上方(x-- y–))
统计4个大方向上的连子数,若存在连子数>=5的情况,则获胜,需要最新的二位数组,需要当前最后下的棋子的行列值。
//判断输赢
if(IsWin.IsWin(chessrecord, ChessX,ChessY))
JOptionPane.showMessageDialog(null, "执白棋者获得胜利");
if(IsWin.IsWin(chessrecord, ChessX,ChessY))
JOptionPane.showMessageDialog(null, "执黑棋者获得胜利");
此时坐标已经存入棋子记录数组
//判断此时棋子上下左右斜上斜下,此时X,Y每按下一次按钮改变一次,函数调用一次,但是数组是总体的数组
public static boolean IsWin(int[][] chessrecord, int ChessX, int ChessY){ //静态函数可以用类名直接调用
if(checkrow(chessrecord, ChessX, ChessY)>=5|| checkcolumn(chessrecord, ChessX, ChessY)>=5|| checkedgeleft(chessrecord, ChessX, ChessY)>=5||checkedgeright(chessrecord, ChessX, ChessY)>=5)
return true;
else
return false;
}
public static int checkrow (int[][] chessrecord, int x, int y) { //传入当前棋子坐标
int count = 1; //局部变量,不然一直加
for (int i = x+1; i < ROW + 1; i++) { //判断纵轴棋子下侧是否赢
if (chessrecord[i][y] == chessrecord[x][y]) {
count++;
} else {
break; //无连续5个棋子,结束循环
}
}
for (int i = x-1; i >= 0; i--) { //判断纵轴棋子上侧是否赢
if (chessrecord[i][y] == chessrecord[x][y]) {
count++;
} else { //无5个连续的棋子,结束循环
break;
}
}
return count;
// System.out.println("row"+count); //多输出坐标判断个数
}
public static int checkcolumn(int[][] chessrecord, int x, int y){
int count = 1;
for(int j=y+1; j<COLUMN+1; j++) { //判断横轴棋子右侧是否赢
if (chessrecord[x][j] == chessrecord[x][y]) {
count++;
} else {
break;
}
}
for(int j=y-1; j>=0; j--) { //判断横轴棋子左侧是否赢
if (chessrecord[x][j] == chessrecord[x][y]) {
count++;
}
else {
break;
}
}
// System.out.println("column"+count); //多输出坐标判断个数
return count;
}
public static int checkedgeleft(int[][] chessrecord, int x, int y){ //判断棋子左斜轴是否有连续5个棋子
int count = 1;
int i = x;
int j = y;
while (i<ROW && j<COLUMN) {
if (chessrecord[i][j] == chessrecord[i+1][j+1]){
count++;
i++;
j++;
}
else {
break;
}
}
i = x;
j = y; //重置x,y坐标,判断下
while(i>0 && j>0){
if(chessrecord[i][j] == chessrecord[i-1][j-1]){
count++;
i--;
j--;
}
else {
break;
}
}
// System.out.println("count"+count); //输出连续棋子的个数
return count;
}
public static int checkedgeright(int[][] chessrecord, int x, int y){ //判断棋子右斜轴是否有连续5个棋子
int count = 1;
int i = x;
int j = y;
while (i>0 && j<COLUMN) {
if (chessrecord[i][j] == chessrecord[i-1][j+1]){
count++;
i--;
j++;
}
else {
break;
}
}
i = x;
j = y;
while(i<ROW && j>0){
if(chessrecord[i][j] == chessrecord[i+1][j-1]){
count++;
i++;
j--;
}
else {
break;
}
}
// System.out.println("右"+count); //输出右斜轴个数
// if (count >= 5) {
// if (chessrecord[x][y] == 1) {
// JOptionPane.showMessageDialog(null, "执黑棋者获得胜利");
// } else if (chessrecord[x][y] == 2) {
// JOptionPane.showMessageDialog(null, "执白棋者获得胜利");
// }
return count;
}
}
6.人机对战
package com.zq0805;
import java.util.HashMap;
public class hashmap {
HashMap<String, Integer> hash = new HashMap<>(); //创建hashmap对象,记录连子情况和得分
public hashmap(){
// 两端通畅为活连;
// hash = hashmap; //将hashmap地址赋给hash
hash.put("010", 10); //黑棋活一连 10分 这个位置10分 黑棋下或者白棋下都好
hash.put("0110", 50); //活二连 50分
hash.put("01110", 500); //活三连 500分
hash.put("011110", 2000); //活四连 2000分
hash.put("020", 10); //白棋活一连 10分
hash.put("0220", 50); //白棋活二连 50分
hash.put("02220", 500); //白棋活三连 500分
hash.put("022220", 2000); //白棋活四连 2000分
// 一端通畅为死连
//黑棋死一连5分
hash.put("01", 5);
hash.put("012", 5);
//黑棋死二连25分
hash.put("011", 25);
hash.put("0112", 25);
//黑棋死三连250分
hash.put("0111", 250);
hash.put("01112", 250);
//黑棋死四连1000分
hash.put("01111", 1000);
hash.put("011112", 1000);
//白棋死一连5分
hash.put("02", 5);
hash.put("021", 5);
//白棋死二连25分
hash.put("022", 25);
hash.put("0221", 25);
//白棋死三连250分
hash.put("0222", 250);
hash.put("02221", 250);
//白棋死四连1000分
hash.put("02222", 1000);
hash.put("022221", 1000);
// System.out.println("hash.get(0222)"+hash.get("0222"));
}
}
package com.zq0805;
public class HashMapChess implements GoBangConfig{
static int[][] chessweight = new int[ROW+1][COLUMN+1];
static hashmap Hashmap = new hashmap();
//测试数据
// static int[][] test = {{0, 0, 1, 2, 0},
// {1, 2, 2, 1, 0},
// {2, 1, 0, 0 ,1},
// {1, 2, 0, 0, 1},
// {2, 2, 1, 0, 0}};
public static int[][] intelligence(int[][] chessrecord) { //主函数中变量为静态变量
//从棋盘记录数组左上角开始遍历找到权值最大的位置下棋
for (int i = 0; i < chessrecord.length; i++) {
for (int j = 0; j < chessrecord[i].length; j++) {
// System.out.print(chessrecord[i][j]+" ");
int weight = 0;
if (chessrecord[i][j] == 0) { //表示当前位置无棋子,可以统计,从当前位置往右统计
int chessnum = chessrecord[i][j]; //记录当前棋子
//向右遍历找棋子
if (j < chessrecord[i].length - 1 && chessnum != chessrecord[i][j + 1]) { //确保当前空位置右边有棋子,缺陷
int chess0 = chessrecord[i][j + 1]; //记录当前空位置右边棋子
String codeStr = "0";
for (int k = j + 1; k < chessrecord[i].length; k++) {
if (chessrecord[i][k] == chess0) {
codeStr = codeStr + chessrecord[i][k]; //字符串能直接为 字符串+数字
}
else {
codeStr = codeStr + chessrecord[i][k];
break;
}
}
// System.out.println(i+" "+j+"codeStr右"+codeStr);
if(Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向左遍历找棋子
if (j > 0 && chessnum != chessrecord[i][j - 1]) { //确保当前空位置左边有棋子,缺陷
int chess0 = chessrecord[i][j - 1]; //记录当前空位置左边棋子
String codeStr = "0";
for (int k = j - 1; k >= 0; k--) {
if (chessrecord[i][k] == chess0) {
codeStr = codeStr + chessrecord[i][k]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[i][k]; //codeStr添加遍历到的不等于左边的棋子,为0或2
break;
}
}
// System.out.println(i+" "+j+"codeStr左"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向上遍历找棋子
if (i > 0 && chessnum != chessrecord[i - 1][j]) { //确保当前空位置上边有棋子,缺陷
int chess0 = chessrecord[i - 1][j]; //记录当前空位置上边棋子
String codeStr = "0";
for (int k = i - 1; k >= 0; k--) {
if (chessrecord[k][j] == chess0) {
codeStr = codeStr + chessrecord[k][j]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[k][j];
break;
}
}
// System.out.println(i+" "+j+"codeStr上"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向下遍历找棋子
if (i < chessrecord.length - 1 && chessnum != chessrecord[i + 1][j]) { //确保当前空位置上边有棋子,缺陷
int chess0 = chessrecord[i + 1][j]; //记录当前空位置上边棋子
String codeStr = "0";
for (int k = i + 1; k < chessrecord.length; k++) {
if (chessrecord[k][j] == chess0) {
codeStr = codeStr + chessrecord[k][j]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[k][j];
break;
}
}
// System.out.println(i+" "+j+"codeStr下"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向左上遍历找棋子
if (i > 0 && j >0 && chessnum != chessrecord[i - 1][j-1]) { //确保当前空位置左上有棋子,缺陷
int chess0 = chessrecord[i - 1][j-1]; //记录当前空位置左上边棋子
String codeStr = "0";
for (int m = i - 1, n = j-1; m >= 0 && n >=0; m--, n--) { //for循环定义两个参数
if (chessrecord[m][n] == chess0) {
codeStr = codeStr + chessrecord[m][n]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[m][n];
break;
}
}
// System.out.println(i+" "+j+"codeStr上"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向右上遍历棋子
if (i > 0 && j < chessrecord[i].length - 1 && chessnum != chessrecord[i - 1][j + 1]) { //确保当前空位置左上有棋子,缺陷
int chess0 = chessrecord[i - 1][j+1]; //记录当前空位置左上边棋子
String codeStr = "0";
for (int m = i - 1, n = j + 1; m >= 0 && n < chessrecord[i].length; m--, n++) { //for循环定义两个参数
if (chessrecord[m][n] == chess0) {
codeStr = codeStr + chessrecord[m][n]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[m][n];
break;
}
}
// System.out.println(i+" "+j+"codeStr上"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向左下遍历棋子
if (i < chessrecord.length - 1 && j > 0 && chessnum != chessrecord[i + 1][j-1]) { //确保当前空位置左上有棋子,缺陷
int chess0 = chessrecord[i + 1][j-1]; //记录当前空位置左上边棋子
String codeStr = "0";
for (int m = i + 1, n = j-1; m <chessrecord.length && n >=0; m++, n--) { //for循环定义两个参数
if (chessrecord[m][n] == chess0) {
codeStr = codeStr + chessrecord[m][n]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[m][n];
break;
}
}
// System.out.println(i+" "+j+"codeStr上"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
//向右下遍历棋子
if (i < chessrecord.length-1 && j <chessrecord[i].length-1 && chessnum != chessrecord[i + 1][j + 1]) { //确保当前空位置左上有棋子,缺陷
int chess0 = chessrecord[i + 1][j+1]; //记录当前空位置右下边棋子
String codeStr = "0";
for (int m = i + 1, n = j + 1; m < chessrecord.length && n < chessrecord[i].length; m++, n++) { //for循环定义两个参数
if (chessrecord[m][n] == chess0) {
codeStr = codeStr + chessrecord[m][n]; //字符串能直接为 字符串+数字
} else {
codeStr = codeStr + chessrecord[m][n];
break;
}
}
// System.out.println(i+" "+j+"codeStr上"+codeStr);
if (Hashmap.hash.containsKey(codeStr)) {
weight += Hashmap.hash.get(codeStr);
}
}
}
chessweight[i][j] = weight;
// System.out.print("weight"+weight+""); //+空格
}
// System.out.println();
}
return chessweight;
}
}
7.存读档
if(text.equals("存档")) { //将当前所有棋子保存下来,已经有两个数组,一个保存坐标数组,绘制棋子,一个保存棋子对象,有坐标
int count = 0; //记录当前棋谱的棋子个数
for (int i = 0; i < chessrecord.length; i++) { //数组不能直接复制,不然传地址
for (int j = 0; j < chessrecord[i].length; j++) {
chessstorage[i][j] = chessrecord[i][j];
if(chessrecord[i][j] != 0){
count++; //所有棋子的个数
}
}
}
//存档不需要改变index,此时index = count-1
for(int k=0; k<=count-1; k++){
chesssave[k] = chesssaving[k]; //不可 chesssave指向chesssaving地址
}
}
if(text.equals("读档")){
int count = 0;
for (int i = 0; i < chessstorage.length; i++) { //数组不能直接复制,不然传地址
for (int j = 0; j < chessstorage[i].length; j++) {
chessrecord[i][j] = chessstorage[i][j];
if(chessrecord[i][j] != 0){
count++; //棋子保存数组中棋子的个数,此时要改变棋子保存数组的索引
}
}
for(int k=0; k<chesssaving.length; k++){
chesssaving[k] = chesssave[k]; //不可 chesssave指向chesssaving地址
}
//读档后要改变
}
index = count-1; //当前棋子个数下标为棋子个数减一
gobangjf.repaint();
}
8.录制回放
private static final Image image1 = new ImageIcon("D:\\IDEA 2019\\test\\Image\\chess111.jpg").getImage(); //Iamge接口
private static final Image image2 = new ImageIcon("D:\\IDEA 2019\\test\\Image\\222.jpeg").getImage();
public void paint() { //重绘传递画笔参数,可用
//绘制图片
g.drawImage(image2, 0, 0, 1000,1000,null);
g.drawImage(image1, X, Y,COLUMN*SIZE, ROW*SIZE, null);
g.setColor(Color.BLACK);
for (int i = 0; i <= ROW; i++) {
g.drawLine(X + SIZE * i, Y, X + SIZE * i, Y + ROW * SIZE);
}
//重绘按钮
for (int i = 0; i <= COLUMN; i++) { //重回功能里面写的函数,拖动窗体不会消失
g.drawLine(X, Y + SIZE * i, X + COLUMN * SIZE, Y + SIZE * i);
}
for (int k = 0; k <= index; k++){
int i = chesssaving[k].ChessX;
int j = chesssaving[k].ChessY;
if(chesssaving[k].chesscolor == 1) {
g.setColor(Color.BLACK);
g.fillOval(i * SIZE - SIZE / 2 + X, j * SIZE - SIZE / 2 + Y, SIZE, SIZE);
} else if (chesssaving[k].chesscolor == 2) {
g.setColor(Color.WHITE);
g.fillOval(i * SIZE - SIZE / 2 + X, j * SIZE - SIZE / 2 + Y, SIZE, SIZE);
}
//代码延时
try {
Thread.sleep(500);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
//录制回放结束后,刷新窗体,绘制的图片消失
}
}
if(text.equals("回放")){
//在图片上重新画棋盘,不需要初始化数组
//repaint函数在线程最后调用
paint();
}