Skip to content

Instantly share code, notes, and snippets.

@rbejar
Last active November 30, 2017 12:22
Show Gist options
  • Save rbejar/5355094 to your computer and use it in GitHub Desktop.
Save rbejar/5355094 to your computer and use it in GitHub Desktop.
A functional implementation of Tic-Tac-Toe in Scala. Human vs. computer, computer moves are legal, but purely random.
/*
* Code contributed to Rosetta Code: <http://rosettacode.org/wiki/Tic-tac-toe#Scala>
*
* Creative commons license "CC0 1.0 Universal (CC0 1.0)"
* <http://creativecommons.org/publicdomain/zero/1.0/>
* To the extent possible under law, Rubén Béjar <http://www.rubenbejar.com>
* has waived all copyright and related or neighboring rights to this
* source file. This work is published from: Spain.
*
* Computers vs. human. Human starts.
* Computer plays 'O' and human plays 'X'.
* Computer moves are legal, but purely random.
*/
package object tictactoe {
val Human = 'X'
val Computer = 'O'
val BaseBoard = ('1' to '9').toList
val WinnerLines = List((0,1,2), (3,4,5), (6,7,8), (0,3,6), (1,4,7), (2,5,8), (0,4,8), (2,4,6))
val randomGen = new util.Random(System.currentTimeMillis)
}
package tictactoe {
class Board(aBoard : List[Char] = BaseBoard) {
def availableMoves = aBoard.filter(c => c != Human && c != Computer)
def availableMovesIdxs = for ((c,i) <- aBoard.zipWithIndex if c != Human && c != Computer) yield i
def computerPlays = new Board(aBoard.updated(availableMovesIdxs(randomGen.nextInt(availableMovesIdxs.length)), Computer))
def humanPlays(move : Char) = new Board(aBoard.updated(aBoard.indexOf(move), Human))
def isDraw = aBoard.forall(c => c == Human || c == Computer)
def isWinner(winner : Char) =
WinnerLines.exists{case (i,j,k) => aBoard(i) == winner && aBoard(j) == winner && aBoard(k) == winner}
def isOver = isWinner(Computer) || isWinner(Human) || isDraw
def print {
aBoard.grouped(3).foreach(row => println(row(0) + " " + row(1) + " " + row(2)))
}
def printOverMessage {
if (isWinner(Human)) println("You win.")
else if (isWinner(Computer)) println("Computer wins.")
else if (isDraw) println("It's a draw.")
else println("Not over yet, or something went wrong.")
}
}
object TicTacToe extends Application {
def play(board : Board, turn : Char) {
// Reads a char from input until it is one of
// the available moves in the current board
def readValidMove() : Char = {
print("Choose a move: ")
val validMoves = board.availableMoves
val move = readChar
if (validMoves.contains(move)) {
move
} else {
println("Invalid move. Choose another one in " + validMoves)
readValidMove()
}
}
board.print
if (board.isOver) {
board.printOverMessage
return
}
if (turn == Human) { // Human plays
val nextBoard = board.humanPlays(readValidMove)
play(nextBoard, Computer)
} else { // Computer plays
println("Computer plays: ")
val nextBoard = board.computerPlays
play(nextBoard, Human)
}
}
play(new Board(),Human)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment