🍍

Ruby で創る RPG 第九章 勇者が回復できるようにする

2024/01/18に公開


gets メソッドで、キー入力を受け取る

「とてもよくできたわね。 勇者が攻撃して、スライムが反撃してきて、どちらか死んだらメッセージを表示して終わるのね。」
「うん、そうなんだ。そしてね、勇者が死にそうになったときに、このまま死んじゃうんじゃなくて、回復できるようにしたいんだけれど、どうしたらいいのかな?」
「そうね。勇者が弱ってきている時に、そのまま攻め続けてスライムを倒せればいいけれど、失敗したら死んでしまうものね。攻撃するか、回復するか、次の行動を選択できるようにするといいわよね。」

「うん、そうだね。どうやったらできるの?」
「そうね〜、みっくんはどうやったらできると思う?」
「1番なら攻撃、2番なら回復、3番なら逃げるって、コマンドを表示するのはどうかな〜」
「それはとってもいい考えね。じゃあ早速書いてみましょうか。」
「うん。 画面に攻撃、回復、逃走って出すのは、puts "1: 攻撃" って書けばいいんだよね。」
「ええ、その通りよ。」
「1番を押されたらっていうのはどうやったらいいの?」
「キーボードからの入力を受け取るためのメソッドもいろいろあるけれど、基本となるのは、getsメソッドを使うといいわ。getは得るっていう意味の英単語で、sはString文字列の略ね。」
「あ〜、これ、いままでも登場してきているよ。」
「そうね。エンターキーを押したら次の画面が表示されるようにしたのよね。」
「うん、そうなんだ。」
「ケンタッキーの時には、特に入力された文字を処理する必要がなかったから、とだけ書いたんだけれど、次のように書けば、入力された文字を受け取ることもできるわ。」

gets.rb
puts "1: 攻撃"
puts "2: 回復"
puts "3: 逃走"
command = gets.chomp
puts command

gets関数で受け取った値をmenuという変数に代入して、puts関数で表示させているんだね。」
「ええ、そうよ。」
「で、getsの後ろについている chompって何?」
「あ〜、これね。chomp は、音を立てて噛む、噛み切るなんて意味の英単語なんだけれど、改行文字を削除するメソッドなの。」
「かいぎょうもじ?」
「改行文字って、キーボードから文字を入力じゃない。例えば、この場合だったら 1というキーとエンターキーを押すわよね。そうすると目には見えないけれども、このエンターキーを押したことで改行文字って言うのだけれど、この文字も一緒にキーボードから読み取られるの。」
「ふーん、そうなんだ〜」
「えー、そうするとね、そのままメニューを画面に表示してしまうと、1のほかに改行文字も表示されてしまうわ。」
「あ〜。それで、chompメソッドを使うと、その改行文字を取り除いてくれるんだね。」
「ええ、そうよ。みっくん、飲み込みが早くて助かるわ。」

ここ数日、大好きな遙香お姉さまに教えて、僕もプログラミングセンスが身についてきたみたい。

「じゃあ、ちょっと実験してみようか。gets.rbってファイルを作って。さっきのコードを書くの。そしたら、ruby gets.rbって、ターミナル画面からコマンド入力すると実行できるわ。」

さっそく、僕は実行して見た。
1番攻撃を指示したいので、キーボードから1って入力して、エンターキーを押して実行。
無事に、動いているみたいだ。

to_i メソッドで、文字列型から整数型に変換する

バグ???

「遙香さん、動いたよ〜」
「良かったわね♡」
「じゃあ、続きも書いて見るね。」

動いていることが確認できたので、僕はコマンドに応じた処理を書いていく。
case文を使うとすっきり書けそうだ。

gets.rb
# コマンドを表示する
puts "1: 攻撃"
puts "2: 回復"
puts "3: 逃走"

# キーボードからの入力を受け取って、改行文字を噛みちぎる。
command = gets.chomp
puts command

# コマンドに応じた行動
case command
when 1
  puts "勇者の攻撃"
when 2
  puts "勇者は回復呪文を唱えた"
when 3
  puts "勇者は逃げ出した"
end

出来上がったので、実行させて見る。が・・・、結果は先ほどと変わらない。

「遙香さん、1番だったら攻撃、2番だったら回復って書いて見たんだけれど、動かないんだ〜。エラーは出ていないようだし、どうしてなんだろう? 何か、間違っているのかな〜?」

「ええ、エラーが出ていないし、Rubyの文法としては間違ってはいないわ。そしてみっくんが思ったようには、意図したようには動いていないわよね。いわゆるバグって呼ばれるやつだわね。」
「バグって何?」
「ちょっとしたプログラムの不具合のことを言うのだけれど、英語ではbug、文字通り虫の事よ。昔々のコンピュータは、様々な回路を配線でつないでいたことは話したわよね。」
「うん、そこから今のMacBook Airまでたくさんの人が進歩させてきたんだよね。」
「ええ、そうね。そしてね、うまく動かなかったので繋いだ電気回路を調べてみたんだって。そしたらね、一匹の蛾が回路に挟まっていたそうなの。」
「へ〜。そうなんだ。」
「ええ、いまでも標本になって残っているそう。そこからうまく動かない不具合のことをバグって名付けられたそうよ。バグがいるって、不具合の原因となる虫が回路中のどこかにいるっていうことなのね。」
「へー、そうなんだー。じゃあこの場合もどこかにバグがいるんだね。」
「そうね、今はもう物理的に線をつないだりはしないから、Macの中に虫がいるってわけではないけれど。」
そう言ってお姉さまは笑う。そしてお姉様の冗談に、僕も一緒になって笑う。笑っている顔も綺麗だし、口元からのぞく白い歯も素敵だ。

「そして、良くある、ほんとに良くあることなのだけれど、文字列としての"1"と数値としての1との違いを理解するのは大切だわ。」
「どういうこと?」
「う〜ん、そうねぇ。例えば漢数字のと、算用数字の1の違いはわかる?」
「うん、漢数字の一は漢字で、算用数字は計算に用いる数字って言うくらいだし、普通に計算に使う数字のことだよね。」
「ええ、そうよ。例えば一生懸命とかとは書くけれど、1生懸命って書いたら変だよね。」
「うん、そうだね。」
「文章中に書く数字のときには漢数字の一を使うわ。そして計算で使うときには算用数字の1を使う。これが普通だわよね。」
「うん、そうだね。」
「コンピュータもそれと一緒で、文字列としての"1"と計算に使う数値としての1というのは区別されているの。」
「そうなんだ〜。」
「ええ、Rubyをはじめとして多くのプログラミング言語では、文字列を""(ダブルコーテーション)で、囲むわ。だからあたしも文字列としての"1"と計算に使う数値としての1と書くわね。」

アスキーコード

「これはアスキーコード表といって、文字と文字に割り当てられた番号との対応表なの。Dec は十進数、Hex は十六進数、Char は文字のことね。」

Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char
0 00 NUL 32 20 64 40 @ 96 60 `
1 01 SOH 33 21 ! 65 41 A 97 61 a
2 02 STX 34 22 " 66 42 B 98 62 b
3 03 ETX 35 23 # 67 43 C 99 63 c
4 04 EOT 36 24 $ 68 44 D 100 64 d
5 05 ENQ 37 25 % 69 45 E 101 65 e
6 06 ACK 38 26 & 70 46 F 102 66 f
7 07 BEL 39 27 ' 71 47 G 103 67 g
8 08 BS 40 28 ( 72 48 H 104 68 h
9 09 HT 41 29 ) 73 49 I 105 69 i
10 0A LF 42 2A * 74 4A J 106 6A j
11 0B VT 43 2B + 75 4B K 107 6B k
12 0C FF 44 2C , 76 4C L 108 6C l
13 0D CR 45 2D - 77 4D M 109 6D m
14 0E SO 46 2E . 78 4E N 110 6E n
15 0F SI 47 2F / 79 4F O 111 6F o
16 10 DLE 48 30 0 80 50 P 112 70 p
17 11 DC1 49 31 1 81 51 Q 113 71 q
18 12 DC2 50 32 2 82 52 R 114 72 r
19 13 DC3 51 33 3 83 53 S 115 73 s
20 14 DC4 52 34 4 84 54 T 116 74 t
21 15 NAK 53 35 5 85 55 U 117 75 u
22 16 SYN 54 36 6 86 56 V 118 76 v
23 17 ETB 55 37 % 87 57 W 119 77 w
24 18 CAN 56 38 & 88 58 X 120 78 x
25 19 EM 57 39 ' 89 59 Y 121 79 y
26 1A SUB 58 3A : 90 5A Z 122 7A z
27 1B ESC 59 3B ; 91 5B [ 123 7B {
28 1C FS 60 3C < 92 5C \ 124 7C
29 1D GS 61 3D = 93 5D ] 125 7D }
30 1E RS 62 3E > 94 5E ^ 126 7E ~
31 1F US 63 3F ? 95 5F _ 127 7F DEL

「一つ一つの文字に数値が割り振られているんだね。」
「ええ。例えばアルファベットのAだったら41、10進数にすると65と言うふうに番号が割り振られているの。」
「そうなんだね〜。」
「だから、人から見るとアルファベットのAと言う文字なんだけれど、コンピュータの内部では65番だって扱っているの。」
「そうなんだね。」
「数値の1っていくつだと思う。」
「16進数で31、10進数だと49だよ。」
「ええ、そうね。なので、キーボードから入力された"1"と言う文字なんだけれど、コンピュータの中では49って言う数字としての扱いになっているの。だからこれを文字としての"1"ではなくて数値としての1だよって、変換してあげる必要があるわ。」
「うん、そうだね。何かきっと変換するために便利なコマンドが用意されているんだよね。」
「ええ、みっくん賢いわね。Rubyでは to_iと言うメソッドを使うと、文字列をに変換することができるわ。iinteger 整数の i ね。」

「そうか〜。じゃあ、さっき書いたのはcommandに入っているのは文字としての"1"だったから、これを整数にしてあげればいいから、こんな風に書けばいいんだね。」

gets.rb
# コマンドを表示する
puts "1: 攻撃"
puts "2: 回復"
puts "3: 逃走"

# キーボードからの入力を受け取って、改行文字を噛みちぎる。
command = gets.chomp
# to_iメソッドで整数型に変換する
command = command.to_i
puts command

# コマンドに応じた行動
case command
when 1
  puts "勇者の攻撃"
when 2
  puts "勇者は回復呪文を唱えた"
when 3
  puts "勇者は逃げ出した"
end

.to_iメソッドを使って、バグが取れたはずなので、動かして見る。

「お〜、ちゃんと攻撃してくれるよ。」
「ええ、バグも取れて良かったわね。」
と、お姉さまは微笑んでくださる。

メソッドチェーン

「メソッドチェーンという書き方もあるから、ついでに紹介しておくね。」
「それってどういう書き方なの?」
「さっきはこう書いたじゃない。」

# キーボードからの入力を受け取って、改行文字を噛みちぎる。
command = gets.chomp
# to_iメソッドで整数型に変換する
command = command.to_i

「うん、そうだよ。getsでキーボードから読み込んで、それをchompしたのをcommandに代入するんだ。そして、commandは文字列型だから、to_iメソッドで整数型に変換するんだよね。」
「ええ、そのとおり。そしてね。getsでキーボードから読み込んで、chompしてto_iで整数型にするのだから、続けて次のように書けるの。」

# キーボードからの入力を受け取って、改行文字を噛みちぎったのを整数型に変換する
command = gets.chomp.to_i

「簡潔にかけたと思うけれど、どうかしら。」
「ほんとだ。とってもスッキリしてるよ。」
「ええ、Rubyでは.でメソッドを呼び出せるのだけれど、メソッドを呼び出した後の結果を受けて、.を続けることでさらにメソッドを呼び出せるの。メソッドチェーンと呼ばれる由縁よ。」

to_s メソッドで、整数型から文字列型に変換する

「ついでだから、紹介しておくと、to_sメソッドを使うと、整数型から文字列型にも変換できるわ。」
「そうなの。どうやってやるの?」
1.to_s これだけよ。これで整数型の1が、文字列型の"1"に変換できるわ。」
「そうなんだね〜。で、どんなことに役立つの? キーボードから読み込んだ時に数字にしなくちゃいけないのはわかったけれど、せっかく数字になってるんだから、わざわざ文字にしなくてもいいと思うんだけれど。」
「そうね〜。式展開って今までにも良く登場してきているわよね。」
「うん、#{hp}って書いたりしているよ。」
「ええ、この式展開の書き方だけれど、これもRubyの内部的には、to_sメソッドが自動で呼ばれているの。」
「あっ、そうなんだ。」
「みっくんがいちいちto_sって書かなくても良いように面倒見てくれていたのね。」
「うん、簡単だったし楽だったし良かったよ。」
「ええ、あと他の使い方としては、じゃあ、12345678て言う数があったときに、何桁の数かわかる?」
「もちろん8桁だよ。」
「ええ、そうね。 数が並んでいるからわかりやすいものね。並んでいなかったりしたら、ちょっと数えるのが大変よね。そんな時に文字列にしてみるの。Rubyの文字列にはいろいろな便利なメソッドが用意されているわ。例えば、長さを求めるメソッドもあるの。長さは英語でlengthって言うわ。だからね、"12345678".lengthって書くと、8って文字数が返ってくるの。」
「うん、そうするとどうなるの?」
「なので、さっき紹介したメソッドチェーンを使って書くと 12345678.to_s.lengthって書けるわ。何桁の数かすぐわかって便利よね。」
「ほんとだね。」

数値を三桁区切りにする

「で、もう少し加工すると、三桁ごとに,(カンマ)区切りを入れたりもできるわ。」
「え〜。どうやるの。」

12345678.to_s.reverse.scan(/[0-9]{1,3}/).join(",").reverse

「この一行ね。結果は "12,345,678"って、三桁区切りになるの。」
「お〜凄い。いったいどうなっているの」
「ええ、説明していくわね。メソッドチェーンを使って繋いでいるのだけれど、まずto_sメソッドで文字列に変換するのはいいわよね。」
「うん、大丈夫だよ。その次のreverseって言うのは、逆ってこと?」
「ええ、その通り。トランプでもreverseって言ったら順番が逆になったりするわよね。文字列にreverseメソッドを適用すると並び順が逆になって"87654321"になるの。」

正規表現の紹介

「そうなんだね。じゃあ、その次のscanっていうのは?」
「scanは、走査するっていう意味なんだけれど、先頭から順番に走り読みしながら検査する、そういう意味ね。どういう風に検査するのかを指定するためにscanメソッドへ引数が渡されているわ。」
「うん、/.{1,3}/って書いてある。これってどういう意味なの?」
「文字列を書く時には"(ダブルコーテーション)で囲むんだったわよね。」
「うん。そうだよ。」
「それと同じで、正規表現を書く時には、/(スラッシュ)で囲むの。」
「そうなんだね。で、正規表現って何?」
「英語ではregular expressionと言うのだけれど、文字列があるパターンに一致するかどうかを表現するためのミニ言語のようなものね。」
「あるパターンに一致するかどうかって、どういうこと?」
「例えば、"Slime" っていう文字列があるわよね。これが、あるパターン、そうね〜 例えば小文字が含まれているかっていうパターンに一致するかどうか調べたいとするわ。」
「うん、そうするとどう書くの?」
「一致するっていうのは、英語ではmatchするっていうのだけれど、するとね、次のように書けるわ。」

if "Slime".match?(/[a-z]/)
  puts "小文字が含まれています"
else
  puts "小文字は含まれていません"
end

[a-z]は、小文字のaからzまでの一文字でもいいから一致している?match している?って聞いている感じね。」

「うん、すると、どうなるの?」
「この場合には、小文字のlだったりiだったりが含まれているから、match?メソッドは trueを返すの。だから if節が実行されて、"小文字が含まれています" って表示されるわ。」

「うん、うん」
「じゃあ、全部小文字かどうか調べたい時は?」

if "Slime".match?(/^[a-z]+$/)
  puts "全部小文字です。"
else
  puts "小文字以外の文字も含まれています"
end

「って感じね。」
「さっきと良く似ているけれど・・・」
「ええ、そうね。そしてね。良く見比べて欲しいのだけれど、さっきは[a-z]だったけれど、今度は^[a-z]+$って書かれているじゃない。」
「うん、ちょっと記号が増えている。」
「ええ、そこが味噌ね。[a-z]はさっきと一緒で小文字のアルファベットのどれかに一致すればいいの。その後に+って書かれているじゃない。」
「うん、書いてあるよ。」
「ええ、正規表現での世界では、+ は、一回以上の繰り返しを意味しているの。だから英小文字が一回以上繰り返すという意味になるわ。」
「うん、そうだね。じゃあ残っている^$は、どういう意味なの?」
^はハットと読むのだけれど、正規表現の世界では行頭を指す記号ね。$は行末を意味するの。」
「そうなんだ〜。」
「これで、登場人物が全員出揃ったわ。まとめると英小文字が行頭から行末まで一回以上繰り返していると言う文字列のパターンを意味していることになるの。」
「そうなんだ〜。だから小文字以外の文字も含まれていますって表示されるんだね。」
「ええ、そうね。素朴に書くのなら、文字列の先頭から一文字ずつ取り出して、小文字かどうかって見ていくしかないけれど、正規表現を使ったらすぐにできるわ。」
「うん、正規表現って凄いんだね。」
「ええ、そうね。正規表現のことをパターンに一致するかどうか表現するミニ言語って言ったけれど、ミニ言語ってところに納まらなくて、語り出すと分厚い一冊の本ができるくらいの量になるわ。」
「凄いんだね〜。」
「ええ、全部いきなり収穫するのはとても大変だから、もうちょっと便利な方法は無いかなって。思った時に、少しずつ学んでいくようにすると良いわ。」
「うん、わかった。」

「正規表現を練習するなら、Rubular ってサイトが役立つわ。rubular は ruby と 正規表現 regular expression との合成ね。」

「じゃあ、続きいくわね。」
「うん、/[0-9]{1,3}/っていう部分だね。」
「ええ、そうよ。」
「[0-9]は、数字っていう意味だよね。その後に続いている {1,3}っていう書き方は?」
「回数を示しているの。さっきの小文字の例だったら+って書かれていて、一回以上っていう意味だったわよね。」
「うん、そうだったよ。」
{1,3}は、一回以上三回以下っていう意味なの。」
「そうなんだ。つまり一桁か二桁か三桁の数字っていうことなんだね。」
「ええ、そう。」
「うん、わかったよ。じゃあ、さっきはmatch?メソッドで、一致するかどうかしていたけれど、今度は scanっていうメソッドに変わっているんだよね。どう違うの?」
「結果の返し方が違うの。match?メソッドはパターンに一致しているかどうかを返してくれたんだけれども、scanメソッドは、パターンに一致している部分を取り出して、それを配列にして返してくれるの。」
「そうなんだ。配列ってな〜に?」
「そうね〜。複数のデータを順番に並べたデータ構造・入れ物って感じかしら。今やっている"87654321"の例だと、 ["876", "543", "21"]って感じになるわ。数字が三つ並んでいる部分があったら取り出すから、"876"がまず取り出されるわ。次に"543"が取り出されて、残った```"21"が取り出される。つまり、パターンに一致するところが三回あったから、最初のデータ、次のデータ、最後のデータというように三回取り出されるのね。」
「うん、それで。」
「で取り出されたデータをバラバラにしてしまうと、扱いがいろいろ不便だわ。なので、ひとまとめの入れ物の中に仕舞っておきたいわよね。」
「うん、そうだね。」
「その仕舞っておくための入れ物、箱のことをデータ構造とも言うんだけれど、配列っていうの。」
「データ構造と配列は違うものなの?」
「そうね、たくさんのデータがあったときに、どのように仕舞うのか、その仕舞い方でデータ構造は区別されるわ。先頭から順番に詰めていくのが配列ね。」
「うん、そうなんだね。」
「ええ、そして配列のそれぞれのデータを区別するには、配列のそれぞれのデータのことを要素って呼んだりもするけれど、それぞれ一つ一つの要素は、添え字っていう数字で区別するわ。そうね、例えば学校で言うと出席番号5番の人みたいな、そんな感じね。クラスっていう大きな入れ物の中に一人ひとりの生徒がいる。そして一ノ瀬さん、二階堂くん、三鷹さん、四谷さん、五代くんなんて、名前で呼ぶのではなくて、出席番号5番の人って呼ぶような感じね。」
「うん、何となくだけれど、配列わかったよ。」
「良かったわ。」
「配列はとっても大切なデータ構造だし、プログラミングの基本だから、また後から説明するね。」
「うん、お姉さま。ありがとうございます。」
「あらあら、お姉さまだなんて。」

思わず心の中の声が出てしまった僕。
お姉さまは笑って続きを話してくださる。

scanメソッドで配列に取り出したのを、ひとまとめにするのがjoinメソッドの役割。一緒になるっていう意味の英単語だけれど、その名の通りで、配列の要素それぞれを一緒にした文字列にしてくれるの。」
「うん」
"87654321".scan(/[0-9]{1,3}/)を実行すると["876", "543", "21"]っていう配列になるわ。そしてjoinメソッドで繋ぐのだけれど、",(カンマ)"って引数を書いてあるじゃない。」
「うん、そうだね。」
「何も引数を書かなかなったらそのまま繋いじゃうのだけれど、ここではカンマを入れてあるじゃない。」
「うん、入れてある。」
「だから、繋ぐ時にカンマを入れながら繋いでくれるの。そうするとね。"876,543,21"ってなるのよ。」
「なるほど〜。凄いね。じゃあ、それで最後はもう一度、reverseメソッドで元の順番に戻してあげたら完成って言うわけだね。」

「その通りよ。みっくんとても賢いわ。」

そう言って、お姉さまは僕の頭を撫でる。ちょっと愛玩動物、ペットのような感じがしないでもないけれど、僕はとっても嬉しい。

定数の活用

「ちょっとおまけにって紹介するつもりだったはずのじゃあ、ここで、定数を紹介するわ。」
「ていすうって?」
「定数、定まった数なことね。今みっくんは1番が攻撃で2番が回復、3番は逃げるって覚えているわよね。」
「もちろん、そうだよ。書いたばっかりだもの。覚えているよ。」
「ええ、だからコード中にも、when 1 puts "勇者の攻撃" って書いてあるものね。」
「うん。何かあるの?」
「さて、半年経ったらどうかしら? 1番は攻撃の事ってしっかり思い出せるかしら?」
「え〜、覚えてはちょっといないかもしれない。」
「ええそうね。もちろんそのすぐ後に勇者の攻撃って書いてあるから、1だったら攻撃なんだなっていうことはプログラムから読み取れるとは思うけれど、ちょっと理解するまでに時間がかかるわよね。そこで登場するのが定数よ。」
「どうやるの?」
「ええ、プログラム中で直接数字を書くのではなくて、こんな風にしてみるの。」

# 定数の定義
ATTACK = 1
HEAL   = 2
ESCAPE = 3

# コマンドに応じた行動
case command
when ATTACK
  puts "勇者の攻撃"
when HEAL
  puts "勇者は回復呪文を唱えた"
when ESCAPE
  puts "勇者は逃げ出した"
end

「プログラム中では、数値を直接扱うのではなくて、その代わりに定数を使って見たの。わかりやすくなったと思うんだけれど、どうかしら。」
「うん。とってもいいよ。これなら半年経ってもATTACKは攻撃ってすぐ分かる!」
「みっくんに喜んでもらえて良かったわ。これなら他にもコマンドが増えても対応しやすいし、何かの事情で、10番を攻撃にしたくなっても変更も楽だからお薦めよ。プログラミングの定跡ってところかしら。」
「うん、遙香さん、ありがとう。」
「どういたしまして。」

低水準入力

「無事に攻撃できるようになったのだけれど、1が二回表示されているけれど・・・」
「そうね。二回表示されているわね。」
「う〜ん、二回目は puts commandって書いたから、それの結果だと思うんだけれど、もしかして最初のキーボードから入力した1も表示されているってこと?」
「ええ、その通りよ。gets関数でキーボードから入力した1が表示されているの。」
「そうなんだ〜。なんかちょっとかっこ悪いし、消せないのかな〜。」
「ええ、そうね。ない方がすっきりしているし、格好いいものね。」

「じゃあ、ちょっとだけ、自分で面倒を見ることになるけれど、やってみようか。」
「うん、お願いします。」

getsって、結構高水準な入力関数なの。キーボードから一文字ずつ打ち込んでエンターキーを押すところまでを全部まとめて見てくれるわ。で取り込んだ文字たちを全部繋げて、文字列にして返してくれるの。だから、とっても便利よね。」
「うん、そうだね。」
「で、普段はこれで良いのだけれど、今回のように、キーボードから押された文字を表示させたくない場合はちょっと困るわね。」
「うん、そうなんだ。」
「そういう場合に備えて、低水準の入出力関数もRubyには用意されているわ。今回の場合だとgetchあたりがオススメかしら?」
getsは、get string の名前の通り、文字列をまとめて得るものだったから、きっと getはキーボードからの入力を得るという意味だよね。」
「ええ、そうよ。」
chは何のことなんだろう?」
ch は character 文字 のことね。一文字一文字がcharacterで、それらが列(つら)なったものが、文字列 String ね。」
「そうなんだ〜。だから、getch を使うと、キーボードから打ち込んだ一文字一文字が得られるんだね。」
「ええ、そうよ。じゃあ、getchを使って、ちょっと練習して見ましょう。次のように書いて見て。」

getch.rb
require "io/console"
key = STDIN.getch
puts "#{key.inspect} キーが押されました。"

お姉さまに言われた通り、僕はカタカタ、キーボードからタイプしていく。

「ファイル名は、getch.rb っていう名前にして、保存したら、ruby getch.rbで実行して見て。」
「うん、できたよ。やって見るね。」

と、お姉さまに言われた通り、実行して見る。

「どうかしら?」
「実行したよ。次は何をすればいいの?」
「じゃあ、キーボードのAというキーを押してみましょう。」
「押したよ。」
「どうかしら。」
"a" キーが押されました。って、表示されてるよ。」
「良かったわね。キーボードのaを押したって認識してくれたみたいね。shiftキーを押しながらAって入力して見るとどうかしら。あ、このプログラム一回実行すると終わってしまうから、また再度ruby getch.rbで実行して見てね。」
「うん、わかった。あ、今度は "A" キーが押されました。って、表示されたよ。」
「ええ、じゃあ、プログラムが動いていることが確認できたところで、一行一行見ていきましょうか。」
「うん。」
「じゃあ、まず一行目。require って書いてあるのだけれど、意味は分かるかな?」
「う〜ん、リクエストに似ている綴りだけれど・・・」
「ええ、そうね。大体おんなじ意味だわ。」
「あ〜そうなんだ、リクエストなら、コンサートなんかで最後にアンコールをリクエストしたりするよ。」
「ええ、そうね。最後にもう一曲、おまけで歌ってくださいってお願いするのよね。request ってお願いっていう意味なの。そしてね。require もおんなじような意味愛なんだけれど、もう少し語調が強くて、必要とする、要求する、求めるって感じね。」
「あーそうなんだー。」
「ええ、そうよ、もしコンサートでrequireされたら怒って帰っちゃうかもね。」
「そうだね。うん、コンサートではrequestして歌ってもらえるようお願いしなくちゃだものね。」
「そうね。で、Rubyの世界で、ここにrequireって書いているけれど、プログラマがRubyに要求しているわけなの。今から getchを使いたいから、io/consoleを用意しとけって要求しているのね。」
「そうなんだ〜。いろいろ要求されてRubyも大変だね。怒ったりしないの?」
「ええ、怒ってみっくんのMacが火を吹くなんてことはないから、安心してね。」
とお姉さまは、軽く冗談を言う。

「よかった。そしてね。getsの時には特に要求なんてしなかったけれど、何でなの?」
「ええ、Rubyにはたくさんのクラスや便利なメソッドが用意されているけれど、大きく分けて二種類あるの。一つはみんなが良く使うもので、これは組み込みライブラリって呼ばれているわ。ライブラリは図書館っていう意味の英単語なんだけれど、ちょうど学校にある図書館のように、誰でもすぐ読みたい時に読めるように準備してある、そういうイメージね。Ruby本体に組み込まれているので、特に要求しなくてもそのまま使うことができるわ。」
「うん、そうなんだ。だから getsは何も要求しなくてもすぐに使えたんだね。」
「ええ、そういうことよ。」
「じゃあ、もう一つは?」
「もう一つは、標準添付ライブラリって呼ばれているもの。図書館の喩えで言うと、ちょっと専門的な書籍だから、学校の図書館にはないけれど、市立図書館だったり大学の図書館だったり大きな図書館から取り寄せてもらうイメージね。」
「そうなんだ〜」
「ええ、getchっていうメソッドは、io/consoleっていうライブラリの中に書かれているの。だからgetchを使う前に、事前に要求(require)して、準備しておくのね。」
「うん、わかったよ。ちなみにこのioだったりconsoleっていうのは、どういう意味なの?」
「ioは、input output の略よ。入力と出力という意味ね。キーボードから入力したり、画面に出力したり、そう言ったことを行うわ。console っていうのは、操作盤という意味なの。みっくんは今ターミナル画面を開いて、いろいろRubyに命令を与えたり、結果を受け取ったりしているけれど、ちょうどそんな感じね。昔は中央の大きなコンピュータに向かって、手元の小さな操作盤からいろいろ指示を出したの。そしてその結果が手元の操作盤に戻ってくるのね。そこから来てコンソールという言葉が残っているわ。」
「うん、わかったよ。じゃあ、二行目だね。」
「ええ、そうね。keyというのは、STDIN.getchの結果が入るんだよね。」
「ええ、そうよ。」
「で、getchっていうのは、一文字キーボードから取ってくるという意味ということだったけれど、STDINってなに?」
「これは、STandard INput の略。日本語にすると標準入力ね。」
「標準入力?」
「つまり早い話がキーボードの事ね。」
「そうなんだー。じゃあKEYBOARDって書けばいいのに。」
「あぁそうね、というより今はコンピュータに命令を与えるには、キーボードはもちろんだけれど、マウスもあるし、トラックパッドもあるわ。音声で指示を与えることもできるし、画面をタッチすることもできるわよね。」
「うん、そうだね。」
「こんなふうにいろいろな入力ができるようになったのって結構最近のことなの。ずっと入力の主流はキーボードだったわ。だから標準入力ってそういうこと。」
「そうなんだ。わかったよ。じゃあ、最後の行だけれど、#{}で囲んであるのは、式展開って言ってkeyの実際の中身を表示しているんだよね。」
「ええ、そうよ。」
「で、このinspectってなに?」
「inspectは検査っていう意味なんだけれど、そうね〜違いが分かるように、こんな風に最後に一行付け加えて書いてみましょうか。」

getch.rb
require "io/console"
key = STDIN.getch
puts "#{key.inspect} キーが押されました。"
puts "#{key} キーが押されました。"

「同じようにやって見るとどうかしら。」
「"a" キーが押されました。というのと、a キーが押されました。 って表示されるよ。」
「ええ、そうね。key.inspectって書いてあると、"(ダブルコーテーション)が付いていて文字列だってことがわかりやすいわよね。」
「うん、そうだね。」
「もう少し違いを分かるために、特別なキーを押してみるのもオススメね。例えばエンターキーをそのまま押してみたらどうかしら。あるいはターミナルで実行しているプログラムを途中で止めるときには、これよくCtrl+Cって書かれるのだけれど、コントロールキーとCを一緒に押すしてみたらどうかしら?」
「うん、やって見る。」

ruby getch.rb
"\r" キーが押されました。
 キーが押されました。
ruby getch.rb
"\u0003" キーが押されました。
 キーが押されました。

inspectが付いていると、何か表示してくれるんだね。」
「ええ、そうね。なるべく人が見てわかりやすいように、工夫して表示してくれるのがinspectメソッドの利点ね。」

「じゃあ、ということで、いままでgetsを使っていたから画面に表示されちゃったけれど、getchなら自分で表示させない限り、自動で画面に出たりはしないから大丈夫ってことだね。」
「ええ、そうね。」
「じゃあ、これ使って書いて見る!」

command.rb
# 定数の定義
ATTACK = 1
HEAL   = 2
ESCAPE = 3

# コマンドを表示する
puts "1: 攻撃"
puts "2: 回復"
puts "3: 逃走"

# キーボードからの入力を一文字受け取る
require "io/console"
command = STDIN.getch.to_i

# コマンドに応じた行動
case command
when ATTACK
  puts "勇者の攻撃"
when HEAL
  puts "勇者は回復呪文を唱えた"
when ESCAPE
  puts "勇者は逃げ出した"
end

「みっくん、どうかしら?」 と、お姉様の語りかける澄んだ声。

「うん、できたよ!」と弾む声で返す僕。

「じゃあ、この小さなコードで練習できたから、じゃあ、今まで作ってきていた rpg.rbに組み込んでみようね。」

「うん!」

僕は、お姉さまに励まされ、かたかたキーボードをタイプしていく。
外はすっかり陽が落ちて暗くなっている。夜になるまで付き合ってくれたお姉さまに大感謝。
出来上がったプログラムはこちらだ。
https://github.com/Atelier-Mirai/Ruby_RPG/blob/master/rpg/rpg09.rb

次は何を教えてもらえるのだろう。配列のことかな、データ構造やオブジェクト指向っていうのもいろいろ奥が深いみたいだし、正規表現ってのも学ばなくちゃ。

僕はわくわくしながら眠りにつく。夢の中では魔法使いの遙香お姉様が杖を振っていて、僕はかっこいい勇者。あ、遙香お姫様を助けに行く騎士がかっこいいかな〜

そんな夢を見た。

<< 未來のレベルアップ!! >>
<< HPが3上がった!! >>
<< MPが2上がった!! >>

Discussion