[ruby]gemを使ってテキストベースのゲームを超高速に作る


## はじめに

![asad.gif](https://qiita-image-store.s3.amazonaws.com/0/106272/bc46b0ae-adf7-4f46-7ee1-41124604faf6.gif)

みんなー、雑にゲーム作って遊んでるー！？ (ゝω・)v
今日はrubyでCLI(CUI)、つまりコンソール上で操作するためのUIを簡単に実装できるgemの**TTY**について紹介しよう。同様のことをしてくれるgemは他にもcursesやthorなどがあるがTTYは最も様々な機能が充実している。つよい。

TTYが提供するものは

 - yes/no、複数選択肢の入力UI
 - Pager、unixのlessのように長文を表示するUI
 - プログレスバーの表示
 - 表示する文字列の装飾(前景色/背景色/bold...)
 - テーブル表示(mysqlのコンソール出力のような)

などなど。上記は代表的なものだけを抜き出しただけで14種類ものライブラリがTTYにパッケージされている。詳しくは本家githubを参照してほしい。
https://github.com/piotrmurach/tty

## 題材とするゲーム

今回、CUIゲーム化するのはこのゲーム。ゲームマーケット2013に出版された可愛い動物のドラフトゲーム、**Banquet**。数分で終わるお手軽な読み合いゲームだ。

![20140512230245.png](https://qiita-image-store.s3.amazonaws.com/0/106272/2e16baa3-f72a-100e-4955-1f7a07d17f4c.png)

[Banquet - ラウンドテーブル](http://www.d-roundtable.com/?pid=73988756)


勝手にプログラムなんか書いて公開して怒られないのかって？ **大丈夫、制作者は自分だ。**

遊べる状態のコードをgithubに上げておいたから参考にしたり遊んだりしてくれ。
…ただ、すまないけどまともなAIはまだ書いてないんだ。
https://github.com/owlworks/banquet

## 選択肢の表示

さて。では実際にコードを書いてみよう。まずは選択肢UIについて。
TTYには強力な選択肢機能がある。実際に試してみよう。

```ruby:sample.rb
require 'tty'

animal = TTY::Prompt.new.select('好きな動物は？') do |menu|
  menu.choice 'ねこ'
  menu.choice 'Cat'
  menu.choice 'にゃんこ'
  menu.choice 'Felis silvestris catus'
end

puts animal
```

![a.gif](https://qiita-image-store.s3.amazonaws.com/0/106272/380b045f-f968-21b8-e681-11bc13f85cfd.gif)

ちょうクールだね！
でも返り値が表示されてる名前そのままじゃ実際の処理では使いにくいよね。大丈夫、TTY::Prompt#selectはmenu.choiceで表示名ではなくハッシュ型の返り値を得ることも出来るんだ。例えばこんな風にね。

```ruby:sample.rb
require 'tty'

animal = TTY::Prompt.new.select('好きな動物は？') do |menu|
  menu.choice 'ねこ', {key: :neko, num: 1}
  menu.choice 'Cat', {key: :cat, num: 2}
  menu.choice 'にゃんこ', {key: :nyanko, num: 3}
  menu.choice 'Felis silvestris catus', {key: :catus, num:4}
end

puts animal.inspect

=> {:key=>:nyanko, :num=>3}
```

ぐれいと。こうなればBanquetに含まれる、手札の中からカードを選んでもらうような処理も書けるね。

```ruby:banquet.rb
  # 引数pileはCardオブジェクトを要素とする配列, descriptionは選択股の説明文
  def select_card_cli(pile, description)
    refresh_layout_game # カードを選ぶ前に盤面の情報を更新する
    choice = TTY::Prompt.new.select(description) do |menu|
      pile.each_with_index do |card, index|
        menu.choice card.layout, {index_num: index}
      end
    end
    choice
  end

  pick = select_card_cli(pile, 'Pickup card for your hand.')
  puts pick.inspect
  => {:index_num=>0}
```
また、menu.choiceで指定する表示名は改行コードを含めて2行となってもよい。その場合も１行めの文字列にカーソルが合う。実際、上のコードに書かれているcard.layoutはこんな内容になっていて改行コードを含んでいる。

```ruby
pile.first.layout
=> "\e[30;41;1m[   ネコ   ] (3)\e[0m\n     プレイしたとき対戦相手がプレイしたカードとこのカードを入れ替える"
```

## 文字の装飾

「\e[30;41;1m」とかいうのは何かって？　よし、その話をしよう。これはTTYの一部であるpastelの機能だ。Card#layoutの中身はこうなっている。

```ruby:banquet.rb
# 説明に関係ある部分だけ抜粋
class Card
  attr :card_key, :name, :text, :color
  INDENT = ' ' * 5

  def layout
    [name_layout, text_layout ].join("\n")
  end

  def name_layout
    PASTEL.black.send("on_#{@color.to_s}").bold("[#{@name.center(8)}] (#{@score})")
  end

  def text_layout
    (INDENT + @text).each_char.each_slice(80).map(&:join).join("\n#{INDENT}")
  end
end
```

ちょっと書き方がわかりにくいので公式githubのシンプルな例で説明しよう。

```ruby:usage.rb
require 'tty'
pastel = Pastel.new
decorated = pastel.black.on_red.bold('Oh, How beautiful you are!')
puts decorated
puts decorated.inspect
```

![スクリーンショット 2017-08-06 14.42.30.png](https://qiita-image-store.s3.amazonaws.com/0/106272/8774add2-abf7-70ac-3034-a6168b6d64f2.png)

Pastelオブジェクトをメソッドチェーンして末端のメソッドに文字列を引数として渡して上げるとコンソール上に標準出力された際に装飾される文字記号が付与されて返って来る。
(指定可能な色やメソッドについては公式githubを。　https://github.com/piotrmurach/pastel)

## 締め

今回のゲームで利用したのはここまで。TTYの機能を利用すればCUIアプリケーションのUI部分で困ることはまずないだろう。さっそくちょっとだけUIがクールなCUIアプリを書こう！　まったくこの内容でコードの総量がたったの250行くらいだっていうからお手軽だよ。

現場から以上です。╭( ･ㅂ･)و