Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package models.games.rpschess
- import scala.language.implicitConversions
- case class RPSChessGame(val board: Board, val turn: Colour) {
- val nonTurn =
- if (turn == Black) White else Black
- def afterMove(fromSquare: Square, toSquare: Square): MoveResult = {
- if (legalMovesFrom(fromSquare) contains toSquare) {
- val newBoard = board
- .withUpdatedSquare(fromSquare, None)
- .withUpdatedSquare(toSquare, board(fromSquare))
- board(toSquare) match {
- //Victory if the square moved to captures the last remaining piece of a type
- case Some(piece) if (board.numPiecesOfType(piece) == 1) => Victory(turn, newBoard)
- case _ => Ok(new RPSChessGame(newBoard, nonTurn))
- }
- } else if (!board.hasSquare(fromSquare))
- NoSuchSquare(fromSquare)
- else if (!board.hasSquare(toSquare))
- NoSuchSquare(toSquare)
- else board(fromSquare) match {
- case None => NoPieceToMove
- case Some(Rock(`turn`)) => IllegalRockMove
- case Some(Paper(`turn`)) => IllegalPaperMove
- case Some(Scissors(`turn`)) => IllegalScissorsMove
- case _ => WrongColourPiece
- }
- }
- def legalMovesFrom(square: Square): List[Square] = {
- def oneTwoMoves(
- singleMoveOffsets: List[(Int, Int)],
- doubleMoveOffsets: List[(Int, Int)],
- canCapture: Piece) = {
- def canMoveOnto(square: Square) =
- board(square) == None || board(square) == Some(canCapture)
- def canMoveThrough(square: Square) =
- board(square) == None
- val singleMoves = singleMoveOffsets.map(square + _)
- val doubleMoves = doubleMoveOffsets.map(square + _)
- singleMoves.zip(doubleMoves).flatMap {
- case (single, double) if board.hasSquare(single) && canMoveOnto(single) =>
- if (board.hasSquare(double) && canMoveThrough(single) && canMoveOnto(double))
- List(single, double)
- else List(single)
- case _ => Nil
- }
- }
- def rockMoves = oneTwoMoves(
- List((0, 1), (0, -1), (1, 0), (-1, 0)),
- List((0, 2), (0, -2), (2, 0), (-2, 0)),
- Scissors(nonTurn))
- def paperMoves = oneTwoMoves(
- List((1, 1), (1, -1), (-1, 1), (-1, -1)),
- List((2, 2), (2, -2), (-2, 2), (-2, -2)),
- Rock(nonTurn))
- def scissorMoves = {
- def canMoveOnto(square: Square) =
- board(square) == None || board(square) == Some(Paper(nonTurn))
- val moves = List((1, 2), (1, -2), (-1, 2), (-1, -2),
- (2, 1), (2, -1), (-2, 1), (-2, -1)
- ).map(square + _)
- moves.filter(x => board.hasSquare(x) && canMoveOnto(x))
- }
- if (!board.hasSquare(square)) Nil
- else board(square) match {
- case None => Nil
- case Some(piece) => piece match {
- case Rock(`turn`) => rockMoves
- case Paper(`turn`) => paperMoves
- case Scissors(`turn`) => scissorMoves
- case _ => Nil
- }
- }
- }
- def legalMoves = for {
- square <- board.squares
- move <- legalMovesFrom(square)
- } yield (square, move)
- }
- sealed abstract class Colour
- case object Black extends Colour
- case object White extends Colour
- sealed abstract class Piece
- case class Rock(owner: Colour) extends Piece
- case class Paper(owner: Colour) extends Piece
- case class Scissors(owner: Colour) extends Piece
- object Piece {
- val list = List(
- Rock(White), Paper(White), Scissors(White),
- Rock(Black), Paper(Black), Scissors(Black))
- }
- sealed abstract class MoveResult
- case class Ok(val game: RPSChessGame) extends MoveResult
- case class Victory(val winner: Colour, val board: Board) extends MoveResult
- case class NoSuchSquare(val square: Square) extends MoveResult
- case object NoPieceToMove extends MoveResult
- case object WrongColourPiece extends MoveResult
- case object IllegalRockMove extends MoveResult
- case object IllegalPaperMove extends MoveResult
- case object IllegalScissorsMove extends MoveResult
- //Squares are denoted with the column first, and the lower-left square is (1,1)
- case class Square(col: Int, row: Int) {
- //Get the square that's some number of rows and columns away
- def +(colRowOffset: (Int, Int)) = Square(col + colRowOffset._1, row + colRowOffset._2)
- override def toString = s"($col,$row)"
- }
- object Square {
- implicit def intTuple2Square(colRow: (Int, Int)): Square = Square(colRow._1, colRow._2)
- }
- case class Board(val rows: Vector[Vector[Option[Piece]]]) {
- require(rows.size > 0)
- require(rows.forall(_.size == rows(0).size))
- val numRows = rows.size
- val numCols = rows(0).size
- val squares: List[Square] = for {
- row <- (1 to numRows).toList
- col <- 1 to numCols
- } yield Square(row, col)
- lazy val numPiecesOfType = {
- var counts = rows.flatten.flatten.groupBy(x => x).mapValues(_.size)
- Piece.list.foreach{ piece =>
- if(!counts.contains(piece))
- counts += piece -> 0
- }
- counts
- }
- private def index(s: Square) = (numRows - s.row, s.col - 1)
- def apply(square: Square) = {
- require(hasSquare(square))
- val (row, col) = index(square)
- rows(row)(col)
- }
- def hasSquare(square: Square) =
- (1 to numRows).contains(square.row) &&
- (1 to numCols).contains(square.col)
- def withUpdatedSquare(square: Square, content: Option[Piece]) = {
- require(hasSquare(square))
- val (row, col) = index(square)
- Board(rows.updated(row, rows(row).updated(col, content)))
- }
- override def toString = {
- import util.Properties
- val nl = Properties.lineSeparator
- val boardString = rows.map(_.map {
- case None => "."
- case Some(Rock(White)) => 'r'
- case Some(Paper(White)) => 'p'
- case Some(Scissors(White)) => 's'
- case Some(Rock(Black)) => 'R'
- case Some(Paper(Black)) => 'P'
- case Some(Scissors(Black)) => 'S'
- }.mkString("")).mkString(nl)
- nl + boardString + nl
- }
- }
- object RPSChessGame {
- /*
- * Allow board to be constructed with a list of strings for each row,
- * rps for white pieces, RPS for black, anything else for empty squares
- */
- def apply(rows: List[String], turn: Colour) = {
- val boardRows = rows.map(_.toVector.map {
- case 'r' => Some(Rock(White))
- case 'p' => Some(Paper(White))
- case 's' => Some(Scissors(White))
- case 'R' => Some(Rock(Black))
- case 'P' => Some(Paper(Black))
- case 'S' => Some(Scissors(Black))
- case _ => None
- }).toVector
- new RPSChessGame(Board(boardRows), turn)
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement