見出し画像

プログラミング初めてでも出来る!Rubyの基礎だけでヌメロン風数字当てゲームを作ろう!

こんにちは!よーすけ(@yosapro)と申します。

今回はプログラミング初めての方でも楽しくゲームを作りながら基礎がしっかり身に付く内容の教材を作成しました。

全体の半分以上を無料でご覧いただけますので、どうぞお気軽に読み進めて下さい。


自己紹介

都内の自社サービス開発に携わるWeb系エンジニアです。

文系大学卒/30歳/異業種出身から一念発起でプログラミングを学び、持ち前の論理的思考力で学び始めからポートフォリオ完成まで3ヶ月で終了させ転職活動を開始。Twitter転職で

・100以上のRT
・250以上のいいね
・20社以上から問い合わせ

をもらい、転職活動期間3週間で自社サービスをRuby on Rails, Vue.jsで開発するチームに加わることができました。

画像1

フロントエンドの微妙な見た目の調整より、バックエンドでロジックを書くほうが大好きです。

自分で言うのもなんですが、学生時代に塾の講師(算数/数学)を3年続けたことで人にわかりやすく教えるのがとても得意になり、エンジニアになる前からエンジニアコミュニティで初学者の質問にどんどん答えてきました。現在は個人でも活動を広げ、エンジニア転職を目指す初学者の方4名のオンラインメンターをさせていただいております。


この教材の対象者

🔸プログラミング初めての方
🔸Rubyの基礎理解が不安な方
🔸他言語をやったことがあるけどRubyにも挑戦してみたい方
🔸プログラミングでシンプルなゲームを作ってみたい方
🔸ターミナルやエディタの効率的な使い方が学びたい方
🔸アウトプットの練習がしたい方
🔸家族や友人に楽しんでもらえるものを作りたい方

プログラミング初めての人も手を動かしながら理解してもらえる内容にしちゃいました。


この教材のいいところ

🔸Rubyの基本しか使わないので初学者も安心
🔸家族や友人にも楽しんでもらえる対戦ゲームが作れる
🔸丁寧な解説で置いていかれない安心感
🔸現役エンジニアのコードの書き方やテキストエディタの使い方が学べる
🔸GitHubで章/節ごとにソースコードを用意
🔸作者に質問ができる

どのようなプログラミング教材にも演習問題や課題はあったりしますが、なかなか自分の身近な人に紹介できるようなものってあまりないですよね。そこでシンプルなルールのゲームを作り、それを一緒に楽しんでもらうことでプログラミングをよりわかりやすく一般の方にも伝えられると考えました。

プログラミングって、全く知らない人からしたら少しでもできる人のことは「神」のように見えるみたいです。それを身近な方で是非実感してくださいwそしてそれをモチベーションに繋げてよりレベルアップしていって欲しいです。


この教材で学べる内容

🔸ターミナルの基本的な使い方
🔸テキストエディタ(Visual Studio Code)のインストール、拡張機能、初期設定、主な使い方
🔸文字列、ヒアドキュメント
🔸ローカル変数
🔸ユーザーからの入力受付
🔸改行コード
🔸式展開
🔸演算子
🔸配列
🔸ハッシュ
🔸シンボル
🔸条件分岐
🔸繰り返し処理
🔸メソッド
🔸引数
🔸キーワード引数
🔸型変換
🔸正規表現
🔸リファクタリング

もしRuby初学者のバイブル「プロを目指す人のためのRuby入門(通称:チェリー本)」をお持ちの方がいましたら是非この本の目次を見ていただきたいのですが、今回扱う内容の95%は5章まで(全455ページ中175ページ)の内容で作れるので、いかに基礎に重きを置いているかがご理解いただけるかと思います。


この教材で扱わない内容

🔹クラス、インスタンス
🔹インスタンス変数
🔹例外処理
🔹その他Rubyの高度な内容
🔹Ruby on Rails
🔹HTML/CSS
🔹テストコード


作るゲームの内容

作るゲームは昔テレビで流行っていた「ヌメロン」と言う3桁の数字を当てる2人対戦のゲームです。

ご存じない方のために簡単に説明すると

🔸プレイヤーは3桁の数字を手札として作ります
🔸手札は0〜9の10個の数字の中から各1回しか使えません
🔸プレイヤーは相手の数字をピタリと当てたら勝ちです
🔸プレイヤーAは3桁の数字をコールします
🔸プレイヤーBはコールされた数字が自分の手札にどれだけ近いかを情報として返します
🔸返す情報は ①位も数字も一致していたら「EAT」、②数字のみが合っていたら「BITE」
🔸(例えば手札が123でコールされた数字が821なら、1EAT-1BITE という情報を返します)
🔸コールするプレイヤーを交代し、以後交互に進め、3EATが出たら終了です

本来は複数のアイテムや使える数字カードの枚数制限など細かなルール設定があるのですが、ロジックを複雑にしかねないので今回はシンプルな内容にしました。


完成イメージ動画

実際にターミナル上で動かしてみた動画です。


注意事項

🔹PCのOSはMacをご準備ください。私がWindowsでの開発経験がないためです。
🔹Windowsの方はご自身の責任でPCにRubyをインストールしていただくか、AWSのCloud9の様なブラウザ上でRubyが使える仮想環境をご用意いただく必要があります。(どちらも検索すれば導入方法はたくさん出てきますが、ある程度導入工程がかかります)
🔹なおその場合もMacとコマンドが異なる場合がありますので、適宜読み替えていただく必要があります。
🔹実行確認バージョン:Ruby 2.3.7 以上
🔹質問はtwitterアカウント@yosaproまでDMでします(Windowsの質問はお受けできません。ご了承ください。)

それでは早速作っていきましょう!


【第0章 事前準備】

・0-1 ターミナルを開く

ターミナルとはMac標準のコマンドラインのこと。

コマンドラインとはコマンドと呼ばれる命令文を使用してMacの操作や設定を行うツールです。

ドラマや映画で見る天才プログラマーがものすごいスピードで黒い画面に文字を打ってEnterキーをターン!!!ってシーンがあるじゃないですか。その黒い画面のやつです。

ちなみにWindowsではコマンドプロンプトがそれにたります。

それでは実際にターミナルを開いてみましょう。

Launchedpadから

画像4

その他をクリック

画像5

ターミナルをクリック

画像6

黒い画面が現れました!

画像7

ターミナルと仲良くできないとプログラミングはできないので、これから仲良くなっていきましょう。このゲーム作りで必要なターミナルとの付き合い方はすべて教えますのでご安心ください。


・0-2 Rubyのバージョンを確認する

今回のゲーム作りで使用するプログラミング言語は「Ruby」です。Rubyは他のプログラミング言語に比べて非常に書きやすく、日本人の方が開発されたということで日本では未だに人気が高い言語です。より詳細なRubyの特徴については検索してみてくださいね。

Macには標準でRubyが搭載されていますが、バージョンが古すぎると今回のプログラムが動かない可能性がありますので、まずはターミナルを開きRubyのバージョンを確認しましょう。

(ターミナル)
$ ruby -v⏎
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]
$
画像8

コマンド表記に出てくる「$」のマークは打つ必要がないのでご注意ください。これはターミナル上でコマンドが打てる状態のとき必ず左に「$」マークが付いているのでその目印として表示しています。

また「⏎」この記号ですが、これはReturnキー(Enterキー)を表しています。この記号を見たら同じようにReturnキーを押してください。なので「$」と「⏎」の間にあるコマンド、今回の場合は「ruby -v」だけを打ってReturnキーを押せばOKです。

そして2行目が出力結果です。「2.5.1」の部分がバージョンですね。それより後ろの文字は気にしなくて大丈夫です。

この数字が2.3.7以上の方は問題なし!

これより数字が低い方は動作を保証できないのでアップグレードをおすすめしますが、バージョンが合わない状態でプログラムを実行したからといって別にパソコンが壊れるわけじゃないので、バージョンが古いことにより前に進めなくなってからでも大丈夫です。

今回使う少々特殊なライブラリがRuby1.9.3以降であれば使えるみたいなので、そこまで神経質にならなくてもいいかもしれませんけどね。


・0-3 Rubyのファイルを作成する

それではゲーム作りに必要なものをターミナルに指示してどんどん作っていきましょう。

(ターミナル)
$ mkdir -p ~/Desktop/RubyPractice/NumberGame⏎

mkdir」は「make directory」の略で、右側に指定した場所にディレクトリ(フォルダ)を作成するコマンドです。

-p」オプションをつけることで存在しない親ディレクトリがあってもエラーを返さず必要に応じて親ディレクトリも一緒に作成してくれます。今回の例では-pオプションをつけないと、DesktopにRubyPracticeディレクトリが元々なかったらエラーになってしまいます。

~」はホームディレクトリを表します。Finderで開いたときに家のマークで表されているディレクトリですね。デフォルトの名前はみなさんが最初に登録した名前なんかがついてるはずです。

画像9

私のMacBookの場合は家のマークに「sasaki」という名前が付いています。これが「~」で表されるホームディレクトリです。

まとめると1行目のコマンドは「ホームディレクトリの下のDesktopの下のRubyPracticeの下にNumberGameディレクトリを作成してね。もし途中の親ディレクトリがなかったら一緒に作ってね。」という意味です。ここに今回作成するゲームを作ります。デスクトップにしたのは見失わないため、RubyPracticeディレクトリを作成したのは他にRubyの練習をするためのディレクトリとして使っていただけるようにしたためです。

画像10

コマンドをReturnで実行して、画像の通りディレクトリが作成できていればOKです。

(ターミナル)
$ cd ~/Desktop/RubyPractice/NumberGame⏎

次に「cd」は「change directory」の略で、ディレクトリを変えるという意味です。コマンドを実行する際には今どのディレクトリにいるかという概念が存在します。

例えば「書類ディレクトリ」にある「sample.txt」が開きたいのに、

画像11

「デスクトップディレクトリ」にいては開くことができないですよね?

画像12

ここではFinderを使って視覚的に分かりやすく見せていますが、ターミナルの操作でも同じことが起きています。

2行目のコマンドは、「今いるディレクトリから、先ほど作ったNumberGameディレクトリに場所を変えて(移動して)ね」ということですね。これにより現在はNumberGameディレクトリに移動しています。

(ターミナル)
$ touch number_game.rb⏎

最後に「touch」はその右側の名前でファイルを作るコマンドです。

3行目は「(現在いるディレクトリで)number_game.rbというファイルを作成してね」ということですね。このファイルにプログラムを書いていきます。

ちなみに「.rb」はRubyファイルの拡張子です。


・0-4 Visual Studio Codeを導入する

Visual Studio Code(以下VSCode)はMicrosoftが開発した無料のテキストエディタで、高機能かつ動作が軽く利用者も多いことで有名です。

テキストエディタとはその名の通り、テキストを編集するためのツールのことです。最も有名なテキストエディタは「Windowsのメモ帳」ではないでしょうか。あれも立派なテキストエディタです。文章を書いて保存することができますからね。ただプログラミングには向かないので、プログラミング用のテキストエディタを用意します。

「テキストエディタなんてどれを使っても同じじゃないか?」

という意見や記事をよく見かけます。世の中にあるテキストエディタはどれもほとんと同じですし、今回作成するゲーム程度の内容を書くだけなら必要十分なのも確かです。ですが、私がVSCodeを導入していただきたい理由は以下の3点です。

🔸全く同じ画面で開発することで初学者の方にエディタの違いによる不安を感じて欲しくない(私の画面だとなんか違う…?)
🔸日本だけでなく世界的にもここ数年注目を集めるメジャーなソフトなので困った時に検索しやすく拡張機能のメンテナンス性が高い
🔸講座内で紹介するショートカットキーがエディタごとに異なる可能性があるので同じものを使えばいちいち調べなくて良い

プログラミング初学者にオススメされるものはいくつかあり、AtomでもSublimeTextでも当然プログラムを書くことはできるのですが、同じものを使うことで、便利な機能も一緒に覚えてもらいたいなと思っています。実際に私の現場のチームでは全員VSCodeを使用しています。

前置きが長くなりましたがインストールしていきましょう。

1. 「vscode mac download」で検索し、一番上に出てきたこちらのページにアクセスします。

画像13


2. 右にあるリンゴマークの下のボタンからダウンロードします。

画像14


3. ダウンロードが完了したら、ブラウザ左下のzipファイルをクリックします。(Google Chromeを使っていない方はFinderからダウンロードフォルダに行き、zipファイルをダブルクリックしてください。)

画像15


4. Finderが表示され、解凍された「Visual Studio Code.app」が確認できます。

画像16


5. appファイルをアプリケーションフォルダにドラッグ&ドロップで移動します。

画像17


6. それではVSCodeを開いてみましょう。こんな画面です。

画像18


7. 拡張機能という便利機能の追加をしていきます。サイドメニューの一番下のアイコンをクリックし、検索窓に文字を入力してパッケージの絞り込みを行います。

画像19


8. まずは日本語化をします。「japanese」と入力し、一番上の「Japanese Language Pack for Visual Studio Code」をインストールします。

画像20


9. インストールが完了すると右下に「日本語で使いたいならVSCodeを再起動してね」と英語で言われるので、「Restart Now」を押してVSCodeを再起動します。

画像21


10. 再起動後、日本語になりました!

画像22


11. 2つ目に「ruby」と検索し、Rubyをいい感じに書けるように補助してくれる「Ruby」をインストールします。

画像23


12. 3つ目に「endwise」と検索し、Rubyにおいて何度も書くことになる「end」を自動補完してくれる「endwise」をインストールします。

画像24

便利な拡張機能は他にもたくさんありますが、今回は必要最小限としました。

「vscode 拡張機能 初心者」などで検索していただいて、好みにカスタマイズしてください!


13. 続いて設定変更をしていきます。左下の歯車マークから設定をクリック、もしくはショートカットキー

Command + ,(カンマ)

から設定画面を開きます。

画像25


14. 検索窓に「tabsize」と入力し、「Editor: Tab Size」を「2」にします。

画像26

こちらはインデント(字下げ)を半角スペース何個分にするかの設定です。Rubyでは半角スペース2個分が一般的です。

画像27


15. 検索窓に「autosize」と入力し、「Editor: Auto Save」を「afterDelay」にします。ソースコードを書き換えた時に、1秒後に自動保存してくれます。毎回保存コマンドを押さなくていいので便利です!

画像28


16. 検索窓に「minimap」と入力し、「Editor: Minimap Enabled」のチェックを外します。(説明割愛)

画像29


17. 検索窓に「preview」と入力し、「Workbench > Editor: Enable Preview」と「Workbench > Editor: Enable Preview From Quick Open」のチェックを外します。(説明割愛)

画像30


18. 検索窓に「panel」と入力します。「Workbench: Panel: Default Location」が初期状態では「bottom」になっていることを確認してください。

画像31

これはパネルの表示位置を変更する項目で、パネルにはVSCode内で開くことができるターミナルが含まれています。0-1でターミナルの起動方法を説明しましたが、エディタとターミナルが別々の場所にあると、お互いを表示する手間が生まれます。ですがこのパネル内のターミナルもほとんど同じ機能が使えるので、1つの画面で両方表示することが可能になるのです。

パネルの起動方法は

Control + ` (= Control + Shift + @)

です。ターミナルタブを開いてcdコマンドなどが使えることを確認してください。


19. 「right」にすることで画面右側にパネルを移動することができます。今回作るゲームのソースコードは横には短く縦に長いので、こちらはお好みではありますが右側配置も是非お試し下さい。

画像32


20. これで一通りの設定が終わりました。先ほどパネルを右側に設定してみた方は、アプリの再起動をしてください。

画像33


21. 続いて先ほど作成した「number_game.rb」を実際に開いてみましょう。メニューバーのファイル→開く またはショートカットキー

Command + O

から開きます。


22. デスクトップ→RubyPractice→NumberGameディレクトリを選択し、開くを押します。

画像34


23. 左側のエクスプローラーにNumberGameディレクトリが追加され、その中にある「number_game.rb」が表示されています。

画像35


24. ファイルをクリックするとタブに追加され、コードが書けるようになります。

画像36


25. それでは試しにターミナルにHello World!!!と表示させるプログラムを書いてみましょう。number_game.rbに

puts "Hello World!!!"

と書きます。

画像37

自動保存がきいているので、何もしなくてもOKです。

以上でVSCodeのインストール、設定や主な使い方について解説は終わりです。


・0-5 rubyコマンドでファイルを実行する

では編集したファイルを実行する前に、現在どのディレクトリにいるかを確認しておきましょう。

(ターミナル)
$ pwd⏎
/Users/(username)/Desktop/RubyPractice/NumberGame

pwd」は「print working directory」の略で、現在のディレクトリを表示するという意味です。

(username)の部分は皆さんがそれぞれ設定したホームディレクトリの名前です。上記のように表示されていたらNumberGameディレクトリにいるのでOKです。

先頭の「/」はルートディレクトリを表します。ルートディレクトリとはもうそれ以上は上に行けない最上位のディレクトリのことです。Macの場合は下の画像の「Macintosh HD」がルートディレクトリです。「Users」は「ユーザ」のことです。

もし誤って移動してしまっていたら、先ほど紹介した「cd」コマンドで移動しましょう。

(ターミナル)
$ cd ~/Desktop/RubyPractice/NumberGame⏎

念の為NumberGameディレクトリ直下にnumber_game.rbが存在するか確認してみましょう。

(ターミナル)
$ ls⏎
number_game.rb

ls」は「list segments」の略で、今いるディレクトリから見てどんなディレクトリやファイルがあるかを表示するコマンドです。ちゃんとnumber_game.rbがありますね。

いよいよrubyファイルを実行してみましょう。

実行の仕方は「ruby (ファイル名)」です。間に半角スペースを入れてください。

(ターミナル)
$ ruby number_game.rb⏎
Hello World!!!

ちゃんと「Hello World!!!」が表示されましたね!これで開発準備は万端です!

続いてRubyの基礎の一部を事前知識としてやっておきましょう。


・0-6 ターミナルへの出力メソッド

rubyではコマンドラインに出力結果を表示するためのメソッドがいくつかあります。

今回使用するメソッドは

・puts(プットエスまたはプッツ)メソッド
・printメソッド
・pメソッド

の3つです。3つともそのメソッドの右側に指定したものをコマンドラインに返してくれます。違いは

・putsメソッドは右側のものを文字列に変換し改行する
・printメソッドは右側のものを文字列に変換し改行しない
・pメソッドは右側のものをそのまま出力し最後に改行する

です。それでは試しに使ってみましょう。

ちなみに文字列は「"文字列"」のようにダブルクォートで囲みます。

Rubyの場合はシングルクォートで囲んでも問題ないのですが、ダブルクォートの方ができることが多いので今回はダブルで統一していきます。(現場では厳密にシングルとダブルを場合によって使い分けていたりします。)

(number_game.rb)
puts "putsメソッド1"
print "printメソッド1"
p "pメソッド1"
p "pメソッド2"
puts "putsメソッド2"
print "printメソッド2"

number_game.rbをVSCodeで上記のようにputsとprintとpをランダムに6行配置して保存しましょう。

ターミナルで実行します。

(ターミナル)
$ ruby number_game.rb⏎
putsメソッド1
printメソッド1"pメソッド1"
"pメソッド2"
putsメソッド2
printメソッド2$

それぞれ

・putsメソッドの文末は改行される
・printメソッドの文末では改行されない
・pメソッドは文字列であることを表すダブルクォートも出力され、文末で改行する

以上のことが確認できました。コードを書くときはプログラムに文字列であることを認識させるためにダブルクォートで囲みましたが、出力されるときはpメソッド以外はダブルクォートがついていない点にも注目してください。


・0-7 演算子

演算子とはコンピュータに演算を行わせるための記号の総称です。以下基礎的ないくつかの例です。

・aとbを足す → a + b
・aからbを引く → a - b
・aとbを掛ける → a * b
・aをbで割る → a / b
・aをbで割ったときの余り → a % b
・aがbより大きい → a > b
・aがb以上 → a >= b
aにbを代入する → a = b
・aとbが一致するかどうか真偽を返す → a == b
・aとbが一致しないかどうか真偽を返す → a != b
・aかつbなら真、それ以外は偽を返す → a && b
・aまたはbなら真、それ以外は偽を返す → a || b

=」は特に気をつけてください。算数、数学では「aとbが等しい」という意味でしたが、プログラミングでは「aにbを代入する」という意味です。(等しいかどうかの判定は「a == b」を使います。)

代入するということは、=の右のものを=の左に入れること。この右から左という流れはプログラミングではとても大事です。

特に変数という入れ物に数字や文字や配列を入れるときによく使います。

(例)
# 変数xに3という数値を代入する
x = 3
# 変数yに"Rubyは楽しい"という文字列を代入する
y = "Rubyは楽しい"
# 変数chestに靴下とTシャツとズボンが入った配列を代入する(0-8で解説)
chest = ["靴下", "Tシャツ", "ズボン"]


・0-8 配列

配列とはタンスのように引き出しがたくさんついてる入れ物です。1段目に靴下、2段目にTシャツ、3段目にズボンを入れたタンスを配列では

["靴下", "Tシャツ", "ズボン"]

このように表します。「[ ](大括弧)」で囲み、その中で区切り文字として「,(カンマ)」を使います。見えづらいですがカンマの後ろには半角スペースを入れるのが慣習です。

そして普通の数え方だったら左から1番目、2番目、3番目ですが、配列の場合は0番目、1番目、2番目と数えます。初めはなれないかと思いますがプログラミングでは最初が0番です。

この配列をchestという変数に入れるとすると、「靴下」や「ズボン」は

(例)
chest = ["靴下", "Tシャツ", "ズボン"]
puts chest[0]
# => 靴下
puts chest[2]
# => ズボン

このように取得が可能です。chestという変数に入っている配列の0番目を「chest[0]」で表しています。この右側に大括弧で囲んだ数字のことを添え字と言います。


・0-9 真偽値

真偽値とは真か偽かの二択の値のことで、プログラミングでは

・真のことを「ture
・偽のことを「false

で表します。


・0-10 条件分岐

Rubyでは条件に応じて処理を変える際に「if文」を使います。下記は一例です。

(例)
niku = 400
if niku < 300
 "晩御飯はハンバーグにします"
else
 "晩御飯はもやし炒めにします"
end

「変数nikuが300より小さい」が真ならば「"晩御飯はハンバーグにします"」、偽ならば「"晩御飯はもやし炒めにします"」に処理が分かれます。

今回変数nikuには400が入っており、「変数nikuが300より小さい」は偽なので、残念ながら「"晩御飯はもやし炒めにします"」の方が選ばれます。

さらに3段階以上に分岐することも可能です。

(例)
score = 65
if score >= 80
 "たいへんよくできました!"
elsif score >= 50
 "よくできました"
else
 "もっとがんばりましょう"
end

scoreが80以上なら「たいへんよくできました!」が選ばれるのですが、scoreは65に設定したので条件に合わず、次のelsifに移動します。ここではscoreが50以上ならという条件に合っているので、「よくできました」が選ばれて終了です。

このelsifを何個も用意すれば条件分岐は何個でも作ることが可能です。

elsifをelseifと各間違いが多発するので気をつけましょう。他の言語だと逆に正しかったりするのですがRubyではelsifです。

そしてRubyでは条件分岐の際、判定結果が

・falseまたはnilの場合は
・それ以外

というルールがあります。

nilは他の言語でいうNULLと同じで、「無」を意味します。「""(空の文字列)」や「[](空の配列)」は先ほどのタンスで例えるなら「空っぽのタンス」は存在するので無であるnilとは異なります。無は何も存在ことを表します。間違えやすいので注意してください。

(例)
a = []
if a
 "真です"
else
 "偽です"
end

この場合aは「空の配列」で「falseでもnilでもない」ので、判定はとなり、「"真です"」が選ばれます。

(例)
b = 5
if b == 7
 "bは7です"
else
 "bは7ではありません"
end

この場合bは5で「bと7が一致する」の判定はとなり、「"bは7ではありません"」が選ばれます。

より実践的な使い方はコーディングの中で触れていきます。


・0章 まとめ

・0-1 ターミナルを開く
・0-2 Rubyのバージョンを確認する
・0-3 Rubyのファイルを作成する
・0-4 Visual Studio Codeを導入する
・0-5 rubyコマンドでファイルを実行する
・0-6 ターミナルへの出力メソッド
・0-7 演算子
・0-8 配列
・0-9 真偽値
・0-10 条件分岐

これで開発する環境と基礎知識が身につきました。

次の1章から実際にゲームを作っていきましょう!


【第1章 設計】

・1-1 ゲームの流れの言語化

ヌメロンというゲームはどの様な流れで進んでいくのかを確認するために、まずはテレビで行われていた様に実際に2人の会話ベースで行われるものの流れをざっくりと洗い出してみましょう

1.両プレイヤーが0〜9の10枚のカードの中から手札を決める
2.プレイヤーAが数字をコールする
3.プレイヤーBは[EAT-BITE]を発表する
4.プレイヤーBが数字をコールする
5.プレイヤーAは[EAT-BITE]を発表する
6.以降2〜5を繰り返し3EATが出たら終了する

こう見ると結構単純ですね。


・1-2 プログラムの役割

今回作るプログラムは言わば「ゲームマスター」の様な役割を果たしてくれる様に作成します。プログラムを介して2人は数字を当てていくのです。

本来はプレイヤーが2人と紙とペンぐらいあればゲームは成立するのですが、アナログではなくデジタル表示の下でプレイを可能にする、そんなプログラムを作ります。

その流れを改めてイメージしてみてください。すると先ほど確認した流れとは少し異なってきます。

1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
2.プレイヤーBが0〜9の10枚のカードの中から手札を決める

3.プレイヤーAが数字をコールする
4.プログラムで[EAT-BITE]を発表する
5.プレイヤーBが数字をコールする
6.プログラムで[EAT-BITE]を発表する
7.以降3〜6を繰り返し3EATが出たら終了する

プログラムに手札を認識させる際にお互いに見えない様に同時に入力することはできないので、ここは交互に入力してもらう必要があります。

また、お互い自分の手札はもちろんわかっていますが、[EAT-BITE]の判定はプログラムに行ってもらうことで、その人のターンが終了することを明確にでき、さらに履歴を出力することも可能になるでしょう。

1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
3.プレイヤーAが過去にコールしたことがあれば履歴を表示する
4.プレイヤーAが数字をコールする
5.プログラムで[EAT-BITE]を発表する
6.プレイヤーBが過去にコールしたことがあれば履歴を表示する
7.プレイヤーBが数字をコールする
8.プログラムで[EAT-BITE]を発表する
9.以降3〜8を繰り返し3EATが出たら終了する

少しずつ項目が増えてきましたね。

実際のWebアプリ開発でも最初になるべく必要な機能を洗い出し、開発する中で新たに必要になった機能は順次追加をしていきます。

全体の流れをざっくり把握できたので、機能の洗い出しはここまでとしましょう。


・1-3 実装項目の見える化(コメントアウト)

それじゃあバリバリRubyを書いていくぞー!

と言いたいところなのですが、ちょっと待ってください。

流れの洗い出しで出てきた項目はこれから実装するプログラムのいわゆる設計書のようなものです。

いちいちこのページまでスクロールして戻ってくるのは煩わしいので、この設計書を実際のコードの中にコメントとして先に記しておきましょう。

ではVSCodeでnumber_game.rbを編集していきます。先述したHello Worldは消しちゃってください。

(~/Desktop/RubyPractice/NumberGame/number_game.rb)
# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める

# 3.プレイヤーAが過去にコールしたことがあれば履歴を表示する

# 4.プレイヤーAが数字をコールする

# 5.プログラムで[EAT-BITE]を発表する

# 6.プレイヤーBが過去にコールしたことがあれば履歴を表示する

# 7.プレイヤーBが数字をコールする

# 8.プログラムで[EAT-BITE]を発表する

# 9.以降3〜8を繰り返し3EATが出たら終了する

設計書が最初から書いてあったほうが見やすくていいですね。

ちなみになぜ「#」を先頭に付け足しているのかというと、Rubyでは「#」を先頭に書くとコメントとして認識され、その行はプログラムを実行する上で無視されるからです。メモを残しておく際に便利ですね。

VSCodeでは

Command + /

でカーソルのある行をコメントアウトできます。複数行選択したら一気にコメントアウトすることも可能です。


・1-4 GitHubからソースコードをダウンロード

2章から実際にコードを書き始める前に、私が用意した章/節ごとの完成品ソースコードをGitHubからダウンロードしてきましょう。

GitHubとはソースコード管理サービスです。エンジニアはチームのメンバー同士でこのGitHubを使ってソースコードを管理/共有し、共同開発を行います。

教材を読み進めながら同じように書いて行けばゲームは完成するのですが、章/節ごとのソースコードを一緒に表示しておくことで、

・エラーが出た場合に問題を細分化できる
・タイプミスや書き方間違いに気がつきやすい
・エディタ上で検索がしやすい

などのメリットがあります。答えがすぐ近くにある方が安心ですよね。


1. では早速ダウンロードしていきましょう。まずはこちら(https://github.com/YousukeSasaki/NumberGame)にアクセスします。


2. 画像の赤丸の「Clone or download」をクリックします。

画像38


3. 出てきた小さな画面右下の「Download ZIP」をクリックします。

画像39


4. ブラウザ左下にダウンロードされたzipファイルが現れますので、クリックしてください。

画像40


5. Finderが起動し、ダウンロードフォルダの中にzipファイルと、解凍されたものが表示されています。

画像41


6. これをnumber_game.rbと同じ場所に移動させましょう。Finderをもう1つ起動し、RubyPractice/NumberGameディレクトリを開いて横に並べます。解凍した全てのファイルをドラッグして選択状態にし、RubyPractice/NumberGameディレクトリに持って行ってください。

画像42


7. マウスを離すとファイルの移動ができました。

画像43


8. VSCodeを見てみると、ワークスペースに移動したファイルが表示されます。

画像44


9. 試しに「number_game_1-03.rb」をクリックしてみると、新しくそのファイルのタブが増えましたね。

画像45


10. 便利な使い方として、画面分割のやり方も紹介します。タブをドラッグして…

画像46

画面右半分の上の方に持っていくと、薄くハイライトされます。このまま離すと…

画像47

画面が2分割されました!

画像48

Command + B

でエクスプローラーも閉じれば…

画像49

このように、左に自分がコードを書くファイル、右にその章/節のゴールとなるファイルを置くことでお手本を見ながらコーディングできるので、検索やコピペなど、適宜お使いください。

本教材を見るためのブラウザも表示すると結構画面いっぱいになってしまいますので、それもご自身の見やすい形を探してくださいね。


・1章 まとめ

・1-1 ゲームの流れの言語化
・1-2 プログラムの役割
・1-3 実装項目の見える化(コメントアウト)
・1-4 GitHubからソースコードをダウンロード

作るゲーム全体の流れを把握し、ファイルの中に記述することができました。

2章から今度こそコードを書いていきますよ!


【第2章 手札の決定】

・2-1 入力を受け付ける

まずは手札をパソコンに打ち込んでもらうところからです。

ユーザーからの入力を受け付けるメソッドは「getsメソッド」です。コード上に「gets」と書くとプログラム実行時に処理が止まり、ユーザーからの入力待ち状態になります。ユーザーは文字を入力後Returnで決定すると、止まっていた処理が再開されます。

getsは入力された文字列に改行を足してしまう性質があるのですが、gets.chompとすると最後の改行は含めません。今回は数字のみが欲しいのでgets.chompを使用します。

それではプレイヤーAさんに手札を決めてもらうような処理を書いていきましょう。

(number_game.rb)
# 1.プレイヤーAが0910枚のカードの中から手札を決める
puts "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。"
a_hand = gets.chomp
puts a_hand

putsは改行されるので「...入力してください。」の次の行で入力待ち状態になり処理が止まります。本来は入力された手札は相手に知られないために隠さなければいけませんが、ここではa_handに代入されたかどうかを確認するために敢えてputsメソッドで出力してみましょう。

ターミナルに戻りファイルを実行します。

(ターミナル)
$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
_

処理が止まりましたね。では適当な数字を入力してみましょう。入力が完了するとその数字を変数「a_hand」に代入して、次の行でa_handの中身を出力しています。

(ターミナル)
$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
123123
$

これでプレイヤーが入力した手札を変数に入れられたことが確認できました。

ただ「...入力してください。」の直後に入力待ち状態になると、なんだかわかりづらいですね。

入力受付中 =>」のような案内を次の行で示してあげると視覚的にわかりやすくなるでしょう。

以下のように編集してください。

(number_game.rb)
# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
puts "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。"
print "入力受付中 => "
a_hand = gets.chomp
puts a_hand

矢印の先でそのまま入力してもらいたいのでここでは改行しないprintですね。

では実行してみましょう。

(ターミナル)
$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>_

先ほどよりわかりやすくなりましたね!

ちなみに処理を中断する場合は

Control + C

です。今後処理が長くなって来た場合などで活用してください。


※(注)以降コード上に「number_game.rb」のことか「ターミナル」のことかは記述をしません。中身で判断していただけるかと思いますが、まだ不慣れな方は

$ ruby number_game.rb⏎

で始まる方が「ターミナル」、それ以外のロジックが書かれている方が「number_game.rb」でご判断ください。

また、たまに「(例)」と行頭に書いてあるものは例を示しているだけなのでコードの編集は必要ありません。


・2-2 改行コード

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
puts "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。"
print "入力受付中 => "

こちらの2行はどちらも文字列の出力処理なので、1行にまとめてしまいたいと思います。しかしputsを使わずに改行をさせるにはどうすれば良いのでしょうか?

Rubyでは文字列の中では「\n」で改行を表します。ただしダブルクォートで囲まれた文字列では有効ですが、シングルクォートの場合はそのまま\nと出力されてしまいますので注意してください。

\(バックスラッシュ)」は

Option + ¥

で打つことができます。

では上記2行の文字列を1行にまとめてみましょう。

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
print "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
a_hand = gets.chomp
puts a_hand

では前回と表示が変わらないか確認してみましょう。

(ターミナル)
$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>_

問題なく改行されていますね。


・2-3 入力を非表示にする

ではAさんのコードをコピペしてBさんの分も書き加えて、2人分の手札を登録できるようにしましょう。Bさんの手札はb_handとい変数に代入しています。(puts a_handの行は削除しました)

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
print "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
a_hand = gets.chomp

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
print "プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
b_hand = gets.chomp

実行してみます。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>529⏎
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>831⏎
$

裏側で変数に代入はできているのですが、数字が見えてしまっているのでいるので、これではゲームにならないですね…

そこで、あまり書籍や教材で出てこないものではあるのですが「io/consoleライブラリ」というものを使います。初学者の方はこのライブラリの詳細を調べたりする必要はありません。この手札入力でのみ使用します。

require "io/console"

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
print "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
a_hand = STDIN.noecho(&:gets).chop

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
print "プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
b_hand = STDIN.noecho(&:gets).chop

行った変更は以下の2つです。

・ファイルの最上部に「require "io/console"」を加える
・「gets.chomp」を「STDIN.noecho(&:gets).chop」に置き換える

これで実行してみます。入力受付中が表示されたら画面には映りませんが数字を打ってReturnキーを押してください。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>⏎プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 =>⏎$

数字を入力しても画面に表示されなくなりましたね!ただし改行がうまくいってないみたいで文が続いてしまっていますので、無理やり改行を設置します。

require "io/console"

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
print "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
a_hand = STDIN.noecho(&:gets).chop
puts "\n\n"

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
print "プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
b_hand = STDIN.noecho(&:gets).chop
puts "\n\n"
$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

$

これで画面に数字を表示することなく2人分の手札を別々の変数に入れることもできました!


・2-4 DRYの原則とメソッド分離

(この章は初学者の方には少し難しいと思うので1回で理解できなかった場合は繰り返し読んでみてください。)

突然ですが現在のコードを見て思うことはありませんか?もし気が付いた人はプログラマーとしていい着眼点を持っています。

そうです。似たような処理が繰り返し書かれているのです。(print → 手札入力 →改行)

今回は2回しか繰り返されていないのであまり気にならないかもしれませんが、例えば4人で遊ぶゲームだった場合、4回同じ処理が書いてあったら流石に気になりますよね。

プログラミングの有名な格言の一つに「DRY」というものがあります。これは「Don't Repeat Yourself」の略で、「(同じようなコード)自身を繰り返すな!」という意味ですね。意訳すると、「似たようなコードは共通化しよう」ということです。

共通化するにはメソッド(他の言語の関数と同義)というものを使います。プログラミングにおける関数とは「複数の処理を1つにまとめた部品」のこと。

まだよくわからないと思うので具体的に中身を見てみましょう。

require "io/console"

# 手札を入力する
def input_hand
  print "プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  a_hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
input_hand

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
input_hand

前回のコードと比べると大きく変わりました。

まずdef input_hand 〜 endの中に共通化させたいコードを移し、元あった2つの同じようなコードはinput_handに置き換えられています。

この「def」はdefinition(定義)の略で、defからendまでの間にメソッドを定義しますよという宣言をしています。

そしてdefの後ろのinput_handがメソッドの名前(メソッド名)で、このメソッド名を別のところで書くことでメソッドが呼び出され、メソッドの中身の処理がその場所で実行されるようになる仕組みです。

注意していただきたいのは、メソッドは定義するだけではプログラム上何も起きません。必ずそのメソッドを別の場所で呼び出さないと何も処理は実行されないので気をつけましょう。

Rubyはnumber_game.rbを実行すると

・1行目で"io/console"を読み込む
・4〜8行目でinput_handメソッドが定義されていることを認識するが、定義してるだけなので何もしない
・11行目でinput_handメソッドが呼び出されたので3行目に戻り処理を実行し実際にターミナルに出力結果が表示される
・14行目でもinput_handメソッドが呼び出されたので11行目の場合と同様

こんな感じで上から順に読み進めて処理していくのです。

そしてメソッド分離時に気をつけるべきは2点。

・メソッド名は中身をわかりやすく表しているか
・メソッドを実行する場所より上にメソッドを定義しているか

です。

全く関係ない名前を命名してしまったら「これはなんのメソッドだ?」とあとで自分が困ることになるでしょう。エンジニアとしてチーム開発をするならチームメンバーが自分のコードを読むこともあるのでなおさらです。

さらにプログラムは上から実行されていきますが、メソッドの呼び出し元より前にメソッドを定義しておかないと、エラーになってしまうのでその点も注意してください。今回の場合はinput_handの呼び出し元より後ろにinput_handメソッドを定義すると

(例)
$ ruby number_game.rb⏎
Traceback (most recent call last):
number_game.rb:5:in `<main>': undefined local variable or method `input_hand' for main:Object (NameError)
$

「未定義のメソッドは呼べません!(意訳)」とエラーになります。

では試しにメソッドが正しく動くかファイルを実行してみましょう。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

$

大きく変える前と同じ動きをしていますね。なのでメソッドへの共通化は成功しています。

ですがこのままではいくつかの問題を抱えています。


・2-5 メソッドによって違う値を与える

問題の1つ目は、当然ですがどちらも「Aさん」になっていることです。コードの中に「Bさん」と書かれていないので当たり前なのですが…。

では同じようなコードをメソッドに共通化しつつ特定の箇所だけはメソッドを実行する場所によって変えたい場合はどうしたら良いのでしょうか?

ここで用いられるのが「引数(ひきすう)」です。「いんすう」と間違えやすいので注意してください。引数を設定するとメソッドの中で動的に値を変化させることができます。

実際に使い方を見てみましょう。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  a_hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
input_hand("A")

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
input_hand("B")

変更点と共に引数が渡る流れを確認していきましょう。

(1. input_handメソッドに「A」という文字を渡す)

input_hand → input_hand("A")

(2. 渡されたAをメソッドの中ではnameに置き換える)

def input_hand → def input_hand(name)

(3. nameの中身は現在Aなので、「プレイヤーAさん」と表示される)

print "プレイヤーAさん… → print "プレイヤー#{name}さん…

これをBさんの場合は引数に"B"を渡して実現しています。

ダブルクォートの中では全て文字列になってしまうのですが、「#{ }」で囲むことで式を埋め込むことができます。これを式展開と言います。式とは演算子を使った計算や変数、メソッドなどを含みます。今回はnameという変数を文字列の中に入れたいので、式展開を使っています。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

$

これでプレイヤーAさん、プレイヤーBさんと名前を別々に表示することができました!


・2-6 ローカル変数のスコープ

2つ目の問題は手札として入力してもらった「a_hand」をinput_handメソッド実行直後に出力してみることでわかります。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  a_hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
input_hand("A")
puts a_hand

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
input_hand("B")

12行目にa_handを出力するコードを追加しました。

実行してみます。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

Traceback (most recent call last):
number_game.rb:12:in `<main>': undefined local variable or method `a_hand' for main:Object (NameError)
$

あらら、エラーが出てしましました…みんな大嫌いエラーの登場です。ですがプログラマーたる者、エラーから逃げてはいけません。

・Google翻訳を使って英文を訳してみる
・エラー文そのままコピペで検索して同じく苦しんでる方の解決法を探す
・指摘されてる行を確認する

色々アプローチはあるんです。今回は初回なのでこちらで解決してしまいますね。エラー文の中身は

「未定義のローカル変数またはメソッドのa_handがnumber_game.rbの12行目にあるよ(NameError = 名前に関するエラー)」です。

a_handはメソッドはなく変数として定義したはずなのに、Rubyはそれをわかってくれていないみたいです。どうしてこんなことが起きているのでしょうか?

それはローカル変数のスコープについて理解する必要があります。

今まで変数と言っていたものは全てローカル変数のことです。Rubyには他にも○○変数とつく変数がいくつかあるのですが、本教材ではローカル変数しか扱いません。

スコープとは「範囲」のことで、ローカル変数のスコープとはローカル変数が適用される範囲のこと。

その範囲は「宣言された場所からスコープが始まり,宣言されたブロックが終わるとスコープが終了する」です。

ブロックについては詳しく解説しないのでざっくり言うと「def〜end」と思ってもらえばOKです。(厳密には全然違いますが今回はそれで理解できます。)

6行目のローカル変数a_handは4行目から始まったinput_handメソッドの中にあり、8行目のendまでの間でしか使えません。なので6行目のa_handと12行目のa_handは別の物だと勘違いされ、12行目で呼び出しても「僕の見えてる範囲からはa_handなんて定義されていないよ?ローカル変数かメソッドかもわからないなあ」と使えなかったんですね。


・2-7 メソッドの外に値を反映させる

このスコープを解決するメソッドの性質として「返り値」があります。「戻り値」とも言います。メソッドを実行するとメソッドから何かしらが呼び出し元に返ってきます。

工場の機械をイメージしてください。材料を投入し、機械が加工して製品が出てくる。そんなイメージです。

画像50

左のおじさんが呼び出し元ですね。返り値は呼び出し元に返ってくる。ここテストに出ますので暗記するように。

冗談はさておき、返り値の性質として以下の2点があります。

returnを指定するとその行の内容が返り値になり、その行でメソッドから抜けるため次の行以降は無視される
・returnを指定しなかった場合は、最終行の内容が返り値になる

今までのinput_handメソッドはreturnを指定していなかったので特に意識する必要がなかったのですが、では返り値を利用して「メソッドの外のa_hand」に「メソッドの返り値」を代入するという処理を書いていきましょう。これでメソッド中のものを外に出すことができます。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  a_hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
  a_hand
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
a_hand = input_hand("A")
puts a_hand

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
input_hand("B")

8行目に「a_hand」を追記しました。これはメソッドの中にreturnが指定されていないので、メソッドの最終行に敢えて「a_hand」を書くことで「a_hand」の中身を返り値として返すようにしています。

そして返ってきた値を12行目の「a_hand」に代入することでメソッドの外のローカル変数「a_hand」にプレイヤーAが入力した値を保存しておくことが可能になります。では13行目の「puts a_hand」でちゃんと出力されるか確認してみましょう。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (824と入力)

824
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎

$

おお、824と入力したら824が出力されました!

もう少し修正すると、メソッドの返り値は「変数名」ではなく「変数の中身」が返ってくるので、実は変数名はなんでもいいんです。「a_hand」は「Aの手札」という意味で命名していましたが、16行目の「input_hand("B")」でも同じメソッドを使うので、汎用的な「手札」という意味で「hand」というローカル変数に変更しましょう。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
  hand
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
a_hand = input_hand("A")
puts a_hand

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
b_hand = input_hand("B")
puts b_hand

変更点は以下の通りです。

・6行目と8行目の「a_hand」を「hand」に変更
・16行目に「b_hand = 」を追加
・17行目に「puts b_hand」を追加

実行してみます。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (824と入力)

824
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (356と入力)

356
$

どちらも変数に代入できていることを確認できました!


・2-8 手札を数字3桁に制限する

どんどん形になってきましたね。手札決定の2章もあと一息!と言いたいのですが、あと二、三息ぐらいですw 頑張りましょう!

3つ目の問題は「数字3桁じゃない入力も受け付けてしまう」です。今まで間違えて数字3桁以外を入力してしまった方もいるかもしれませんが、何のエラーも表示されませんでしたよね?

桁数や文字の種類を限定するには「正規表現」というものを使います。

世の中の一般的なウェブサイトの会員登録フォームの

・メールアドレス入力欄で「@(アットマーク)」が入っていなかったり
・パスワード入力欄で文字数が足りなかったり

した時にエラーが出たりするのはこの「正規表現」により制限されているからです。

正規表現はとても奥が深く、極めだしたら本当にキリがないので、今回は「数字3桁のみ受け付ける」に限定して説明します。

まず正規表現独特の表現の仕方を以下に並べます。(覚えなくていいです。)

・0から9までのいずれかの数字 → \d
・直前のものと同じものを3回 → {3}
・文頭 → \A
・文末 → \z
・正規表現の始まりと終わり → / /(スラッシュで囲む)

まとめると

/\A\d{3}\z/

これで「文頭から文末の間が数字3桁のもの」を正規表現で表しています。

そして入力された手札がこの正規表現のパターンにマッチしているかを判定するためのメソッドがmatchメソッドです。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
  hand.match(/\A\d{3}\z/)
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
a_hand = input_hand("A")
puts a_hand

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
b_hand = input_hand("B")
puts b_hand

メソッドの返り値であるhand.match(/\A\d{3}\z/)の結果は何が返ってくるのでしょうか

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (123と入力)

123
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (1234と入力)


$

3桁で入力したらその数字が返ってきているのでa_handに代入ができていることが確認できました。対してb_handの方が何も出力されませんね。これは「nil」を返しているからです。nilは「無」でしたね。なので何も表示されないのです。これで一致した場合はその数字自身、一致しない場合はnilを返すことがわかりました。

今回の正規表現は「数字3桁」に制限しているので、文字を含めた3桁や数字3桁の後に文字を入れたりしても当然nilが返ってきます。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (1y3と入力)


プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (123mと入力)

$

しかし実はまだ不具合がありまして、それは「同じ数字を使っても許されてしまう」点です。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (115と入力)

115
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (999と入力)

999
$

ゲームのルール上同じ数字は使えないので、その絞り込みをさらに行います。


・2-9 重複する数字を制限する

(ここから少し難しい話をします。適当に流し読みして「ふ〜ん」で通り過ぎていただいてOKです。)

まず文字列を指定の区切り文字で配列に分割してくれるのがsplitメソッドです。通常は長い文字列の中から句読点やスラッシュで区切って配列に分割したりするのですが、今回は少し特殊な使い方である「1文字ごとに配列に分割する」ために使います。

次に配列の標準メソッドの中にuniqメソッドがあります。これは配列の中に重複するものがあったら2個目以降を取り除いてくれる便利なやつです。

さらに配列の中に要素が何個入っているかを調べるlengthメソッドがあります。

これらと正規表現を組み合わせると「重複しない3桁の数字」を実現することができます。

(例)
hand = "131"

# 文字列"131"を空白文字(文字と文字の間)で区切り、配列に分割する
p hand.split("")
# => ["1", "3", "1"]

# 配列の中で重複するものを取り除く
p hand.split("").uniq
# => ["1", "3"]

# 配列の要素の数を返す
p hand.split("").uniq.length
# => 2

# 配列の要素の数が3個かどうかを真偽値で返す
p hand.split("").uniq.length == 3
# => false

中身がどのようになっているか知りたいのでpメソッドを使って表示した結果を下に記しました。

「正規表現が一致する」と「hand.split("").uniq.length == 3」がどちらもtrueなら重複しない数字3桁に絞り込みできていることになります。条件を2つ組み合わせるのは冒頭の演算子でやった中だと「かつ」を表す「&&」ですね。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
  if hand.match(/\A\d{3}\z/) && hand.split("").uniq.length == 3
    return hand
  else
    ......
  end
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める

8〜12行目を書き加えました。日本語で表すなら「もし入力された手札が数字3桁かつ重複したものを除いた時の要素数が3つだったら手札をreturnで返す、それ以外なら………」です。

では入力が間違っていた場合はどうしたらいいでしょうか?もう一度入力してもらう必要はありそうですが、そのもう一度入力された手札についてもまたif文で条件分岐をして、さらに間違っていたらまた入力させて、またif文を書いて、………(以降無限ループ)……ちょっと恐ろしいですね。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"
  if hand.match(/\A\d{3}\z/) && hand.split("").uniq.length == 3
    return hand
  else
    hand = STDIN.noecho(&:gets).chop
    puts "\n\n"
    if hand.match(/\A\d{3}\z/) && hand.split("").uniq.length == 3
      return hand
    else
      ......(無限ループ)......
    end
  end
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める

「じゃあ3回までやり直しOKにして、それ以上間違えたらゲーム強制終了!」みたいな、作成者側の都合でユーザーを困らせるようなコードにするべきではありません。


・2-10 間違った入力の場合は手札を再入力させる

Rubyには何度も繰り返しさせる機能がちゃんと用意されています。今回は数ある繰り返し処理の中で「while文」と「until文」を紹介します。

while文は条件がtrueの間ずーっとwhile文の中の処理を繰り返します。until文はその逆で条件がfalseの間ずーっとuntil文の中の処理を繰り返します。今回は「数字3桁かつ重複したものを除いた時の要素数が3つ」じゃなかったら繰り返し手札入力処理を行うとしたいので、「until文」を使います。

ちなみに最初に入力された手札が条件に一致している場合はuntil文の中に入らず次のステップへ移動します。

では先ほどの8〜18行目のif文を以下のようなuntil文に書き換えましょう。

require "io/console"

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"

  until hand.match(/\A\d{3}\z/) && hand.split("").uniq.length == 3
    print "無効な入力です。もう一度入力してください。\n入力受付中 => "
    hand = STDIN.noecho(&:gets).chop
    puts "\n\n"
  end
  hand
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める

8行目に空行を挿入し、9〜13行目にuntil文を書きました。無効な入力であることを知らせるために表示されるメッセージも変えています。14行目にも空行を入れました。

空行を入れるか入れないかは単なる可読性(読みやすいかどうか)の問題で、今回は初学者向けということでわかりやすさ重視で空けています。現場ではチームの方針によりますが、全部キチキチに詰めたりはあまりしません。

ちなみに6行目で変数handに代入されたあと、11行目でも同じ変数handに代入させていますがRubyでは同じ変数に複数回代入すると中身は上書きされます。なので1度で手札が決まった場合でも、何度もやり直しをした場合でも、常に最新の入力された値に上書きされるので15行目にhandを返すよう書くだけでどちらにも対応できる処理になります。

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (12と入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (1234と入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (123と入力)

123
プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (c87と入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (987と入力)

987
$


・2-11 複雑なロジックはこまめにメソッド分離

重複なしの数字3桁の判定ですが、少し長くて見た目的に煩わしいですね。後ほど相手の手札を当てるコールのターンでも全く同じロジックを使いますのでDRYの原則に従えば共通化させることを思いつくでしょう。ロジック自体を変えることはできないのですが、条件式をそのままメソッドに分離することで可読性も高まります。

require "io/console"

# 重複なしの数字3桁か判定する
def unique_three_digits?(num)
  num.match(/\A\d{3}\z/) && num.split("").uniq.length == 3
end

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"

  until unique_three_digits?(hand)
    print "無効な入力です。もう一度入力してください。\n入力受付中 => "
    hand = STDIN.noecho(&:gets).chop
    puts "\n\n"
  end

  hand
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める

条件式をそのままunique_three_digits?という別のメソッドに分離し、引数にhandを渡してinput_handメソッドの外で判定をさせます。

メソッドの中では引数をhandからnumに変更しています。これは後ほどコールのターンでは手札ではなくコールされた数字に対して判定を行うため汎用的な「number」の省略形としてよく用いられる「num」の方が適切と考えたためです。

メソッドの中身は1行なのでその1行の結果がそのまま返り値です。返り値は先ほどと同様trueかfalseなので、動きも全く同じです。

メソッド名に「?」が付いている理由は「メソッドの返り値がtrue, falseの真偽値の場合は名前の末尾に?をつける」という慣習があるからです。


・2-12 出力を遅らせる

手札決定最後のテーマです。出力結果を少し整形して終了です。併せて2章の最終的なコードを載せておきます。

require "io/console"

# 重複なしの数字3桁か判定する
def unique_three_digits?(hand)
  hand.match(/\A\d{3}\z/) && hand.split("").uniq.length == 3
end

# 手札を入力する
def input_hand(name)
  print "プレイヤー#{name}さん あなたの手札を好きな数字3桁で入力してください。\n入力受付中 => "
  hand = STDIN.noecho(&:gets).chop
  puts "\n\n"

  until unique_three_digits?(hand)
    print "無効な入力です。もう一度入力してください。\n入力受付中 => "
    hand = STDIN.noecho(&:gets).chop
    puts "\n\n"
  end

  puts "------------- 入力完了 -------------\n\n\n\n"
  sleep(0.5)

  hand
end

# 1.プレイヤーAが0〜9の10枚のカードの中から手札を決める
a_hand = input_hand("A")

# 2.プレイヤーBが0〜9の10枚のカードの中から手札を決める
b_hand = input_hand("B")

# 3.プレイヤーAが過去にコールしたことがあれば履歴を表示する

# 4.プレイヤーAが数字をコールする

# 5.プログラムで[EAT-BITE]を発表する

# 6.プレイヤーBが過去にコールしたことがあれば履歴を表示する

# 7.プレイヤーBが数字をコールする

# 8.プログラムで[EAT-BITE]を発表する

# 9.以降3〜8を繰り返し3EATが出たら終了する

a_hand = input_hand("A")の下にあった「puts a_hand」と、b_hand = input_hand("B")の下にあった「puts b_hand」はもう削除してしまっていいでしょう。

20行目で入力が完了したという表示と改行を入れてプレイヤーAさんのあとのプレイヤーBさんの番の時に文字がくっついてしまわないようにします。

そして21行目でsleepメソッドを使っています。sleepメソッドは引数に指定した秒数だけ次の出力を遅らせることができます。今のままだと毎回一瞬で文字が表示されてしまい少しゲーム感が薄れると感じたので、それぞれのターンごとに0.5秒間隔を空けてみました。

では最後に実行結果をみてみましょう。(sleepメソッドの遅延表示はテキストでは説明できないのでみなさんで実感してくださいね)

$ ruby number_game.rb⏎
プレイヤーAさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (12と入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (1234と入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (123と入力)

------------- 入力完了 -------------



プレイヤーBさん あなたの手札を好きな数字3桁で入力してください。
入力受付中 => ⏎ (9bdと入力)

無効な入力です。もう一度入力してください。
入力受付中 => ⏎ (987と入力)

------------- 入力完了 -------------



$


・2章 まとめ

・2-1 入力を受け付ける
・2-2 改行コード
・2-3 入力を非表示にする
・2-4 DRYの原則とメソッド分離
・2-5 メソッドによって違う値を与える
・2-6 ローカル変数のスコープ
・2-7 メソッドの外に値を反映させる
・2-8 手札を数字3桁に制限する
・2-9 重複する数字を制限する
・2-10 間違った入力の場合は手札を再入力させる
・2-11 複雑なロジックはこまめにメソッド分離
・2-12 出力を遅らせる

かなりのボリュームでしたが手札を決めることができましたね!次はコールのターンを実装します。


【第3章 コールターン】

・3-1 ターン数を保持する

Rubyにもだいぶ慣れてきたと思いますので、3章からはなるべく新しい使い方や難しい箇所以外はスピードアップして行きますね。

まずはコールターンのコールのみに注目し、早速メソッドに切り出していきましょう。手札を決めるinput_handメソッドの下に書いていきます。

ここから先は

36,495字 / 5画像

¥ 100

この記事が気に入ったらチップで応援してみませんか?