Advertisement
Guest User

Untitled

a guest
Feb 5th, 2015
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 6.40 KB | None | 0 0
  1. package models.games.rpschess
  2.  
  3. import scala.language.implicitConversions
  4.  
  5. case class RPSChessGame(val board: Board, val turn: Colour) {
  6.  
  7.   val nonTurn =
  8.     if (turn == Black) White else Black
  9.  
  10.   def afterMove(fromSquare: Square, toSquare: Square): MoveResult = {
  11.     if (legalMovesFrom(fromSquare) contains toSquare) {
  12.       val newBoard = board
  13.         .withUpdatedSquare(fromSquare, None)
  14.         .withUpdatedSquare(toSquare, board(fromSquare))
  15.       board(toSquare) match {
  16.         //Victory if the square moved to captures the last remaining piece of a type
  17.         case Some(piece) if (board.numPiecesOfType(piece) == 1) => Victory(turn, newBoard)
  18.         case _ => Ok(new RPSChessGame(newBoard, nonTurn))
  19.       }
  20.     } else if (!board.hasSquare(fromSquare))
  21.       NoSuchSquare(fromSquare)
  22.     else if (!board.hasSquare(toSquare))
  23.       NoSuchSquare(toSquare)
  24.     else board(fromSquare) match {
  25.       case None => NoPieceToMove
  26.       case Some(Rock(`turn`)) => IllegalRockMove
  27.       case Some(Paper(`turn`)) => IllegalPaperMove
  28.       case Some(Scissors(`turn`)) => IllegalScissorsMove
  29.       case _ => WrongColourPiece
  30.     }
  31.   }
  32.  
  33.   def legalMovesFrom(square: Square): List[Square] = {
  34.  
  35.     def oneTwoMoves(
  36.       singleMoveOffsets: List[(Int, Int)],
  37.       doubleMoveOffsets: List[(Int, Int)],
  38.       canCapture: Piece) = {
  39.       def canMoveOnto(square: Square) =
  40.         board(square) == None || board(square) == Some(canCapture)
  41.       def canMoveThrough(square: Square) =
  42.         board(square) == None
  43.       val singleMoves = singleMoveOffsets.map(square + _)
  44.       val doubleMoves = doubleMoveOffsets.map(square + _)
  45.       singleMoves.zip(doubleMoves).flatMap {
  46.         case (single, double) if board.hasSquare(single) && canMoveOnto(single) =>
  47.           if (board.hasSquare(double) && canMoveThrough(single) && canMoveOnto(double))
  48.             List(single, double)
  49.           else List(single)
  50.         case _ => Nil
  51.       }
  52.     }
  53.  
  54.     def rockMoves = oneTwoMoves(
  55.       List((0, 1), (0, -1), (1, 0), (-1, 0)),
  56.       List((0, 2), (0, -2), (2, 0), (-2, 0)),
  57.       Scissors(nonTurn))
  58.  
  59.     def paperMoves = oneTwoMoves(
  60.       List((1, 1), (1, -1), (-1, 1), (-1, -1)),
  61.       List((2, 2), (2, -2), (-2, 2), (-2, -2)),
  62.       Rock(nonTurn))
  63.  
  64.     def scissorMoves = {
  65.       def canMoveOnto(square: Square) =
  66.         board(square) == None || board(square) == Some(Paper(nonTurn))
  67.       val moves = List((1, 2), (1, -2), (-1, 2), (-1, -2),
  68.         (2, 1), (2, -1), (-2, 1), (-2, -1)
  69.       ).map(square + _)
  70.       moves.filter(x => board.hasSquare(x) && canMoveOnto(x))
  71.     }
  72.  
  73.     if (!board.hasSquare(square)) Nil
  74.     else board(square) match {
  75.       case None => Nil
  76.       case Some(piece) => piece match {
  77.         case Rock(`turn`) => rockMoves
  78.         case Paper(`turn`) => paperMoves
  79.         case Scissors(`turn`) => scissorMoves
  80.         case _ => Nil
  81.       }
  82.     }
  83.   }
  84.  
  85.   def legalMoves = for {
  86.     square <- board.squares
  87.     move <- legalMovesFrom(square)
  88.   } yield (square, move)
  89. }
  90.  
  91. sealed abstract class Colour
  92. case object Black extends Colour
  93. case object White extends Colour
  94.  
  95. sealed abstract class Piece
  96. case class Rock(owner: Colour) extends Piece
  97. case class Paper(owner: Colour) extends Piece
  98. case class Scissors(owner: Colour) extends Piece
  99.  
  100. object Piece {
  101.   val list = List(
  102.     Rock(White), Paper(White), Scissors(White),
  103.     Rock(Black), Paper(Black), Scissors(Black))
  104. }
  105.  
  106. sealed abstract class MoveResult
  107. case class Ok(val game: RPSChessGame) extends MoveResult
  108. case class Victory(val winner: Colour, val board: Board) extends MoveResult
  109. case class NoSuchSquare(val square: Square) extends MoveResult
  110. case object NoPieceToMove extends MoveResult
  111. case object WrongColourPiece extends MoveResult
  112. case object IllegalRockMove extends MoveResult
  113. case object IllegalPaperMove extends MoveResult
  114. case object IllegalScissorsMove extends MoveResult
  115.  
  116. //Squares are denoted with the column first, and the lower-left square is (1,1)
  117. case class Square(col: Int, row: Int) {
  118.   //Get the square that's some number of rows and columns away
  119.   def +(colRowOffset: (Int, Int)) = Square(col + colRowOffset._1, row + colRowOffset._2)
  120.  
  121.   override def toString = s"($col,$row)"
  122. }
  123.  
  124. object Square {
  125.   implicit def intTuple2Square(colRow: (Int, Int)): Square = Square(colRow._1, colRow._2)
  126. }
  127.  
  128. case class Board(val rows: Vector[Vector[Option[Piece]]]) {
  129.   require(rows.size > 0)
  130.   require(rows.forall(_.size == rows(0).size))
  131.  
  132.   val numRows = rows.size
  133.   val numCols = rows(0).size
  134.  
  135.   val squares: List[Square] = for {
  136.     row <- (1 to numRows).toList
  137.     col <- 1 to numCols
  138.   } yield Square(row, col)
  139.  
  140.   lazy val numPiecesOfType = {
  141.     var counts = rows.flatten.flatten.groupBy(x => x).mapValues(_.size)
  142.     Piece.list.foreach{ piece =>
  143.       if(!counts.contains(piece))
  144.         counts += piece -> 0
  145.     }
  146.     counts
  147.   }
  148.  
  149.   private def index(s: Square) = (numRows - s.row, s.col - 1)
  150.  
  151.   def apply(square: Square) = {
  152.     require(hasSquare(square))
  153.  
  154.     val (row, col) = index(square)
  155.     rows(row)(col)
  156.   }
  157.  
  158.   def hasSquare(square: Square) =
  159.     (1 to numRows).contains(square.row) &&
  160.       (1 to numCols).contains(square.col)
  161.  
  162.   def withUpdatedSquare(square: Square, content: Option[Piece]) = {
  163.     require(hasSquare(square))
  164.  
  165.     val (row, col) = index(square)
  166.     Board(rows.updated(row, rows(row).updated(col, content)))
  167.   }
  168.  
  169.   override def toString = {
  170.     import util.Properties
  171.     val nl = Properties.lineSeparator
  172.     val boardString = rows.map(_.map {
  173.       case None => "."
  174.       case Some(Rock(White)) => 'r'
  175.       case Some(Paper(White)) => 'p'
  176.       case Some(Scissors(White)) => 's'
  177.       case Some(Rock(Black)) => 'R'
  178.       case Some(Paper(Black)) => 'P'
  179.       case Some(Scissors(Black)) => 'S'
  180.     }.mkString("")).mkString(nl)
  181.     nl + boardString + nl
  182.   }
  183. }
  184.  
  185. object RPSChessGame {
  186.  
  187.   /*
  188.    * Allow board to be constructed with a list of strings for each row,
  189.    * rps for white pieces, RPS for black, anything else for empty squares
  190.    */
  191.   def apply(rows: List[String], turn: Colour) = {
  192.     val boardRows = rows.map(_.toVector.map {
  193.       case 'r' => Some(Rock(White))
  194.       case 'p' => Some(Paper(White))
  195.       case 's' => Some(Scissors(White))
  196.       case 'R' => Some(Rock(Black))
  197.       case 'P' => Some(Paper(Black))
  198.       case 'S' => Some(Scissors(Black))
  199.       case _ => None
  200.     }).toVector
  201.     new RPSChessGame(Board(boardRows), turn)
  202.   }
  203. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement