Last active
December 14, 2017 15:10
-
-
Save DavidSanf0rd/3807b3621bb6b93b919dca1d95d262a8 to your computer and use it in GitHub Desktop.
ic: Tic tac toe in kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package br.ind.sanford.tictactoe | |
/** | |
* Created by sanf0rd on 14/09/17. | |
*/ | |
import java.util.ArrayList | |
internal class TicTacToe { | |
//singleton methods | |
var board: String | |
var size: Int = 0 | |
var player: Int = 0 | |
constructor() { | |
board = "_________" | |
size = 0 | |
player = 0//0 is X. 1 is O. | |
} | |
constructor(str: String) { | |
if (!checkStr(str)) { | |
board = "_________" | |
size = 0 | |
player = 0//0 is X. 1 is O. | |
println(str + ": string is in the incorrent format.") | |
println("Initialized to default state.") | |
} else { | |
board = str | |
size = countSize(str) | |
player = size % 2 | |
} | |
} | |
//verify the format of a string | |
fun checkStr(str: String): Boolean { | |
if (str.length == 9) { | |
for (i in 0..8) { | |
val temp = str[i] | |
if (temp == '_' || temp == 'X' || temp == 'O') { | |
} else { | |
return false | |
} | |
} | |
return true | |
} else { | |
return false | |
} | |
} | |
//count the current size of the board | |
fun countSize(str: String): Int { | |
var count = 0 | |
for (i in 0 until str.length) { | |
if (str[i] == '_') { | |
count++ | |
} | |
} | |
return 9 - count | |
} | |
//read in a string and populate the board represented by the string | |
fun read(str: String) { | |
if (!checkStr(str)) { | |
println(str + ": string is in the incorrent format.") | |
} else { | |
board = str | |
size = countSize(str) | |
player = size % 2 | |
} | |
} | |
//print out the board in the tictactoe format | |
fun print() { | |
var i = 0 | |
while (i < 9) { | |
println(board.substring(i, i + 3)) | |
i += 3 | |
} | |
} | |
//return the string representation of the board with the new move | |
fun move(n: Int): String { | |
return if (player == 0) { | |
board.substring(0, n) + "X" + board.substring(n + 1) | |
} else { | |
board.substring(0, n) + "O" + board.substring(n + 1) | |
} | |
} | |
//return a new game instance with the new move | |
fun newMove(n: Int): TicTacToe { | |
return if (player == 0) { | |
TicTacToe(board.substring(0, n) + "X" + board.substring(n + 1)) | |
} else { | |
TicTacToe(board.substring(0, n) + "O" + board.substring(n + 1)) | |
} | |
} | |
//return a list of all available moves | |
fun availableMoves(): List<Int> { | |
val list = ArrayList<Int>() | |
for (i in 0 until board.length) { | |
if (board[i] == '_') { | |
list.add(i) | |
} | |
} | |
return list | |
} | |
//return 0 for X's win, 1 for O's win, 2 for Tie, 3 for not ended | |
fun gameEnd(): Int { | |
val count = IntArray(16) | |
//horizontal check | |
run { | |
var i = 0 | |
while (i <= 6) { | |
for (j in 0..2) { | |
if (board[i + j] == 'X') { | |
count[0 + i / 3]++ | |
} | |
if (board[i + j] == 'O') { | |
count[8 + i / 3]++ | |
} | |
} | |
i += 3 | |
} | |
} | |
//vertical check | |
for (i in 0..2) { | |
var j = 0 | |
while (j <= 6) { | |
if (board[i + j] == 'X') { | |
count[3 + i]++ | |
} | |
if (board[i + j] == 'O') { | |
count[11 + i]++ | |
} | |
j += 3 | |
} | |
} | |
//diagonal top left to bottom right | |
run { | |
var i = 0 | |
while (i <= 8) { | |
if (board[i] == 'X') { | |
count[6]++ | |
} | |
if (board[i] == 'O') { | |
count[14]++ | |
} | |
i += 4 | |
} | |
} | |
//diagonal top left to bottom right | |
run { | |
var i = 2 | |
while (i <= 6) { | |
if (board[i] == 'X') { | |
count[7]++ | |
} | |
if (board[i] == 'O') { | |
count[15]++ | |
} | |
i += 2 | |
} | |
} | |
//return the result if any count is equal to 3 | |
for (i in 0..15) { | |
if (count[i] == 3) { | |
return if (i <= 7) { | |
0 | |
} else { | |
1 | |
} | |
} | |
} | |
//tie when no winner and size is 9 | |
return if (size == 9) { | |
2 // tie | |
} else { //not ended | |
3 | |
} | |
} | |
} | |
object TicTacToeAI { | |
internal var count: Int = 0 | |
internal var alphaCount: Int = 0 | |
internal var betaCount: Int = 0 | |
//return the score of the current game | |
internal fun score(game: TicTacToe): Int { | |
return if (game.gameEnd() == 0) { | |
1 | |
} else if (game.gameEnd() == 1) { | |
-1 | |
} else { | |
0 | |
} | |
} | |
//return the minimum number of the list | |
internal fun minScore(list: List<Int>): Int { | |
var min = list[0] | |
for (n in list) { | |
if (n < min) { | |
min = n | |
} | |
} | |
return min | |
} | |
//return the minimum number of the list | |
internal fun maxScore(list: List<Int>): Int { | |
var max = list[0] | |
for (n in list) { | |
if (n > max) { | |
max = n | |
} | |
} | |
return max | |
} | |
//encapsulation of the minimax method and display the result | |
internal fun minimax(game: TicTacToe) { | |
count = 0 | |
println("Game Result:" + minimaxHelper(game)) | |
println("Moves considered without alpha-beta pruning: " + count) | |
} | |
//minimax method which increment the count and calculate the winner of the game | |
internal fun minimaxHelper(game: TicTacToe): Int { | |
val winner = game.gameEnd() | |
if (winner != 3) { | |
count++ | |
return score(game) | |
} | |
val children = game.availableMoves() | |
val scores = ArrayList<Int>() | |
for (n in children) { | |
scores.add(minimaxHelper(game.newMove(n))) | |
} | |
return if (game.player == 0) { | |
maxScore(scores) | |
} else { | |
minScore(scores) | |
} | |
} | |
//encapsulation method for the minimax with alpha beta pruning | |
internal fun minimaxAlphaBeta(game: TicTacToe) { | |
count = 0 | |
alphaCount = 0 | |
betaCount = 0 | |
println("Game Result:" + minimaxAlphaBeta(game, -10000, 10000)) | |
println("Moves considered with alpha-beta pruning: " + count) | |
println("Alpha cuts: " + alphaCount) | |
println("Beta cuts: " + betaCount) | |
} | |
//minimax with alpha beta pruning implemented to optimize the speed of the algorithm | |
internal fun minimaxAlphaBeta(game: TicTacToe, alpha: Int, beta: Int): Int { | |
var alpha = alpha | |
var beta = beta | |
val winner = game.gameEnd() | |
if (winner != 3) { | |
count++ | |
return score(game) | |
} | |
val children = game.availableMoves() | |
if (game.player == 0) { | |
for (n in children) { | |
val score = minimaxAlphaBeta(game.newMove(n), alpha, beta) | |
//update alpha | |
if (score > alpha) { | |
alpha = score | |
} | |
//cutoff | |
if (alpha >= beta) { | |
alphaCount++ | |
break | |
} | |
} | |
return alpha | |
} else { | |
for (n in children) { | |
val score = minimaxAlphaBeta(game.newMove(n), alpha, beta) | |
//update beta | |
if (score < beta) { | |
beta = score | |
} | |
//cutoff | |
if (alpha >= beta) { | |
betaCount++ | |
break | |
} | |
} | |
return beta | |
} | |
} | |
@JvmStatic | |
fun main(args: Array<String>) { | |
val game = TicTacToe(args[0]) | |
println("Initial board:") | |
game.print() | |
println() | |
minimax(game) | |
println() | |
minimaxAlphaBeta(game) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment