1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
package me.robbyzambito.othello.game
import scala.util.Try
/**
* Represents a party which is partaking in the game.
* Both the AI and the end user are instances of Player.
*
* Written by Robby Zambito
* Written on 11/20/2019
* Targeting Scala 2.13.1
*
* @param color The color of the player.
*/
abstract class Player(val color: Position) {
def nextMove(board: Board): Move
def canMove(board: Board): Boolean =
possibleMoves(board).nonEmpty
def possibleMoves(board: Board): List[Move] = {
val enemyPosition = if (color == Position.WHITE) Position.BLACK else Position.WHITE
def possibleAcross(rowCount: Int, colCount: Int): Option[Move] = {
val row = board.positions(rowCount)
/**
* Return the list of positions which will be flipped if a move is made at row(rowCount, pos)
*
* @param pos Offset in the current row
* @param step Direction which to check for the next piece.
* @param isPossible
* @return List of positions which will change if one is placed. Empty list means move is invalid.
*/
def checkInDirection(pos: Int, step: Int, isPossible: Boolean = false): List[(Int, Int)] = {
if (pos == colCount && row(pos) != Position.EMPTY) {
List.empty
} else if (isPossible && Try(row(pos + step)).getOrElse(Position.EMPTY) == color) {
List((rowCount, pos))
} else if (Try(row(pos + step)).getOrElse(Position.EMPTY) == enemyPosition) {
val nextDirections = checkInDirection(pos + step, step, isPossible = true)
if (nextDirections.nonEmpty)
(rowCount, pos) :: nextDirections
else List.empty
} else List.empty
}
// Check can move left or right
val changes = checkInDirection(colCount, 1) ::: checkInDirection(colCount, -1)
if (changes.nonEmpty)
Some(Move(rowCount, colCount, changes))
else None
}
def possibleVertical(rowCount: Int, colCount: Int): Option[Move] = {
val col = board.positions.indices.map(i => board.positions(i)(colCount))
def checkInDirection(pos: Int, step: Int, isPossible: Boolean = false): List[(Int, Int)] = {
if (pos == rowCount && col(pos) != Position.EMPTY) {
List.empty
} else if (isPossible && Try(col(pos + step)).getOrElse(Position.EMPTY) == color) {
List((pos, colCount))
} else if (Try(col(pos + step)).getOrElse(Position.EMPTY) == enemyPosition) {
val nextDiections = checkInDirection(pos + step, step, isPossible = true)
if (nextDiections.nonEmpty)
(pos, colCount) :: nextDiections
else List.empty
} else List.empty
}
// Check can move down or up
val changes = checkInDirection(rowCount, 1) ::: checkInDirection(rowCount, -1)
if (changes.nonEmpty)
Some(Move(rowCount, colCount, changes))
else None
}
(for (rowCount <- board.positions.indices) yield {
(for (colCount <- board.positions(rowCount).indices) yield {
val (acc, vert) = (possibleAcross(rowCount, colCount), possibleVertical(rowCount, colCount))
if (acc.isDefined || vert.isDefined)
Some(
Move(rowCount,
colCount,
acc.map(_.takenPositions).getOrElse(List.empty) ::: vert.map(_.takenPositions).getOrElse(List.empty))
)
else None
}).flatten
}).flatten
.toList
}
override def toString: String = color match {
case Position.WHITE => "White"
case Position.BLACK => "Black"
case _ => "Default"
}
}
|