Merge pull request #339 from patimen/main

Add Kotlin for Tic-Tac-Toe
This commit is contained in:
Jeff Atwood
2022-01-01 20:53:01 -08:00
committed by GitHub
3 changed files with 179 additions and 0 deletions

View 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)
}
}

View File

@@ -0,0 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Kotlin](https://kotlinlang.org/)

View 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")
}
}
}