Created
April 8, 2011 13:39
-
-
Save dnene/909852 to your computer and use it in GitHub Desktop.
Early work in progress implementation of a statically typed API poser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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