【code.org】アプリラボ入門3〜30を言ったら負けゲーム〜

code.org

このページで学べること
1 while繰り返し処理
2 appendItem 配列の末尾に追加
3 list.length 配列の要素数

アプリの設計

  • 1度に言える数字は3つ、1から順番に交互に数字を言い合い、30を言ったら負けというゲーム。
  • 自分が先攻のときの必勝法を教えてくれる。
  • 言ったら負けになる数字や、1度に言える数字の個数を自由に変えることができる。

このゲームの必勝法
 1度に言える数字が3つで、言ったら負けになる数字が30の場合、自分が29を言えば勝ちである。また、相手が数字を何個言おうと、自分と相手を合わせれば必ず「4つ(3+1)」の数字を言うようにすることができる。なので、29−4=25を言えば、相手の言う数字に関係なく29を言える。
 つまり、29から4つずつ減らした数列「29,25,21,17,13,9,5,1」を言えば勝ちということになる。

UIデザイン

 最終的なデザインはこのような感じになります。それぞれのidは次のように設定しましょう。
 1回に言える数のtext_input → text_inputNumber
 ゴールのtext_input → text_inputGoal
 必勝法を表示するラベル(「ここに数字が表示されます」の部分) → labelNumbers
 ※これら以外は初期のままでも大丈夫です。

コーディング

whileブロックによる繰り返し

 まずは30ー3(ゴールが30、言える数が3つ)で作ってみましょう。ここでは、29から4つずつ引き算した値を配列に記録していく方法でやってみます。

 ボタンのonEventの中に処理を記述していきます。
 まず数列を記録する空の配列numbersを宣言します。次にゴールとなる数字より1だけ小さい数字(つまりこれを言えば勝てる数字)を変数xに代入します。

 そして4ずつ引き算して配列numbersに格納する繰り返し処理を記述します。今回はゴールを30と設定していますから、引き算を7回繰り返すということは計算によって分かります。それを使ってforブロックによる繰り返し処理をしても良いのですが、最終的な目標は「任意のゴールと言える数字の個数について必勝法を調べること」なので、繰り返しの回数は分かりません。そんなときはwhileブロック(Controlにあります)による繰り返し処理が有効です。これは条件式が正しい場合(真の場合またはtrue)に処理を行うという繰り返し処理です。

while(条件式)
…条件式が真(true)の場合に処理を実行する。処理によって条件が変わらなければ無限ループになるので要注意。
appendItem(配列名,値)
…指定した配列の末尾に値を追加する。

 図のコードを見ると、条件式は「x > 0」となっています。つまり、変数xが0より大きい時は次の処理をしなさい、という命令になっているわけです。whileブロックの中には、「appendItemブロック(Variablesにあります)によって配列numbersに変数xの値を追加する」処理(5行目)と、「変数xの値から(3+1)を引いた数を変数xに代入する」処理(6行目)が書かれています。

 whileブロックの処理をする前、変数xは29ですから、条件式はtrue。なので配列に変数xの値を追加し、変数xに25(29-4)を代入します。次の処理でも変数xは0より大きいので、同じように処理を繰り返します。これを表に表すと次のようになります。

処理前の変数x条件式配列numbers処理後の変数x
1回目29true[29]25
2回目25true[29,25]21
3回目21true[29,25,21]17
4回目17true[29,25,21,17]13
5回目13true[29,25,21,17,13]9
6回目9true[29,25,21,17,13,9]5
7回目5true[29,25,21,17,13,9,5]1
8回目1true[29,25,21,17,13,9,5,1]-3
9回目-3false処理しない処理しない

 このように繰り返しの回数が分からない場合はwhileブロックを用いて処理するのが良いです。試しにconsole.logブロックを使って、処理後の配列の中身を確認してみましょう。

ゴールの数字を入力値にする

 ここまでくれば後は簡単。先程のコードの中で、どの部分を入力値(getNumber)にすれば良いかを考えて書き換えましょう。ゴールの数字は2行目「var x = 30 – 1」の「30」の部分ですね。ここをgetNumberブロックを使って、入力値を変数xに代入するように変更します。

1回に言える数字を入力値にする

 これも簡単。whileブロックの中の「x = x – ( 3 + 1 )」の「3」の部分を書き換えれば良いですね。ここをgetNumberブロックを使って、入力値を変数xに代入するように変更します。

 細かい話になりますが、1度に言える数は、whileブロックの外で別の変数に記録しておくほうが良いかもしれません。(ありえない話かもしれませんが)もし処理中にユーザーがtext_inputを書き換えてしまうと、書き換えた新しい値で処理が進んでしまうからです。ボタンを押した瞬間に例えば変数yに入力値を代入し、whileブロックの中ではyを使って計算するようにすれば、このようなエラーは考えられません。

完成かと思いきや…

 最後にconsole.logブロックを消して、setTextブロックに置き換えます。表示するテキストは配列をそのまま呼び出して大丈夫です。カンマ区切りの文字列として表示してくれます。

 さて、これで完成!と言いたいところですが、実はまだやらなければいけないことがあります。それは…例えば30−28でやってみてください。表示される数列は「29」のみですね。1度に言える数が28個で、最初に29を言わなければいけない。つまり「勝てない」ということです。

 そうです。数字の組み合わせによっては、勝てない場合もあるのです。そのときは「残念ですが勝てません」って教えて上げたほうが親切ですよね。さあ、やってみましょう。

勝てない時の条件

 もう一つの例として「65−7」でやってみてください。「64, 56, 48, 40, 32, 24, 16, 8」と表示されますね。1度に言える数が7で、最初に8を言うことはできませんから、これも勝てないパターンということです。つまり、「配列の最後の値が、1度に言える数より大きい場合」は勝てないということが分かります。

 そこで8行目にif-elseブロックを使って条件分岐を記述します。

 配列numbersには何個の数字が入っているかは分かりませんので、配列の最後のインデックスを数字で指定することはできません。しかしlist.lengthブロック(Variablesにあります)を使えば、配列の要素の数を取得することができます。これを使えば、どんな配列でも、最後のインデックスは「list.length – 1」と計算することができます。つまり、8行目の条件式は「配列の最後の値が、1度に言える数より大きい場合」となっているということです。

 if-elseブロックは条件式がtrueの場合に上の処理を、falseの場合に下の処理を行う条件分岐です。今回は「配列の最後の値が、1度に言える数より大きい場合」は「残念ながら勝てません」と表示し、そうでない場合(つまり勝てる場合)は、数列を表示するように分岐させています。

list.length
…配列の要素数を取得する。
if(条件式)…else…
…条件式がtrueの場合とfalseの場合の両方の処理を記述する。

完成

 これでプログラムは完成です。このアプリを使えば、ゴールの数字や1回に言える数字を変えても絶対勝てるようになります(先攻の場合のみ。ただし特殊なケースを除く)。ぜひこのアプリのことを知らない友達と対戦して、驚かせてあげましょう。

コメント