Android實現五子棋遊戲(二) 人機對戰實現
下面簡單介紹一下實現人機對戰的思路以及程式碼實現:
思路
人機對戰的總體思路是通過遍歷所有的無棋子的位置,通過模擬在每個無棋子點落子,並根據其周圍的棋子來獲取該點的兩個優先順序評分:
模擬使用者棋子來獲取一個優先順序評分,用於防守(使用者優先順序)
模擬AI棋子來獲取一個優先順序評分,用於進攻(AI優先順序)
然後通過使用者優先順序以及AI優先順序來獲取該點的最終的優先順序評分,最後遍歷所有無棋子的優先順序評分,找到優先順序最高的點落子。
邏輯程式碼分析
AI.java
/**
* Created by ZhangHao on 2017/7/20.
* 五子棋簡單的AI
*/
public class AI implements Runnable {
//棋盤資訊
private int[][] chessArray;
//電腦執子(預設黑子)
private int aiChess = FiveChessView.BLACK_CHESS;
//所有無子位置的資訊集合
private List<Point> pointList;
//ai落子結束回撥
private AICallBack callBack;
//棋盤寬高(panelLength)
private int panelLength;
/**
* 評分表(落子優先順序評分)
* FIVE 至少能五子相連
* LIVE_X 表示X個連在一起的子,兩邊都沒有被堵住
* DEAD_X 表示X個連在一起的子,一邊被堵住
* DEAD 表示兩邊被堵住
*/
private final static int FIVE = 10000;
private final static int LIVE_FOUR = 4500;
private final static int DEAD_FOUR = 2000;
private final static int LIVE_THREE = 900;
private final static int DEAD_THREE = 400;
private final static int LIVE_TWO = 150;
private final static int DEAD_TWO = 70 ;
private final static int LIVE_ONE = 30;
private final static int DEAD_ONE = 15;
private final static int DEAD = 1;
public AI(int[][] chessArray, AICallBack callBack) {
pointList = new ArrayList<>();
this.chessArray = chessArray;
this.callBack = callBack;
this.panelLength = chessArray.length;
}
//ai開始落子
public void aiBout() {
new Thread(this).start();
}
//判斷落子的優先順序評分
private void checkPriority(Point p) {
int aiPriority = checkSelf(p.getX(), p.getY());
int userPriority = checkUser(p.getX(), p.getY());
p.setPriority(aiPriority >= userPriority ? aiPriority : userPriority);
}
//獲取當前點,ai優先順序評分
private int checkSelf(int x, int y) {
return getHorizontalPriority(x, y, aiChess)
+ getVerticalPriority(x, y, aiChess)
+ getLeftSlashPriority(x, y, aiChess)
+ getRightSlashPriority(x, y, aiChess);
}
//獲取當前點,玩家優先順序評分
private int checkUser(int x, int y) {
int userChess;
if (aiChess == FiveChessView.WHITE_CHESS) {
userChess = FiveChessView.BLACK_CHESS;
} else {
userChess = FiveChessView.WHITE_CHESS;
}
return getHorizontalPriority(x, y, userChess)
+ getVerticalPriority(x, y, userChess)
+ getLeftSlashPriority(x, y, userChess)
+ getRightSlashPriority(x, y, userChess);
}
//通過執行緒選擇最佳落點
@Override
public void run() {
//清空pointList
pointList.clear();
int blankCount = 0;
for (int i = 0; i < panelLength; i++)
for (int j = 0; j < panelLength; j++) {
if (chessArray[i][j] == FiveChessView.NO_CHESS) {
Point p = new Point(i, j);
checkPriority(p);
pointList.add(p);
blankCount++;
}
}
//遍歷pointList,找到優先順序最高的Point
Point max = pointList.get(0);
for (Point point : pointList) {
if (max.getPriority() < point.getPriority()) {
max = point;
}
}
//AI先手或者使用者先手第一次落子時
if (blankCount >= panelLength * panelLength - 1) {
max = getStartPoint();
}
//休眠2秒
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//落子,並將結果回撥
chessArray[max.getX()][max.getY()] = aiChess;
callBack.aiAtTheBell();
}
public void setAiChess(int aiChess) {
this.aiChess = aiChess;
}
//AI先手或者使用者先手第一次落子時,隨機獲取一個點落子
private Point getStartPoint() {
//該點是否可用標識
boolean isUse = true;
//在中間位置隨機生成一個點
Random random = new Random();
int x = random.nextInt(5) + 5;
int y = random.nextInt(5) + 5;
//確保周圍不存在其他棋子
for (int i = x - 1; i <= x + 1; i++)
for (int j = y - 1; j <= y + 1; j++) {
if (chessArray[i][j] != FiveChessView.NO_CHESS) {
isUse = false;
}
}
if (isUse) {
return new Point(x, y);
} else {
return getStartPoint();
}
}
/**
* 判斷指定點chessArray[x][y]橫向優先順序
*
* @param x 陣列下標
* @param y 陣列下標
* @param chess 棋子顏色
* @return 該點優先順序評分
*/
private int getHorizontalPriority(int x, int y, int chess) {
//指定棋子相連數
int connectCount = 1;
//左邊是否被堵住
boolean isStartStem = false;
//右邊是否被堵住
boolean isEndStem = false;
//先向左邊計算
//如果當前位置y = 0,即在棋盤的邊緣位置,則左邊必然被堵住
if (y == 0) {
isStartStem = true;
} else {
//遍歷左邊
for (int i = y - 1; i >= 0; i--) {
//如果不是指定棋子
if (chessArray[x][i] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isStartStem = chessArray[x][i] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == 0) {
//在邊緣位置,則被擋住
isStartStem = true;
}
}
}
}
//再向右邊計算
//如果當前位置y = panelLength,即在棋盤的邊緣位置,則右邊必然被堵住
if (y == panelLength - 1) {
isEndStem = true;
} else {
//遍歷右邊
for (int i = y + 1; i < panelLength; i++) {
//如果不是指定棋子
if (chessArray[x][i] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isEndStem = chessArray[x][i] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == panelLength - 1) {
//在邊緣位置,則被擋住
isEndStem = true;
}
}
}
}
//計算優先順序評分
return calcPriority(connectCount, isStartStem, isEndStem);
}
/**
* 判斷指定點chessArray[x][y]縱向優先順序
*
* @param x 陣列下標
* @param y 陣列下標
* @param chess 棋子顏色
* @return 該點優先順序評分
*/
private int getVerticalPriority(int x, int y, int chess) {
//指定棋子相連數
int connectCount = 1;
//左邊是否被堵住
boolean isStartStem = false;
//右邊是否被堵住
boolean isEndStem = false;
//先向上邊計算
//在棋盤的邊緣位置,則上邊必然被堵住
if (x == 0) {
isStartStem = true;
} else {
//向上遍歷
for (int i = x - 1; i >= 0; i--) {
//如果不是指定棋子
if (chessArray[i][y] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isStartStem = chessArray[i][y] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == 0) {
//在邊緣位置,則被擋住
isStartStem = true;
}
}
}
}
//再向右邊計算
//如果當前位置y = panelLength,即在棋盤的邊緣位置,則下邊必然被堵住
if (x == panelLength - 1) {
isEndStem = true;
} else {
//向下遍歷
for (int i = x + 1; i < panelLength; i++) {
//如果不是指定棋子
if (chessArray[i][y] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isEndStem = chessArray[i][y] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == panelLength - 1) {
//在邊緣位置,則被擋住
isEndStem = true;
}
}
}
}
//計算優先順序評分
return calcPriority(connectCount, isStartStem, isEndStem);
}
/**
* 判斷指定點chessArray[x][y]左斜(左上到右下)優先順序
*
* @param x 陣列下標
* @param y 陣列下標
* @param chess 棋子顏色
* @return 該點優先順序評分
*/
private int getLeftSlashPriority(int x, int y, int chess) {
//指定棋子相連數
int connectCount = 1;
//左邊是否被堵住
boolean isStartStem = false;
//右邊是否被堵住
boolean isEndStem = false;
//先向左上計算
//在棋盤的邊緣位置,則左上必然被堵住
if (x == 0 || y == 0) {
isStartStem = true;
} else {
//向左上遍歷
for (int i = x - 1, j = y - 1; i >= 0 && j >= 0; i--, j--) {
//如果不是指定棋子
if (chessArray[i][j] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isStartStem = chessArray[i][j] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == 0 || j == 0) {
//在邊緣位置,則被擋住
isStartStem = true;
}
}
}
}
//再向右下計算
//在棋盤的邊緣位置,則右下必然被堵住
if (x == panelLength - 1 || y == panelLength - 1) {
isEndStem = true;
} else {
//遍歷右下
for (int i = x + 1, j = y + 1; i < panelLength && j < panelLength; i++, j++) {
//如果不是指定棋子
if (chessArray[i][j] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isEndStem = chessArray[i][j] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == panelLength - 1 || j == panelLength - 1) {
//在邊緣位置,則被擋住
isEndStem = true;
}
}
}
}
//計算優先順序評分
return calcPriority(connectCount, isStartStem, isEndStem);
}
/**
* 判斷指定點chessArray[x][y]右斜(右上到左下)優先順序
*
* @param x 陣列下標
* @param y 陣列下標
* @param chess 棋子顏色
* @return 該點優先順序評分
*/
private int getRightSlashPriority(int x, int y, int chess) {
//指定棋子相連數
int connectCount = 1;
//左邊是否被堵住
boolean isStartStem = false;
//右邊是否被堵住
boolean isEndStem = false;
//先向右上計算
//在棋盤的邊緣位置,則右上必然被堵住
if (x == panelLength - 1 || y == 0) {
isStartStem = true;
} else {
//向左上遍歷
for (int i = x + 1, j = y - 1; i < panelLength && j >= 0; i++, j--) {
//如果不是指定棋子
if (chessArray[i][j] != chess) {
//不是自己的棋子,則左邊被堵住或者是空位
isStartStem = chessArray[i][j] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == panelLength - 1 || j == 0) {
//在邊緣位置,則被擋住
isStartStem = true;
}
}
}
}
//再向左下計算
//在棋盤的邊緣位置,則左下必然被堵住
if (x == 0 || y == panelLength - 1) {
isEndStem = true;
} else {
//遍歷右邊
for (int i = x - 1, j = y + 1; i >= 0 && j < panelLength; i--, j++) {
//如果不是指定棋子
if (chessArray[i][j] != chess) {
//不是自己的棋子,則被堵住或者是空位
isEndStem = chessArray[i][j] != FiveChessView.NO_CHESS;
break;
} else {
connectCount++;
if (i == 0 || j == panelLength - 1) {
//在邊緣位置,則被擋住
isEndStem = true;
}
}
}
}
//計算優先順序評分
return calcPriority(connectCount, isStartStem, isEndStem);
}
/**
* 根據相連數以及開始結束是否被堵住計算優先順序評分
*
* @param connectCount 相連數
* @param isStartStem 開始是否被堵住
* @param isEndStem 結束是否被堵住
* @return 優先順序評分
*/
private int calcPriority(int connectCount, boolean isStartStem, boolean isEndStem) {
//優先順序評分
int priority = 0;
if (connectCount >= 5) {
//能夠五連
priority = FIVE;
} else {
//不能五連
if (isStartStem && isEndStem) {
//開始結束都被堵住,死棋
priority = DEAD;
} else if (isStartStem == isEndStem) {
//兩邊都沒被堵住
if (connectCount == 4) {
priority = LIVE_FOUR;
} else if (connectCount == 3) {
priority = LIVE_THREE;
} else if (connectCount == 2) {
priority = LIVE_TWO;
} else if (connectCount == 1) {
priority = LIVE_ONE;
}
} else {
//其中一邊被堵住
if (connectCount == 4) {
priority = DEAD_FOUR;
} else if (connectCount == 3) {
priority = DEAD_THREE;
} else if (connectCount == 2) {
priority = DEAD_TWO;
} else if (connectCount == 1) {
priority = DEAD_ONE;
}
}
}
return priority;
}
}
AI是實現人機對戰的核心類,實現了包括獲取所有無棋子點的優先順序,落子,AI先手落子等邏輯。這裡簡單介紹下AI的相關方法:
- 優先順序評分表 :這裡的評分表來自網路,主要的作用是模擬落子可能出現的不同相連情況進行評分,從而獲取使用者以及AI評分優先順序,進而確定最終的落子點。
aiBout:開啟執行緒,計算最佳(優先順序評分最高)的落子點,並落子。
checkPriority : 判斷指定點的落子的優先順序評分
checkSelf:獲取指定點,ai優先順序評分
checkUser:獲取指定點,玩家優先順序評分
getHorizontalPriority(getVerticalPriority/getLeftSlashPriority/getRightSlashPriority):四個方法邏輯上基本相同,分別是獲取指定點橫向/縱向/左斜/右斜方向上的指定顏色的棋子的評分優先順序。這裡判斷的方法主要是獲取相同顏色棋子的相連數,以及開始和結束位置是否被堵住(在棋盤邊緣或者有因為其他顏色的棋子而無法落子的情況),在利用這些引數通過calcPriority方法獲取優先順序評分。
calcPriority:根據相連數以及開始結束是否被堵住來判斷當前型別(五連/活四/活三等),然後根據評分表計算優先順序評分。
getStartPoint:主要是在AI先手或者使用者先手第一次落子時,通過隨機數在棋盤的中心位置隨機生成一個落子點。
Point
/**
* Created by ZhangHao on 2017/7/25.
* 記錄每個點的優先順序
*/
public class Point {
//在陣列中位置
private int x, y;
//該點的優先順序(AI根據優先順序落子)
private int priority = 0;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
@Override
public String toString() {
return "Point{" +
"x=" + x +
", y=" + y +
", priority=" + priority +
'}';
}
}
Point就是用來儲存每個點的座標以及優先順序,沒什麼太多可說的。
AICallBack
/**
* Created by ZhangHao on 2017/7/25.
* AI落子完成回撥
*/
public interface AICallBack {
void aiAtTheBell();
}
FiveChessView
/**
* Created by ZhangHao on 2017/6/27.
* 五子棋 View
*/
public class FiveChessView extends View implements View.OnTouchListener {
//畫筆
private Paint paint;
//棋子陣列
private int[][] chessArray;
//當前下棋順序(預設白棋先下)
private boolean isWhite = true;
//遊戲是否結束
private boolean isGameOver = false;
//bitmap
private Bitmap whiteChess;
private Bitmap blackChess;
//Rect
private Rect rect;
//棋盤寬高
private float len;
//棋盤格數
private int GRID_NUMBER = 15;
//每格之間的距離
private float preWidth;
//邊距
private float offset;
//回撥
private GameCallBack callBack;
//當前黑白棋勝利次數
private int whiteChessCount, blackChessCount;
//是否是玩家的回合
private boolean isUserBout = true;
//玩家執子顏色
private int userChess = WHITE_CHESS;
//玩家/AI勝利次數
private int userScore = 0, aiScore = 0;
/**
* 一些常量
*/
//白棋
public static final int WHITE_CHESS = 1;
//黑棋
public static final int BLACK_CHESS = 2;
//無棋
public static final int NO_CHESS = 0;
//白棋贏
public static final int WHITE_WIN = 101;
//黑棋贏
public static final int BLACK_WIN = 102;
//平局
public static final int NO_WIN = 103;
public FiveChessView(Context context) {
this(context, null);
}
public FiveChessView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FiveChessView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//初始化Paint
paint = new Paint();
//設定抗鋸齒
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
//初始化chessArray
chessArray = new int[GRID_NUMBER][GRID_NUMBER];
//初始化棋子圖片bitmap
whiteChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.white_chess);
blackChess = BitmapFactory.decodeResource(context.getResources(), R.drawable.black_chess);
//初始化勝利局數
whiteChessCount = 0;
blackChessCount = 0;
//初始化Rect
rect = new Rect();
//設定點選監聽
setOnTouchListener(this);
//重置棋盤狀態
for (int i = 0; i < GRID_NUMBER; i++) {
for (int j = 0; j < GRID_NUMBER; j++) {
chessArray[i][j] = 0;
}
}
}
/**
* 重新測量寬高,確保寬高一樣
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//獲取高寬值
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//獲取寬高中較小的值
int len = width > height ? height : width;
//重新設定寬高
setMeasuredDimension(len, len);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//棋盤為一個GRID_NUMBER*GRID_NUMBER的正方形,所有棋盤寬高必須一樣
len = getWidth() > getHeight() ? getHeight() : getWidth();
preWidth = len / GRID_NUMBER;
//邊距
offset = preWidth / 2;
//棋盤線條
for (int i = 0; i < GRID_NUMBER; i++) {
float start = i * preWidth + offset;
//橫線
canvas.drawLine(offset, start, len - offset, start, paint);
//豎線
canvas.drawLine(start, offset, start, len - offset, paint);
}
//繪製棋子
for (int i = 0; i < GRID_NUMBER; i++) {
for (int j = 0; j < GRID_NUMBER; j++) {
//rect中點座標
float rectX = offset + i * preWidth;
float rectY = offset + j * preWidth;
//設定rect位置
rect.set((int) (rectX - offset), (int) (rectY - offset),
(int) (rectX + offset), (int) (rectY + offset));
//遍歷chessArray
switch (chessArray[i][j]) {
case WHITE_CHESS:
//繪製白棋
canvas.drawBitmap(whiteChess, null, rect, paint);
break;
case BLACK_CHESS:
//繪製黑棋
canvas.drawBitmap(blackChess, null, rect, paint);
break;
}
}
}
}
/**
* 判斷是否結束
*/
private void checkGameOver() {
//獲取落子的顏色(如果當前是白棋,則落子是黑棋)
int chess = isWhite ? BLACK_CHESS : WHITE_CHESS;
//棋盤是否填滿
boolean isFull = true;
//遍歷chessArray
for (int i = 0; i < GRID_NUMBER; i++) {
for (int j = 0; j < GRID_NUMBER; j++) {
//判斷棋盤是否填滿
if (chessArray[i][j] != BLACK_CHESS && chessArray[i][j] != WHITE_CHESS) {
isFull = false;
}
//只需要判斷落子是否五連即可
if (chessArray[i][j] == chess) {
//判斷五子相連
if (isFiveSame(i, j)) {
//五子相連遊戲結束
isGameOver = true;
if (callBack != null) {
//判斷黑白棋勝利
if (chess == WHITE_CHESS) {
whiteChessCount++;
} else {
blackChessCount++;
}
//判斷玩家/AI 勝利
if (userChess == chess) {
userScore++;
} else {
aiScore++;
}
callBack.GameOver(chess == WHITE_CHESS ? WHITE_WIN : BLACK_WIN);
}
return;
}
}
}
}
//如果棋盤填滿,平局結束
if (isFull) {
isGameOver = true;
if (callBack != null) {
callBack.GameOver(NO_WIN);
}
}
}
/**
* 重置遊戲
*/
public void resetGame() {
isGameOver = false;
//重置棋盤狀態
for (int i = 0; i < GRID_NUMBER; i++) {
for (int j = 0; j < GRID_NUMBER; j++) {
chessArray[i][j] = 0;
}
}
//更新UI
postInvalidate();
}
/**
* 判斷是否存在五子相連
*
* @return
*/
private boolean isFiveSame(int x, int y) {
//判斷橫向
if (x + 4 < GRID_NUMBER) {
if (chessArray[x][y] == chessArray[x + 1][y] && chessArray[x][y] == chessArray[x + 2][y]
&& chessArray[x][y] == chessArray[x + 3][y] && chessArray[x][y] == chessArray[x + 4][y]) {
return true;
}
}
//判斷縱向
if (y + 4 < GRID_NUMBER) {
if (chessArray[x][y] == chessArray[x][y + 1] && chessArray[x][y] == chessArray[x][y + 2]
&& chessArray[x][y] == chessArray[x][y + 3] && chessArray[x][y] == chessArray[x][y + 4]) {
return true;
}
}
//判斷斜向(左上到右下)
if (y + 4 < GRID_NUMBER && x + 4 < GRID_NUMBER) {
if (chessArray[x][y] == chessArray[x + 1][y + 1] && chessArray[x][y] == chessArray[x + 2][y + 2]
&& chessArray[x][y] == chessArray[x + 3][y + 3] && chessArray[x][y] == chessArray[x + 4][y + 4]) {
return true;
}
}
//判斷斜向(左下到右上)
if (y - 4 > 0 && x + 4 < GRID_NUMBER) {
if (chessArray[x][y] == chessArray[x + 1][y - 1] && chessArray[x][y] == chessArray[x + 2][y - 2]
&& chessArray[x][y] == chessArray[x + 3][y - 3] && chessArray[x][y] == chessArray[x + 4][y - 4]) {
return true;
}
}
return false;
}
//電腦判斷遊戲結束
public void checkAiGameOver() {
isWhite = userChess == WHITE_CHESS;
checkGameOver();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!isGameOver && isUserBout) {
//獲取按下時的位置
float downX = event.getX();
float downY = event.getY();
//點選的位置在棋盤上
if (downX >= offset / 2 && downX <= len - offset / 2
&& downY >= offset / 2 && downY <= len - offset / 2) {
//獲取棋子對應的位置
int x = (int) (downX / preWidth);
int y = (int) (downY / preWidth);
//判斷當前位置是否已經有子
if (chessArray[x][y] != WHITE_CHESS &&
chessArray[x][y] != BLACK_CHESS) {
//給陣列賦值
chessArray[x][y] = userChess;
//修改當前落子顏色
isWhite = userChess == WHITE_CHESS;
//修改當前為電腦執子
isUserBout = false;
//更新棋盤
postInvalidate();
//判斷是否結束
checkGameOver();
//回調當前執子
if (callBack != null) {
callBack.ChangeGamer(isWhite);
}
}
}
} else if (isGameOver) {
Toast.makeText(getContext(), "遊戲已經結束,請重新開始!",
Toast.LENGTH_SHORT).show();
}
break;
}
return false;
}
public void setCallBack(GameCallBack callBack) {
this.callBack = callBack;
}
public int getWhiteChessCount() {
return whiteChessCount;
}
public int getBlackChessCount() {
return blackChessCount;
}
public int[][] getChessArray() {
return chessArray;
}
public void setUserBout(boolean userBout) {
isUserBout = userBout;
}
public void setUserChess(int userChess) {
this.userChess = userChess;
}
public int getUserScore() {
return userScore;
}
public int getAiScore() {
return aiScore;
}
}
FiveChessView主要實現五子棋遊戲的基本邏輯,包括棋子繪製,遊戲勝利判斷,重開遊戲等邏輯,上篇部落格已經介紹過了,主要講一下修改的部分:
新增一些變數
- isUserBout : 用於判斷是否是玩家的回合(非玩家回合無法落子)
- userChess:玩家執子顏色
- userScore/aiScore :玩家/AI勝利次數
新增checkAiGameOver方法,用於AI落子結束,判斷遊戲是否結束。
在onTouch中增加了一個判斷條件,當時AI回合時,使用者無法落子。
其他的就是一些GET/SET方法,沒什麼好說的。
GameCallBack
/**
* Created by ZhangHao on 2017/6/27.
* 遊戲相關回調
*/
public interface GameCallBack {
//遊戲結束回撥
void GameOver(int winner);
//遊戲更換執子回撥
void ChangeGamer(boolean isWhite);
}
佈局程式碼分析
佈局檔案
activity佈局檔案
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/back_ground"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical">
<!--遊戲資訊-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<!--玩家資訊-->
<LinearLayout
android:layout_width=
相關推薦
Android實現五子棋遊戲(二) 人機對戰實現
下面簡單介紹一下實現人機對戰的思路以及程式碼實現:
思路
人機對戰的總體思路是通過遍歷所有的無棋子的位置,通過模擬在每個無棋子點落子,並根據其周圍的棋子來獲取該點的兩個優先順序評分:
模擬使用者棋子來獲取一個優先順序評分,用於防守(使用者
遊戲開發經驗談(二):對戰類全球服遊戲的設計與實現
bbr tro 機房 產品 sql 服務器 通過 簡單 自己實現 上篇文章《遊戲開發經驗談(一):遊戲架構裏隱藏的五個坑及其應對方案》,我們主要講解了遊戲架構設計當中隱藏的一些坑及其應對方案,錯過的小夥伴可以點擊鏈接回溯之前的內容。本期內容,將會重點介紹對戰類全球服遊戲的設
五子棋專案的實現(三)人機對戰類的具體設計
在之前描述了博弈樹演算法的思想,現在則是關鍵類的設計實現。在具體的過程中我們先要設計一個遍歷棋型演算法,來遍歷整個棋盤中的各種棋型
通過最後返回值的不同,來確定不同的棋型
當中有評估函式對當前的棋型進行打分。再選取區域性最優的幾個落子點作為下一步擴充套件的節點。
//bwf 棋色
從零開始實現放置遊戲(二)——框架搭建
上一篇,我們講解了遊戲的大概背景,知道了要做什麼內容。現在已經可以開始搭建遊戲的程式碼框架。
整體架構
我們將整個專案命名為idlewow,目前包含以下幾個模組idlewow-facade, idlewow-core, idlewow-hessianserver, idlewow-rms, idl
Android Design Support Library(二)用NavigationView實現抽屜菜單界面
mpi tar bar board contex != print 滑動 頭部
NavigationView在MD設計中很重要,之前Google也提出了使用DrawerLayout來實現導航抽屜。這次,在Android Design Support Li
Android進階筆記:AIDL內部實現詳解 (二)
ucc == 筆記 null stack 直接 android 最好 public
接著上一篇分析的aidl的流程解析。知道了aidl主要就是利用Ibinder來實現跨進程通信的。既然是通過對Binder各種方法的封裝,那也可以不使用aidl自己通過Binder來實現跨進
Android廣播機制實現原始碼淺析(二)
緊接著上篇的分析,我們現在來分析一下處理廣播的程式碼流程,也就是在方法queue.scheduleBroadcastsLocked();之後的操作
這些方法在BroadcastQueue.java中。在這裡能看到我們常說的廣播超時,以及我們重寫onReceive什麼時候執行
android介面設計筆記(二)實現頂部底部二級導航欄
下載demo:https://github.com/linliangliang/BottomNavagationBar
二級導航欄的實現是在之前學習導航欄的兩種實現方式的基礎上實現的。
1、https://blog.csdn.net/qq_25066049/article/details/8
android介面設計筆記(二)底部導航欄的兩種實現方式
demo:https://github.com/linliangliang/BottomNavagationBar
android底部導航欄的實現方式比較多,今天學習其中兩種方式: 一、使用tabLayout+Fragment實現。 二、使用BottomNavagationBar實現。
第一
Unity開發Android遊戲(二)Hello world!
在設定好開發環境後,我們開始編寫一個最簡單的程式,也就是hello world,並讓它在手機上跑起來。
1,建立專案
(1),新建一個空專案。【File】-->【New Project】
(2),新建一個2D背景,用於襯托UI。【GameObject】--&g
Android Design Support Library(二)用NavigationView實現抽屜選單介面
NavigationView在MD設計中非常重要,之前Google也提出了使用DrawerLayout來實現導航抽屜。這次,在Android Design Support Library中,Google提供了NavigationView來實現導航選單介面。
Android Studio Jni開發(二)實現Native呼叫java方法和Native呼叫Android API
這一篇主要內容是Native呼叫java方法和Native呼叫Android API,以及External Tools快速生成.h檔案,依然是使用NDK方式編譯,如果是複製貼上黨,建議跟本文用一樣的工程名,本文後面會提供demo連結
一、建立工程
1.建立名為Jnites
WebRTC-Android 原始碼導讀(二):預覽實現分析
在本系列第一篇中,我們分析了 WebRTC-Android 相機採集的實現,本文中我們將分析預覽的實現。有過一定相機開發經驗的朋友可能會疑惑,預覽還有什麼好分析的,不是直接 camera.setPreviewDisplay 或者 camera.setPreviewTextur
android shape的使用(二)之實現圓角邊框
首先,在res下面新建一個資料夾drawable,在drawable下面新建三個xml檔案:shape_corner_down.xml、shape_corner_up.xml和shape_corner.xml,分別是下面兩個角是圓角邊框,上面兩個角是圓角邊框,四個角全部是
android自定義View,實現折線圖(二)
效果圖:
LineChartView類:
public class LineChartView extends View {
private int width;
private int height;
private float maxVal
Android Java Web實現登入註冊(二)——伺服器端
Eclipse新建Dynamic Web Project,新建index.html,執行看見沒有提示404錯誤的頁面即Tomcat伺服器部署成功。這是搭建Java Web環境時需要完成的工作。詳細內容請看點選開啟連結
涉及的檔案與專案結構:
一、Servlet類
Android sensor 系統框架 (二)
port amp cap 錯誤 str 註釋 hardware war cas 連載上一篇http://www.cnblogs.com/hackfun/p/7327320.html
(D) 如何加載訪問.so庫
在前一篇博客http://www.cnblogs.co
Android短信收發(二)
light undle protect ati res class cast gets new 接收SMS類,代碼如下
//for receive SMS
private SmsReceiver mSmsReceiver;
@Override
AI實現五子棋機器人(一)
而是 fec 指定 pie soft lis href 遊戲 bsp 前言:
前幾天在 csdn 下載資源的時候才發現自己 csdn 有近 200 的下載積分,看了看共享的資源,哈哈 ... 7年前寫的五子棋遊戲很受歡迎。
下載地址:新手入門五子棋遊戲
初識keepalived(二)——keepalived與LVS實現高可用
keepalived lvs 背景介紹通過前面的介紹了解到LVS其實是工作在netfilter框架input鏈上的一組規則,而LVS本身無法實現高可用和對realserver的健康狀態檢測,keepalived為了解決上述2點問題而誕生,同時keepalived還可以直接在配置文件裏設置LVS規則,而