先下的棋子先进数组,后下的棋子后进数组。最后下的棋子在数组的最后一个。
我们希望数组中的每个元素都记录了棋子的坐标信息,这样才能实现悔棋功能。怎么记录坐标信息呢?有两种方法:
class Chess {
int x, y;
char player
Chess(int x, int y, int turn) {
this.x = x;
this.y = y;
player = turn % 2 == 0 ? 'w' : 'b';
}
}
//...
Vector<Chess> history = new Vector<>();
history.add(new Chess(x, y, turn)); //记录
//...
//移除最后一个元素
//remove 方法会返回被移除的对象
Chess t = history.remove(history.lastElement());
由于种种原因,我最后决定不用 short 而是用 int 来存储,代码实现如下:
int x = 横坐标;
int y = 纵坐标;
Integer chess = ((turn % 2) << 8) | (x << 4) | y; //编码
history.add(chess); //存入
//...
chess = history.remove(history.lastElement()); //取出
char player = chess >> 8 == 0 ? 'w' : 'b'; //解码
int x = chess >> 4;
int y = chess & 0xF;
我们希望画出来的圆都在网格线的交叉点上;下棋的时候不想先瞄准再去点击:
class Frame {
//...
//注 1
static final int WIDTH = 600, //窗体宽度
HEIGHT = 600, //窗体高度
LINE_GAP = 50, //网格线间距
FIRST_HORIZONTAL_LINE = 80, //第一水平线纵坐标
FIRST_VERTICAL_LINE = 80; //第一竖直线横坐标
}
//...
int mouseX = 鼠标横坐标;
int mouseY = 鼠标纵坐标;
int realX = -1, realY = -1; //棋子在第 realY 行,第 realX 列。
for (int i = 0; i < 15; i++) {
//注 2
int v = Frame.FIRST_VERTICAL_LINE + i * Frame.LINE_GAP;
if (Math.abs(mouseX - t) < Frame.LINE_GAP * 0.3) {
realX = i;
break;
}
}
注 1:
将变量声明为 final 可以防止它的值被改变。将变量声明为 static 可以通过类名直接访问之。我们常常把常用的常量声明为 static final 变量。 常常把 static final 变量名写为全大写、并用下划线来作为单词的分隔,这样我们就能一眼看出来哪个是 static final 变量。
注 2:
一个数学问题。v 表示是第 i 条竖直线的横坐标。如果鼠标点击的位置与某条线的距离在一定范围内,那么就可以认为棋子被放在这条线上。如果鼠标点在方格的中间位置,那么将没有符合条件的竖直线,realX 的值会保持初始的 -1 。如果 realX 是 -1,那么我们就知道,鼠标没有点在靠近线的地方。对 realY 也是相似的处理方法。
这里我选用的范围是 0.3 倍的网格线间距。应该选用合适的范围,不然下棋的时候会很难受。
我们会重复地使用这段代码,所以最好把它定义为方法:
static final int RADIUS = 圆的半径;
void drawChess(int realX, int realY, Graphics graphics) {
//注 3
int x = Frame.FIRST_VERTICAL_LINE + realX * Frame.LINE_GAP - RADIUS,
y = Frame.FIRST_HORIZONTAL_LINE + realY * Frame.LINE_GAP - RADIUS;
graphics.setColor(turn % 2 == 1 ? Color.BLACK : Color.WHITE);
graphics.fillOval(x, y, RADIUS, RADIUS);
}
有一个很蠢但是很有效的办法:
每下一个棋子,就以这个棋子作为起点,向八个方向看。如果有连续的五个棋子颜色相 同,那么对局就要结束了。
int x = 棋子的横坐标;
int y = 棋子的纵坐标;
char p = 棋子的颜色 ['w' 或者 'b'];
int counter = 1; //记录有多少个相同颜色的棋子
while (true) {
if (counter == 5) {
gameOver();
}
if (x + 1 > 14 || x + 1 < 0 || y + 1 > 14 || y + 1 < 0) {
//如果下一个位置超出了棋盘,终止循环
break;
}
x += 1;
y += 1;
if (array[x][y] != p) {
//如果棋子颜色不同,终止循环
break;
}
counter++;
}
对八个方向的搜索的代码类似。可以只用一个方法来实现:
boolean five(int x, int y, char player, int dx, int dy) {
//注 5
int counter = 1; //记录有多少个相同颜色的棋子
while (counter++ < 5) {
//如果 counter == 5,终止循环,并返回 true
if (x + dx > 14 || x + dx < 0 || y + dy > 14 || y + dy < 0) {
//如果下一个位置超出了棋盘,返回 false
return false;
}
x += dx;
y += dy;
if (array[x][y] != player) {
//如果棋子颜色不同,返回 false
return false;
}
}
return true;
}
然后,我们通过下面的代码,从八个不同的方向进行搜索:
if(five(x, y, player, -1, -1) || five(x, y, player, -1, 0)
|| five(x, y, player, -1, 1) || five(x, y, player, 0, -1)
|| five(x, y, player, 0, 1) || five(x, y, player, 1, -1)
|| five(x, y, player, 1, 0) || five(x, y, player, 1, 1)) {
gameOver();
}
也可以改用下面的代码段。下面的代码段与上面的代码段效果相同,不过下面的代码好看一点(大概):
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (i == 0 && j == 0) {
continue;
}
if (five(x, y, player, i, j)) {
gameOver();
break;
}
}
}