いものやま。

雑多な知識の寄せ集め

「BirdHead」の思考ルーチンを作ってみた。(その2)

昨日は「BirdHead」に対するランダムAIを実装した。

今日はもうちょっとマシなAIを実装してみる。

グリーディAI

「22」や「5本のキュウリ」のルールを聞いたときに誰もが思うのが、「それなら一番強いカードを出し続ければいいんじゃないの?」というもの。
実際には、リードが取れないならしゃがんでディスカードしたり、後で邪魔になりそうなカードをプレイしたりするというのが必要になってくるのだけど、「常に一番強いカードを出し続ける」というのも一つの戦略。
そこで、これをグリーディAIとして実装してみる。
(グリーディは「貪欲」という意味で、一番効果の高い選択(今回の場合、一番強いカードを出す)を常に選び続けるアルゴリズムを一般に「グリーディ法」と呼ぶ)

//==============================
// BirdHead
//------------------------------
// GreedyCom.swift
//==============================

import Foundation

class GreedyCom: Player {
  private(set) var name: String
  private(set) var isCom: Bool

  init(name: String) {
    self.name = name
    self.isCom = true
  }

  func select(view: GameInfo.PlayerView) throws -> Action {
    var action: Action! = nil
    var legalActions = view.legalActions
    if view.actionsInTrick.count == 0 {
      var cardCount: Int = 0
      for legalAction in legalActions {
        if case .Play(let cards) = legalAction {
          if cards.count >= cardCount {
            cardCount = cards.count
            action = legalAction
          }
        }
      }
    } else {
      action = legalActions.removeLast()
    }
    return action
  }
}

リードのときには、出来るだけ枚数が多く、その中でも一番数が大きいカードをプレイし、そうでない場合、合法手の一番最後の手(手札は強さ順にソートされているので、生成された合法手の一番最後が一番強いカードになっている)を選ぶようにしている。

実行例

グリーディAI同士で対戦させるコードは、以下。

import Foundation

let deck = Deck()
let game = GameInfo(deck: deck, playerCount: 4)
let players: [Player] = [
  GreedyCom(name: "Greedy Com 1"),
  GreedyCom(name: "Greedy Com 2"),
  GreedyCom(name: "Greedy Com 3"),
  GreedyCom(name: "Greedy Com 4"),
]

let controller = GameController(gameInfo: game, players: players)
controller.output = true
try! controller.start()

これをビルドして実行すると、次のような感じ。

--------------------
deal...
----------
[Greedy Com 1]
selected action: Play([10, 10])
[Greedy Com 2]
selected action: Play([10, 10])
[Greedy Com 3]
selected action: Play([11, 11])
[Greedy Com 4]
selected action: Discard([2, 3])
----------
trick 0 is done.
Greedy Com 3 takes trick.
----------
[Greedy Com 3]
selected action: Play([9, 9])
[Greedy Com 4]
selected action: Play([9, 11])
[Greedy Com 1]
selected action: Discard([2, 2])
[Greedy Com 2]
selected action: Discard([2, 2])
----------
trick 1 is done.
Greedy Com 4 takes trick.
----------
[Greedy Com 4]
selected action: Play([4, 4])
[Greedy Com 1]
selected action: Play([7, 7])
[Greedy Com 2]
selected action: Discard([3, 5])
[Greedy Com 3]
selected action: Play([8, 11])
----------
trick 2 is done.
Greedy Com 3 takes trick.
----------
[Greedy Com 3]
selected action: Play([8])
[Greedy Com 4]
selected action: Play([9])
[Greedy Com 1]
selected action: Discard([3])
[Greedy Com 2]
selected action: Discard([6])
----------
trick 3 is done.
Greedy Com 4 takes trick.
----------
[Greedy Com 4]
selected action: Play([8])
[Greedy Com 1]
selected action: Discard([3])
[Greedy Com 2]
selected action: Discard([6])
[Greedy Com 3]
selected action: Discard([4])
----------
trick 4 is done.
Greedy Com 4 takes trick.
----------
[Greedy Com 4]
selected action: Play([6])
[Greedy Com 1]
selected action: Discard([4])
[Greedy Com 2]
selected action: Play([7])
[Greedy Com 3]
selected action: Play([7])
----------
trick 5 is done.
Greedy Com 3 takes trick.
----------
deal is done.
last cards:
Greedy Com 1: 5
Greedy Com 2: 6
Greedy Com 3: 5
Greedy Com 4: 5
["Greedy Com 2"] lose in deal.
minus points:
Greedy Com 1: []
Greedy Com 2: [6]
Greedy Com 3: []
Greedy Com 4: []
--------------------
deal...
----------

〜省略〜

--------------------
deal...
----------
[Greedy Com 1]
selected action: Play([10, 10])
[Greedy Com 2]
selected action: Play([11, 11])
[Greedy Com 3]
selected action: Discard([2, 2])
[Greedy Com 4]
selected action: Discard([2, 3])
----------
trick 0 is done.
Greedy Com 2 takes trick.
----------
[Greedy Com 2]
selected action: Play([8, 8, 8])
[Greedy Com 3]
selected action: Play([9, 9, 11])
[Greedy Com 4]
selected action: Discard([4, 4, 4])
[Greedy Com 1]
selected action: Discard([2, 3, 4])
----------
trick 1 is done.
Greedy Com 3 takes trick.
----------
[Greedy Com 3]
selected action: Play([5, 5, 5])
[Greedy Com 4]
selected action: Play([7, 10, 11])
[Greedy Com 1]
selected action: Discard([7, 7, 8])
[Greedy Com 2]
selected action: Discard([2, 3, 5])
----------
trick 2 is done.
Greedy Com 4 takes trick.
----------
[Greedy Com 4]
selected action: Play([6])
[Greedy Com 1]
selected action: Play([9])
[Greedy Com 2]
selected action: Play([10])
[Greedy Com 3]
selected action: Discard([3])
----------
trick 3 is done.
Greedy Com 2 takes trick.
----------
deal is done.
last cards:
Greedy Com 1: 9
Greedy Com 2: 10
Greedy Com 3: 3
Greedy Com 4: 4
["Greedy Com 2"] lose in deal.
minus points:
Greedy Com 1: [6]
Greedy Com 2: [6, 6, 10]
Greedy Com 3: [7, 9]
Greedy Com 4: [6, 8, 7]
--------------------
game ended.
total minus points:
Greedy Com 1: 6
Greedy Com 2: 22
Greedy Com 3: 16
Greedy Com 4: 21
["Greedy Com 2"] lose.

積極的に複数枚リードや強いカードでのリードを行っていることが分かる。

ただ、最後のディールのGreedy Com 2の動きを追ってみると、手札が最初2, 3, 5, 8, 8, 8, 10, 10, 11, 11で、序盤に8, 8, 8, 11, 11を使ったことで10が2枚残ってしまっていて、これが最後まで響いてしまった感じ。
この辺りは難しいところだけれど、先に2枚の10を使ってしまって、どこかで11を使ってトリックをとり、3枚の8や残った11でリードしたいところかも。
もちろん、それで2枚の11が取り残されてしまう可能性もあるけど。

あと、全体的にプレイが単調。
まぁ、シンプルな戦略なので、仕方ないのだけど・・・

そこで、明日からは強化学習の方法を使って思考ルーチンを作ることを考えてみる。

今日はここまで!