MarkerはEnumeration
object Marker extends Enumeration {
type Marker = Value
val Blank, Dark, Light = Value
}
import Marker._
ゲームを抽象的に表すためにMarker, Move, Nodeを次のように定義。
class Move()
object Move {
val empty: Move = new Move
}
case object Pass extends Move
case class PutMarker(x: Int, y: Int) extends Move {
override def toString: String = {
val xs = ('a' + x).asInstanceOf[Char]
val ys = ('1' + y).asInstanceOf[Char]
"" + xs + ys
}
}
trait Node[Repr <: Node[Repr]] {
def play(move: Move): Option[Repr]
def possibleMoves(): Seq[Move]
def isTerminal: Boolean
val marker: Marker = Dark
lazy val opponentMarker: Marker =
if (marker == Dark) Light else Dark
}
実際のreversiゲームにはNodeを通してアクセスされることになる。
class ReversiNode (
override val marker: Marker,
val board: ListBoard
) extends Node[ReversiNode] {
override def play(move: Move): Option[ReversiNode] =
(move: @unchecked) match {
case Pass =>
Some(new ReversiNode(opponentMarker, board))
case PutMarker(x, y) =>
if (board(x, y) != Blank) return None
val (b, n) = reverse(x, y)
if (n > 0)
Some(new ReversiNode(opponentMarker, b))
else
None
}
private def reverse(x: Int, y: Int): (ListBoard, Int) = {
var b = board.updated(x, y, marker)
var n = 0
for (t <- List((-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1),
(1, -1), (1, 0), (1, 1))) {
val (b2, dn) = reverseDxDy(b, x, y, t._1, t._2)
b = b2
n += dn
}
(b, n)
}
private def reverseDxDy(b: ListBoard, x: Int, y: Int, dx: Int, dy: Int): (ListBoard, Int) = {
val n = opponentMarker
var b2 = b
var c = 0
var s = x + dx
var t = y + dy
while (s >= 0 && s < 8 && t >= 0 && t < 8 && b2(s, t) == n) {
b2 = b2.updated(s, t, marker)
c += 1
s += dx
t += dy
}
if (c > 0 && s >= 0 && s < 8 && t >= 0 && t < 8 && b2(s, t) == marker) (b2, c)
else (b, 0)
}
def possibleMoves(): Seq[Move] = {
val moves =
for { x <- 0 until 8
y <- 0 until 8
if (board(x, y) == Blank)
(b, n) = reverse(x, y)
if (n > 0)
} yield PutMarker(x, y)
if (moves.isEmpty) List(Pass)
else moves
}
def isTerminal: Boolean = {
if (board.isFull) return true
if (possibleMoves == List(Pass)) {
val next = play(Pass).get
if (next.possibleMoves == List(Pass)) {
return true
}
}
false
}
def winner: Marker = {
val nums = board.numOfMarkers
if (nums(Dark) < nums(Light)) Light
else if (nums(Light) < nums(Dark)) Dark
else Blank
}
override def toString: String =
board.toString
}
object ReversiNode {
val Start: ReversiNode =
new ReversiNode(Dark,
new ListBoard().updated(3, 3, Light)
.updated(4, 4, Light)
.updated(3, 4, Dark)
.updated(4, 3, Dark))
}
trait Board { def apply(x: Int, y: Int): Marker
def updated(x: Int, y: Int, m: Marker): Board
def numOfMarkers: Map[Marker, Int]
}
class ListBoard (
private val list: List[Marker]
) extends Board {
def this() = this(List.fill(64) { Blank })
override def apply(x: Int, y: Int): Marker = list(index(x, y))
override def updated(x: Int, y: Int, m: Marker): ListBoard =
new ListBoard(list.updated(index(x, y), m))
private def index(x: Int, y: Int) = x + y * 8
override def numOfMarkers: Map[Marker, Int] = {
val map = mutable.HashMap.empty ++
(Marker.values map {_ -> 0})
for (m <- list) {
map(m) = map(m) + 1
}
map.toMap
}
override def toString: String = {
val map = Map(Blank -> '.', Dark -> 'X', Light -> 'O')
val ax = " abcdefgh \n"
val b = for {y <- 0 until 8
ay = ('1' + y).asInstanceOf[Char]
l = for (x <- 0 until 8) yield map(list(index(x, y)))
} yield ay + l.mkString + ay + '\n'
ax + b.mkString + ax
}
def isFull: Boolean = list.forall { _ != Blank }
}
Playerは自分のmarkerを持ち、playすることができる。
trait Player[N <: Node[N]] extends NodeCount {
var marker: Marker = _
var opponentMarker: Marker = _
var name: String = ""
def init(m: Marker) {
marker = m
}
def play(ply: Int, node: N, last: Move): Move
}
可能な手の中からランダムに手を選びプレイするRandomPlayer。馬鹿らしいPlayerだけど、これに5割以上で勝てないPlayerもあり得ることに注意。
class RandomPlayer[N <: Node[N]] extends Player[N] {
override def play(ply: Int, node: N, last: Move): Move = {
val moves = node.possibleMoves()
if (moves.length > 0)
moves(Random.nextInt(moves.length))
else
Move.empty
}
}
Github https://github.com/stn/reversi/blob/master/src/main/scala/
2011/06/07: コードを更新
2011/06/25: コードを更新
0 件のコメント:
コメントを投稿