Last active
August 29, 2015 14:03
-
-
Save arturopala/63d4cd18cd0c335f38db to your computer and use it in GitHub Desktop.
FrogStorkGame example of Scala language dealing with complexity
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
object Example3 extends App { | |
import games.board.xgame._ | |
val game = new XGame() with reporting.ConsoleReporter | |
implicit val board = game.board(5, 5) | |
import Commands._ | |
import BluePlayer.{ Frog => BlueFrog, Stork => BlueStork } | |
import RedPlayer.{ Frog => RedFrog, Stork => RedStork } | |
game play Forward(BlueFrog) | |
game play Forward(RedStork) | |
game play Forward(BlueStork) | |
game play Forward(RedFrog) | |
game play Left(RedStork) | |
game play Right(RedStork) | |
game play Forward(BlueStork) | |
game play Forward(BlueStork) | |
game play Right(BlueStork) | |
game play Right(BlueStork) | |
game play Right(BlueStork) | |
} | |
object games { | |
trait Command | |
trait Event { | |
override def toString = this.getClass().getName().split('$').last | |
} | |
trait Success extends Event | |
trait Failure extends Event | |
trait State | |
type Events = List[Event] | |
type Result = (Command, Events) | |
trait Game[T <: State] { def play(command: Command)(implicit state: T): Result } | |
object board { | |
trait Board extends State | |
trait BoardGame[T <: Board] extends Game[T] | |
trait Cell | |
type Position = (Int, Int) | |
type Orientation = (Int, Int) => (Int, Int) | |
trait Player { def orientation: Orientation } | |
implicit class IntPairExt(p: (Int, Int)) { | |
def +(v: (Int, Int)): Position = (p._1 + v._1, p._2 + v._2) | |
def <(v: (Int, Int)): Boolean = p._1 < v._1 && p._2 < v._2 | |
def >=(v: (Int, Int)): Boolean = p._1 >= v._1 && p._2 >= v._2 | |
} | |
object xgame { | |
type Transformation = (Position, Cell) => ((Position, Cell), Events) | |
case class Board(w: Int, h: Int) extends games.board.Board { | |
val size = (w, h) | |
private val cells: Array[Array[Cell]] = (for (i <- 1 to w) yield { | |
(for (j <- 1 to h) yield EmptyCell).toArray[Cell] | |
}).toArray[Array[Cell]] | |
private val positions: List[Position] = (for (i <- 0 until w; j <- 0 until h) yield (i, j)).toList | |
var count = 1; | |
def apply(x: Int, y: Int): Cell = cells(x)(y) | |
def apply(p: Position): Cell = apply(p._1, p._2) | |
def update(p: Position, cell: Cell): Unit = update(p._1, p._2, cell) | |
def update(x: Int, y: Int, cell: Cell): Unit = { | |
cells(x)(y) = cell | |
} | |
def findFirst(filter: Cell => Boolean): Option[(Position, Cell)] = { | |
positions.find(pos => filter(this(pos))).map(pos => (pos, this(pos))) | |
} | |
def findAll(filter: Cell => Boolean): List[(Position, Cell)] = { | |
positions.foldLeft(List.empty[(Position, Cell)]) { | |
case (acc, pos) => | |
if (filter(this(pos))) { (pos, this(pos)) :: acc } else acc | |
} | |
} | |
def print(report: Any => Unit): Unit = { | |
def separator = report { | |
(for (i <- 0 until w) yield '-') mkString ("--", "---", "--") | |
} | |
separator | |
for (j <- 0 until h) { | |
report { | |
(for (i <- 0 until w) yield this(i, j)) map { case FullCell(f) => f.symbol; case _ => ' ' } mkString ("| ", " | ", " |") | |
} | |
separator | |
} | |
} | |
} | |
object EmptyCell extends Cell | |
case class Figure(player: Player, symbol: Char) | |
case class FullCell(figure: Figure) extends Cell | |
trait Frog | |
trait Stork | |
trait Player extends games.board.Player { | |
object Frog extends Figure(this, 'F') with Frog | |
object Stork extends Figure(this, 'S') with Stork | |
} | |
object BluePlayer extends Player { | |
val orientation = (x: Int, y: Int) => (1 * x, 1 * y) | |
override def toString = "Blue" | |
} | |
object RedPlayer extends Player { | |
val orientation = (x: Int, y: Int) => (-1 * x, -1 * y) | |
override def toString = "Red" | |
} | |
object Commands { | |
case class Forward(figure: Figure) extends Command | |
case class Left(figure: Figure) extends Command | |
case class Right(figure: Figure) extends Command | |
} | |
object Events { | |
object Moved extends Success | |
object CannotEscapeBoard extends Failure | |
object CannotMove extends Failure | |
object UnknownCommand extends Failure | |
object FrogWasEaten extends Failure | |
} | |
abstract class XGame extends BoardGame[Board] with reporting.Reporter { | |
def board(w: Int, h: Int): Board = { | |
val board = new Board(w, h) | |
board(0, 0) = FullCell(BluePlayer.Frog) | |
board(w - 1, 0) = FullCell(BluePlayer.Stork) | |
board(0, h - 1) = FullCell(RedPlayer.Stork) | |
board(w - 1, h - 1) = FullCell(RedPlayer.Frog) | |
report("New board:") | |
board.print(report) | |
return board | |
} | |
def byFigure(figure: Figure): Cell => Boolean = { case FullCell(fig) if fig == figure => true; case _ => false } | |
def move(figure: Figure, x: Int, y: Int)(implicit board: Board): ((Position, Cell)) => Events = { | |
case (position, cell) => | |
val newposition = position + figure.player.orientation(x, y) | |
if (newposition >= (0, 0) && newposition < board.size) { | |
val toBeReplaced:Cell = board(newposition) | |
toBeReplaced match { | |
case EmptyCell => { | |
board(position) = EmptyCell | |
board(newposition) = cell | |
Events.Moved :: Nil | |
} | |
case FullCell(existingFigure) => { | |
if(figure.player == existingFigure.player){ | |
Events.CannotMove :: Nil | |
} else { | |
(figure,existingFigure) match { | |
case (f1:Frog,f2:Frog) => { | |
Events.CannotMove :: Nil | |
} | |
case (s1:Stork,s2:Stork) => { | |
Events.CannotMove :: Nil | |
} | |
case (f:Frog,s:Stork) => { | |
board(position) = EmptyCell | |
Events.FrogWasEaten :: Nil | |
} | |
case (s:Stork,f:Frog) => { | |
board(position) = EmptyCell | |
board(newposition) = cell | |
Events.FrogWasEaten :: Nil | |
} | |
} | |
} | |
} | |
} | |
} else { | |
Events.CannotEscapeBoard :: Nil | |
} | |
} | |
def findFigureAndMove(figure: Figure, x: Int, y: Int)(implicit board: Board): Events = { | |
board findFirst byFigure(figure) map move(figure, x, y) getOrElse Nil | |
} | |
override def play(command: Command)(implicit board: Board): Result = { | |
val events: Events = command match { | |
case Commands.Forward(figure) => findFigureAndMove(figure, 0, 1) | |
case Commands.Left(figure) => findFigureAndMove(figure, 1, 0) | |
case Commands.Right(figure) => findFigureAndMove(figure, -1, 0) | |
case _ => List(Events.UnknownCommand) | |
} | |
report(s"${board.count}. $command -> $events") | |
board.print(report) | |
board.count += 1 | |
(command, events) | |
} | |
} | |
} | |
} | |
} | |
package reporting { | |
trait Reporter { def report(event: Any): Unit } | |
trait ConsoleReporter extends Reporter { | |
def report(event: Any): Unit = Console.println(event) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment