计算机能够判断步骤是否合法后,就可以让计算机走出合法的步骤了。通常使用MiniMax算法来使计算机做出最优选择,在运用该算法之前,首先让计算机能走出合法的步骤,这也是下一步运用MiniMax算法的基础。这里以电脑走黑棋为例进行说明。
“Network”总共有两种走棋方式,一种是“add"一种是”step",这在(一)中已经说明过,当棋盘上一方的棋子没有达到10个时走的是"add",达到10个后走的是"step"。因此这就需要棋盘对象要记录棋盘上双方棋子数,具体代码见(二)中的代码。本实现采用MachinePlayer类来代表计算机。
(1)首先计算机判断棋盘上的黑棋棋子数,如果小于10,则走"add",如果大于10,则走"step"。
(2)由于在"step"的时候需要选择一个棋子来进行移动,因此需要知道棋盘上双方棋子的坐标,这里采用NodeRecord[]数组来存储双方各自的棋子坐标。
(3)首先判断是”add"还是“step”,如果是“add",则首先判断黑方的老家区域是否有棋子,因为老家区域的棋子是构成"Network"的必要条件。如果没有则在老家区域放上棋子,在放之前要首先检查是否是合法步骤。
(4)在"add"情况下,若上下两个老家区域都有棋子,则选择棋盘内部的节点放置棋子,这里先暂时采用扫描坐标的方式进行放置(刚开始采用的是取随机数的方式,但是测试时发现这样效率很低,因为棋盘上坐标比较多,并且每次还需要判断是否合法,因此经常陷入无限循环的状态),后面会采用Minimax算法对这里进行改进。放置完棋子后,注意更新相关的域,包括棋盘上的黑棋子树以及NodeRecord数组。
(5)当10个黑棋子以放满,这时进入”step"阶段。由于NodeRecord中记录了所有黑棋子的坐标,因此通过选择一个0到9的随机数来决定移动哪个棋子。移动后需要将原有单元格清空,并且保留原有坐标(这是为了判断移动后的坐标做准备,首先清空是为了不影响原有坐标不影响新坐标的合法性判断,其次保留原有坐标是为了判断新坐标是否和老坐标相同,如果相同说明并没有走,因为这里暂时是以扫描的方式判断新坐标的,因此这步和老坐标的比较必不可少)。走完后,更新相关的域,包括NodeRecord,棋盘的状态。
具体代码如下:
/* MachinePlayer.java */
package player;
import java.util.Random;
import utils.Evaluation;
import gameboard.GameBoard;
import gameboard.NodeRecord;
/**
* An implementation of an automatic Network player. Keeps track of moves
* made by both players. Can select a move for itself.
*/
public class MachinePlayer extends Player {
int color,serchDepth;
GameBoard myGameBoard;
public final static int BLACK = -1;
public final static int WHITE = 1;
public final static int EMPTY = 0;
//保存已经放在棋盘上的棋子坐标
private NodeRecord[] nodeRecord;
private NodeRecord[] enemyNodeRecord;
private int recordIndex, enemyIndex;
// Creates a machine player with the given color. Color is either 0 (black)
// or 1 (white). (White has the first move.)
public MachinePlayer(int color) {
this.color = color;
this.serchDepth = 2;
myGameBoard = new GameBoard();
nodeRecord = new NodeRecord[10];
enemyNodeRecord = new NodeRecord[10];
recordIndex = 0;
enemyIndex = 0;
}
public int getColor() {
return color;
}
public NodeRecord[] getNodeRecord() {
return nodeRecord;
}
public NodeRecord[] getEnemyNodeRecord() {
return enemyNodeRecord;
}
// Creates a machine player with the given color and search depth. Color is
// either 0 (black) or 1 (white). (White has the first move.)
public MachinePlayer(int color, int searchDepth) {
this.color = color;
this.serchDepth = searchDepth;
myGameBoard = new GameBoard();
nodeRecord = new NodeRecord[10];
enemyNodeRecord = new NodeRecord[10];
recordIndex = 0;
enemyIndex = 0;
}
// Returns a new move by "this" player. Internally records the move (updates
// the internal game board) as a move by "this" player.
public Move chooseMove() {
Move myMove = null;
//white moves
//white add move
if(this.color == WHITE){
if(myGameBoard.getWhiteNum() < 10){
if(myGameBoard.getWhiteNum() == 0){
myMove = new Move(0, 4);
myGameBoard.setBoard(0, 4, WHITE);
nodeRecord[recordIndex] = new NodeRecord(0, 4, WHITE);
recordIndex++;
}else if(isWLGoalEmpty()){
for(int vIndex = 1; vIndex < 7; vIndex++){
if(myGameBoard.isLeagleMove(0, vIndex, WHITE)){
myGameBoard.setBoard(0, vIndex, WHITE);
myMove = new Move(0, vIndex);
myGameBoard.setBoard(0, vIndex, WHITE);
nodeRecord[recordIndex] = new NodeRecord(0, vIndex, WHITE);
recordIndex++;
break;
}
}
//右边white goal空
}else if(isWRGoalEmpty()){
for(int vIndex = 1; vIndex < 7; vIndex++){
if(myGameBoard.isLeagleMove(7, vIndex, WHITE)){
myMove = new Move(7, vIndex);
myGameBoard.setBoard(7, vIndex, WHITE);
nodeRecord[recordIndex] = new NodeRecord(7, vIndex, WHITE);
recordIndex++;
break;
}
}
}else{
//随机选择内部点
// Random rand = new Random();
// rand.setSeed(System.currentTimeMillis());
// int hIndex = rand.nextInt(6) + 1;
// int vIndex = rand.nextInt(6) + 1;
// while(!myGameBoard.isLeagleMove(hIndex, vIndex, WHITE)){
// hIndex = rand.nextInt(6) + 1;
// hIndex = rand.nextInt(6) + 1;
// }
int hIndex = 0;
int vIndex = 1;
//扫描棋盘 ,找到合法的放置点
boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, WHITE);
while(flag == false){
for(int v = 1; v <= 6 ; v++){
for(int h = 0; h <= 7; h++){
flag = myGameBoard.isLeagleMove(h, v, WHITE);
if(flag == true){
hIndex = h;
vIndex = v;
break;
}
}
if(flag == true){
break;
}
}
}
myMove = new Move(hIndex, vIndex);
myGameBoard.setBoard(hIndex, vIndex, WHITE);
nodeRecord[recordIndex] = new NodeRecord(hIndex, vIndex, WHITE);
recordIndex++;
}
//White step moves
}else{
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
recordIndex = rand.nextInt(10);
// int hIndex = rand.nextInt(6) + 1;
// int vIndex = rand.nextInt(6) + 1;
// while(!myGameBoard.isLeagleMove(hIndex, vIndex, WHITE)){
// hIndex = rand.nextInt(6) + 1;
// hIndex = rand.nextInt(6) + 1;
// }
int oldHIndex = nodeRecord[recordIndex].gethIndex();
int oldVIndex = nodeRecord[recordIndex].getvIndex();
myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
int hIndex = 0;
int vIndex = 1;
//若旧坐标为(1,0),搜索起点应该从第二个节点开始,防止把棋子重新放回(1,0),没有此步骤程序则直接跳过下边的while循环
if(oldHIndex == 0 && oldVIndex == 1){
hIndex = 1;
}
//扫描棋盘 ,找到合法的放置点
boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, WHITE);
while(flag == false){
for(int v = 1; v <= 6 ; v++){
for(int h = 0; h <= 7; h++){
flag = myGameBoard.isLeagleMove(h, v, WHITE);
if(flag == true){
//排除掉原有坐标,不允许走重复的步骤
if(h != oldHIndex || v != oldVIndex){
hIndex = h;
vIndex = v;
break;
//重新将标志置假,防止当棋子旧坐标为边缘点时内层循环跳出,但是此时坐标仍为旧坐标
}else{
flag = false;
}
}
}
if(flag == true){
break;
}
// System.out.println("scaning......" + hIndex);
}
}
myMove = new Move(hIndex, vIndex, oldHIndex, oldVIndex);
nodeRecord[recordIndex].sethIndex(hIndex);
nodeRecord[recordIndex].setvIndex(vIndex);
nodeRecord[recordIndex].setColor(WHITE);
myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
myGameBoard.setBoard(hIndex, vIndex, WHITE);
}
//Black moves
}else{
if(myGameBoard.getBlackNum() < 10){
if(myGameBoard.getBlackNum() == 0){
myMove = new Move(4, 0);
myGameBoard.setBoard(4, 0, BLACK);
nodeRecord[recordIndex] = new NodeRecord(4, 0, BLACK);
recordIndex++;
}else if(isBUGoalEmpty()){
for(int hIndex = 1; hIndex < 7; hIndex++){
if(myGameBoard.isLeagleMove(hIndex, 0, BLACK)){
myMove = new Move(hIndex, 0);
myGameBoard.setBoard(hIndex, 0, BLACK);
nodeRecord[recordIndex] = new NodeRecord(hIndex, 0, BLACK);
recordIndex++;
break;
}
}
//下边black goal空
}else if(isBDGoalEmpty()){
for(int hIndex = 1; hIndex < 7; hIndex++){
if(myGameBoard.isLeagleMove(hIndex, 7, BLACK)){
myMove = new Move(hIndex, 7);
myGameBoard.setBoard(hIndex, 7, BLACK);
nodeRecord[recordIndex] = new NodeRecord(hIndex, 7, BLACK);
recordIndex++;
break;
}
}
}else{
//随机选择内部点
// Random rand = new Random();
// rand.setSeed(System.currentTimeMillis());
// int hIndex = rand.nextInt(6) + 1;
// int vIndex = rand.nextInt(6) + 1;
int hIndex = 1;
int vIndex = 0;
int i = 0;
//扫描棋盘 ,找到合法的放置点
boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, BLACK);
while(flag == false){
for(int v = 0; v <= 7 ; v++){
for(int h = 1; h <= 6; h++){
flag = myGameBoard.isLeagleMove(h, v, BLACK);
if(flag == true){
hIndex = h;
vIndex = v;
break;
}
}
if(flag == true){
break;
}
i++;
System.out.println("scaning......" + i);
System.out.println(flag);
}
}
// while(!myGameBoard.isLeagleMove(hIndex, vIndex, BLACK)){
// System.out.println("getRandom...........");
// hIndex = rand.nextInt(6) + 1;
// hIndex = rand.nextInt(6) + 1;
// }
myMove = new Move(hIndex, vIndex);
//将对应坐标点设置为指定颜色 并且更新棋盘上对应棋子的数目
myGameBoard.setBoard(hIndex, vIndex, BLACK);
nodeRecord[recordIndex] = new NodeRecord(hIndex, vIndex, BLACK);
recordIndex++;
}
System.out.println(myGameBoard.getBlackNum());
//Black step moves, 需要仔细考虑重放的过程,扫描到原有的坐标后继续扫描
}else{
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
recordIndex = rand.nextInt(10);
// int hIndex = rand.nextInt(6) + 1;
// int vIndex = rand.nextInt(6) + 1;
// while(!myGameBoard.isLeagleMove(hIndex, vIndex, BLACK)){
// hIndex = rand.nextInt(6) + 1;
// hIndex = rand.nextInt(6) + 1;
// System.out.println(hIndex);
// }
int oldHIndex = nodeRecord[recordIndex].gethIndex();
int oldVIndex = nodeRecord[recordIndex].getvIndex();
//将原有坐标点清空以免影响判断
myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
int hIndex = 1;
int vIndex = 0;
//若旧坐标为(1,0),搜索起点应该从第二个节点开始,防止把棋子重新放回(1,0),没有此步骤程序则直接跳过下边的while循环
if(oldHIndex == 1 && oldVIndex == 0){
hIndex = 2;
}
//扫描棋盘 ,找到合法的放置点
boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, BLACK);
while(flag == false){
for(int v = 0; v <= 7 ; v++){
for(int h = 1; h <= 6; h++){
flag = myGameBoard.isLeagleMove(h, v, BLACK);
if(flag == true){
//排除掉原有坐标,不允许走重复的步骤
if(h != oldHIndex || v != oldVIndex){
hIndex = h;
vIndex = v;
break;
//重新将标志置假,防止当棋子旧坐标为边缘点时内层循环跳出,但是此时坐标仍为旧坐标
}else{
flag = false;
}
}
}
if(flag == true){
break;
}
// System.out.println("scaning......" + hIndex);
}
}
myMove = new Move(hIndex, vIndex, oldHIndex, oldVIndex);
nodeRecord[recordIndex].sethIndex(hIndex);
nodeRecord[recordIndex].setvIndex(vIndex);
nodeRecord[recordIndex].setColor(BLACK);
myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
myGameBoard.setBoard(hIndex, vIndex, BLACK);
}
}
return myMove;
}
private boolean isBDGoalEmpty() {
for(int hIndex = 1; hIndex < 7; hIndex++){
if(myGameBoard.getGameBoard(hIndex, 7) != EMPTY){
return false;
}
}
return true;
}
private boolean isBUGoalEmpty() {
for(int hIndex = 1; hIndex < 7; hIndex++){
if(myGameBoard.getGameBoard(hIndex, 0) != EMPTY){
return false;
}
}
return true;
}
private boolean isWRGoalEmpty() {
for(int vIndex = 1; vIndex < 7; vIndex++){
if(myGameBoard.getGameBoard(7, vIndex) != EMPTY){
return false;
}
}
return true;
}
private boolean isWLGoalEmpty() {
for(int vIndex = 1; vIndex < 7; vIndex++){
if(myGameBoard.getGameBoard(0, vIndex) != EMPTY){
return false;
}
}
return true;
}
// If the Move m is legal, records the move as a move by the opponent
// (updates the internal game board) and returns true. If the move is
// illegal, returns false without modifying the internal state of "this"
// player. This method allows your opponents to inform you of their moves.
public boolean opponentMove(Move m) {
int opponentColor;
if(this.color == WHITE){
opponentColor = BLACK;
}else{
opponentColor = WHITE;
}
int hIndex, vIndex;
hIndex = m.x1;
vIndex = m.y1;
//在判断前将原来坐标点清空,防止影响判断
if(m.moveKind == Move.STEP){
myGameBoard.setBoard(m.x2, m.y2, EMPTY);
for(int i = 0; i < 10; i++){
if(enemyNodeRecord[i].gethIndex() == m.x2 && enemyNodeRecord[i].getvIndex() == m.y2){
enemyIndex = i;
}
}
}
if(myGameBoard.isLeagleMove(hIndex, vIndex, opponentColor)){
myGameBoard.setBoard(hIndex, vIndex, opponentColor);
if(m.moveKind == Move.ADD){
NodeRecord newNode = new NodeRecord(hIndex, vIndex, opponentColor);
enemyNodeRecord[enemyIndex] = newNode;
enemyIndex++;
}else{
enemyNodeRecord[enemyIndex].sethIndex(hIndex);
enemyNodeRecord[enemyIndex].setvIndex(vIndex);
}
return true;
//若不合法,将原有坐标还原,step move不成功
}else{
myGameBoard.setBoard(m.x2, m.y2, opponentColor);
}
return false;
}
// If the Move m is legal, records the move as a move by "this" player
// (updates the internal game board) and returns true. If the move is
// illegal, returns false without modifying the internal state of "this"
// player. This method is used to help set up "Network problems" for your
// player to solve.
public boolean forceMove(Move m) {
int hIndex, vIndex;
hIndex = m.x1;
vIndex = m.y1;
if(myGameBoard.isLeagleMove(hIndex, vIndex, this.color)){
myGameBoard.setBoard(hIndex, vIndex, this.color);
return true;
}
return false;
}
}