本程序使用alpha-beta算法解决井字棋人机对弈的问题,运行环境要求是jdk1.8。
初始化时,玩家需要选择棋子种类,输入1代表选择X,输入2代表选择O,输入其他的属于错误输入,默认选O。初始时玩家需要选择先手还是后手,输入1表示选择先手,输入2表示选择后手,输入其他为错误操作,默认是先手。棋盘为3*3大小,分别用1-9表示,按1-9在对应的位置下棋。输入1-9以外的按键或者在有棋子的地方输入属于非法操作,系统会要求重新输入。
程序设计说明
1、棋盘采用的存储结构:3*3的二维数组。
2、涉及角色:玩家、电脑两方,使用O和X区分。初始化时玩家可以选择自己的符号,默认为O;玩家同时也可以选择先手还是后手,默认是先手。
3、判断程序结束条件:玩家或电脑其中一方取得胜利,用数组表示为a11a12a13、a21a22a23、a31a32a33、a11a22a33、a13a22a31、a11a21a31、a12a22a32、a13a23a33其中一组的符号相等。或者棋盘已满。
Alpha-beta剪枝的过程主要应用在回溯遍历所有生成的可能结果中,各个节点的取值为-1,0,1,。1表示电脑取得胜利,-1表示玩家取得胜利,0表示平局。遍历的过程与下述例子一致:
设当前棋盘为:
回溯法生成的树结构如下:
初始时alpha为-∞,beta为+∞,为树的所有节点从上到下从左到右标号为0-11。
1、从第一棵子树开始使用回溯法遍历,第一颗子树只有一个节点,值为1,根节点暂时为1,MAX层改变alpha的值,此时alpha为1,beta为-∞。
2、开始用回溯法遍历第二棵子树,第8个节点值为0,第4个节点是第8个节点的父节点,且4只有一个子节点,故4值为0,第2个节点暂时为0,此时alpha为1,beta为0,beta<alpha,需要进行剪枝,剪掉2的右子树。
3、开始用回溯法遍历第三棵子树,第10个节点值为0,第6个节点值为0,第三个节点值暂时为0,alpha>beta开始剪枝,3的右子树减去,根的值为1,所以后一步结果为第1个节点,电脑胜利。
代码:
package homework;
import java.util.Random;
import java.util.Scanner;
public class Chess {
char[][] chess = new char[4][4];
char player,computer;
boolean playerF;
boolean detectWin(char player) {//判断玩家player是否胜利
for(int i=1;i<4;i++)
if(chess[i][1]==player&&chess[i][2]==player&&chess[i][3]==player) {
return true;
}else {
if(chess[1][i]==player&&chess[2][i]==player&&chess[3][i]==player)
return true;
}
if(chess[1][1]==player&&chess[2][2]==player&&chess[3][3]==player)
return true;
if(chess[1][3]==player&&chess[2][2]==player&&chess[3][1]==player)
return true;
return false;
}
boolean isEmpty() {//判断棋盘是否为空
for(int i=1;i<4;i++) {
for(int j=1;j<4;j++) {
if(chess[i][j]=='-')return false;
}
}
return true;
}
//初始化游戏
public void startGame() {
for(int i=1;i<4;i++)
for(int j=1;j<4;j++)
chess[i][j]='-';
Scanner input = new Scanner(System.in);
player = 'O';
computer='X';
System.out.print("请选择玩家符号X/O,(输入1代表选择X,输入2代表选择O,默认为O):");
int get = input.nextInt();
if(get==1) {
player = 'X';
computer = 'O';
}else if(get!=2) {
System.out.println("输入有误,默认为O");
}
playerF = true;
System.out.println("是否先手1/2(1代表是,2代表否,默认是):");
get = input.nextInt();
if(get==2)playerF=false;
else if(get!=1) {
System.out.println("输入有误,默认玩家先手!");
}
if(playerF) {
Print();
}
else {
Random rand = new Random();
int startPoint = rand.nextInt(9)+1;
int row;
if(startPoint%3==0)row=startPoint/3;
else row = startPoint/3+1;
int col = startPoint-(row-1)*3;
chess[row][col]=computer;
Print();
}
}
void Print() {
System.out.println("-------------");
for(int i=1;i<4;i++) {
for(int j=1;j<4;j++) {
System.out.print("| ");
System.out.print(chess[i][j]+" ");
if(j==3)System.out.println("|");
}
if(i==3)System.out.println("-------------");
}
}
void playerInput() {
System.out.print("轮到你走了,请输入棋的位置:");
int row,col;
Scanner input = new Scanner(System.in);
while(true) {
int num = input.nextInt();
if(num>=1&&num<=9) {
if(num%3==0)row = num/3;
else row = num/3+1;
col = num-3*(row-1);
if(chess[row][col]!='-')System.out.println("该位置已有棋子");
else break;
}else {
System.out.println("输入有误,请重新输入");
continue;
}
}
chess[row][col]=player;
Print();
// input.close();
}
//-1,0,1分别代表玩家玩家胜利,平局,电脑胜利时节点的值
int bestInput(String state,String nextState,int alpha,int beta) {//输入,调用剪枝的过程
char ch;
if(state.equals("computer"))ch=computer;
else ch=player;
if(detectWin(ch)) {
if(state.equals("computer"))return 1;
else return -1;
}else if(isEmpty()) {
return 0;
}
else {
int score;
for(int i=1;i<4;i++) {
for(int j=1;j<4;j++) {
if(chess[i][j]=='-') {
chess[i][j]=ch;
score=bestInput(nextState,state,alpha,beta);
chess[i][j]='-';
if(state.equals("computer")) {
if(score>=alpha)alpha=score;
if(alpha>beta)return beta;
}else {
if(score<beta)beta=score;
if(beta<=alpha)return alpha;
}
}
}
}
if(state.equals("computer"))return alpha;
else return beta;
}
}
void computerInput() {
int best=0;
int bestScore=-1000;
int score;
for(int i=1;i<=3;i++) {
for(int j=1;j<=3;j++) {
if(chess[i][j]=='-') {
chess[i][j]=computer;
score = bestInput("player","computer",-1000,1000);//alpha-beta剪枝是一个根据上下界限剪枝的算法,初始的上下界限为无穷
if(score>bestScore) {//在同一层的节点里面需要不断试探性递归,用回溯法找到最合适的下棋点使自己胜算最大
bestScore=score;
best=(i-1)*3+j;
}
chess[i][j]='-';
}
}
}
int row,col;
if(best%3==0)row = best/3;
else row = best/3+1;
col = best-(row-1)*3;
chess[row][col]=computer;
Print();
}
public static void main(String[] args) {
Chess c = new Chess();
c.startGame();
String current = "player";
while(!c.detectWin(c.computer)&&!c.detectWin(c.player)&&!c.isEmpty()) {//终止条件是当前棋盘为空或者有一方胜利
switch(current) {
case "player":
c.playerInput();current="computer";break;//当玩家下完后轮到电脑下
case "computer":
c.computerInput();current="player";break;
default:break;
}
}
if(c.detectWin(c.computer))System.out.println("电脑胜利!");
else if(c.detectWin(c.player))System.out.println("玩家胜利");
else System.out.println("平局!");
}
}