Skip to content

Instantly share code, notes, and snippets.

@DavidSanf0rd
Last active December 14, 2017 15:10
Show Gist options
  • Save DavidSanf0rd/3807b3621bb6b93b919dca1d95d262a8 to your computer and use it in GitHub Desktop.
Save DavidSanf0rd/3807b3621bb6b93b919dca1d95d262a8 to your computer and use it in GitHub Desktop.
ic: Tic tac toe in kotlin
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