mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 23:26:40 -08:00
80
89_Tic-Tac-Toe/kotlin/Board.kt
Normal file
80
89_Tic-Tac-Toe/kotlin/Board.kt
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @author John Long based on Java by Ollie Hensman-Crook
|
||||
*/
|
||||
|
||||
enum class Player(val char: Char) {
|
||||
X('X'),
|
||||
O('O')
|
||||
}
|
||||
|
||||
class Board {
|
||||
// Initialize an array of size nine with all values set to null
|
||||
private var boxes: Array<Player?> = arrayOfNulls(9)
|
||||
|
||||
/**
|
||||
* Place 'X' or 'O' on the board position passed
|
||||
* @param position
|
||||
* @param player
|
||||
*/
|
||||
fun setArr(position: Int, player: Player) {
|
||||
boxes[position - 1] = player
|
||||
}
|
||||
|
||||
fun printBoard() {
|
||||
System.out.format(
|
||||
"""
|
||||
%c ! %c ! %c
|
||||
----+----+----
|
||||
%c ! %c ! %c
|
||||
----+----+----
|
||||
%c ! %c ! %c
|
||||
""",
|
||||
// converts each box to a char and then passes them in order to format
|
||||
// If the person is unassigned, use a space ' '
|
||||
*(boxes.map{it?.char ?: ' '}.toTypedArray()))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x
|
||||
* @return the value of the char at a given position
|
||||
*/
|
||||
fun getBoardValue(x: Int): Player? {
|
||||
return boxes[x - 1]
|
||||
}
|
||||
|
||||
private val winningCombos = listOf(
|
||||
// horizontal
|
||||
listOf(0,1,2),
|
||||
listOf(3,4,5),
|
||||
listOf(6,7,8),
|
||||
// diagonal
|
||||
listOf(0,4,8),
|
||||
listOf(2,4,6),
|
||||
// vertical
|
||||
listOf(0,3,6),
|
||||
listOf(1,4,7),
|
||||
listOf(2,5,8)
|
||||
)
|
||||
/**
|
||||
* Go through the board and check for win
|
||||
* @param player
|
||||
* @return whether a win has occurred
|
||||
*/
|
||||
fun isWinFor(player: Player): Boolean {
|
||||
// Check if any winningCombos have all their boxes set to player
|
||||
return winningCombos.any{ combo ->
|
||||
combo.all { boxes[it] == player }
|
||||
}
|
||||
}
|
||||
|
||||
fun isDraw(): Boolean {
|
||||
return !isWinFor(Player.X) && !isWinFor(Player.O) && boxes.all { it != null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the board
|
||||
*/
|
||||
fun clear() {
|
||||
boxes = arrayOfNulls(9)
|
||||
}
|
||||
}
|
||||
3
89_Tic-Tac-Toe/kotlin/README.md
Normal file
3
89_Tic-Tac-Toe/kotlin/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Kotlin](https://kotlinlang.org/)
|
||||
96
89_Tic-Tac-Toe/kotlin/TicTacToe2.kt
Normal file
96
89_Tic-Tac-Toe/kotlin/TicTacToe2.kt
Normal file
@@ -0,0 +1,96 @@
|
||||
import java.util.Random
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* @author John Long based on Java from Ollie Hensman-Crook
|
||||
*/
|
||||
private val compChoice = Random()
|
||||
private val gameBoard = Board()
|
||||
|
||||
fun main() {
|
||||
println(" TIC-TAC-TOE")
|
||||
println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
||||
println("\nTHE BOARD IS NUMBERED: ")
|
||||
println(" 1 2 3\n 4 5 6\n 7 8 9\n")
|
||||
while (true) {
|
||||
// Let the player choose whether to be X or O (Player.X or Player.O)
|
||||
val (human, computer) = readXOrO()
|
||||
while (true) {
|
||||
// Get a valid move from the user and then move there
|
||||
val validMoveIndex = readValidMove()
|
||||
gameBoard.setArr(validMoveIndex, human)
|
||||
gameBoard.printBoard()
|
||||
|
||||
// Computer randomly fills a square (if the game isn't already over)
|
||||
// This uses Kotlin's null handling and will only set the board
|
||||
// if validRandomMove returned a non-null value
|
||||
validRandomMove()?.let {
|
||||
gameBoard.setArr(it, computer)
|
||||
gameBoard.printBoard()
|
||||
}
|
||||
|
||||
// if there is a win print if player won or the computer won and ask if they
|
||||
// want to play again
|
||||
when {
|
||||
gameBoard.isWinFor(human) -> {
|
||||
checkPlayAgain("YOU WIN")
|
||||
break
|
||||
}
|
||||
gameBoard.isWinFor(computer) -> {
|
||||
checkPlayAgain("YOU LOSE")
|
||||
break
|
||||
}
|
||||
gameBoard.isDraw() -> {
|
||||
checkPlayAgain("DRAW")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPlayAgain(result: String) {
|
||||
println("$result, PLAY AGAIN? (Y/N)")
|
||||
gameBoard.clear()
|
||||
if (!readYesOrNo()) exitProcess(0)
|
||||
}
|
||||
|
||||
private fun readYesOrNo(): Boolean {
|
||||
while (true) {
|
||||
when (readLine()?.get(0)?.uppercaseChar()) {
|
||||
'Y' -> return true
|
||||
'N' -> return false
|
||||
else -> println("THAT'S NOT 'Y' OR 'N', TRY AGAIN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun validRandomMove(): Int? {
|
||||
if (gameBoard.isDraw() || gameBoard.isWinFor(Player.O) || gameBoard.isWinFor(Player.X)) return null
|
||||
println("THE COMPUTER MOVES TO")
|
||||
// keep generating a random value until we find one that is null (unset)
|
||||
return generateSequence { 1 + compChoice.nextInt(9) }.first { gameBoard.getBoardValue(it) == null }
|
||||
}
|
||||
|
||||
private fun readValidMove(): Int {
|
||||
println("WHERE DO YOU MOVE")
|
||||
while (true) {
|
||||
val input = readln().toIntOrNull()
|
||||
if (input != null && gameBoard.getBoardValue(input) == null) {
|
||||
return input
|
||||
} else {
|
||||
println("INVALID INPUT, TRY AGAIN")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readXOrO(): Pair<Player, Player> {
|
||||
println("DO YOU WANT 'X' OR 'O'")
|
||||
while (true) {
|
||||
when (readln()[0].uppercaseChar()) {
|
||||
'X' -> return Player.X to Player.O
|
||||
'O' -> return Player.O to Player.X
|
||||
else -> println("THAT'S NOT 'X' OR 'O', TRY AGAIN")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user