プログラミング言語/Ruby/コードリーディング/しりとりゲーム
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
* Rubyコードリーディング:しりとりゲーム [#kfb1075e]
[[「プログラミングに詳しい香具師ちょっと来い」:http://dat...
[[要件をまとめた練習問題はこちら>練習問題]]~
しりとりゲームの全ソースは以下のリンクをクリックしてダウ...
#ref(shiritori.rb)
Ver.3 2007/05/15 構成を修正。ほか細々~
Ver.2 2007/05/10 AutoPlayerのバグを修正
#contents
* shiritori.rb インタフェース [#k61701e5]
[[rdefs.rb:http://jp.rubyist.net/magazine/?c=plugin;plugi...
図はOpenOffice.org Draw。
#ref(shiritori01.png,noimg)
** Rands [#k1b22987]
module Rands
def bool_rand
def array_rand(array)
def char_rand
** GameMaster [#bf2e1bae]
class GameMaster
def initialize
def log_search(word)
def log_size
def dictionary_load(f)
def entry(pl)
def now
def succ
def each
def twice?(word)
private
def turn_player
** Player [#f6986cc4]
class Player
def initialize(id, dictionary=[], gm=nil)
def game_master=(gm)
attr_reader :id
attr_accessor :dictionary
** ManualPlayer [#qd321ffc]
class ManualPlayer < Player
include Rands
def answer(before)
def before_input
private
def read_answer(before, prompt, err_msg)
** AutoPlayer [#i723bb6b]
class AutoPlayer < Player
include Rands
def answer(before)
private
def think_answer(before)
* クラス・モジュールごとの解説 [#v0e5dea7]
+[[./GameMasterクラス]]
+[[./Playerクラス]]
+[[./ManualPlayerクラス]]
+[[./AutoPlayerクラス]]
+[[./Randsモジュール]]
* メインフロー(トップレベル)の解説 [#j12a2291]
** リスト6-1 トップレベルの全体像 [#t7da5d41]
USAGE = "ruby shiritori.rb DICT_FILE"
begin
##(省略)##
rescue RuntimeError => ex
$stderr.puts "Error: #{ex.message}"
$stderr.puts "Usage: #{USAGE}"
exit(1)
end
しりとりゲームを処理しているメインの部分は後述する。
外枠のこの処理はそう複雑なものではない。Rubyの例外は最後...
捕捉されないと、バックトレースを吐いてからプログラムを終...
それはデバックに重宝するのだが、アプリケーションプログラ...
としてはちょっと見た目が悪い。そこで例外を捕捉して、エラ...
使用方法(USAGE)を言ってから、改めて死んでいる。
rescue文では、捕捉する例外クラスを指定しないと自動的にRun...
クラスを捕捉するようにしてくれる。今回省略しなかったのは...
エラーだけを捕まえて、重大な例外は自前で異常終了させるぞ...
ちなみに(名前で判るぞ、と言う方もおられるだろうが)$stderr...
IOオブシェクトだ。似たようなものに$stdinや$stdoutがある。...
** リスト6-2 トップレベルのメイン部分(1) [#oecab4e3]
raise "辞書ファイルが指定されていません" if ARGV.empty?
gm = GameMaster.new
gm.dictionary_load ARGF
Rubyプログラムを起動するときに与えたコマンドライン引数は、~
ARGVという定数に、Arrayオブジェクトとして収まっている。
それと名前がよく似たARGFという定数は、少々特殊な擬似的なI...
(擬似的とは … p ARGF.class #=> Object)
ARGFはUnixのフィルタプログラムを作成する際に理想的な機能...
即ち、コマンドライン引数があればそれをファイル名としてオ...
引数がなければ標準入力を読む。ARGFの場合、いくら引数があ...
ひとつの連続したファイルを開いているかのように扱える。
しかし、このコードではその便利な特性を一つ潰してしまって...
Array#empty?でARGVが空かどうかを事前にチェックしているか...
ただ、このプログラムで、標準入力を使う必要性があまり感じ...
のでこういう措置を取った。sedかなにかで加工した辞書データ...
流し入れたいという人は我慢して一旦ファイルにリダイレクト...
** リスト6-3 トップレベルのメイン部分(2) [#g2bddd32]
pls = [ManualPlayer.new("あなた"), AutoPlayer.new("わた...
pls.reverse! if Rands.bool_rand
pls.each{|x| gm.entry x}
少し見づらいかもしれないが、plsに代入しているのは~
リテラルで生成された配列だ。中身は見てのとおりである。
Array#reverse!は配列の順番を逆転させる。Array#reverseと違...
前者は呼び出し元のオブジェクトを直接操作する(破壊的操作)...
後者はその操作が施された新しいオブジェクトが生成されて返...
Rands.bool_randは半々の確率で真偽どちらかを返す。つまり
... if Rands.bool_rand
は、「50%の確率で...を実行する」と読める。~
その後、順番にGameMasterにentryされているので、~
これはプレイヤーとコンピュータの先攻後攻をランダムで決定...
** リスト6-4 トップレベルのメイン部分(3) [#j5615b0d]
word = nil
gm.each do |word|
$stderr.puts "#{gm.now.id}の回答 ... #{word}"
end
ここがしりとり勝負のループだ。wordにnilを代入しているのは...
しつこく言うが、Rubyのブロックローカルスコープ対策である。
** リスト6-5 トップレベルのメイン部分(4) [#n8fac67e]
if gm.now.id == "あなた" #now.id ... Looser
log = gm.log_search(gm.now.before_input)
$stderr.print "「その言葉は #{log[0]} 回目に #{log[1]...
$stderr.puts "わたしの勝ちです。"
else
$stderr.puts "「まいりました!あなたの勝ちです。"
end
$stderr.puts " 今回のしりとりでは #{gm.log_size} 個の...
GameMaster#nowはループの外では直前の回答者を返す。故に、...
「直前の回答者」はつまり「敗者」のことだ。
要件によれば、プレイヤーの負けかコンピュータの負けかで通...
GameMaster#entryするときに設定したidで分岐している。
プレイヤーが負けの場合、敗因の回答であるbefore_inputを~
log_searchにかけて、必要な情報を得ている。
* おわりに [#s8e5a4c6]
以上、長々とひとつのプログラムを解説してきた。
正直このプログラムには反省点も多々ある。~
例えばGameMaster#nowなどである。~
が、そのへんの改良は皆さんへの課題としておく。~
とか言って都合よく締めておきたい。
また、人にモノを教える文章など書く機会がないので、~
「ここわかりづれーよ」と思ったらスレがどこかで吐き捨てて...
確実に改善できる保証はないが、可能な限り善処する。
終了行:
* Rubyコードリーディング:しりとりゲーム [#kfb1075e]
[[「プログラミングに詳しい香具師ちょっと来い」:http://dat...
[[要件をまとめた練習問題はこちら>練習問題]]~
しりとりゲームの全ソースは以下のリンクをクリックしてダウ...
#ref(shiritori.rb)
Ver.3 2007/05/15 構成を修正。ほか細々~
Ver.2 2007/05/10 AutoPlayerのバグを修正
#contents
* shiritori.rb インタフェース [#k61701e5]
[[rdefs.rb:http://jp.rubyist.net/magazine/?c=plugin;plugi...
図はOpenOffice.org Draw。
#ref(shiritori01.png,noimg)
** Rands [#k1b22987]
module Rands
def bool_rand
def array_rand(array)
def char_rand
** GameMaster [#bf2e1bae]
class GameMaster
def initialize
def log_search(word)
def log_size
def dictionary_load(f)
def entry(pl)
def now
def succ
def each
def twice?(word)
private
def turn_player
** Player [#f6986cc4]
class Player
def initialize(id, dictionary=[], gm=nil)
def game_master=(gm)
attr_reader :id
attr_accessor :dictionary
** ManualPlayer [#qd321ffc]
class ManualPlayer < Player
include Rands
def answer(before)
def before_input
private
def read_answer(before, prompt, err_msg)
** AutoPlayer [#i723bb6b]
class AutoPlayer < Player
include Rands
def answer(before)
private
def think_answer(before)
* クラス・モジュールごとの解説 [#v0e5dea7]
+[[./GameMasterクラス]]
+[[./Playerクラス]]
+[[./ManualPlayerクラス]]
+[[./AutoPlayerクラス]]
+[[./Randsモジュール]]
* メインフロー(トップレベル)の解説 [#j12a2291]
** リスト6-1 トップレベルの全体像 [#t7da5d41]
USAGE = "ruby shiritori.rb DICT_FILE"
begin
##(省略)##
rescue RuntimeError => ex
$stderr.puts "Error: #{ex.message}"
$stderr.puts "Usage: #{USAGE}"
exit(1)
end
しりとりゲームを処理しているメインの部分は後述する。
外枠のこの処理はそう複雑なものではない。Rubyの例外は最後...
捕捉されないと、バックトレースを吐いてからプログラムを終...
それはデバックに重宝するのだが、アプリケーションプログラ...
としてはちょっと見た目が悪い。そこで例外を捕捉して、エラ...
使用方法(USAGE)を言ってから、改めて死んでいる。
rescue文では、捕捉する例外クラスを指定しないと自動的にRun...
クラスを捕捉するようにしてくれる。今回省略しなかったのは...
エラーだけを捕まえて、重大な例外は自前で異常終了させるぞ...
ちなみに(名前で判るぞ、と言う方もおられるだろうが)$stderr...
IOオブシェクトだ。似たようなものに$stdinや$stdoutがある。...
** リスト6-2 トップレベルのメイン部分(1) [#oecab4e3]
raise "辞書ファイルが指定されていません" if ARGV.empty?
gm = GameMaster.new
gm.dictionary_load ARGF
Rubyプログラムを起動するときに与えたコマンドライン引数は、~
ARGVという定数に、Arrayオブジェクトとして収まっている。
それと名前がよく似たARGFという定数は、少々特殊な擬似的なI...
(擬似的とは … p ARGF.class #=> Object)
ARGFはUnixのフィルタプログラムを作成する際に理想的な機能...
即ち、コマンドライン引数があればそれをファイル名としてオ...
引数がなければ標準入力を読む。ARGFの場合、いくら引数があ...
ひとつの連続したファイルを開いているかのように扱える。
しかし、このコードではその便利な特性を一つ潰してしまって...
Array#empty?でARGVが空かどうかを事前にチェックしているか...
ただ、このプログラムで、標準入力を使う必要性があまり感じ...
のでこういう措置を取った。sedかなにかで加工した辞書データ...
流し入れたいという人は我慢して一旦ファイルにリダイレクト...
** リスト6-3 トップレベルのメイン部分(2) [#g2bddd32]
pls = [ManualPlayer.new("あなた"), AutoPlayer.new("わた...
pls.reverse! if Rands.bool_rand
pls.each{|x| gm.entry x}
少し見づらいかもしれないが、plsに代入しているのは~
リテラルで生成された配列だ。中身は見てのとおりである。
Array#reverse!は配列の順番を逆転させる。Array#reverseと違...
前者は呼び出し元のオブジェクトを直接操作する(破壊的操作)...
後者はその操作が施された新しいオブジェクトが生成されて返...
Rands.bool_randは半々の確率で真偽どちらかを返す。つまり
... if Rands.bool_rand
は、「50%の確率で...を実行する」と読める。~
その後、順番にGameMasterにentryされているので、~
これはプレイヤーとコンピュータの先攻後攻をランダムで決定...
** リスト6-4 トップレベルのメイン部分(3) [#j5615b0d]
word = nil
gm.each do |word|
$stderr.puts "#{gm.now.id}の回答 ... #{word}"
end
ここがしりとり勝負のループだ。wordにnilを代入しているのは...
しつこく言うが、Rubyのブロックローカルスコープ対策である。
** リスト6-5 トップレベルのメイン部分(4) [#n8fac67e]
if gm.now.id == "あなた" #now.id ... Looser
log = gm.log_search(gm.now.before_input)
$stderr.print "「その言葉は #{log[0]} 回目に #{log[1]...
$stderr.puts "わたしの勝ちです。"
else
$stderr.puts "「まいりました!あなたの勝ちです。"
end
$stderr.puts " 今回のしりとりでは #{gm.log_size} 個の...
GameMaster#nowはループの外では直前の回答者を返す。故に、...
「直前の回答者」はつまり「敗者」のことだ。
要件によれば、プレイヤーの負けかコンピュータの負けかで通...
GameMaster#entryするときに設定したidで分岐している。
プレイヤーが負けの場合、敗因の回答であるbefore_inputを~
log_searchにかけて、必要な情報を得ている。
* おわりに [#s8e5a4c6]
以上、長々とひとつのプログラムを解説してきた。
正直このプログラムには反省点も多々ある。~
例えばGameMaster#nowなどである。~
が、そのへんの改良は皆さんへの課題としておく。~
とか言って都合よく締めておきたい。
また、人にモノを教える文章など書く機会がないので、~
「ここわかりづれーよ」と思ったらスレがどこかで吐き捨てて...
確実に改善できる保証はないが、可能な限り善処する。
ページ名: