Skip to content

Instantly share code, notes, and snippets.

@dnene
Created April 8, 2011 13:39
Show Gist options
  • Save dnene/909852 to your computer and use it in GitHub Desktop.
Save dnene/909852 to your computer and use it in GitHub Desktop.
Early work in progress implementation of a statically typed API poser
package tictactoe
import scala.collection.immutable.Map
// Players
sealed abstract case class Player(val number: Int, val mark: Char, val next: () => Player)
case object player_0 extends Player(0,'O', () => player_1)
case object player_1 extends Player(1,'X', () => player_0)
// Cell
sealed abstract class Cell (val row: Int, val col: Int){}
sealed abstract class UnusedCell (override val row: Int, override val col: Int) extends Cell(row, col) {
// Only on an unused cell can one set a player. Once a player is set - it becomes a used cell
def setPlayer(player: Player): UsedCell = {
new UsedCell(row, col, player)
}
}
// universe of the only possible unused cells
case object cell_0_0 extends UnusedCell(0,0)
case object cell_0_1 extends UnusedCell(0,1)
case object cell_0_2 extends UnusedCell(0,2)
case object cell_1_0 extends UnusedCell(1,0)
case object cell_1_1 extends UnusedCell(1,1)
case object cell_1_2 extends UnusedCell(1,2)
case object cell_2_0 extends UnusedCell(2,0)
case object cell_2_1 extends UnusedCell(2,1)
case object cell_2_2 extends UnusedCell(2,2)
// Leaving used cell as a class and not making it a case object
// (since players are dynamic, .. even though there are only 2 players)
class UsedCell (override val row: Int, override val col: Int, val player: Player) extends Cell(row, col)
// Responses
// Just a simple marker response
sealed abstract case class Response {}
// A move was successful
case class ProgressResponse(val nextPlayer: Player, val board: InProgressBoard) extends Response
// Game is now over
case class GameOverResponse(val board: CompletedBoard) extends Response
// The suggested player to make a move is invalid - need to move with a different player
case class InvalidPlayer (val expected: Player, val actual: Player) extends Response
// The particular cell which is being attempted to be marked, has already been marked earlier
case class CellAlreadyUsed(val cell: Cell) extends Response
sealed abstract class Board(val cells: Array[Array[Cell]])
sealed case class EmptyBoard (override val nextPlayer: Player, override val cells: Array[Array[Cell]]) extends InProgressBoard(nextPlayer, cells)
sealed case class CompletedBoard(override val cells: Array[Array[Cell]]) extends Board (cells)
case object starterBoard extends EmptyBoard(
player_0,
Array(
Array(cell_0_0,cell_0_1,cell_0_2),
Array(cell_1_0,cell_1_1,cell_1_2),
Array(cell_2_0,cell_2_1,cell_2_2)))
class InProgressBoard (val nextPlayer: Player, val ncells: Array[Array[Cell]]) extends Board(ncells) {
def getPossibleMoves(): Array[UnusedCell] = {
for(row <- cells;
cell <- row;
if cell.isInstanceOf[UnusedCell])
yield cell.asInstanceOf[UnusedCell]
}
def replaceInRow(row: Array[Cell], replaceWith: UsedCell): Array[Cell] =
{
for ((cell,index) <- row.zipWithIndex)
yield if (index == replaceWith.col) replaceWith else cell
}
def replaceCell(cells: Array[Array[Cell]], replaceWith: UsedCell): Array[Array[Cell]] = {
for ((row,index) <- cells.zipWithIndex)
yield if (index == replaceWith.row) replaceInRow(row,replaceWith) else row
}
def generateBoard(replaceWith: UsedCell): Response = {
val newCells = replaceCell(cells,replaceWith)
val newPlayer = nextPlayer.next()
val newBoard = new InProgressBoard(newPlayer, newCells)
ProgressResponse(newPlayer, newBoard)
}
def move(player: Player, targetCell: UnusedCell): Response = {
if (player == nextPlayer) {
cells(targetCell.row)(targetCell.col) match {
case unusedCell: UnusedCell => generateBoard(unusedCell.setPlayer(player))
case usedCell: UsedCell => CellAlreadyUsed(usedCell)
}
}
else {
InvalidPlayer(nextPlayer, player)
}
}
}
object Player {
def main(args : Array[String]) : Unit = {
println(player_0.next())
println(player_1.next())
val random = new scala.util.Random()
val board = starterBoard
val moves = starterBoard.getPossibleMoves
val selected = moves(random.nextInt(moves.length))
println("Selected => " + selected)
val response = board.move(player_0, selected)
println(response)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment