V1.0版本搭建了框架,这次将重点实现落子与悔棋功能
-落子
-落子的逻辑
因为当时我们使用的是面板来放置棋盘,所以棋盘以及落子的坐标都以面板的坐标为基准。之前设计的棋盘为16*16格子,所以棋盘大小为17*17*SIZE,即左右分别多出半个SIZE大小,毕竟棋子是圆的,中心在直线交汇处,最边上的棋子有半颗棋子在外面。
当我们点击棋盘上的某个位置时,应当根据坐标进行一系列换算,得出该坐标对应棋盘的横纵坐标,再根据横纵坐标画出棋子。
可以看到下图,对于落子的计算,我们应该将棋盘划分为如图的区域,点击对应区域,便在对应区域的中心点,即坐标点处画一个圆。算法放在代码中,自行理解,重点就是利用整除,再进行微调,X、SIZE等都在GoData里。
int X = 70, Y = 70, SIZE = 40, COL = 15, ROW = 15;
换算为棋盘坐标
public static int calC(int x) {
int c = (x - X + SIZE / 2) / SIZE;
return c;
}
public static int calR(int y) {
int r = (y - Y + SIZE / 2) / SIZE;
return r;
}
换算为面板坐标
public static int calTX(int c) {
int x = c * SIZE + X - SIZE / 2;
return x;
}
public static int calTY(int r) {
int y = r * SIZE + Y - SIZE / 2;
return y;
}
-棋子的存储
直接创建一个Chess类,存储棋子的颜色,坐标以及落子顺序。
在每次绘制棋子时,都要同步将棋子存储起来
考虑到后续功能,我们创建了一个动态数组 ArrayList<Chess> chesses 来存储顺序,一个二维数组 Int[][] chessList 来存储坐标和实现悔棋
public class Chess {
int chessFlag;
int x,y,index;
public Chess(int x,int y, int chessFlag ,int i){
this.x = x;
this.y = y;
this.chessFlag = chessFlag;
index = i;
}
}
int chessFlag = 1;
int index = 0;
ArrayList<Chess> chesses = new ArrayList<>();
int[][] chessList = new int[ROW + 1][COL + 1];
-棋子的绘制
利用Java swing 自带的填充椭圆的方法,将参数设置成SIZE即可,在绘制前改变颜色,即可绘制不同颜色的棋子
g.setColor(Color.white);
g.fillOval(GoData.calTX(maxC), GoData.calTY(maxR), SIZE, SIZE);
-结合按钮,实现完整功能
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
System.out.println(x + "," + y);
if(isWin){
return;
}
if (cl.equals("开始游戏") && !isAI || cl.equals("悔棋") && !isAI) {
if (GoData.peopleSet1(x, y, chessList, chessFlag, index, chesses, g)) {
if (chessFlag == 1) { //棋子换色
chessFlag = 2;
} else if (chessFlag == 2) {
chessFlag = 1;
}
}
}
这部分代码再GoData接口中
public static boolean peopleSet1(int x, int y, int[][] chessList, int chessFlag, int index, ArrayList<Chess> chesses, Graphics g) {
if (!GoData.isChessPad(x, y)) { //确保棋子不出棋盘
return false;
}
int c = GoData.calC(x); //c,d为棋子在棋盘上的格子坐标
int d = GoData.calR(y);
if (chessList[c][d] != 0) { //判断棋子是否重叠
return false;
}
chessList[c][d] = chessFlag; //棋子坐标和颜色
Chess chess = new Chess(c, d, chessFlag, index++);
chesses.add(chess); //棋子顺序
int chessX = GoData.calTX(c); //格子坐标换算窗口坐标
int chessY = GoData.calTY(d);
if (chessFlag == 1) { //棋子换色
g.setColor(Color.black);
} else if (chessFlag == 2) {
g.setColor(Color.white);
}
g.fillOval(chessX, chessY, SIZE, SIZE);
return true;
}
-悔棋
-悔棋的逻辑
人人对战时,悔棋只回退一步,所以只需找到chesses数组里的最后一颗棋子,获得其坐标,颜色,然后将其删除,再用坐标将二维数组里的对应位置改回空,即0。最后还要按照棋子顺序将所有棋子以及棋盘重绘一次,最后一颗棋子覆盖掉。
case "悔棋" -> {
if (chesses.isEmpty()) {
return;
}
if (isWin){
isWin = false;
}
if (chesses.get(chesses.size() - 1).chessFlag == 1) { //确保悔棋后棋子颜色正确
chessFlag = 1;
} else if (chesses.get(chesses.size() - 1).chessFlag == 2) {
chessFlag = 2;
}
chessList[chesses.get(chesses.size() - 1).x][chesses.get(chesses.size() - 1).y] = 0;
chesses.remove(chesses.size() - 1);
GoData.drawGamePad(g, chessList);
}
以下代码再GoData中
public static void drawGamePad(Graphics g, int[][] cl) {
drawChessPad(g);
drawChesses(g, cl);
}
public static void drawChessPad(Graphics g) { //绘制棋盘
g.setColor(Color.orange);
g.fillRect(X - 20, Y - 20, SIZE * (COL + 1), SIZE * (ROW + 1));
g.setColor(Color.black);
for (int i = 0; i <= 15; i++) {
g.drawLine(X, Y + (i * SIZE), X + (ROW * SIZE), Y + (i * SIZE));
g.drawLine(X + (i * SIZE), Y, X + (i * SIZE), Y + (COL * SIZE));
}
}
public static void drawChesses(Graphics g, int[][] cl) {
for (int i = 0; i <= ROW; i++) {
for (int j = 0; j <= COL; j++) {
if (cl[i][j] == 1) {
g.setColor(Color.black);
} else if (cl[i][j] == 2) {
g.setColor(Color.white);
} else {
continue;
}
int chessX = i * SIZE + X - SIZE / 2;
int chessY = j * SIZE + Y - SIZE / 2;
g.fillOval(chessX, chessY, SIZE, SIZE);
}
}
}