challenge マルバツゲーム:賢いプレイヤー

#6190 の続編です。
マルバツゲームで、賢いプレイヤーの思考ルーチンを実装してください。

賢いといってもいろいろありますが、
1.負けない
2.できるだけ勝つ
という条件でいってみたいと思います。

ランダムプレイヤーと1万回バトルした結果(勝ち・負け・分け)を表示してください。
先攻になっても後攻になっても無敗!となれば言うことなしです。

Posted feedbacks - Scala

微妙な感じですがとりあえず・・・CleverPlayerを追加しました。

1万回対戦させてみたところ

Cleverが先行

Clever won:9,379
Random won:0
draw      :621

Randomが先行

Clever won:8,596
Random won:0
draw      :1,404

Clever vs Clever

Clever won:0
Clever won:0
draw      :10,000

という感じでした。戦略は優先度順に

  1. 置けば勝ち、置かなければ負ける、という位置があればそこに置く
  2. 自分が後手で1ターン目に相手が真ん中に置かなかった場合の2ターン目
    • 相手が置いているところが全て角なら角以外の淵に置く
    • でなければ相手の置いているところから距離が一番近い角に置く
  3. 真ん中が空いていれば真ん中に置く
  4. 角が空いていれば角に置く
  5. 置けるところに置く

としてみました。

 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
import java.util.Random

class TicTacToe(val players:List[Player]) {
  val size = 3
  protected val field = new Array[Array[char]](size,size)
  val lines = (_stline((v,v2) => (v,v2))++_stline((v,v2) => (v2,v))++
              List(((size-1).until(-1,-1)).map(v=> (v,(size-1-v)))) ++
              List((0 until size).map(v=>(v,v)))).map(_.toList)

  def _stline(f:(int,int) => Pair[int,int]) = 
    (0 until size).map(v => (0 until size).map(v2 => f(v,v2)))

  def start:Option[Player] = {
    for(val i<-(0 until size); val j<-(0 until size)) { field(i)(j) = ' '}
    Stream.const(players).flatMap(v=>v).take(size*size).find {player => 
      val p@Pair(y,x) = player.put(this)
      if(!available_?(p)) error("Oops!")
      field(y)(x) = player.mark
      judge
    }
  }

  def judge = lines.exists(l => l.forall(!available_?(_)) && l.forall(v => at(l(0)) == at(v)))
  def available_?(pos:Pair[int,int]) = at(pos) equals ' '
  def at(pos:Pair[int,int]) = field(pos._1)(pos._2)
  def at_eq(pos:Pair[int,int], mark:char) = at(pos) equals mark
  def enemy_of(player:Player) = players.find{_.mark != player.mark}.get
}

abstract class Player{
  val mark:char
  val name:String
  def put(ttt:TicTacToe):Pair[int,int]
}

class RandomPlayer(override val mark:char, override val name:String) extends Player{
  val rnd = new Random
  override def put(ttt:TicTacToe) = {
    def _n = (rnd.nextInt(ttt.size), rnd.nextInt(ttt.size))
    def next(v:Pair[int,int]):Stream[Pair[int,int]] = Stream.cons(v, next(_n))
    next(_n).find(ttt.available_?).get
  }
}

class CleverPlayer(override val mark:char, override val name:String) extends Player {
  override def put(ttt:TicTacToe):Pair[int,int] = {
    val enemy = ttt.enemy_of(this)
    val return_if_available_find = (l:Seq[Pair[int,int]]) => l.find(ttt.available_?) match {
      case Some(pos) => return pos
      case _ => ()
    }

    def line2(v:char) = ttt.lines.filter(_.count(ttt.at_eq(_,v)) == 2)
    return_if_available_find(List(mark, enemy.mark).flatMap(line2).flatMap(v=>v))

    val all  = (for(val i<-(0 until ttt.size); val j<-(0 until ttt.size)) yield (i,j)).toList
    val center = (ttt.size/2, ttt.size/2)
    val corners = List((0,0), (ttt.size-1,ttt.size-1), (0,ttt.size-1), (ttt.size-1, 0))
    val others = all.diff(center::corners)

    if(all.count(ttt.at_eq(_, enemy.mark)) == 2 && ttt.at_eq(center, this.mark)) {
      if(!others.forall(ttt.available_?)) {
        val ps = all.filter(ttt.at_eq(_, enemy.mark))
        def vecv(a:Pair[int,int]) = (0 /: ps){(r, p) => r+(a._1 - p._1).abs+(a._2 - p._2).abs}
        return_if_available_find(corners.sort(vecv(_) < vecv(_)))
      }
      return_if_available_find(others)
    }

    return_if_available_find(List(center))
    return_if_available_find(corners)
    others.find(ttt.available_?).get
  }
}

Index

Feed

Other

Link

Pathtraq

loading...