summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Zambito <Zambito101@gmail.com>2019-11-30 21:20:27 -0500
committerRobby Zambito <Zambito101@gmail.com>2019-11-30 21:20:27 -0500
commit0a33acbf0dd5ca40bbf18089b20885fa832acfc5 (patch)
treef9b06804f37865b320dba69f66aa0aca6ec10f4c
parentcdd6bbd57e9dcdea555fd410158304f94679e153 (diff)
Made moveScore overloadable for AIPlayer.
Correctly assign currentOpponent. Show final board after last move. Handle ties with Tie object. Added multiple AI implementations.
-rw-r--r--src/main/scala/me/robbyzambito/othello/Main.scala6
-rw-r--r--src/main/scala/me/robbyzambito/othello/game/AIPlayer.scala9
-rw-r--r--src/main/scala/me/robbyzambito/othello/game/Game.scala51
-rw-r--r--src/main/scala/me/robbyzambito/othello/game/Player.scala2
-rw-r--r--src/main/scala/me/robbyzambito/othello/game/package.scala7
5 files changed, 55 insertions, 20 deletions
diff --git a/src/main/scala/me/robbyzambito/othello/Main.scala b/src/main/scala/me/robbyzambito/othello/Main.scala
index 3158f10..d0d4b6f 100644
--- a/src/main/scala/me/robbyzambito/othello/Main.scala
+++ b/src/main/scala/me/robbyzambito/othello/Main.scala
@@ -1,9 +1,13 @@
package me.robbyzambito.othello
-import me.robbyzambito.othello.game.Game
+import me.robbyzambito.othello.game._
object Main extends App {
Game().loop()
+// Game.easy.loop()
+// Game.random.loop()
+// Game(List(AIPlayer(Position.WHITE), AIPlayer(Position.BLACK, -_.takenPositions.length))).loop()
+ //Game(List(AIPlayer(Position.WHITE, -_.takenPositions.length), AIPlayer(Position.BLACK, -_.takenPositions.length))).loop()
}
diff --git a/src/main/scala/me/robbyzambito/othello/game/AIPlayer.scala b/src/main/scala/me/robbyzambito/othello/game/AIPlayer.scala
index b2e8a68..8704db9 100644
--- a/src/main/scala/me/robbyzambito/othello/game/AIPlayer.scala
+++ b/src/main/scala/me/robbyzambito/othello/game/AIPlayer.scala
@@ -9,17 +9,18 @@ package me.robbyzambito.othello.game
*
* See [[Player]]
*/
-case class AIPlayer(override val color: Position) extends Player(color) {
+case class AIPlayer(override val color: Position,
+ moveScore: (Move) => Int = _.takenPositions.length) extends Player(color) {
/**
- * Create an implicit method score used for decision making.
+ * Create an implicit value `score` used for decision making.
* This is the method can be as simple or as involved
* as need be.
*
* @param move
*/
implicit class MoveScore(move: Move) {
- def score: Int = move.takenPositions.length
+ val score: Int = moveScore(move) // By default this maps a move to the # of
}
override def nextMove(board: Board): Move = {
@@ -27,7 +28,7 @@ case class AIPlayer(override val color: Position) extends Player(color) {
implicit val moveOrdering: Ordering[Move] = (x, y) => x.score.compareTo(y.score)
println(s"$this moving...")
- Thread.sleep(1000L)
+ Thread.sleep(1000L) // It is jarring if the computer immediately plays its turn.
println()
// Select the move with the highest score.
diff --git a/src/main/scala/me/robbyzambito/othello/game/Game.scala b/src/main/scala/me/robbyzambito/othello/game/Game.scala
index 6fc278f..d3f76e1 100644
--- a/src/main/scala/me/robbyzambito/othello/game/Game.scala
+++ b/src/main/scala/me/robbyzambito/othello/game/Game.scala
@@ -15,10 +15,16 @@ case class Game(board: Board,
turnCount: Int = 0) {
val currentPlayer: Player = players(turnCount % players.length)
- val currentOpponent: Player = players(turnCount % players.length)
+ val currentOpponent: Player = players((turnCount + 1) % players.length)
+
+ // The winner is decided by seeing who has the most positions on the board.
+ private lazy val whiteCount = board.positions.flatten.count(_ == Position.WHITE)
+ private lazy val blackCount = board.positions.flatten.count(_ == Position.BLACK)
/**
- * Plays the game.
+ * Progresses the state of the game. When the game is finished,
+ * the final board is printed out.
+ *
* @param game Current state of the game.
*/
@tailrec
@@ -26,7 +32,14 @@ case class Game(board: Board,
if (game.winner.isEmpty) {
loop(game.takeTurn)
} else {
- println(game.winnerMessage)
+ println(game.winnerMessage + "\n\nFinal board:")
+ println(game.board)
+
+ println(
+ s"""
+ |Final score:
+ |\t${game.players.find(_.color == Position.WHITE).get}\t:\t${game.whiteCount}
+ |\t${game.players.find(_.color == Position.BLACK).get}\t:\t${game.blackCount}""".stripMargin)
}
}
@@ -38,7 +51,7 @@ case class Game(board: Board,
def save(): Boolean = ???
/**
- * The winner of the game. Ties are not yet handled
+ * The winner of the game. Ties are represented by the [[Tie]] object.
*
* [[None]] if there has not been a winner yet. Otherwise return the [[Player]] which has won.
*/
@@ -46,22 +59,19 @@ case class Game(board: Board,
if (currentPlayer.canMove(board) || currentOpponent.canMove(board))
None
else { // No one can move => Game is finished
- val whiteCount = board.positions.flatten.count(_ == Position.WHITE)
- val blackCount = board.positions.flatten.count(_ == Position.BLACK)
-
if (whiteCount > blackCount)
- players.find(p => p.color == Position.WHITE)
+ players.find(_.color == Position.WHITE)
else if (whiteCount < blackCount)
- players.find(p => p.color == Position.BLACK)
- else None
- // throw new Error("Game tied")
+ players.find(_.color == Position.BLACK)
+ else
+ Some(Tie)
}
}
- lazy val winnerMessage: String = s"${winner.map(_.toString).getOrElse("Nobody")} has won!"
+ lazy val winnerMessage: String = s"${winner.get} has won!"
/**
- * Take a turn
+ * Take a turn. It is assumed that the game is not over.
*
* @return the game with the next turn state
*/
@@ -88,5 +98,18 @@ case class Game(board: Board,
object Game {
def apply(players: List[Player]): Game = new Game(Board.init(), players)
- def apply(): Game = Game( List(UserPlayer(Position.WHITE), AIPlayer(Position.BLACK)) )
+ // Default AI will choose the moves which flip the most on the current turn.
+ def apply(): Game = Game(List(
+ UserPlayer(Position.WHITE),
+ AIPlayer(Position.BLACK)))
+
+ // The AI will always flip the least number of positions per turn.
+ def easy: Game = Game(List(
+ UserPlayer(Position.WHITE),
+ AIPlayer(Position.BLACK, -_.takenPositions.length)))
+
+ // Select a random available move.
+ def random: Game = Game(List(
+ UserPlayer(Position.WHITE),
+ AIPlayer(Position.BLACK, _ => (math.random() * 100).toInt)))
} \ No newline at end of file
diff --git a/src/main/scala/me/robbyzambito/othello/game/Player.scala b/src/main/scala/me/robbyzambito/othello/game/Player.scala
index c55f6db..262e4e3 100644
--- a/src/main/scala/me/robbyzambito/othello/game/Player.scala
+++ b/src/main/scala/me/robbyzambito/othello/game/Player.scala
@@ -106,7 +106,7 @@ abstract class Player(val color: Position) {
override def toString: String = color match {
case Position.WHITE => "White"
case Position.BLACK => "Black"
- case _ => "Default"
+ case _ => "UNKNOWN"
}
}
diff --git a/src/main/scala/me/robbyzambito/othello/game/package.scala b/src/main/scala/me/robbyzambito/othello/game/package.scala
index 250da56..61d1154 100644
--- a/src/main/scala/me/robbyzambito/othello/game/package.scala
+++ b/src/main/scala/me/robbyzambito/othello/game/package.scala
@@ -8,4 +8,11 @@ package object game {
}
type Position = Position.Position
+
+ object Tie extends Player(Position.EMPTY) {
+ override def nextMove(board: Board): Move = ???
+
+ override def toString: String = "Nobody"
+ }
+
}