コンソールライブラリで遊んでみる
最後に応用例として、Cursesを使ったゲームを作ってみたいと思います。どんなゲームを作るかというと、大昔からある「ハングマン」というゲームです。ハングマンというのは、出題者(今回はコンピュータ)が英単語を一つ問題として決めておき、解答者(人間)は問題で使われている(と思う)文字を一文字ずつ入力していきます。一定回数内の入力で正解の英単語が当てられれば解答者の勝ちです。正解できなければ出題者の勝ちになります。
今回作るソースは全部で3本です。
- hanging.rb メインプログラムです。
- mondaiwind.rb 問題を出力するウィンドウを表現します。
- mondai.rb 問題の英単語を表現します。
なおこのプログラムを動作させるには"Hpricot"というライブラリが必要ですのでRubyGemsでインストールしてください。このライブラリについては後述します。
gem install hpricot
まずメインプログラム(hanging.rb)です。
大まかな流れとしては、問題作成→表示→入力待ち→正解or解答回数終了となります。メインプログラムは流れを作っているだけで細かな部分は他のクラスに任せています。
Curses::getch
はユーザーの入力を受け付けるメソッドですが、結果は文字コード(Integerオブジェクト)で返されます。このままでは扱いにくいのでchr
メソッドで文字列に変換しています。
require "mondai" require "mondaiwind" require "curses" include Curses init_screen cbreak #バッファリングモードオフ begin puts "making mondai....." # 問題文の生成 m = Mondai.new mondai = m.make # 問題文の表示 mon_win = MondaiWind.new(stdscr) mon_win.display(mondai) # 解答入力待ち max_input = mondai.length * 2 # 問題の長さの2倍まで入力を試せる max_input.times do if m.all_collect? # 全文字正解した場合「SEIKAI!」と表示して終了 mon_win.seikai break end input_str = getch.chr addstr(input_str) if index = m.check(input_str) # 入力した文字が正解ならその文字を正解の位置に表示 mon_win.turn_up(input_str,index) end end getch ensure close_screen # 最後に解答を表示 puts "answer=>#{mondai}" end
次は、問題出力用のウィンドウクラス(mondaiwind.rb)です。
これまで説明した事を少し応用して組み立てています。
require "curses" class MondaiWind def initialize(main_wind) # 初期化(newする時に呼ばれる) max_y = main_wind.maxy max_x = main_wind.maxx # メインウィンドウの高さを少し小さくしたサブウィンドウを作成 @window = main_wind.subwin(max_y-3,max_x,0,0) # サブウィンドウの境界線を描く @window.box(?|,?-,?*) # 解答入力欄を作成 main_wind.setpos(@window.maxy,0) main_wind.addstr("input=>") end def display(mondai_str) # 問題文の表示 @mondai_str = mondai_str @mondai_y = @window.maxy/2 @mondai_x = (@window.maxx/2)-(mondai_str.length/2) # サブウィンドウのちょうど真ん中に問題文を表示 @window.setpos(@mondai_y,@mondai_x) # 問題文を表示(最初は全部"_"を出しておく) @window.addstr("_" * @mondai_str.length) @window.refresh end def turn_up(moji_str,index) # 正解となった文字を表示 # カーソルを、正解した文字の位置に移動 @window.setpos(@mondai_y,@mondai_x+index) # '_'の代わりに正解した文字を表示 @window.addstr(moji_str) @window.refresh end def seikai # 全文字正解になったときに呼ばれる @window.setpos(@mondai_y + 1,@mondai_x) @window.addstr("SEIKAI!!") @window.refresh end end
最後に問題文クラス(mondai.rb)です。
ここは少し複雑なので多めにコメントを入れておきました。
HpricotというのはRubyでHTMLを解析するためのライブラリです。簡単に使える上に非常に高速なのでお勧めです。解析するだけでなくプログラム上で書き換えたものを再びHTMLとして出力する機能も備えています。
require 'rubygems' require 'hpricot' require 'open-uri' class Mondai def make # 問題となる英単語を文字列として取得 mondaibun = get_from_remote # その文字列を一文字ずつ分割して配列にする @mondai = mondaibun.split(//) # 正解数を初期化 @collect_num = 0 # 問題文(文字列)を返却 return mondaibun end def check(input_c) # 入力された文字が問題文の配列の中にあるか探査 pos = @mondai.index(input_c) if pos # 該当した場合はその箇所を空文字にする(再度ヒットしないように) @mondai[pos] = "" @collect_num +=1 # 正解数を増やす end # 入力された文字の配列中の位置を返却 # 該当しなかった場合はnilを返却 return pos end def all_collect? # 正解数が問題文の長さと同じかそれ以上なら全文字正解 return (@collect_num >= @mondai.length) end private # これ以降のメソッドは外からは見えないよ宣言 def get_from_remote # del.icio.usのタグ一覧からページを取得 doc = Hpricot(open("http://del.icio.us/tag/")) # ページ内からタグに相当する部分のみを切り出す(結果は配列) arr = doc.search("div.alphacloud a") # 沢山あるタグの中から一つランダムで選び返却する return arr[rand(arr.size-1)].inner_html end end
まとめ
今回は、Rubyの練習課題としてコンソールアプリを作り始めました。
Cursesライブラリの準備の仕方や使い方、そしてその応用例を説明しました。
次回はもう少し大きなコンソールアプリを作ってみたいと思います。