読者です 読者をやめる 読者になる 読者になる

いものやま。

雑多な知識の寄せ集め

「BirdHead」を遊べるようにしてみた。(その7)

ゲーム開発 Swift BirdHead

昨日はコントローラの実装を行った。

今日は人間プレイヤーの実装を行って、実際に遊べるようにしてみる。

Humanクラス

ということで、さっそくコードを。

//==============================
// BirdHead
//------------------------------
// Human.swift
//==============================

import Foundation

extension NSFileHandle {
  func readString() -> String {
    let data = self.availableData
    let string = NSString(data: data, encoding: NSUTF8StringEncoding)!
    return string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
  }

  func writeString(string: String) -> Void {
    let data = string.dataUsingEncoding(NSUTF8StringEncoding)!
    self.writeData(data)
  }
}

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

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

  func select(view: GameInfo.PlayerView) throws -> Action {
    let stdin = NSFileHandle.fileHandleWithStandardInput()
    let stdout = NSFileHandle.fileHandleWithStandardOutput()

    var action: Action! = nil
    let legalActions = view.legalActions
    while action == nil {
      print("hands: \(view.hands)")
      for i in 0..<legalActions.count {
        let legalAction = legalActions[i]
        switch legalAction {
        case let .Play(cards):
          print("\(i). play \(cards)")
        case let .Discard(cards):
          print("\(i). discard \(cards)")
        }
      }
      print("q. quit")

      stdout.writeString("> ")
      let input = stdin.readString()

      if input == "q" {
        print("quit.")
        throw PlayerError.SelectIsCanceled
      } else {
        if let selectIndex = Int(input) {
          if (0 <= selectIndex) && (selectIndex < legalActions.count) {
            action = legalActions[selectIndex]
            break
          } else {
            print("invalid number.")
          }
        } else {
          print("invalid input.")
        }
      }
    }
    return action
  }
}

先頭でNSFileHandleを拡張しているのは、標準入出力を簡単に扱えるようにするため。
これについては、変種オセロをSwiftに移植してみた。(その9) - いものやま。を参照。

そして、肝心のHuman#select(_: GameInfo.PlayerView)では、手札と合法手の一覧を表示させて、合法手からアクションを選ぶようにしている。
このとき、qが入力されたら、例外を投げて選択をキャンセルするようにもしている。

動作確認

さて、いよいよ動作確認。

まずは、以下のようなコードを用意。

// HumanTest.swift

import Foundation

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

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

do {
  try controller.start()
} catch _ {
}

そして、Makefile

# source files of module
SOURCE = Random.swift Deck.swift Action.swift GameInfo.swift \
        GameInfoObserver.swift GameController.swift \
        Player.swift Human.swift

# source files for test
TEST_SOURCE = DeckTest.swift ActionTest.swift GameInfoTest.swift HumanTest.swift

# test application name (DON'T EDIT)
TEST_APP = $(TEST_SOURCE:%.swift=%)

# make rules (DON'T EDIT)

testbuild: $(TEST_APP)

$(TEST_APP): $(SOURCE)

$(TEST_APP):%:%.swift
  cp $< main.swift
  xcrun -sdk macosx swiftc -o $@ main.swift $(SOURCE)
  rm main.swift

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

$ ./HumanTest
--------------------
deal...
----------
[Player 1]
hands: [2, 3, 6, 7, 7, 7, 8, 9, 10, 10]
0. play [2]
1. play [3]
2. play [6]
3. play [7]
4. play [7, 7]
5. play [7, 7, 7]
6. play [8]
7. play [9]
8. play [10]
9. play [10, 10]
q. quit
> 5
selected action: Play([7, 7, 7])
[Player 2]
hands: [3, 4, 4, 4, 5, 6, 7, 10, 11, 11]
0. discard [3, 4, 4]
1. play [7, 10, 11]
2. play [7, 11, 11]
3. play [10, 11, 11]
q. quit
> 1
selected action: Play([7, 10, 11])
[Player 3]
hands: [2, 3, 4, 5, 6, 6, 7, 8, 9, 9]
0. discard [2, 3, 4]
q. quit
> 0
selected action: Discard([2, 3, 4])
[Player 4]
hands: [2, 2, 3, 4, 5, 5, 8, 8, 9, 10]
0. discard [2, 2, 3]
q. quit
> 0
selected action: Discard([2, 2, 3])
----------
trick 0 is done.
Player 2 takes trick.
----------
[Player 2]
hands: [3, 4, 4, 4, 5, 6, 11]
0. play [3]
1. play [4]
2. play [4, 4]
3. play [4, 4, 4]
4. play [5]
5. play [6]
6. play [11]
q. quit
> 6
selected action: Play([11])
[Player 3]
hands: [5, 6, 6, 7, 8, 9, 9]
0. discard [5]
q. quit
> 0
selected action: Discard([5])
[Player 4]
hands: [4, 5, 5, 8, 8, 9, 10]
0. discard [4]
q. quit
> 0
selected action: Discard([4])
[Player 1]
hands: [2, 3, 6, 8, 9, 10, 10]
0. discard [2]
q. quit
> 0
selected action: Discard([2])
----------
trick 1 is done.
Player 2 takes trick.
----------
[Player 2]
hands: [3, 4, 4, 4, 5, 6]
0. play [3]
1. play [4]
2. play [4, 4]
3. play [4, 4, 4]
4. play [5]
5. play [6]
q. quit
> 5
selected action: Play([6])
[Player 3]
hands: [6, 6, 7, 8, 9, 9]
0. play [6]
1. play [7]
2. play [8]
3. play [9]
q. quit
> 1
selected action: Play([7])
[Player 4]
hands: [5, 5, 8, 8, 9, 10]
0. discard [5]
1. play [8]
2. play [9]
3. play [10]
q. quit
> 2
selected action: Play([9])
[Player 1]
hands: [3, 6, 8, 9, 10, 10]
0. discard [3]
1. play [9]
2. play [10]
q. quit
> 1
selected action: Play([9])
----------
trick 2 is done.
Player 1 takes trick.
----------
[Player 1]
hands: [3, 6, 8, 10, 10]
0. play [3]
1. play [6]
2. play [8]
3. play [10]
4. play [10, 10]
q. quit
> 4
selected action: Play([10, 10])
[Player 2]
hands: [3, 4, 4, 4, 5]
0. discard [3, 4]
q. quit
> 0
selected action: Discard([3, 4])
[Player 3]
hands: [6, 6, 8, 9, 9]
0. discard [6, 6]
q. quit
> 0
selected action: Discard([6, 6])
[Player 4]
hands: [5, 5, 8, 8, 10]
0. discard [5, 5]
q. quit
> 0
selected action: Discard([5, 5])
----------
trick 3 is done.
Player 1 takes trick.
----------
[Player 1]
hands: [3, 6, 8]
0. play [3]
1. play [6]
2. play [8]
q. quit
> 2
selected action: Play([8])
[Player 2]
hands: [4, 4, 5]
0. discard [4]
q. quit
> 0
selected action: Discard([4])
[Player 3]
hands: [8, 9, 9]
0. play [8]
1. play [9]
q. quit
> 1
selected action: Play([9])
[Player 4]
hands: [8, 8, 10]
0. discard [8]
1. play [10]
q. quit
> 1
selected action: Play([10])
----------
trick 4 is done.
Player 4 takes trick.
----------
[Player 4]
hands: [8, 8]
0. play [8]
q. quit
> 0
selected action: Play([8])
[Player 1]
hands: [3, 6]
0. discard [3]
q. quit
> 0
selected action: Discard([3])
[Player 2]
hands: [4, 5]
0. discard [4]
q. quit
> 0
selected action: Discard([4])
[Player 3]
hands: [8, 9]
0. play [8]
1. play [9]
q. quit
> 1
selected action: Play([9])
----------
trick 5 is done.
Player 3 takes trick.
----------
deal is done.
last cards:
Player 1: 6
Player 2: 5
Player 3: 8
Player 4: 8
["Player 3", "Player 4"] lose in deal.
minus points:
Player 1: []
Player 2: []
Player 3: [8]
Player 4: [8]
--------------------
deal...
----------

〜省略〜

--------------------
deal...
----------
[Player 3]
hands: [3, 3, 6, 6, 8, 9, 9, 10, 10, 11]
0. play [3]
1. play [3, 3]
2. play [6]
3. play [6, 6]
4. play [8]
5. play [9]
6. play [9, 9]
7. play [10]
8. play [10, 10]
9. play [11]
q. quit
> 6
selected action: Play([9, 9])
[Player 4]
hands: [2, 3, 4, 5, 6, 6, 7, 8, 9, 10]
0. discard [2, 3]
1. play [9, 10]
q. quit
> 1
selected action: Play([9, 10])
[Player 1]
hands: [2, 4, 5, 7, 7, 9, 10, 10, 11, 11]
0. discard [2, 4]
1. play [9, 10]
2. play [9, 11]
3. play [10, 10]
4. play [10, 11]
5. play [11, 11]
q. quit
> 5
selected action: Play([11, 11])
[Player 2]
hands: [2, 2, 2, 3, 3, 4, 4, 5, 5, 8]
0. discard [2, 2]
q. quit
> 0
selected action: Discard([2, 2])
----------
trick 0 is done.
Player 1 takes trick.
----------
[Player 1]
hands: [2, 4, 5, 7, 7, 9, 10, 10]
0. play [2]
1. play [4]
2. play [5]
3. play [7]
4. play [7, 7]
5. play [9]
6. play [10]
7. play [10, 10]
q. quit
> 4
selected action: Play([7, 7])
[Player 2]
hands: [2, 3, 3, 4, 4, 5, 5, 8]
0. discard [2, 3]
q. quit
> 0
selected action: Discard([2, 3])
[Player 3]
hands: [3, 3, 6, 6, 8, 10, 10, 11]
0. discard [3, 3]
1. play [8, 10]
2. play [8, 11]
3. play [10, 10]
4. play [10, 11]
q. quit
> 2
selected action: Play([8, 11])
[Player 4]
hands: [2, 3, 4, 5, 6, 6, 7, 8]
0. discard [2, 3]
q. quit
> 0
selected action: Discard([2, 3])
----------
trick 1 is done.
Player 3 takes trick.
----------
[Player 3]
hands: [3, 3, 6, 6, 10, 10]
0. play [3]
1. play [3, 3]
2. play [6]
3. play [6, 6]
4. play [10]
5. play [10, 10]
q. quit
> 5
selected action: Play([10, 10])
[Player 4]
hands: [4, 5, 6, 6, 7, 8]
0. discard [4, 5]
q. quit
> 0
selected action: Discard([4, 5])
[Player 1]
hands: [2, 4, 5, 9, 10, 10]
0. discard [2, 4]
1. play [10, 10]
q. quit
> 1
selected action: Play([10, 10])
[Player 2]
hands: [3, 4, 4, 5, 5, 8]
0. discard [3, 4]
q. quit
> 0
selected action: Discard([3, 4])
----------
trick 2 is done.
Player 1 takes trick.
----------
[Player 1]
hands: [2, 4, 5, 9]
0. play [2]
1. play [4]
2. play [5]
3. play [9]
q. quit
> 3
selected action: Play([9])
[Player 2]
hands: [4, 5, 5, 8]
0. discard [4]
q. quit
> 0
selected action: Discard([4])
[Player 3]
hands: [3, 3, 6, 6]
0. discard [3]
q. quit
> 0
selected action: Discard([3])
[Player 4]
hands: [6, 6, 7, 8]
0. discard [6]
q. quit
> 0
selected action: Discard([6])
----------
trick 3 is done.
Player 1 takes trick.
----------
[Player 1]
hands: [2, 4, 5]
0. play [2]
1. play [4]
2. play [5]
q. quit
> 2
selected action: Play([5])
[Player 2]
hands: [5, 5, 8]
0. play [5]
1. play [8]
q. quit
> 1
selected action: Play([8])
[Player 3]
hands: [3, 6, 6]
0. discard [3]
q. quit
> 0
selected action: Discard([3])
[Player 4]
hands: [6, 7, 8]
0. discard [6]
1. play [8]
q. quit
> 1
selected action: Play([8])
----------
trick 4 is done.
Player 4 takes trick.
----------
[Player 4]
hands: [6, 7]
0. play [6]
1. play [7]
q. quit
> 1
selected action: Play([7])
[Player 1]
hands: [2, 4]
0. discard [2]
q. quit
> 0
selected action: Discard([2])
[Player 2]
hands: [5, 5]
0. discard [5]
q. quit
> 0
selected action: Discard([5])
[Player 3]
hands: [6, 6]
0. discard [6]
q. quit
> 0
selected action: Discard([6])
----------
trick 5 is done.
Player 4 takes trick.
----------
deal is done.
last cards:
Player 1: 4
Player 2: 5
Player 3: 6
Player 4: 6
["Player 4", "Player 3"] lose in deal.
minus points:
Player 1: []
Player 2: [7]
Player 3: [8, 7, 6]
Player 4: [8, 6, 5, 6]
--------------------
game ended.
total minus points:
Player 1: 0
Player 2: 7
Player 3: 21
Player 4: 25
["Player 4"] lose.

全員人間プレイヤーにしているので手札が丸見えだけど、ちゃんと遊べているのが分かると思う。

今日はここまで!