- 締切済み
1つの変数と2つの乱数を比較する
Flash Lite1.1で三択クイズの勉強をしています。 3つの選択肢のうち、1つは答え、あとの2つはランダムで表示させたい のですが、スクリプトを書いていて疑問が出てきました。 例えば、変数kotaeが現在の答え、r1、r2が残りの選択肢とすると r1=random(10)+1; r2=random(10)+1; if(r1 == kotae || r2 == kotae || r1 == r2){ r1=random(10)+1; r2=random(10)+1;} とするとr1、r2が同じ数字になってしまう可能性が出てきます。 r1、r2が「現在の答えではなく、且つr1とr2は等しくならない」 乱数の発生というのは、どのようなスクリプトになるのでしょうか? お詳しい方、どうぞご教授願えればと存じます。
- みんなの回答 (3)
- 専門家の回答
みんなの回答
- DPE
- ベストアンサー率85% (666/776)
乱数の発生の話ではありませんが。 いくつかの候補の中から「正答1つと、誤答を2つ重複せずにランダムに選ぶ」のでしたら、複雑な条件の乱数の発生の仕組みを考えるより、発想を変えて、選択肢のデータの持ち方を工夫してはいかがでしょう。 仮に、正答1つと誤答9つの計 10 個の選択肢の候補があり、これらを a0 ~ a9 という変数で管理するとします。 正答を必ず a0 に入れると決めると、残る a1 ~ a9 は全て誤答の候補になります。 ランダムに選択したいのは誤答の候補だけですから、正答が入っている a0 は触らず、誤答の a1 ~ a9 だけをシャッフルして値をランダムに並べかえます。 こうして並べかえたものの中から先頭の3つを取り出せば、正答である a0 と、重複しない誤答をランダムに2つ取り出すことができます。 しかし、a0 ~ a2 の選択肢をただ順番に表示すると、正解が常に最初の項目に来ることになり、これではクイズになりません。 そこで、取り出した3つの選択肢もシャッフルし、ランダムな並び方で表示するようにします。 プレイヤーに提示する選択肢の並び方を決める処理を工夫すると、クイズの正誤判定も作りやすくなります。 ------------------------------------------------------------- スクリプトの一例です。 このスクリプトは、タイムラインのフレームに設定してください。 (↓各行頭に全角のスペースが入っています。コピーする際は、全て半角のスペースかタブに置き換えてください。このまま使うとシンタックスエラーになります) /*********************************************************/ ///////////////////////////////////////////////////// //初期設定 ///////////////////////////////////////////////////// //選択肢の候補:正答を0番に入れておく a0 = "富士山"; a1 = "槍ヶ岳"; a2 = "普賢岳"; a3 = "立山"; a4 = "月山"; a5 = "妙義山"; a6 = "乗鞍岳"; a7 = "岡山"; a8 = "富山"; a9 = "和歌山"; //誤答の候補の総数 wrong_max = 9; //表示される選択肢の中での正答の番号(1~3)を保持 //初期値はダミー answer = 1; //表示する選択肢(a0~a2)の表示順序を保持 //初期値はダミー index1 = 0; index2 = 1; index3 = 2; //表示する選択肢の並び順をシャッフルする回数 shuffle = 10; ///////////////////////////////////////////////////// //誤答の候補をシャッフル ///////////////////////////////////////////////////// //正答のa0を除くa1~a9の内容をシャッフル for( i = 1 ; i < wrong_max ; i++ ) { //i番目以降の要素(i番目も含む)の1つをランダムに選ぶ rnd1 = random( wrong_max + 1 - i ) + i; //i番目の要素と選んだ要素の値を取りかえる temp = eval( "a" add i ); eval( "a" add i ) = eval( "a" add rnd1 ); eval( "a" add rnd1 ) = temp; } ///////////////////////////////////////////////////// //選択肢の表示順を決定し、正解のある項目を調べる ///////////////////////////////////////////////////// //選択肢の並び順をシャッフル for( i = 0 ; i < shuffle ; i++ ) { //index1~3の中から2つの要素をランダムに選ぶ rnd1 = random( 3 ) + 1; rnd2 = random( 3 ) + 1; //選んだ2つの要素の値を取りかえる temp = eval( "index" add rnd1 ); eval( "index" add rnd1 ) = eval( "index" add rnd2 ); eval( "index" add rnd2 ) = temp; } //正答が入っている項目(=0が入っている変数indexの番号)を調べる //調べた番号は変数answerに入る for( i = 1 ; eval( "index" add i ) <> 0 ; i++ , answer = i ) ; ///////////////////////////////////////////////////// //デバッグ:問題文と選択肢・正解の表示 ///////////////////////////////////////////////////// //問題文 q = "日本で一番高い山は?" //選択肢の表示データを作る disp = ""; for( i = 1 ; i <= 3 ; i++ ) { //項目の順序と、表示する選択肢を取得 a_index = eval( "index" add i ); item = eval( "a" add a_index ); //項目の番号と選択肢の本文を連結 disp = disp add " " add i add " : " add item add "\n"; } //問題文と選択肢を連結 disp = q add "\n\n" add disp add "\n"; //正答を連結 disp = disp add " 答え: " add answer; /*********************************************************/ ダイナミックテキストのテキストフィールド(行タイプは”複数行”にします)を作り、「変数:」の項目に” disp ”と記述して動作を確認してみてください。 日本で一番高い山は? 1: 槍ヶ岳 2: 富士山 3: 妙義山 答え: 2 というように表示されます。 正答である”富士山”は必ず選択肢に出てきますが、残る2つの誤答はランダムに選出されます。 また、上記の例では”富士山”が2番になっており、この例での正解は2番ということになりますが、必ず2番目に”富士山”が来るとは限りません。1番や3番になることもあります。 ”富士山”が何番目に表示されていても、「答え:」には”富士山”が表示されている項目の番号が表示されます。 「答え:」で表示している値である、プレイヤーに提示される選択肢の中での正解の番号は、変数 answer の中に入っています。 つまり、変数 answer の値とプレイヤーが入力した番号を照合することで、クイズの正解・不正解を判断することができます。 ------------------------------------------------------------- スクリプトの主な構成です。 まず、変数 a0 ~ a9 の中に 10 個の選択肢の候補を用意します。 正答を a0 に入れ、誤答が入っている a1 ~ a9 だけをシャッフルして内容をランダムに並べ替えます。 シャッフルには様々な方法がありますが、a1 ~ a9 を並べ替える時は「n番目と、n番目以降の要素のいずれか1つを取りかえる」方法を使っています。 これは、 a1と、a1 ~ a9 のいずれかを取りかえる a2と、a2 ~ a9 のいずれかを取りかえる a3と、a3 ~ a9 のいずれかを取りかえる : という作業を繰り返してランダムな並びを作る方法です。 最後の a9 は取りかえる相手がいないので、作業は a9 の1つ手前の a8 まで行います。 ******************************* こうしてできた並びの選択肢の候補の中から先頭の3つ( a0 ~ a2 )を取り出し、これらを更にシャッフルしてランダムな並び方で表示します。 正答が入っている a0 は先のシャッフルで移動していませんから、a0 は必ず選択肢に含まれるようになります。 a0 ~ a2 を直接シャッフルすると、a0 の内容が a1 や a2 に移動してしまい、a0 が必ずしも正答とは言えなくなる可能性が出てきます。 そこで、0 ~ 2 を入れた別の変数 index1 ~ index3 を用意し、こちらで選択肢を表示する順序を決めて、この番号を見ながら実際に提示する選択肢である a0 ~ a2 を表示していくことにします。 例えば、index1 ~ index3 の値をシャッフルした結果、 index1 → 2 index2 → 0 index3 → 1 と決まったとすると、実際の選択肢は a2 → a0 → a1 の順に表示されます。 なお、index1 ~ index3 の値をシャッフルする方法は「2つの要素をランダムに選び、お互いに交換する。これを数回繰り返す」方法を使っています。 シャッフルの回数は、変数 shuffle の値を書き換えると変更できます。 ******************************* index1 ~ index3 のシャッフルは a0 ~ a2 の値を見ていく順番を決めるだけで、a0 ~ a2 の値は何も変更しません。 ですから、正答はやはり a0 に入っています。 それから、変数 index1 ~ index3 の”index ”の後ろに付いている番号は、画面上での選択肢の番号、つまりプレイヤーが選択する番号に対応します。 合わせますと、画面に表示された3つの選択肢の中での正解とは、a0 ~ a2 の表示順番を決めた index1 ~ index3 のうち、0 が入っているものが何番であるかを調べることで分かると言えます。 上記のスクリプトでは、for 文の「 ( ) の2番目に記述されている条件が成立している間だけループが継続される」特徴を利用して調べ、最終的に変数 answer に正解の項目の番号が入ります。 クイズでは、プレイヤーが選んだものが正解かどうかを判別する処理も必要です。 複数ある選択肢の候補の中から正答と誤答を選んで提示する方法だけでなく、こちらの処理も合わせて考慮しながら、システムを設計してみてください。
- BlurFiltan
- ベストアンサー率91% (1611/1754)
方法を2例ほど回答します。 ---------------------------------------------- // 【例】kotae の値を仮に 5 だとします kotae = 5; // カウント用の変数 cnt の初期化 cnt = 1; // 変数 i に 1~10 で 1 を加算しながらループ for (i=1; i<=10; i++) { // もし i が kotae 以外の時 【例】i が 5 以外の時 if (i != kotae) { // 変数 rnd1~rnd9 に i の値を代入 eval("rnd" add cnt) = i; // 変数 cnt に 1 を加算 cnt++; } } //【例】↑rnd1=1, …,rnd4=4, rnd5=6, …, rnd9=10 がでます // 変数 i に 9~1 で 1 を減算しながらループ for (i=9; i>=1; i--) { // 変数 tmp_n に変数 rnd+i の値をキープ tmp_n = eval("rnd" add i); // 変数 tmp_n に 1~i の間の乱数を取得 tmp_r = random(i-1)+1; // 変数 rnd+i に変数 rnd+tmp_r の値を代入 eval("rnd" add i) = eval("rnd" add tmp_r); // 変数 rnd+tmp_r にキープしておいた値を代入 eval("rnd" add tmp_r) = tmp_n; } //【例】↑ここまででrnd1=4, rnd2=9, rnd=3, …, rnd9=2 // のように rnd1~9 にランダムシャッフルされた // 5以外のランダムな数が代入されます // 変数 r1 と r2 にシャッフルされた rnd1 と rnd2 の値を代入 r1 = rnd1; r2 = rnd2; // ちなみに検証まで trace(r1 add "," add r2); ---------------------------------------------- 上のActionScriptですが, アルゴリズムは次のページのものをそのまま使用させていただきました。 「FN0212003 - 配列を偏りなくランダムに並替える」 http://www.fumiononaka.com/TechNotes/Flash/FN0212003.html コードの書き方などはまるで違いますが, 考え方は同じですので参考になると思います。 同じActionScriptでも,FlashLite1.1対応 と Flash 5 以上とでは,他言語に近い状態ですね。 /-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/- 別法です。 --------------------------------------- // 【例】kotae の値を仮に 5 だとします kotae = 5; // カウント用の変数 cnt の初期化 cnt = 1; // 変数 i に 1~10 の間で 1 を加算しながらループ for (i=1; i<=10; i++) { // もし i が kotae 以外の時 if (i != kotae) { // 変数 rnd1~rnd9 に i の値を代入 eval("rnd" add cnt) = i; // 変数 cnt に 1 を加算 cnt++; } } // 変数 r1 に rnd? の値を代入 r1 = eval("rnd" add (random(9)+1)); // カウント用の変数 cnt の再初期化 cnt = 1; // 変数 i に 1~10 の間で 1 を加算しながらループ for (i=1; i<=10; i++) { // もし i が kotae 以外で i が r1 以外の時 if (i != kotae && i != r1) { // 変数 rnd1~rnd8 に i の値を代入 eval("rnd" add cnt) = i; // 変数 cnt に 1 を加算 cnt++; } } // 変数 r2 に rnd? の値を代入 r2 = eval("rnd" add (random(8)+1)); // ちなみに検証まで trace(r1 add "," add r2); --------------------------------------- ↓これを短く書きなおします↓ --------------------------------------- // 【例】kotae の値を仮に 5 だとします kotae = 5; // 変数 r1 にダミーの値 0 を代入しておく r1 = 0; // 変数 i(注:アイ) に 1~2 の間で 1 を加算しながらループ for (i=1; i<=2; i++) { // カウント用の変数 cnt の初期化 cnt = 1; // 変数 j(注:ジェイ) に 1~10 の間で 1 を加算しながらループ for (j=1; j<=10; j++) { // もし j が kotae 以外で j が r1 以外の時 if (j != kotae && j != r1) { // 変数 rnd1~rnd9 に j の値を代入 eval("rnd" add cnt) = j; // 変数 cnt に 1 を加算 cnt++; } } // 変数 r1 か r2 に rnd? の値を代入 eval("r" add i) = eval("rnd" add (random(10-i)+1)); } // ちなみに検証まで trace(r1 add "," add r2); --------------------------------------- こちらは, Flash 5 以上の Array(配列) クラスの splice メソッド を使って 配列の中身を1つずつ抜いていくという方法を FlashLite1.1 に当てはめてみたものです。 Array.splice演算の自作バージョン ということになります。 考え方をイメージで言うと, 最初に書いた方法例が 「トランプのランダムシャッフル」 的な考え方で, こちら方法例が 「ダルマ落とし」 的な考え方だと言えます。
- 15mm
- ベストアンサー率65% (65/100)
ifをwhileに変えるだけでできますよ。 条件式がtrueである間、ですのでうまくいくまで乱数を生成し続けます。 あまり選択肢数(例では10)が少ないとループ回数が増えて処理速度が落ちかねませんが・・・