This commit is contained in:
louiscklaw
2025-01-31 19:15:17 +08:00
parent 09adae8c8e
commit 6c60a73f30
1546 changed files with 286918 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
package com.game.tictacteo.engine;
import android.content.Context;
import android.util.Log;
import com.game.tictacteo.localDB.GameLogDB;
import com.game.tictacteo.model.GameLog;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Date;
public class Game {
private Timestamp startTime;
private Timestamp endTime;
private GameBoard board;
private Context ctx;
public Game(Context ctx) {
startTime = new Timestamp(System.currentTimeMillis());
board = new GameBoard();
this.ctx = ctx;
}
// User move
public GameStatus player1Place(int row, int col) throws GameBoardPlacedException {
GameStatus gs = place(row, col, 1);
checkGameStatus(gs); // check status if won not need AI move
if (gs.status == GameBoard.Status.CONTINUE) {
gs = aiPlayerPlace();
checkGameStatus(gs); // check status
}
return gs;
}
private GameStatus place(int row, int col, int player) throws GameBoardPlacedException {
if (player == 1)
return this.board.placePlayer1(row, col);
else if (player == 2)
return this.board.placePlayer2(row, col);
return null;
}
private void checkGameStatus(GameStatus gs) {
// check status if Player1 or Player2 win need to insert the log to db
if (gs.status != GameBoard.Status.CONTINUE) {
endTime = new Timestamp(System.currentTimeMillis());
int duration = (int) ((endTime.getTime() - startTime.getTime()) / 1000);
int winingStatus = ((gs.status == GameBoard.Status.DRAW) ? 2 : (gs.status == GameBoard.Status.PLAYER2_WIN) ? 0 : 1);
GameLogDB.getInstance(this.ctx).addLog(new GameLog(new Date(endTime.getTime()), new Time(endTime.getTime()), duration, winingStatus));
}
}
// AI move
private GameStatus aiPlayerPlace() throws GameBoardPlacedException {
Log.i("game-OBJ-AI", "aiPLayerPlace");
int[] move = GameAI.findBestMove(board.getBoard());
return place(move[0], move[1], 2);
}
}

View File

@@ -0,0 +1,151 @@
package com.game.tictacteo.engine;
import android.util.Log;
public class GameAI {
static final byte player = 2;
static final byte opponent = 1;
private static Boolean isMovesLeft(byte board[][]) {
for (int i = 0; i < board.length; i++)
for (int j = 0; j < board[i].length; j++)
if (board[i][j] == 0)
return true;
return false;
}
private static int evaluate(byte[][] board) {
// Checking for Rows is player or opponent victory.
for (int row = 0; row < board.length; row++) {
if (board[row][0] == board[row][1] &&
board[row][1] == board[row][2]) {
if (board[row][0] == player)
return +10;
else if (board[row][0] == opponent)
return -10;
}
}
for (int col = 0; col < board[0].length; col++) {
if (board[0][col] == board[1][col] &&
board[1][col] == board[2][col]) {
if (board[0][col] == player)
return +10;
else if (board[0][col] == opponent)
return -10;
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
if (board[0][0] == player)
return +10;
else if (board[0][0] == opponent)
return -10;
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
if (board[0][2] == player)
return +10;
else if (board[0][2] == opponent)
return -10;
}
// Else if none of them have won then return 0
return 0;
}
private static int minimax(byte board[][], int depth, Boolean isMax) {
int score = evaluate(board);
// Evaluated score if Maximizer has won the game
if (score == 10)
return score;
if (score == -10)
return score;
// no winner then it is a tie if false
if (isMovesLeft(board) == false)
return 0;
if (isMax) {
int best = -1000;
// Traverse all cells
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
// Check cell is empty
if (board[i][j] == 0) {
// Make the move
board[i][j] = player;
// calculate minimax recursively and choose
best = Math.max(best, minimax(board, depth + 1, !isMax));
// Undo move
board[i][j] = 0;
}
}
}
return best;
}else {
int best = 1000;
// Traverse all cells
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
// Check cell is empty
if (board[i][j] == 0) {
// Make the move
board[i][j] = opponent;
// calculate minimax recursively and choose
best = Math.min(best, minimax(board,
depth + 1, !isMax));
// Undo move
board[i][j] = 0;
}
}
}
return best;
}
}
public static int[] findBestMove(byte board[][]) {
int bestVal = -1000;
int[] bestMove = new int[2];
bestMove[0] = -1;
bestMove[1] = -1;
// evaluate minimax func
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[i].length; j++) {
// Check cell is empty
if (board[i][j] == 0) {
// Make the move
board[i][j] = player;
// compute evaluation function
int moveVal = minimax(board, 0, false);
// Undo the move
board[i][j] = 0;
// If the value of the current move is higher then best value, then update
if (moveVal > bestVal) {
bestMove[0] = i;
bestMove[1] = j;
bestVal = moveVal;
}
}
}
}
return bestMove;
}
}

View File

@@ -0,0 +1,139 @@
package com.game.tictacteo.engine;
import android.util.Log;
public class GameBoard {
private final static int ROW = 3; // Game Board ROW
private final static int COL = 3; // Game Board COL
private byte[][] board;
private short step;
private short winner; // Winner
public enum Status {
CONTINUE,
PLAYER1_WIN,
PLAYER2_WIN,
DRAW
}
// 0 0 0
// 0 0 0
// 0 0 0
GameBoard() {
board = new byte[ROW][COL];
step = 0;
winner = -1;
}
public byte[][] getBoard() {
return board;
}
public GameStatus placePlayer1(int row, int col) throws GameBoardPlacedException {
return place(row, col, 1);
}
public GameStatus placePlayer2(int row, int col) throws GameBoardPlacedException{
return place(row, col, 2);
}
private GameStatus place(int row, int col, int player) throws GameBoardPlacedException {
if(board[row][col] != 0)
throw new GameBoardPlacedException("The position is placed: " + row + " " + col);
board[row][col] = (byte) player;
step++; // count step for game
return genGameStatus();
}
private GameStatus genGameStatus(){
// return current game board status
// step == board.length mean all is placed
if(step == board[0].length * board.length)
return new GameStatus(Status.DRAW, this.board);
// check has winner?
if(winner == -1 && !checkWin())
return new GameStatus(Status.CONTINUE, this.board);
else
return new GameStatus((this.winner == 1) ? Status.PLAYER1_WIN: Status.PLAYER2_WIN, this.board);
}
private boolean checkWin(){
return (checkRows() || checkColumns() || checkDiagonals());
}
private boolean checkRows() {
for (int i = 0; i < board.length; i++) {
int count = 1;
for (int j = 1; j < board[i].length; j++) {
// compare other col is same?
if (board[i][0] != 0 && board[i][0] == board[i][j])
count++;
else
break;
}
if (count == board[i].length){
this.winner = board[i][0];
return true;
}
}
return false;
}
private boolean checkColumns() {
for (int i = 0; i < board[0].length; i++) {
int count = 1;
for (int j = 1; j < board.length; j++) {
// compare other col is same?
if (board[0][i] != 0 && board[0][i] == board[j][i])
count++;
else
break;
}
if (count == board.length) {
this.winner = board[0][i];
return true;
}
}
return false;
}
// checkDiagonals
private boolean checkDiagonals() {
int countX = 1; // for count left-top to right-bottom
int countY = 1; // for count right-top to left-bottom
for (int j = 1; j < board.length; j++) {
// compare other col is same?
if (board[0][0] != 0 && board[0][0] == board[j][j])
countX++;
if (board[0][board[0].length - 1] != 0 && board[0][board[0].length - 1] == board[j][board[0].length - 1 - j])
countY++;
}
if (countX == board.length) {
this.winner = board[0][0];
return true;
}
if (countY == board.length) {
this.winner = board[0][board[0].length-1];
return true;
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
package com.game.tictacteo.engine;
public class GameBoardPlacedException extends Exception{
public GameBoardPlacedException(String mes){
super(mes);
}
}

View File

@@ -0,0 +1,12 @@
package com.game.tictacteo.engine;
public class GameStatus {
public GameBoard.Status status;
public byte[][] board;
public GameStatus(GameBoard.Status status, byte[][] board){
this.status = status;
this.board = board;
}
}