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

なんちゃって魔王、添削

jrubyを試してみようとおもったんだけど、適当なプログラムを思いつかないから、どんジレの人(id:hrkt0115311)が始めたばかりのrubyのソースを晒しているので、「これからもおもしろいエントリを期待してます」というおもいを込めて添削してみた。

動作時の出力があるのでそれを参考にして、以下のような動作をするプログラムであることがわかりました。

  1. ユーザーと魔王が交互にアクションを行う(ターン制)
  2. ユーザーはコマンドを入力し、そのアクションに対する効果が表示される
  3. 魔王のアクションが表示される。

これを前提知識としてソースを読みます

=begin
a = []
a << "テスト\nはたして改行を\n含む\nテキストは格納できる?"
a << "テスト2\n改行\nどう処理\nされるかな?"
puts a.size
puts a

配列の中に、改行こみの文章を格納できる。


#また、配列の中の配列に関しては、下記方法で取り出せる。
#.timesを使うときはブロック内にrand()も入れないと、5回連続で同じ乱数になってしまう。
=end

最初のコメントの部分でこのプログラムが何をするのかわかりません。まあ、突発的につくったとのことですが、僕が前提のところに書いたようなことが書いてあってほしいです。

com = [["会心の一撃!!!!\n魔王に致命的なダメージを与えた。\nのだけど一瞬で回復してる。\nずるい!!","そろそろ本気だしとくかと、\n徹底的にボコボコにしたものの\n魔王はうっとりしている……。","横浜テイストの攻撃呪文を唱えた!!\nミス!!\n魔王に命中しなかった。","関西テイストの召喚魔法を唱えた!!\n魔王は、関西のおじちゃんおばちゃんと\n楽しそうに話し込んでいる。\n無視か……。","通称「レンガ本」を思いっきりぶつけた。\n魔王ナイスキャッチ\n「あー、この本、××の設定とかいいよねぇ」と\n魔王が感想を語り出した。","魔王に鏡を見せた\n「いや、ペルセウスの真似されてもなぁ」と言いつつ、\n魔王は鏡をのぞきこんでいる。\nしわが気になるらしい。",],
        ["回復できる状況ではない","回復呪文を唱えたが、食後でなければ効果がない!","薬草を使ったが、とにかく苦い。\n効果が出るのは、数週間後のようだ!","痛いと思うから痛いのだと自分に言い聞かせてみた。\nズキズキする。\nなかなか無理みたいだ!!","こんなこともあろうかと用意した\n救急箱をあけてみると\nなぜかパンとおにぎりが入っていた\nWhy?","ていうかもはや、怪我ってレベルじゃねーよ\nと、魔王に笑われてしまった。",],
        ["仲間の返事どころか、こだまも返ってこない","携帯で仲間に連絡した!\n「魔王? なにその冗談笑えないんだけど。\nごめんまた今度な」\n仲間に相手にされなかった!","仲間が大量に現れた\n「うわ、マジ魔王」「超魔王じゃね?」などと\nもりあがり、記念撮影して帰っていった……。","「この番号は現在使われておりません」\n……仲間だと思ったら仲間じゃなかった!!",],
        ["魔王は絶望している","魔王は超絶技巧を駆使して、ピアノを引いている","魔王は孫のお守りがいかに楽しいか熱弁をふるった","魔王は脈絡もなく秘密の趣味について語り出した","魔王は様子を見ている","魔王はあからさまにどうでもよさそうにしている","魔王は恐ろしい呪文を唱えた! だがMPが足りない","魔王はダンビラを振りかざしたものの、よろめいた",],]

2重配列になっていて、ユーザーアクションに対するするフィードバックらしいということがわかる。comの名前はcommandの略かな?という感じで読み進めます。

puts "薮から棒に、魔王が現れた!!\n"

最初に表示されるところですね。
特に問題ないですが、ヒアドキュメントの方が綺麗だとおもいます。これでどうでしょう。

puts <<START_SCENE
薮から棒に、魔王が現れた!!
コマンド?
START_SCENE

次がメインの部分です。

5.times{
  r1 = rand(6)   #1.たこなぐり
  r2 = rand(6)   #2.かいふく
  r5 = rand(4)   #5.なかまをよぶ
  r6 = rand(8)   #魔王の行動用乱数
  puts "1.たこなぐり\n2.かいふく\n3.ぼうぎょ\n4.にげる\n5.なかまをよぶ"
  act = gets.chomp

  if act == "1"
    puts com[0][r1] #1.たこなぐり
  elsif act == "2"
    puts com[1][r2] #2.かいふく
  elsif act == "3"
    puts "貧弱な盾と、くたびれた鎧で身を守っている"
  elsif act == "4"
    puts "しかし、お約束通り回り込まれてしまった"
  elsif act == "5"
    puts com[2][r5] #5.なかまをよぶ
  else
    puts "しかし、そうはいかない!!"
  end

  sleep 2
  puts com[3][r6]#魔王の行動
  puts "コマンド?"
  puts "\n"
}

まず5.timesはどうやらターンで、そしてゲームの終了条件のようです。ソースからターンの存在が分かりにくいので、コメントを書くか、変数もしくは定数を用意する方がよいでしょう。今回はmax_turnという変数を用意します。途中で抜ける条件がないので、規定のターンという意味でregulation_turnもよいと思いますが長いとおもいました。

max_turn = 5
max_turn.times {
# メインの処理を書く
}

randが並ぶ部分は飛ばして、まず入力できるコマンドをユーザーに表示する部分です。

  puts "1.たこなぐり\n2.かいふく\n3.ぼうぎょ\n4.にげる\n5.なかまをよぶ"

先ほど行った修正のようにヒアドキュメントでもいいかと思いますが、配列を使ってどんなコマンドがあるのか視覚的にわかるほうにしましょう。

  actions = ["たこなぐり","かいふく","ぼうぎょ","にげる","なかまをよぶ"]
  actions.each_with_index {|action,i|
    puts "#{i+1}.#{action}"
  }

次は入力からコマンドを実行し、フィードバックを表示するところです。

  r1 = rand(6)   #1.たこなぐり
  r2 = rand(6)   #2.かいふく
  r5 = rand(4)   #5.なかまをよぶ
  r6 = rand(8)   #魔王の行動用乱数
  # 中略...
  act = gets.chomp
  if act == "1"
    puts com[0][r1] #1.たこなぐり
  elsif act == "2"
    puts com[1][r2] #2.かいふく
  elsif act == "3"
    puts "貧弱な盾と、くたびれた鎧で身を守っている"
  elsif act == "4"
    puts "しかし、お約束通り回り込まれてしまった"
  elsif act == "5"
    puts com[2][r5] #5.なかまをよぶ
  else
    puts "しかし、そうはいかない!!"
  end

getsでユーザーの入力を受けて、if文でフィードバックを表示しています。最初に飛ばしたrandがフィードバックを選ぶのに使われていますが、細かいことをいうとユーザーが行動を決定するときには決められた未来が待っていますね。ifに入ってから決める方が気分はいいです。

さらにすすめて魔王のアクションのところを見ます。

  puts com[3][r6]#魔王の行動

comというのはユーザーのコマンドだけかと思ったんですが、魔王の行動も入っていますね。
ここは配列に詰め込みすぎなので分けましょう。
あと気になるのは、if-elseの連続のうち"3","4"の部分だけ行動が決まっていますが、この部分も配列なら処理的には同じになるので、一つにまとめることができます。

気になった部分を修正するためにデータ構造をちょっと変えます。それが、以下です。

maou_commands = [
  "魔王は絶望している",
  "魔王は超絶技巧を駆使して、ピアノを引いている",
  "魔王は孫のお守りがいかに楽しいか熱弁をふるった",
  "魔王は脈絡もなく秘密の趣味について語り出した",
  "魔王は様子を見ている",
  "魔王はあからさまにどうでもよさそうにしている",
  "魔王は恐ろしい呪文を唱えた! だがMPが足りない",
  "魔王はダンビラを振りかざしたものの、よろめいた"
]

commands = {
  "たこなぐり" => [
    "会心の一撃!!!!\n魔王に致命的なダメージを与えた。\nのだけど一瞬で回復してる。\nずるい!!",
    "そろそろ本気だしとくかと、\n徹底的にボコボコにしたものの\n魔王はうっとりしている……。",
    "横浜テイストの攻撃呪文を唱えた!!\nミス!!\n魔王に命中しなかった。",
    "関西テイストの召喚魔法を唱えた!!\n魔王は、関西のおじちゃんおばちゃんと\n楽しそうに話し込んでいる。\n無視か……。",
    "通称「レンガ本」を思いっきりぶつけた。\n魔王ナイスキャッチ\n「あー、この本、××の設定とかいいよねぇ」と\n魔王が感想を語り出した。",
    "魔王に鏡を見せた\n「いや、ペルセウスの真似されてもなぁ」と言いつつ、\n魔王は鏡をのぞきこんでいる。\nしわが気になるらしい。"
  ],
  "かいふく" => [
    "回復できる状況ではない",
    "回復呪文を唱えたが、食後でなければ効果がない!",
    "薬草を使ったが、とにかく苦い。\n効果が出るのは、数週間後のようだ!",
    "痛いと思うから痛いのだと自分に言い聞かせてみた。\nズキズキする。\nなかなか無理みたいだ!!",
    "こんなこともあろうかと用意した\n救急箱をあけてみると\nなぜかパンとおにぎりが入っていた\nWhy?",
    "ていうかもはや、怪我ってレベルじゃねーよ\nと、魔王に笑われてしまった。"
  ],
  "ぼうぎょ" => ["貧弱な盾と、くたびれた鎧で身を守っている"],
  "にげる" => ["しかし、お約束通り回り込まれてしまった"],
  "なかまをよぶ" => [
    "仲間の返事どころか、こだまも返ってこない",
    "携帯で仲間に連絡した!\n「魔王? なにその冗談笑えないんだけど。\nごめんまた今度な」\n仲間に相手にされなかった!",
    "仲間が大量に現れた\n「うわ、マジ魔王」「超魔王じゃね?」などと\nもりあがり、記念撮影して帰っていった……。",
    "「この番号は現在使われておりません」\n……仲間だと思ったら仲間じゃなかった!!"
  ]
}
commands.default = ["しかし、そうはいかない!!"]

Hashをつかっていますが、見た目に各コマンドについてどんな結果がまっているのか、わかりやすくなったとおもいます。defaultはHashに無いキーのときの指定です。

入力からフィードバックを選ぶ部分もこれにあわせて変更しました。

  action = "hoge"
  begin
    user_input = gets.chomp.to_i - 1
    action = actions[user_input]
  rescue
  end

  fb_candiate = commands[action]
  puts fb_candiate[rand(fb_candiate.size)]

  # change maou's turn

  puts maou_commands[rand(maou_commands.size)]

gets.chomp.to_i - 1で数字にしています。-1しているのは、コマンドの表示に#{i+1}しているからです。
begin-rescueは数字以外が入力された部分ですね。数字に変換できない時はactionは"hoge"になりますので、さきほどのdefaultの部分になります。
ちゃんと数字が入力された場合はactionにはユーザーにコマンド入力を促すときにつかった配列の文字が入ります。
fb_candiateはユーザーへのフィードバックの候補(配列)です。配列のサイズをもとにrandでユーザーへのフィードバックを決定します。

ターンを抜けたあとも同様の変更をしました。最終的なソースが以下です。

=begin
1. ユーザーと魔王が交互にアクションを行う(ターン制)
2. ユーザーはコマンドを入力し、そのアクションに対する効果が表示される
3. 魔王のアクションが表示される。
4. 5ターン行ったら、最終ターンへ移行する。


a = []
a << "テスト\nはたして改行を\n含む\nテキストは格納できる?"
a << "テスト2\n改行\nどう処理\nされるかな?"
puts a.size
puts a

配列の中に、改行こみの文章を格納できる。


#また、配列の中の配列に関しては、下記方法で取り出せる。
#.timesを使うときはブロック内にrand()も入れないと、5回連続で同じ乱数になってしまう。
=end

max_turn = 5

maou_commands = [
  "魔王は絶望している",
  "魔王は超絶技巧を駆使して、ピアノを引いている",
  "魔王は孫のお守りがいかに楽しいか熱弁をふるった",
  "魔王は脈絡もなく秘密の趣味について語り出した",
  "魔王は様子を見ている",
  "魔王はあからさまにどうでもよさそうにしている",
  "魔王は恐ろしい呪文を唱えた! だがMPが足りない",
  "魔王はダンビラを振りかざしたものの、よろめいた"
]

commands = {
  "たこなぐり" => [
    "会心の一撃!!!!\n魔王に致命的なダメージを与えた。\nのだけど一瞬で回復してる。\nずるい!!",
    "そろそろ本気だしとくかと、\n徹底的にボコボコにしたものの\n魔王はうっとりしている……。",
    "横浜テイストの攻撃呪文を唱えた!!\nミス!!\n魔王に命中しなかった。",
    "関西テイストの召喚魔法を唱えた!!\n魔王は、関西のおじちゃんおばちゃんと\n楽しそうに話し込んでいる。\n無視か……。",
    "通称「レンガ本」を思いっきりぶつけた。\n魔王ナイスキャッチ\n「あー、この本、××の設定とかいいよねぇ」と\n魔王が感想を語り出した。",
    "魔王に鏡を見せた\n「いや、ペルセウスの真似されてもなぁ」と言いつつ、\n魔王は鏡をのぞきこんでいる。\nしわが気になるらしい。"
  ],
  "かいふく" => [
    "回復できる状況ではない",
    "回復呪文を唱えたが、食後でなければ効果がない!",
    "薬草を使ったが、とにかく苦い。\n効果が出るのは、数週間後のようだ!",
    "痛いと思うから痛いのだと自分に言い聞かせてみた。\nズキズキする。\nなかなか無理みたいだ!!",
    "こんなこともあろうかと用意した\n救急箱をあけてみると\nなぜかパンとおにぎりが入っていた\nWhy?",
    "ていうかもはや、怪我ってレベルじゃねーよ\nと、魔王に笑われてしまった。"
  ],
  "ぼうぎょ" => ["貧弱な盾と、くたびれた鎧で身を守っている"],
  "にげる" => ["しかし、お約束通り回り込まれてしまった"],
  "なかまをよぶ" => [
    "仲間の返事どころか、こだまも返ってこない",
    "携帯で仲間に連絡した!\n「魔王? なにその冗談笑えないんだけど。\nごめんまた今度な」\n仲間に相手にされなかった!",
    "仲間が大量に現れた\n「うわ、マジ魔王」「超魔王じゃね?」などと\nもりあがり、記念撮影して帰っていった……。",
    "「この番号は現在使われておりません」\n……仲間だと思ったら仲間じゃなかった!!"
  ]
}
commands.default = ["しかし、そうはいかない!!"]
# sorted keys of `command`
actions = [
  "たこなぐり",
  "かいふく",
  "ぼうぎょ",
  "にげる",
  "なかまをよぶ"
]

puts <<START
薮から棒に、魔王が現れた!!
START

max_turn.times {|turn|
  # user turn
  actions.each_with_index {|action,i|
    puts "#{i+1}.#{action}"
  }
  puts "コマンド?"
  action = "hoge"
  begin
    user_input = gets.chomp.to_i - 1
    action = actions[user_input] if( -1 < user_input )
  rescue
  end

  fb_candiate = commands[action]
  puts fb_candiate[rand(fb_candiate.size)]

  sleep 2
  # change maou's turn

  puts maou_commands[rand(maou_commands.size)]
}


# last turn
last_actions = [
  "たこなぐり",
  "かいふく",
  "ぼうぎょ",
  "にげる",
  "なかまをよぶ",
  "きょうはこのくらいにしといたる"
]
last_commands = {
  "たこなぐり" => "しかし、魔王には一切の攻撃がきかなかった。\n「おまえつまらんし」と、魔王は言い残し、\n闇の中へ消えた。",
  "かいふく" => "でも、血が止まらない。\n「ちょっと待って、しゃれにならん」とあなたは言い\n携帯で救急車を呼んだ。\n渋滞していなければいいのだが。",
  "ぼうぎょ" => "防ぎきれない攻撃を受けた。\n魔王がスッと目を細めたところで、\nあなたは意識を失った。",
  "にげる" => "「逃すわけないし」と\n魔王が帰らせてくれない。\nしかたがないのであなたは\n携帯で各位に連絡をいれた。",
  "なかまをよぶ" => "やはり仲間はこなかった。\n肩を落とすあなたに、\n魔王は世界の半分をくれた。\n今日からあなたも魔王だ。",
  "きょうはこのくらいにしといたる" => "あなたは魔王をその場に残し、\n日常へと帰っていった。",
}
last_commands.default = "拳を交えて友情が芽生えたらしく\n魔王は仲間にして欲しそうに\nこちらを見ている。\nあなたは魔王を連れて帰路についた。"

last_actions.each_with_index {|action,i|
  puts "#{i+1}.#{action}"
}
last_action = "hoge"

begin
  user_input = gets.chomp.to_i - 1
  last_action = last_actions[user_input] if( -1 < user_input )
rescue
end


puts last_commands[last_action]

追記:
無用なputsがはいっていたのと、sleepが抜けていたので修正しました。
あとaction[-1]とかは例外をだすと思ってました。0以下が入力されたときの挙動がおかしかったので、if文追加しました。