实现中国象棋之类的游戏都会自定义一个View,本次用到SurfaceView,不了解的小伙伴可以自己了解一下。
- 象棋的运行思路
执行过程:点击屏幕(屏幕监听) ——>是否到自己走棋(是) ——>判断是否已经选中棋子(是) ——>判断点击位置是否有自己的棋子(是)——>选中该棋子(true)——>再次点击屏幕——>判断位置是否符合下棋规则(点击位置有对方棋子或空位)——>可以移动,改变棋盘(数组)。
- 代码实现
实现棋盘所有功能(1、显示棋盘 2、新局、悔棋、设置、返回功能 )
activity_pvm.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/relativeLayout"
android:background="@drawable/background"
tools:context="com.example.a77304.chessgame.PvMActivity">
</RelativeLayout>
button_group.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true">
<Button
android:id="@+id/btn_retry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/btn_recall"
android:layout_centerVertical="true"
android:layout_margin="5dp"
android:background="@drawable/selector_btn2"
android:textSize="24dp"
android:text="新局" />
<Button
android:id="@+id/btn_recall"
android:soundEffectsEnabled="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/btn_null"
android:layout_centerVertical="true"
android:layout_margin="5dp"
android:background="@drawable/selector_btn2"
android:textSize="24dp"
android:text="悔棋" />
<Button
android:id="@+id/btn_null"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="" />
<Button
android:id="@+id/btn_setting"
android:soundEffectsEnabled="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_null"
android:layout_centerVertical="true"
android:layout_margin="5dp"
android:background="@drawable/selector_btn2"
android:textSize="24dp"
android:text="设置" />
<Button
android:id="@+id/btn_back"
android:soundEffectsEnabled="false"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_setting"
android:layout_centerVertical="true"
android:layout_margin="5dp"
android:background="@drawable/selector_btn2"
android:textSize="24dp"
android:text="返回" />
</RelativeLayout>
PvMActivity.java
import android.content.SharedPreferences;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;
import AICore.AI;
import AICore.KnowledgeBase;
import AICore.Move;
import AICore.TransformTable;
import ChessMove.Rule;
import CustomDialog.CommonDialog;
import CustomDialog.RetryDialog;
import CustomDialog.SettingDialog_PvM;
import CustomView.RoundView;
import Info.ChessInfo;
import Info.InfoSet;
import Info.Pos;
import Info.SaveInfo;
import CustomView.ChessView;
import static AICore.AI.getBestMove;
import static com.example.a77304.chessgame.HomeActivity.MIN_CLICK_DELAY_TIME;
import static com.example.a77304.chessgame.HomeActivity.backMusic;
import static com.example.a77304.chessgame.HomeActivity.checkMusic;
import static com.example.a77304.chessgame.HomeActivity.clickMusic;
import static com.example.a77304.chessgame.HomeActivity.curClickTime;
import static com.example.a77304.chessgame.HomeActivity.lastClickTime;
import static com.example.a77304.chessgame.HomeActivity.playEffect;
import static com.example.a77304.chessgame.HomeActivity.playMusic;
import static com.example.a77304.chessgame.HomeActivity.selectMusic;
import static com.example.a77304.chessgame.HomeActivity.setting;
import static com.example.a77304.chessgame.HomeActivity.sharedPreferences;
import static com.example.a77304.chessgame.HomeActivity.stopMusic;
import static com.example.a77304.chessgame.HomeActivity.winMusic;
public class PvMActivity extends AppCompatActivity implements View.OnTouchListener, View.OnClickListener {
public RelativeLayout relativeLayout;
public ChessInfo chessInfo;
public InfoSet infoSet;
public ChessView chessView; // 自定义的View,用于绘制棋盘、棋子
public RoundView roundView; // 自定义View(显示 红方回合、黑方回合)
public static KnowledgeBase knowledgeBase;
public static TransformTable transformTable;
public Thread AIThread = new Thread(new Runnable() {
@Override
public void run() {
AIMove(chessInfo.IsRedGo, chessInfo.status);
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pvm);
relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout);
chessInfo = new ChessInfo();
infoSet = new InfoSet();
transformTable = new TransformTable();
knowledgeBase = new KnowledgeBase();
roundView = new RoundView(this, chessInfo);
//添加View(红方回合、黑方回合)
relativeLayout.addView(roundView);
RelativeLayout.LayoutParams paramsRound = (RelativeLayout.LayoutParams) roundView.getLayoutParams();
paramsRound.addRule(RelativeLayout.CENTER_IN_PARENT);
paramsRound.addRule(RelativeLayout.ALIGN_PARENT_TOP);
paramsRound.setMargins(30, 30, 30, 30);
roundView.setLayoutParams(paramsRound);
roundView.setId(R.id.roundView);
chessView = new ChessView(this, chessInfo);
chessView.setOnTouchListener(this);
//添加View(绘制棋盘、棋子)
relativeLayout.addView(chessView);
RelativeLayout.LayoutParams paramsChess = (RelativeLayout.LayoutParams) chessView.getLayoutParams();
paramsChess.addRule(RelativeLayout.BELOW, R.id.roundView);
chessView.setLayoutParams(paramsChess);
chessView.setId(R.id.chessView);
LayoutInflater inflater = LayoutInflater.from(this);
RelativeLayout buttonGroup = (RelativeLayout) inflater.inflate(R.layout.button_group, relativeLayout, false);
//添加View(新局、悔棋、设置、返回)
relativeLayout.addView(buttonGroup);
RelativeLayout.LayoutParams paramsV = (RelativeLayout.LayoutParams) buttonGroup.getLayoutParams();
paramsV.addRule(RelativeLayout.BELOW, R.id.chessView);
buttonGroup.setLayoutParams(paramsV);
for (int i = 0; i < buttonGroup.getChildCount(); i++) {
Button btn = (Button) buttonGroup.getChildAt(i);
btn.setOnClickListener(this);
}
}
/**
* 屏幕监听
* @param view
* @param event
* @return
*/
@Override
public boolean onTouch(View view, MotionEvent event) {
lastClickTime = System.currentTimeMillis();
if (lastClickTime - curClickTime < MIN_CLICK_DELAY_TIME) {
return false;
}
curClickTime = lastClickTime;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
float x = event.getX();
float y = event.getY();
if (chessInfo.status == 1) {
if (x >= 0 && x <= chessView.Board_width && y >= 0 && y <= chessView.Board_height) {
chessInfo.Select = getPos(event);
Pos realPos = Rule.reversePos(new Pos(chessInfo.Select[0], chessInfo.Select[1]), chessInfo.IsReverse);
int i = realPos.x, j = realPos.y;
if (i >= 0 && i <= 8 && j >= 0 && j <= 9) {
if (chessInfo.IsRedGo == true && setting.isPlayerRed == true) {
if (chessInfo.IsChecked == false) {
if (chessInfo.piece[j][i] >= 8 && chessInfo.piece[j][i] <= 14) {
chessInfo.prePos = new Pos(i, j);
chessInfo.IsChecked = true;
chessInfo.ret = Rule.PossibleMoves(chessInfo.piece, i, j, chessInfo.piece[j][i]);
playEffect(selectMusic);
}
} else {
if (chessInfo.piece[j][i] >= 8 && chessInfo.piece[j][i] <= 14) {
chessInfo.prePos = new Pos(i, j);
chessInfo.ret = Rule.PossibleMoves(chessInfo.piece, i, j, chessInfo.piece[j][i]);
playEffect(selectMusic);
} else if (chessInfo.ret.contains(new Pos(i, j))) {
int tmp = chessInfo.piece[j][i];
chessInfo.piece[j][i] = chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x];
chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x] = 0;
if (Rule.isKingDanger(chessInfo.piece, true)) {
chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x] = chessInfo.piece[j][i];
chessInfo.piece[j][i] = tmp;
Toast toast = Toast.makeText(PvMActivity.this, "帅被将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else {
chessInfo.IsChecked = false;
chessInfo.IsRedGo = false;
chessInfo.curPos = new Pos(i, j);
chessInfo.updateAllInfo(chessInfo.prePos, chessInfo.curPos, chessInfo.piece[j][i], tmp);
try {
infoSet.pushInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
playEffect(clickMusic);
int key = 0;
if (Rule.isKingDanger(chessInfo.piece, false)) {
key = 1;
}
if (Rule.isDead(chessInfo.piece, false)) {
key = 2;
}
if (key == 1) {
playEffect(checkMusic);
Toast toast = Toast.makeText(PvMActivity.this, "将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else if (key == 2) {
playEffect(winMusic);
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "红方获得胜利", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
if (chessInfo.status == 1) {
if (chessInfo.peaceRound >= 60) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "双方60回合内未吃子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else if (chessInfo.attackNum_B == 0 && chessInfo.attackNum_R == 0) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "双方都无攻击性棋子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else if (infoSet.ZobristInfo.get(chessInfo.ZobristKeyCheck) >= 4) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "重复局面出现4次,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
AIThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
chessInfo.isMachine = true;
AIMove(chessInfo.IsRedGo, chessInfo.status);
}
});
AIThread.start();
}
}
}
} else if (chessInfo.IsRedGo == false && setting.isPlayerRed == false) {
if (chessInfo.IsChecked == false) {
if (chessInfo.piece[j][i] >= 1 && chessInfo.piece[j][i] <= 7) {
chessInfo.prePos = new Pos(i, j);
chessInfo.IsChecked = true;
chessInfo.ret = Rule.PossibleMoves(chessInfo.piece, i, j, chessInfo.piece[j][i]);
playEffect(selectMusic);
}
} else {
if (chessInfo.piece[j][i] >= 1 && chessInfo.piece[j][i] <= 7) {
chessInfo.prePos = new Pos(i, j);
chessInfo.ret = Rule.PossibleMoves(chessInfo.piece, i, j, chessInfo.piece[j][i]);
playEffect(selectMusic);
} else if (chessInfo.ret.contains(new Pos(i, j))) {
int tmp = chessInfo.piece[j][i];
chessInfo.piece[j][i] = chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x];
chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x] = 0;
if (Rule.isKingDanger(chessInfo.piece, false)) {
chessInfo.piece[chessInfo.prePos.y][chessInfo.prePos.x] = chessInfo.piece[j][i];
chessInfo.piece[j][i] = tmp;
Toast toast = Toast.makeText(PvMActivity.this, "将被将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else {
chessInfo.IsChecked = false;
chessInfo.IsRedGo = true;
chessInfo.curPos = new Pos(i, j);
chessInfo.updateAllInfo(chessInfo.prePos, chessInfo.curPos, chessInfo.piece[j][i], tmp);
try {
infoSet.pushInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
playEffect(clickMusic);
int key = 0;
if (Rule.isKingDanger(chessInfo.piece, true)) {
key = 1;
}
if (Rule.isDead(chessInfo.piece, true)) {
key = 2;
}
if (key == 1) {
playEffect(checkMusic);
Toast toast = Toast.makeText(PvMActivity.this, "将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (key == 2) {
playEffect(winMusic);
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "黑方获得胜利", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
if (chessInfo.status == 1) {
if (chessInfo.peaceRound >= 60) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "双方60回合内未吃子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else if (chessInfo.attackNum_B == 0 && chessInfo.attackNum_R == 0) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "双方都无攻击性棋子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
} else if (infoSet.ZobristInfo.get(chessInfo.ZobristKeyCheck) >= 4) {
chessInfo.status = 2;
Toast toast = Toast.makeText(PvMActivity.this, "重复局面出现4次,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
AIThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
chessInfo.isMachine = true;
AIMove(chessInfo.IsRedGo, chessInfo.status);
}
});
AIThread.start();
}
}
}
}
}
}
}
}
return false;
}
public int[] getPos(MotionEvent e) {
int[] pos = new int[2];
double x = e.getX();
double y = e.getY();
int[] dis = new int[]{
chessView.Scale(3), chessView.Scale(41), chessView.Scale(80), chessView.Scale(85)
};
x = x - dis[0];
y = y - dis[1];
if (x % dis[3] <= dis[2] && y % dis[3] <= dis[2]) {
pos[0] = (int) Math.floor(x / dis[3]);
pos[1] = (int) Math.floor(y / dis[3]);
if (pos[0] >= 9 || pos[1] >= 10) {
pos[0] = pos[1] = -1;
}
} else {
pos[0] = pos[1] = -1;
}
return pos;
}
@Override
public void onClick(View view) {
lastClickTime = System.currentTimeMillis();
if (lastClickTime - curClickTime < MIN_CLICK_DELAY_TIME) {
return;
}
curClickTime = lastClickTime;
playEffect(selectMusic);
switch (view.getId()) {
case R.id.btn_retry:
if (chessInfo.isMachine == true && chessInfo.status == 1) {
Toast toast = Toast.makeText(PvMActivity.this, "请等待电脑思考", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
break;
}
final RetryDialog retryDialog = new RetryDialog(this);
retryDialog.setOnClickBottomListener(new RetryDialog.OnClickBottomListener() {
@Override
public void onPositiveClick() {
playEffect(selectMusic);
try {
chessInfo.setInfo(new ChessInfo());
infoSet.newInfo();
transformTable.transformInfos.clear();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
SharedPreferences.Editor editor = sharedPreferences.edit();
if (setting.isPlayerRed != retryDialog.isPlayerRed) {
setting.isPlayerRed = retryDialog.isPlayerRed;
editor.putBoolean("isPlayerRed", retryDialog.isPlayerRed);
editor.commit();
}
retryDialog.dismiss();
chessInfo.IsReverse = (setting.isPlayerRed == true) ? false : true;
if (chessInfo.IsReverse == true) {
try {
infoSet.curInfo.setInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
if (setting.isPlayerRed == false) {
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
AIFirstGo();
}
}
@Override
public void onNegtiveClick() {
playEffect(selectMusic);
retryDialog.dismiss();
}
});
retryDialog.show();
break;
case R.id.btn_recall:
if (chessInfo.isMachine == true && chessInfo.status == 1) {
Toast toast = Toast.makeText(PvMActivity.this, "请等待电脑思考", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
break;
}
int cnt = 0;
int total = 2;
if (chessInfo.status == 2 && chessInfo.isMachine == true) {
total = 1;
}
if (infoSet.preInfo.size() < total) {
break;
}
while (!infoSet.preInfo.empty()) {
ChessInfo tmp = infoSet.preInfo.pop();
cnt++;
try {
infoSet.recallZobristInfo(chessInfo.ZobristKeyCheck);
chessInfo.setInfo(tmp);
infoSet.curInfo.setInfo(tmp);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
if (cnt >= total) {
break;
}
}
break;
case R.id.btn_setting:
final SettingDialog_PvM settingDialog_pvm = new SettingDialog_PvM(this);
settingDialog_pvm.setOnClickBottomListener(new SettingDialog_PvM.OnClickBottomListener() {
@Override
public void onPositiveClick() {
playEffect(selectMusic);
SharedPreferences.Editor editor = sharedPreferences.edit();
boolean flag = false;
if (setting.isMusicPlay != settingDialog_pvm.isMusicPlay) {
setting.isMusicPlay = settingDialog_pvm.isMusicPlay;
if (setting.isMusicPlay) {
playMusic(backMusic);
} else {
stopMusic(backMusic);
}
editor.putBoolean("isMusicPlay", settingDialog_pvm.isMusicPlay);
flag = true;
}
if (setting.isEffectPlay != settingDialog_pvm.isEffectPlay) {
setting.isEffectPlay = settingDialog_pvm.isEffectPlay;
editor.putBoolean("isEffectPlay", settingDialog_pvm.isEffectPlay);
flag = true;
}
if (setting.mLevel != settingDialog_pvm.mLevel) {
setting.mLevel = settingDialog_pvm.mLevel;
editor.putInt("mLevel", settingDialog_pvm.mLevel);
flag = true;
}
if (flag) {
editor.commit();
}
settingDialog_pvm.dismiss();
}
@Override
public void onNegtiveClick() {
playEffect(selectMusic);
settingDialog_pvm.dismiss();
}
});
settingDialog_pvm.show();
break;
case R.id.btn_back:
final CommonDialog backDialog = new CommonDialog(this, "返回", "确认要返回吗");
backDialog.setOnClickBottomListener(new CommonDialog.OnClickBottomListener() {
@Override
public void onPositiveClick() {
playEffect(selectMusic);
backDialog.dismiss();
finish();
}
@Override
public void onNegtiveClick() {
playEffect(selectMusic);
backDialog.dismiss();
}
});
backDialog.show();
break;
default:
break;
}
}
public void AIMove(boolean isRed, int status) {
if (status != 1) return;
int depth = setting.mLevel * 2;
if (isRed == true) {
// AI获取最佳走法
Move move = knowledgeBase.readBestMoves(chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, depth);
if (move == null) {
long t1 = System.currentTimeMillis();
move = getBestMove(chessInfo.piece, true, depth, chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, infoSet.ZobristInfo);
long t2 = System.currentTimeMillis();
Log.i("AI思考", "AI思考的时间:" + String.valueOf(t2 - t1) + "ms");
knowledgeBase.saveBestMove(chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, depth, move);
}
Pos fromPos = move.fromPos;
Pos toPos = move.toPos;
int tmp = chessInfo.piece[toPos.y][toPos.x];
chessInfo.piece[toPos.y][toPos.x] = chessInfo.piece[fromPos.y][fromPos.x];
chessInfo.piece[fromPos.y][fromPos.x] = 0;
chessInfo.IsChecked = false;
chessInfo.IsRedGo = false;
chessInfo.Select = new int[]{-1, -1};
chessInfo.ret.clear();
chessInfo.prePos = new Pos(fromPos.x, fromPos.y);
chessInfo.curPos = new Pos(toPos.x, toPos.y);
chessInfo.updateAllInfo(chessInfo.prePos, chessInfo.curPos, chessInfo.piece[toPos.y][toPos.x], tmp);
chessInfo.isMachine = false;
try {
infoSet.pushInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
playEffect(clickMusic);
int key = 0;
if (Rule.isKingDanger(chessInfo.piece, false)) {
key = 1;
}
if (Rule.isDead(chessInfo.piece, false)) {
key = 2;
}
if (key == 1) {
playEffect(checkMusic);
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (key == 2) {
playEffect(winMusic);
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "红方获得胜利", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
}
if (chessInfo.status == 1) {
if (chessInfo.peaceRound >= 60) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "双方60回合内未吃子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (chessInfo.attackNum_B == 0 && chessInfo.attackNum_R == 0) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "双方都无攻击性棋子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (infoSet.ZobristInfo.get(chessInfo.ZobristKeyCheck) >= 4) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "重复局面出现4次,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
}
}
} else {
Move move = knowledgeBase.readBestMoves(chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, depth);
if (move == null) {
long t1 = System.currentTimeMillis();
move = getBestMove(chessInfo.piece, false, depth, chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, infoSet.ZobristInfo);
long t2 = System.currentTimeMillis();
Log.i("AI思考", "AI思考的时间:" + String.valueOf(t2 - t1) + "ms");
knowledgeBase.saveBestMove(chessInfo.ZobristKey, chessInfo.ZobristKeyCheck, depth, move);
}
Pos fromPos = move.fromPos;
Pos toPos = move.toPos;
int tmp = chessInfo.piece[toPos.y][toPos.x];
chessInfo.piece[toPos.y][toPos.x] = chessInfo.piece[fromPos.y][fromPos.x];
chessInfo.piece[fromPos.y][fromPos.x] = 0;
chessInfo.IsChecked = false;
chessInfo.IsRedGo = true;
chessInfo.Select = new int[]{-1, -1};
chessInfo.ret.clear();
chessInfo.prePos = new Pos(fromPos.x, fromPos.y);
chessInfo.curPos = new Pos(toPos.x, toPos.y);
chessInfo.updateAllInfo(chessInfo.prePos, chessInfo.curPos, chessInfo.piece[toPos.y][toPos.x], tmp);
chessInfo.isMachine = false;
try {
infoSet.pushInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
playEffect(clickMusic);
int key = 0;
if (Rule.isKingDanger(chessInfo.piece, true)) {
key = 1;
}
if (Rule.isDead(chessInfo.piece, true)) {
key = 2;
}
if (key == 1) {
playEffect(checkMusic);
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "将军", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (key == 2) {
playEffect(winMusic);
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "黑方获得胜利", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
}
if (chessInfo.status == 1) {
if (chessInfo.peaceRound >= 60) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "双方60回合内未吃子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (chessInfo.attackNum_B == 0 && chessInfo.attackNum_R == 0) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "双方都无攻击性棋子,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
} else if (infoSet.ZobristInfo.get(chessInfo.ZobristKeyCheck) >= 4) {
chessInfo.status = 2;
Looper.prepare();
Toast toast = Toast.makeText(PvMActivity.this, "重复局面出现4次,此乃和棋", Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
Looper.loop();
}
}
}
}
public void AIFirstGo() {
Move[] firstMoves = new Move[8];
firstMoves[0] = new Move(new Pos(1, 9), new Pos(2, 7)); //走马
firstMoves[1] = new Move(new Pos(7, 9), new Pos(6, 7)); //走马
firstMoves[2] = new Move(new Pos(2, 9), new Pos(4, 7)); //走相
firstMoves[3] = new Move(new Pos(6, 9), new Pos(4, 7)); //走相
firstMoves[4] = new Move(new Pos(1, 7), new Pos(4, 7)); //走炮
firstMoves[5] = new Move(new Pos(7, 7), new Pos(4, 7)); //走炮
firstMoves[6] = new Move(new Pos(2, 6), new Pos(2, 5)); //走兵
firstMoves[7] = new Move(new Pos(6, 6), new Pos(6, 5)); //走兵
int num = (int) (Math.random() * firstMoves.length);
//Log.e("chen",String.valueOf(num));
Move firstMove = firstMoves[num];
Pos fromPos = firstMove.fromPos;
Pos toPos = firstMove.toPos;
int tmp = chessInfo.piece[toPos.y][toPos.x];
chessInfo.piece[toPos.y][toPos.x] = chessInfo.piece[fromPos.y][fromPos.x];
chessInfo.piece[fromPos.y][fromPos.x] = 0;
chessInfo.IsChecked = false;
chessInfo.IsRedGo = false;
chessInfo.Select = new int[]{-1, -1};
chessInfo.ret.clear();
chessInfo.prePos = new Pos(fromPos.x, fromPos.y);
chessInfo.curPos = new Pos(toPos.x, toPos.y);
chessInfo.updateAllInfo(chessInfo.prePos, chessInfo.curPos, chessInfo.piece[toPos.y][toPos.x], tmp);
try {
infoSet.pushInfo(chessInfo);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
playEffect(clickMusic);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
final CommonDialog backDialog = new CommonDialog(this, "返回", "确认要返回吗");
backDialog.setOnClickBottomListener(new CommonDialog.OnClickBottomListener() {
@Override
public void onPositiveClick() {
playEffect(selectMusic);
backDialog.dismiss();
finish();
}
@Override
public void onNegtiveClick() {
playEffect(selectMusic);
backDialog.dismiss();
}
});
backDialog.show();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onPause() {
stopMusic(backMusic);
super.onPause();
}
@Override
protected void onStop() {
/* try {
SaveInfo.SerializeChessInfo(chessInfo, "ChessInfo_pvm.bin");
SaveInfo.SerializeInfoSet(infoSet, "InfoSet_pvm.bin");
SaveInfo.SerializeKnowledgeBase(knowledgeBase, "KnowledgeBase.bin");
SaveInfo.SerializeTransformTable(transformTable, "TransformTable.bin");
} catch (Exception e) {
Log.e("chen", e.toString());
}*/
super.onStop();
}
@Override
protected void onStart() {
playMusic(backMusic);
if (chessInfo.isMachine) {
if (AIThread.isAlive() == false) {
AIThread = new Thread(new Runnable() {
@Override
public void run() {
AIMove(chessInfo.IsRedGo, chessInfo.status);
}
});
AIThread.start();
}
}
super.onStart();
}
}
规则类 Rule.java
import android.util.Log;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import Info.Pos;
public class Rule {
public static int[][] area = new int[][]{
{1, 1, 1, 2, 2, 2, 1, 1, 1},
{1, 1, 1, 2, 2, 2, 1, 1, 1},
{1, 1, 1, 2, 2, 2, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{3, 3, 3, 3, 3, 3, 3, 3, 3},
{3, 3, 3, 3, 3, 3, 3, 3, 3},
{3, 3, 3, 4, 4, 4, 3, 3, 3},
{3, 3, 3, 4, 4, 4, 3, 3, 3},
{3, 3, 3, 4, 4, 4, 3, 3, 3},
};
//走棋规则
public static int[][] offsetX = new int[][]{
{0, 0, 1, -1}, //帅 将
{1, 1, -1, -1}, //仕 士
{2, 2, -2, -2}, //相 象
{1, 1, -1, -1}, //象眼
{1, 1, -1, -1, 2, 2, -2, -2}, //马
{0, 0, 0, 0, 1, 1, -1, -1}, //蹩马腿
{0}, //卒
{-1, 0, 1}, //过河卒
{0}, //兵
{-1, 0, 1}, //过河兵
{1, 1, -1, -1, 1, 1, -1, -1}, //反向蹩马腿
};
public static int[][] offsetY = new int[][]{
{1, -1, 0, 0}, //帅 将
{1, -1, 1, -1}, //仕 士
{2, -2, 2, -2}, //相 象
{1, -1, 1, -1}, //象眼
{2, -2, 2, -2, 1, -1, 1, -1}, //马
{1, -1, 1, -1, 0, 0, 0, 0}, //蹩马腿
{1}, //卒
{0, 1, 0}, //过河卒
{-1}, //兵
{0, -1, 0}, //过河兵
{1, -1, 1, -1, 1, -1, 1, -1}, //反向蹩马腿
};
//生成某个位置的棋子的所有可能走法的位置
public static List<Pos> PossibleMoves(int[][] piece, int fromX, int fromY, int PieceID) {
List<Pos> ret = new ArrayList<Pos>();
int num;
switch (PieceID) {
case 1://黑将
num = 0;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) == 2 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
Pos eatPos1 = flyKing(1, fromX, fromY, piece);
if (eatPos1.equals(new Pos(-1, -1)) == false) {
ret.add(eatPos1);
}
break;
case 2://黑士
num = 1;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) == 2 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
break;
case 3://黑象
num = 2;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
int blockX = fromX + offsetX[num + 1][i];
int blockY = fromY + offsetY[num + 1][i];
if (InArea(toX, toY) >= 1 && InArea(toX, toY) <= 2 && IsSameSide(PieceID, piece[toY][toX]) == false && piece[blockY][blockX] == 0) {
ret.add(new Pos(toX, toY));
}
}
break;
case 4://黑马
case 11://红马
num = 4;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
int blockX = fromX + offsetX[num + 1][i];
int blockY = fromY + offsetY[num + 1][i];
if (InArea(toX, toY) != 0 && IsSameSide(PieceID, piece[toY][toX]) == false && piece[blockY][blockX] == 0) {
ret.add(new Pos(toX, toY));
}
}
break;
case 5://黑车
case 12: //红车
for (int i = fromY + 1; i < 10; i++) {//向下走
if (CanMove(1, fromX, fromY, fromX, i, piece)) { //可以走时
ret.add(new Pos(fromX, i));
} else {//不可以走时直接 break
break;
}
}
for (int i = fromY - 1; i > -1; i--) {//向上走
if (CanMove(1, fromX, fromY, fromX, i, piece)) {//可以走时
ret.add(new Pos(fromX, i));
} else {//不可以走时
break;
}
}
for (int j = fromX - 1; j > -1; j--) {//向走走
if (CanMove(1, fromX, fromY, j, fromY, piece)) {//可以走时
ret.add(new Pos(j, fromY));
} else {//不可以走时
break;
}
}
for (int j = fromX + 1; j < 9; j++) {//向右走
if (CanMove(1, fromX, fromY, j, fromY, piece)) {//可以走时
ret.add(new Pos(j, fromY));
} else {//不可以走时
break;
}
}
/*for (int i = 0; i < 9; i++) {
for (int j = 0; j < 10; j++) {
if (i != fromX || j != fromY) {
if (CanMove(1, fromX, fromY, i, j, piece)) {
ret.add(new Pos(i, j));
}
}
}
}*/
break;
case 6://黑炮
case 13://红炮
for (int i = fromY + 1; i < 10; i++) {//向下走
if (CanMove(2, fromX, fromY, fromX, i, piece)) { //可以走时
ret.add(new Pos(fromX, i));
}
}
for (int i = fromY - 1; i > -1; i--) {//向上走
if (CanMove(2, fromX, fromY, fromX, i, piece)) {//可以走时
ret.add(new Pos(fromX, i));
}
}
for (int j = fromX - 1; j > -1; j--) {//向走走
if (CanMove(2, fromX, fromY, j, fromY, piece)) {//可以走时
ret.add(new Pos(j, fromY));
}
}
for (int j = fromX + 1; j < 9; j++) {//向右走
if (CanMove(2, fromX, fromY, j, fromY, piece)) {//可以走时
ret.add(new Pos(j, fromY));
}
}
/*for (int i = 0; i < 9; i++) {
for (int j = 0; j < 10; j++) {
if (i != fromX || j != fromY) {
if (CanMove(2, fromX, fromY, i, j, piece)) {
ret.add(new Pos(i, j));
}
}
}
}*/
break;
case 7://黑卒
if (InArea(fromX, fromY) == 1) {
num = 6;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) != 0 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
} else {
num = 7;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) != 0 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
}
break;
case 8://红帅
num = 0;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) == 4 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
Pos eatPos2 = flyKing(2, fromX, fromY, piece);
if (eatPos2.equals(new Pos(-1, -1)) == false) {
ret.add(eatPos2);
}
break;
case 9://红士
num = 1;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) == 4 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
break;
case 10://红象
num = 2;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
int blockX = fromX + offsetX[num + 1][i];
int blockY = fromY + offsetY[num + 1][i];
if (InArea(toX, toY) >= 3 && InArea(toX, toY) <= 4 && IsSameSide(PieceID, piece[toY][toX]) == false && piece[blockY][blockX] == 0) {
ret.add(new Pos(toX, toY));
}
}
break;
case 14://红兵
if (InArea(fromX, fromY) == 3) {
num = 8;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) != 0 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
} else {
num = 9;
for (int i = 0; i < offsetX[num].length; i++) {
int toX = fromX + offsetX[num][i];
int toY = fromY + offsetY[num][i];
if (InArea(toX, toY) != 0 && IsSameSide(PieceID, piece[toY][toX]) == false) {
ret.add(new Pos(toX, toY));
}
}
}
break;
default:
break;
}
return ret;
}
//判断将、帅是否已经被将军
public static boolean isKingDanger(int[][] piece, boolean isRedKing) {
int num = 4;
int op_block_num = 10;
if (isRedKing == true) {
int x = 0, y = 0;
boolean flag = false;
for (y = 7; y <= 9; y++) {
for (x = 3; x <= 5; x++) {
if (piece[y][x] == 8) {
flag = true;
break;
}
}
if (flag) break;
}
for (int i = 0; i < offsetX[num].length; i++) { //马
int toX = x + offsetX[num][i];
int toY = y + offsetY[num][i];
int blockX = x + offsetX[op_block_num][i];
int blockY = y + offsetY[op_block_num][i];
if (InArea(toX, toY) != 0 && piece[toY][toX] == 4 && piece[blockY][blockX] == 0) {
return true;
}
}
for (int i = 5; i <= 6; i++) { //车 炮
List<Pos> moves = PossibleMoves(piece, x, y, i + 7);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
if (piece[pos.y][pos.x] == i) {
return true;
}
}
}
if (flyKing(2, x, y, piece).equals(new Pos(-1, -1)) == false) { //将
return true;
}
if (piece[y - 1][x] == 7 || piece[y][x - 1] == 7 || piece[y][x + 1] == 7) {
return true;
}
} else {
int x = 0, y = 0;
boolean flag = false;
for (y = 0; y <= 2; y++) {
for (x = 3; x <= 5; x++) {
if (piece[y][x] == 1) {
flag = true;
break;
}
}
if (flag) break;
}
for (int i = 0; i < offsetX[num].length; i++) { //马
int toX = x + offsetX[num][i];
int toY = y + offsetY[num][i];
int blockX = x + offsetX[op_block_num][i];
int blockY = y + offsetY[op_block_num][i];
if (InArea(toX, toY) != 0 && piece[toY][toX] == 11 && piece[blockY][blockX] == 0) {
return true;
}
}
for (int i = 12; i <= 13; i++) { //车 炮
List<Pos> moves = PossibleMoves(piece, x, y, i - 7);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
if (piece[pos.y][pos.x] == i) {
return true;
}
}
}
if (flyKing(1, x, y, piece).equals(new Pos(-1, -1)) == false) { //将
return true;
}
if (piece[y + 1][x] == 14 || piece[y][x - 1] == 14 || piece[y][x + 1] == 14) {
return true;
}
}
return false;
}
//判断将、帅是否已经被将军
public static boolean isDead(int[][] piece, boolean isRedKing) {
if (isRedKing == true) {
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if (piece[i][j] >= 8 && piece[i][j] <= 14) {
List<Pos> moves = PossibleMoves(piece, j, i, piece[i][j]);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
int tmp = piece[pos.y][pos.x];
if (tmp == 1) {
return false;
}
piece[pos.y][pos.x] = piece[i][j];
piece[i][j] = 0;
if (isKingDanger(piece, true) == false) {
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
return false;
}
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
}
}
}
}
} else {
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if (piece[i][j] >= 1 && piece[i][j] <= 7) {
List<Pos> moves = PossibleMoves(piece, j, i, piece[i][j]);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
int tmp = piece[pos.y][pos.x];
if (tmp == 8) {
return false;
}
piece[pos.y][pos.x] = piece[i][j];
piece[i][j] = 0;
if (isKingDanger(piece, false) == false) {
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
return false;
}
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
}
}
}
}
}
return true;
}
//0 棋盘外 1 黑盘 2 黑十字(九宫) 3 红盘 4 红十字(九宫)
public static int InArea(int x, int y) {
if (x < 0 || x > 8 || y < 0 || y > 9) {
return 0;
}
return area[y][x];
}
//判断棋子是否为同一方
public static boolean IsSameSide(int fromID, int toID) {
if (toID == 0) {
return false;
}
if ((fromID <= 7 && toID <= 7) || (fromID >= 8 && toID >= 8)) {
return true;
} else {
return false;
}
}
//判断将、帅是否见面
public static Pos flyKing(int id, int fromX, int fromY, int[][] piece) {
int cnt = 0;
boolean flag = false;
int i;
if (id == 1) { //将
for (i = fromY + 1; i <= 9; i++) {
if (piece[i][fromX] > 0 && piece[i][fromX] != 8) {
cnt++;
} else if (piece[i][fromX] == 8) {
flag = true;
break;
}
}
} else { //帅
for (i = fromY - 1; i >= 0; i--) {
if (piece[i][fromX] > 0 && piece[i][fromX] != 1) {
cnt++;
} else if (piece[i][fromX] == 1) {
flag = true;
break;
}
}
}
if (cnt == 0 && flag == true) {
return new Pos(fromX, i);
} else {
return new Pos(-1, -1);
}
}
//判断是否可以移动
public static boolean CanMove(int id, int fromX, int fromY, int toX, int toY, int[][] piece) {
if (fromX == 10 || fromY == 10 || toX == 10 || toY == 10) {
Log.e("chen", String.valueOf(fromX) + " " + String.valueOf(fromY) + " " + String.valueOf(toX) + " " + String.valueOf(toY));
}
if ((fromX != toX && fromY != toY) || IsSameSide(piece[fromY][fromX], piece[toY][toX]) == true) {
return false;
}
if (id == 1) { //车
int start, finish;
if (fromX == toX) {
if (fromY < toY) {
start = fromY + 1;
finish = toY;
} else {
start = toY + 1;
finish = fromY;
}
for (int i = start; i < finish; i++) {
if (piece[i][fromX] != 0) {
return false;
}
}
} else {
if (fromX < toX) {
start = fromX + 1;
finish = toX;
} else {
start = toX + 1;
finish = fromX;
}
for (int i = start; i < finish; i++) {
if (piece[fromY][i] != 0) {
return false;
}
}
}
} else { //炮
if (piece[toY][toX] == 0) {
int start, finish;
if (fromX == toX) {
if (fromY < toY) {
start = fromY + 1;
finish = toY;
} else {
start = toY + 1;
finish = fromY;
}
for (int i = start; i < finish; i++) {
if (piece[i][fromX] != 0) {
return false;
}
}
} else {
if (fromX < toX) {
start = fromX + 1;
finish = toX;
} else {
start = toX + 1;
finish = fromX;
}
for (int i = start; i < finish; i++) {
if (piece[fromY][i] != 0) {
return false;
}
}
}
} else {
int start, finish;
int count = 0;
if (fromX == toX) {
if (fromY < toY) {
start = fromY + 1;
finish = toY;
} else {
start = toY + 1;
finish = fromY;
}
for (int i = start; i < finish; i++) {
if (piece[i][fromX] != 0) {
count++;
}
}
} else {
if (fromX < toX) {
start = fromX + 1;
finish = toX;
} else {
start = toX + 1;
finish = fromX;
}
for (int i = start; i < finish; i++) {
if (piece[fromY][i] != 0) {
count++;
}
}
}
if (count != 1) {
return false;
}
}
}
return true;
}
//判断是否为红方,如果不是,则反转。
public static Pos reversePos(Pos pos, boolean IsReverse) {
return (IsReverse == false) ? pos : new Pos(8 - pos.x, 9 - pos.y);
}
}
AI.java
import Info.Pos;
import ChessMove.Rule;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import static com.example.a77304.chessgame.HomeActivity.zobrist;
import static com.example.a77304.chessgame.PvMActivity.transformTable;
public class AI {
//棋子力价值表
public static int[][][] pieceValue = new int[][][]{
//黑将
{
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
},
//黑士
{
{0, 0, 0, 20, 0, 20, 0, 0, 0},
{0, 0, 0, 0, 23, 0, 0, 0, 0},
{0, 0, 0, 20, 0, 20, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
},
//黑象
{
{0, 0, 20, 0, 0, 0, 20, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{18, 0, 0, 0, 23, 0, 0, 0, 18},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 20, 0, 0, 0, 20, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
},
//黑马
{
{88, 85, 90, 88, 90, 88, 90, 85, 88},
{85, 90, 92, 93, 78, 93, 92, 90, 85},
{93, 92, 94, 95, 92, 95, 94, 92, 93},
{92, 94, 98, 95, 98, 95, 98, 94, 92},
{90, 98, 101, 102, 103, 102, 101, 98, 90},
{90, 100, 99, 103, 104, 103, 99, 100, 90},
{93, 108, 100, 107, 100, 107, 100, 108, 93},
{92, 98, 99, 103, 99, 103, 99, 98, 92},
{90, 96, 103, 97, 94, 97, 103, 96, 90},
{90, 90, 90, 96, 90, 96, 90, 90, 90},
},
//黑车
{
{194, 206, 204, 212, 200, 212, 204, 206, 194},
{200, 208, 206, 212, 200, 212, 206, 208, 200},
{198, 208, 204, 212, 212, 212, 204, 208, 198},
{204, 209, 204, 212, 214, 212, 204, 209, 204},
{208, 212, 212, 214, 215, 214, 212, 212, 208},
{208, 211, 211, 214, 215, 214, 211, 211, 208},
{206, 213, 213, 216, 216, 216, 213, 213, 206},
{206, 208, 207, 214, 216, 214, 207, 208, 206},
{206, 212, 209, 216, 233, 216, 209, 212, 206},
{206, 208, 207, 213, 214, 213, 207, 208, 206},
},
//黑炮
{
{96, 96, 97, 99, 99, 99, 97, 96, 96},
{96, 97, 98, 98, 98, 98, 98, 97, 96},
{97, 96, 100, 99, 101, 99, 100, 96, 97},
{96, 96, 96, 96, 96, 96, 96, 96, 96},
{95, 96, 99, 96, 100, 96, 99, 96, 95},
{96, 96, 96, 96, 100, 96, 96, 96, 96},
{96, 99, 99, 98, 100, 98, 99, 99, 96},
{97, 97, 96, 91, 92, 91, 96, 97, 97},
{98, 98, 96, 92, 89, 92, 96, 98, 98},
{100, 100, 96, 91, 90, 91, 96, 100, 100},
},
//黑卒
{
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{7, 0, 7, 0, 15, 0, 7, 0, 7},
{7, 0, 13, 0, 16, 0, 13, 0, 7},
{14, 18, 20, 27, 29, 27, 20, 18, 14},
{19, 23, 27, 29, 30, 29, 27, 23, 19},
{19, 24, 32, 37, 37, 37, 32, 24, 19},
{19, 24, 34, 42, 44, 42, 34, 24, 19},
{9, 9, 9, 11, 13, 11, 9, 9, 9},
},
//红帅
{
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0},
{0, 0, 0, 8888, 8888, 8888, 0, 0, 0}
},
//红士
{
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 20, 0, 20, 0, 0, 0},
{0, 0, 0, 0, 23, 0, 0, 0, 0},
{0, 0, 0, 20, 0, 20, 0, 0, 0}
},
//红象
{
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 20, 0, 0, 0, 20, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{18, 0, 0, 0, 23, 0, 0, 0, 18},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 20, 0, 0, 0, 20, 0, 0}
},
//红马
{
{90, 90, 90, 96, 90, 96, 90, 90, 90},
{90, 96, 103, 97, 94, 97, 103, 96, 90},
{92, 98, 99, 103, 99, 103, 99, 98, 92},
{93, 108, 100, 107, 100, 107, 100, 108, 93},
{90, 100, 99, 103, 104, 103, 99, 100, 90},
{90, 98, 101, 102, 103, 102, 101, 98, 90},
{92, 94, 98, 95, 98, 95, 98, 94, 92},
{93, 92, 94, 95, 92, 95, 94, 92, 93},
{85, 90, 92, 93, 78, 93, 92, 90, 85},
{88, 85, 90, 88, 90, 88, 90, 85, 88}
},
//红车
{
{206, 208, 207, 213, 214, 213, 207, 208, 206},
{206, 212, 209, 216, 233, 216, 209, 212, 206},
{206, 208, 207, 214, 216, 214, 207, 208, 206},
{206, 213, 213, 216, 216, 216, 213, 213, 206},
{208, 211, 211, 214, 215, 214, 211, 211, 208},
{208, 212, 212, 214, 215, 214, 212, 212, 208},
{204, 209, 204, 212, 214, 212, 204, 209, 204},
{198, 208, 204, 212, 212, 212, 204, 208, 198},
{200, 208, 206, 212, 200, 212, 206, 208, 200},
{194, 206, 204, 212, 200, 212, 204, 206, 194}
},
//红炮
{
{100, 100, 96, 91, 90, 91, 96, 100, 100},
{98, 98, 96, 92, 89, 92, 96, 98, 98},
{97, 97, 96, 91, 92, 91, 96, 97, 97},
{96, 99, 99, 98, 100, 98, 99, 99, 96},
{96, 96, 96, 96, 100, 96, 96, 96, 96},
{95, 96, 99, 96, 100, 96, 99, 96, 95},
{96, 96, 96, 96, 96, 96, 96, 96, 96},
{97, 96, 100, 99, 101, 99, 100, 96, 97},
{96, 97, 98, 98, 98, 98, 98, 97, 96},
{96, 96, 97, 99, 99, 99, 97, 96, 96}
},
//红兵
{
{9, 9, 9, 11, 13, 11, 9, 9, 9},
{19, 24, 34, 42, 44, 42, 34, 24, 19},
{19, 24, 32, 37, 37, 37, 32, 24, 19},
{19, 23, 27, 29, 30, 29, 27, 23, 19},
{14, 18, 20, 27, 29, 27, 20, 18, 14},
{7, 0, 13, 0, 16, 0, 13, 0, 7},
{7, 0, 7, 0, 15, 0, 7, 0, 7},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0}
}
};
public static final int INF = 0x3f3f3f3f; //正无穷大
public static final int Win = 88888;
public static final int hashfEXACT = 1;
public static final int hashfALPHA = 2;
public static final int hashfBETA = 3;
public static boolean isMachineRed;
public static int ZobristKey;
public static long ZobristKeyCheck;
public static int maxDepth;
public static int goodValue;
public static int[][][][] historyTable = new int[9][10][9][10]; //历史表
public static int searchCnt = 0;
public AI() {
}
//迭代加深
public static Move getBestMove(int[][] piece, boolean isRedGo, int depth, int startZobristKey, long startZobristKeyCheck, Map<Long, Integer> ZobristInfo) {
int[][] pieceClone = new int[10][9];
for (int i = 0; i <= 9; i++) {
pieceClone[i] = piece[i].clone();
}
searchCnt = 0;
Move goodMove = new Move(new Pos(-1, -1), new Pos(-1, -1));
Move bestMove = new Move(new Pos(-1, -1), new Pos(-1, -1));
clearHistory();
for (int i = 2; i <= depth; i += 2) {
goodMove = getGoodMove(pieceClone, isRedGo, i, startZobristKey, startZobristKeyCheck, ZobristInfo);
if (goodValue >= -Win / 2) {
bestMove = goodMove;
}
}
if (bestMove.fromPos.equals(new Pos(-1, -1)) == true) {
bestMove = getLegalMove(pieceClone, isRedGo);
}
//Log.i("博弈树搜索算法比对","搜索结点数:"+String.valueOf(searchCnt));
return bestMove;
}
//PVS+置换表+历史表
public static Move getGoodMove(int[][] piece, boolean isRedGo, int depth, int startZobristKey, long startZobristKeyCheck, Map<Long, Integer> ZobristInfo) {
Move goodMove = new Move(new Pos(-1, -1), new Pos(-1, -1));
isMachineRed = isRedGo;
boolean FoundPv = false;
int value = -INF;
int alpha = -INF, beta = INF;
boolean isKingAlive = true;
ZobristKey = startZobristKey;
ZobristKeyCheck = startZobristKeyCheck;
maxDepth = depth;
LinkedList<Move> sortedMoves = allPossibleMoves(piece, isRedGo, depth, alpha, beta);
Iterator<Move> it = sortedMoves.iterator();
while (it.hasNext()) {
Move move = it.next();
Pos fromPos = move.fromPos;
Pos toPos = move.toPos;
int pieceID = piece[toPos.y][toPos.x];
piece[toPos.y][toPos.x] = piece[fromPos.y][fromPos.x];
piece[fromPos.y][fromPos.x] = 0;
updateZobrist(fromPos, toPos, piece[toPos.y][toPos.x], pieceID);
if (pieceID == 1 || pieceID == 8) {
isKingAlive = false;
}
if (ZobristInfo.get(ZobristKeyCheck) != null) {
value = -INF;
} else {
if (FoundPv) {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -alpha - 1, -alpha, isKingAlive);
if ((value > alpha) && (value < beta)) {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -beta, -alpha, isKingAlive);
}
} else {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -beta, -alpha, isKingAlive);
}
}
updateZobrist(fromPos, toPos, piece[toPos.y][toPos.x], pieceID);
piece[fromPos.y][fromPos.x] = piece[toPos.y][toPos.x];
piece[toPos.y][toPos.x] = pieceID;
if (pieceID == 1 || pieceID == 8) {
isKingAlive = true;
}
if (value > alpha) {
alpha = value;
FoundPv = true;
goodMove = new Move(fromPos, toPos);
}
}
goodValue = alpha;
addHistory(goodMove.fromPos, goodMove.toPos, (maxDepth - depth + 1) * (maxDepth - depth + 1));
return goodMove;
}
//通过算法获得最佳结果
public static int PVS(int[][] piece, boolean isRedGo, int depth, int alpha, int beta, boolean isKingAlive) {
int value = transformTable.readTransformTable(ZobristKey, ZobristKeyCheck, maxDepth - depth, alpha, beta); //局面评分
if (value != INF) {
return value;
}
if (isKingAlive == false) {
if (isMachineRed == true && isRedGo == true) {
return -Win + (maxDepth - depth);
} else if (isMachineRed == true && isRedGo == false) {
return Win - (maxDepth - depth);
} else if (isMachineRed == false && isRedGo == true) {
return Win - (maxDepth - depth);
} else {
return -Win + (maxDepth - depth);
}
}
if (depth <= 0) {
searchCnt++;
if (Rule.isDead(piece, isMachineRed)) {
value = -Win + (maxDepth - depth + 2);
} else {
value = evaluate(piece);
}
return value;
}
boolean FoundPv = false;
int hashf = hashfALPHA;
int fatherZobristKey = ZobristKey;
long fatherZobristKeyCheck = ZobristKeyCheck;
value = -INF;
Move goodMove = new Move(new Pos(-1, -1), new Pos(-1, -1));
LinkedList<Move> sortedMoves = allPossibleMoves(piece, isRedGo, depth, alpha, beta);
Iterator<Move> it = sortedMoves.iterator();
while (it.hasNext()) {
Move move = it.next();
Pos fromPos = move.fromPos;
Pos toPos = move.toPos;
int pieceID = piece[toPos.y][toPos.x];
if (maxDepth == 6 && depth == 1 && pieceID == 0) continue;
piece[toPos.y][toPos.x] = piece[fromPos.y][fromPos.x];
piece[fromPos.y][fromPos.x] = 0;
updateZobrist(fromPos, toPos, piece[toPos.y][toPos.x], pieceID);
if (pieceID == 1 || pieceID == 8) {
isKingAlive = false;
}
if (FoundPv) {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -alpha - 1, -alpha, isKingAlive);
if ((value > alpha) && (value < beta)) {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -beta, -alpha, isKingAlive);
}
} else {
value = -PVS(piece, (isRedGo == true) ? false : true, depth - 1, -beta, -alpha, isKingAlive);
}
updateZobrist(fromPos, toPos, piece[toPos.y][toPos.x], pieceID);
piece[fromPos.y][fromPos.x] = piece[toPos.y][toPos.x];
piece[toPos.y][toPos.x] = pieceID;
if (pieceID == 1 || pieceID == 8) {
isKingAlive = true;
}
if (value >= beta) {
transformTable.saveTransformTable(fatherZobristKey, fatherZobristKeyCheck, maxDepth - depth, beta, hashfBETA);
addHistory(fromPos, toPos, (maxDepth - depth + 1) * (maxDepth - depth + 1));
return beta;
}
if (value > alpha) {
alpha = value;
FoundPv = true;
hashf = hashfEXACT;
goodMove = new Move(fromPos, toPos);
}
}
transformTable.saveTransformTable(fatherZobristKey, fatherZobristKeyCheck, maxDepth - depth, alpha, hashf);
if (hashf == hashfEXACT) {
addHistory(goodMove.fromPos, goodMove.toPos, (maxDepth - depth + 1) * (maxDepth - depth + 1));
}
return alpha;
}
//生成所有可能走法
public static LinkedList<Move> allPossibleMoves(int[][] piece, boolean isRedGo, int depth, int alpha, int beta) {
LinkedList<Move> sortedMoves = new LinkedList<Move>();
LinkedList<Move> transformMoves = new LinkedList<Move>();
LinkedList<Move> eatMoves = new LinkedList<Move>();
LinkedList<Move> historyMoves = new LinkedList<Move>();
//遍历整个棋盘
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if ((isRedGo == false && piece[i][j] >= 1 && piece[i][j] <= 7) || (isRedGo == true && piece[i][j] >= 8 && piece[i][j] <= 14)) {
List<Pos> ret = Rule.PossibleMoves(piece, j, i, piece[i][j]); //某个位置的棋子的全部走法
Pos fromPos = new Pos(j, i);
Iterator<Pos> it = ret.iterator();
while (it.hasNext()) {
Pos toPos = it.next();
updateZobrist(fromPos, toPos, piece[fromPos.y][fromPos.x], piece[toPos.y][toPos.x]);
if (transformTable.readTransformTable(ZobristKey, ZobristKeyCheck, maxDepth - depth, alpha, beta) != INF) {
transformMoves.add(new Move(fromPos, toPos));
} else {
if (piece[toPos.y][toPos.x] != 0) {
eatMoves.add(new Move(fromPos, toPos));
} else {
if (historyMoves.isEmpty()) {
historyMoves.add(new Move(fromPos, toPos));
} else {
Move firstMove = historyMoves.getFirst();
int firstVal = getHistory(firstMove.fromPos, firstMove.toPos);
int curVal = getHistory(fromPos, toPos);
if (curVal >= firstVal) {
historyMoves.addFirst(new Move(fromPos, toPos));
} else {
historyMoves.addLast(new Move(fromPos, toPos));
}
}
}
}
updateZobrist(fromPos, toPos, piece[fromPos.y][fromPos.x], piece[toPos.y][toPos.x]);
}
}
}
}
sortedMoves.addAll(transformMoves);
sortedMoves.addAll(eatMoves);
sortedMoves.addAll(historyMoves);
return sortedMoves;
}
public static void clearHistory() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 10; j++) {
for (int k = 0; k < 9; k++) {
for (int p = 0; p < 10; p++) {
historyTable[i][j][k][p] = historyTable[i][j][k][p] = 0;
}
}
}
}
}
public static int getHistory(Pos fromPos, Pos toPos) {
return historyTable[fromPos.x][fromPos.y][toPos.x][toPos.y];
}
public static void addHistory(Pos fromPos, Pos toPos, int val) {
historyTable[fromPos.x][fromPos.y][toPos.x][toPos.y] += val;
}
public static void updateZobrist(Pos fromPos, Pos toPos, int fromID, int toID) {
ZobristKey = ZobristKey ^ zobrist.ZobristTable[fromID - 1][fromPos.y][fromPos.x];
ZobristKeyCheck = ZobristKeyCheck ^ zobrist.ZobristTableCheck[fromID - 1][fromPos.y][fromPos.x];
ZobristKey = ZobristKey ^ zobrist.ZobristTable[fromID - 1][toPos.y][toPos.x];
ZobristKeyCheck = ZobristKeyCheck ^ zobrist.ZobristTableCheck[fromID - 1][toPos.y][toPos.x];
if (toID > 0) {
ZobristKey = ZobristKey ^ zobrist.ZobristTable[toID - 1][toPos.y][toPos.x];
ZobristKeyCheck = ZobristKeyCheck ^ zobrist.ZobristTableCheck[toID - 1][toPos.y][toPos.x];
}
}
//局面评分
public static int evaluate(int[][] piece) {
int score = 0;
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if (piece[i][j] >= 1 && piece[i][j] <= 7) {
score += pieceValue[piece[i][j] - 1][i][j];
} else if (piece[i][j] >= 8 && piece[i][j] <= 14) {
score -= pieceValue[piece[i][j] - 1][i][j];
}
}
}
if (isMachineRed) score *= -1;
return score;
}
//判断是否有杀招走法
public static Move getLegalMove(int piece[][], boolean isRedKing) { //参数true表示红棋
if (isRedKing == true) {
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if (piece[i][j] >= 8 && piece[i][j] <= 14) {
//生成某个位置的棋子的所有走法的位置
List<Pos> moves = Rule.PossibleMoves(piece, j, i, piece[i][j]);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
int tmp = piece[pos.y][pos.x]; //标记可以走的位置的其他棋子号
if (tmp == 1) { //当前棋子下一步可以吃掉黑方将
return new Move(new Pos(j, i), pos); //返回杀招的走法
}
piece[pos.y][pos.x] = piece[i][j];
piece[i][j] = 0;
if (Rule.isKingDanger(piece, true) == false) { //非当前棋子的可以吃掉将
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
return new Move(new Pos(j, i), pos); //返回杀招的走法
}
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
}
}
}
}
} else { //参数false表示黑棋
for (int i = 0; i <= 9; i++) {
for (int j = 0; j <= 8; j++) {
if (piece[i][j] >= 1 && piece[i][j] <= 7) {
List<Pos> moves = Rule.PossibleMoves(piece, j, i, piece[i][j]);
Iterator<Pos> it = moves.iterator();
while (it.hasNext()) {
Pos pos = it.next();
int tmp = piece[pos.y][pos.x];
if (tmp == 8) {
return new Move(new Pos(j, i), pos);
}
piece[pos.y][pos.x] = piece[i][j];
piece[i][j] = 0;
if (Rule.isKingDanger(piece, false) == false) {
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
return new Move(new Pos(j, i), pos);
}
piece[i][j] = piece[pos.y][pos.x];
piece[pos.y][pos.x] = tmp;
}
}
}
}
}
return new Move(new Pos(-1, -1), new Pos(-1, -1));
}
}
未完......