• ベストアンサー

二次元配列

質問失礼します。 二次元配列の練習中で、 nijigen = new Array(); nijigen[0] = new Array(0,0,0,0); nijigen[1] = new Array(0,0,0,0); nijigen[2] = new Array(0,0,0,0); nijigen[3] = new Array(0,0,0,0); という配列を作りました。 そして、その配列と同じ配置にされたボタンを押すと、その位置の配列の数値に「1」が代入されるというものです。 そこでですが、「配列の内容が全て1になった時」を検知出来るようにしたいのですが、どのようにすればよいのでしょうか。 よろしくお願いします。 //↓こうなった時に検知したいのです nijigen = new Array(); nijigen[0] = new Array(1,1,1,1); nijigen[1] = new Array(1,1,1,1); nijigen[2] = new Array(1,1,1,1); nijigen[3] = new Array(1,1,1,1);

質問者が選んだベストアンサー

  • ベストアンサー
  • BlurFiltan
  • ベストアンサー率91% (1611/1754)
回答No.6

#3です。 > 二次元配列にしてる理由は、「ライツアウト」を作っていて、 そのような知的なゲームを作ろうとされていたのですね。 > できれば、タイムライン上にまとめて書きたいのです。 説明するより作った方が早いですね。 しかし,半分答えみたいなことを書いてしまっても良いのですか? まさか本当に課題ではないでしょうね...?? また,考えることが楽しみなのであり勉強になるのでは??? と思いますが。 本当に, タイムライン上 の フレーム1 だけにまとめて書いた ActionScript だけで, ムービークリップなど全てを作成してしまうというものを作ってはみました。 全て AS なので,ステージ上には基本的に全く何も用意しなくて良いです。 (無駄はたくさんあると思いますが。) //////////////////////////////////////////////////////////////////// // ◎ ライツアウト(もどき?) //  ルール: 配列要素が全てが 1 で 表示が全て ● になればゲームクリア //  ActionScript 1.0 Flash Player 6(Flash MX) 以上で使用可能 //  Flash を起動させて フレーム1 にコピペ→パブリッシュするだけでOK //////////////////////////////////////////////////////////////////// // // 縦の行数(row数)を設定 (★2~10で変更可) tate = 10; // 横の列数(column数)を設定 (★2~10で変更可) yoko = 10; // マークの配列 markArr の設定 (★好きな文字に変更可) markArr = new Array("○", "●"); // // 光の配列 lightArr の初期化 /*  1,1,1,… yoko数  1,1,1,… yoko数  1,1,1,… yoko数   …  tate数  の配列を自動作成↓ */ lightArr = new Array(); for (i=0; i<=tate-1; i++) { lightArr[i] = new Array(); for (j=0; j<=yoko-1; j++) { lightArr[i][j] = 1; } } // 照合用文字列(ゴール状態)の作成 goal = "1"; for (i=0; i<=tate*yoko-2; i++) { goal += ",1"; } // // 光の配列 と マークを変更する関数 changeLight を定義 function changeLight(row, col) { // クリックされたMC自体 if (lightArr[row][col] == 0) { lightArr[row][col] = 1; _root["lightMC"+row+col]._txt.text = markArr[lightArr[row][col]]; } else { lightArr[row][col] = 0; _root["lightMC"+row+col]._txt.text = markArr[lightArr[row][col]]; } // クリックされたMCの上下のMC for (i=-1; i<=1; i += 2) { if (lightArr[row+i][col] == 0) { lightArr[row+i][col] = 1; _root["lightMC"+(row+i)+col]._txt.text = markArr[lightArr[row+i][col]]; } else if (lightArr[row+i][col] != undefined) { lightArr[row+i][col] = 0; _root["lightMC"+(row+i)+col]._txt.text = markArr[lightArr[row+i][col]]; } } // クリックされたMCの左右のMC for (i=-1; i<=1; i += 2) { if (lightArr[row][col+i] == 0) { lightArr[row][col+i] = 1; _root["lightMC"+row+(col+i)]._txt.text = markArr[lightArr[row][col+i]]; } else if (lightArr[row][col+i] != undefined) { lightArr[row][col+i] = 0; _root["lightMC"+row+(col+i)]._txt.text = markArr[lightArr[row][col+i]]; } } } // // 明滅ムービークリップ lightMC** の作成(本来は手動で作成) for (i=0; i<=tate-1; i++) { for (j=0; j<=yoko-1; j++) { _root.createEmptyMovieClip("lightMC"+i+j, i*10+j); my_mc = _root["lightMC"+i+j]; my_mc._y = i*20+10; my_mc._x = j*20+10; my_mc.createTextField("_txt", 0, 0, 0, 20, 20); my_mc._txt.border = true; my_mc._txt.background = true; my_mc._txt.text = markArr[lightArr[i][j]]; my_mc.i = i; my_mc.j = j; my_mc.onRelease = function() { // 上で定義した関数 changeLight を実行 changeLight(this.i, this.j); // 下で定義する関数 judgeLight を実行 judgeLight(); }; } } // // 問題(初期状態)の作成 for (m=0; m<=tate-1; m++) { for (n=0; n<=yoko-1; n++) { if (Math.floor(Math.random()*2)) { // 上で定義した関数 changeLight を実行 changeLight(m, n); // ★解答の用意(私が解けないので検証用に勝手に用意しました) //   水色の部分を全て1回ずつクリックするとクリアできます //   本番では消してください↓ (他で利用できるかも) _root["lightMC"+m+n]._txt.backgroundColor = 0xCCFFFF; } } } // // 全て goal 状態になったかを判定する関数 judgeLight の定義 function judgeLight() { if (lightArr.toString() == goal) { _root.createTextField("goal_txt", 100, 10, tate*20+10, 80, 20); _root.goal_txt.text = "おめでとう!"; for (i=0; i<=tate-1; i++) { for (j=0; j<=yoko-1; j++) { _root["lightMC"+i+j]._txt.backgroundColor = 0xFF0000; delete _root["lightMC"+i+j].onRelease; } } } } //////////////////////////////////////////////////////////////////// // 問題(初期状態)の作成 の部分に関してですが。 とりあえず配列の要素は全て 1 にしておいて, 5×5=25個のムービークリップを総当たりでアクセスし, 適当に 1 か 0 かの乱数を発生させて, 1 であればそのムービークリップをプログラムで1回クリックするという方式をとっています。 したがって,決して 「クリア不可能問題」 は作成されないはずです。 しかし, 本当に適当に 1 か 0 かの乱数を発生させているだけなので, 例えば 5×5の場合,2の25乗=33554432 回に1回くらいは, 「最初から全て 1 (最初からゲームクリア)な問題」 が作成される可能性があります。 また,問題作成には色々な考え方も出てくると思いますから,この辺をどうするのかは考えてみてください。 上のプログラムコード自体は数十分ほどでできたのですが, 悲しいことに,ゲームが難しくて検証ができない(何時間やってもゲームがクリアできない)ので,  // ★解答の用意(私が解けないので検証用に勝手に用意しました)  //   水色の部分を全て1回ずつクリックするとクリアできます  //   本番では消してください↓ (他で利用できるかも)  _root["lightMC"+i+j]._txt.backgroundColor = 0xCCFFFF; という解答カンニング部分を勝手に付け加えました。 困ったことに,私はカンニングしないとぜんぜん解けません。 ライツアウトというゲーム初体験ですが難しいですね...。 ゲームのプログラムを作成するより,ゲームを攻略する方がはるかに難しいですよ (ノ_・、) 。 MC もしくは ボタン というインスタンスは作成すると思うので, 配列を用意する必要もとくにないと言えばないのですが, 他で配列の演算を使うのかもしれないと思い,配列と連動した動きのものにしてみました。 1行1行なにをしているのかというコメント行は入れていません。 スクリプト(語)が不明な場合は,調べてみてください。 ※ PS #4の方。 なるほど! 参考になりました。

noname#77303
質問者

お礼

詳しい回答ありがとうございます。 なるほど、文面だけでは複雑に見えるので「う~ん」と唸ってしまったのですが、実際に動作させると意味が分かってきました。 >例えば 5×5の場合,2の25乗=33554432 回に1回くらいは, 「最初から全て 1 (最初からゲームクリア)な問題」 が作成される可能性があります。 コレに関しては、大して気にする事でもないので放置するつもりですが、公開後に苦情があったら対処したいと思います。 大変参考になりました。課題ではないので教えていただいて助かりました。 ありがとうございました。

その他の回答 (5)

  • taku2888
  • ベストアンサー率60% (15/25)
回答No.5

アルゴリズムは、いろんな方法が出てくるので楽しくて良いですね。 is_mayさんは、具体的に表現頂きわかりやすいです。 でも下記「◆◆」が無いと途中で0があっても最後までloopする可能性が残っているのでは。 全然たいしたことではないのに申し訳ありません。 kenshutu=true; for(var i=0;i<4;i++){  for(var t=0;t<4;t++){   if(!nijigen[i][t]){ //一つでも「0」があれば、    kenshutu=false; //「kenshutu」変数を負にして、    break; //forを抜ける。   }  }  if (kenshutu) break; //←◆◆これが無いと1発目が0の時とか抜けられません。 } ボタン一つづつに条件が入れられるのであれば「suzuki-_-さん」に1票です。 でも、わざわざ2次元配列にしてるのですから何らかの別処理があるのでしょうか。 そうすると別変数を持つのは面倒かもですね。 いっそのこと、配列をやめてビット処理にすれば ffffなら4×4の配列にも見立てできるし、どこのボタンかも判断できる。 そして0xffffなら、全て1でloopもいらないし。 好き勝手言って済みません。 プログラムは十人十色、がんばってください。

noname#77303
質問者

お礼

二次元配列にしてる理由は、「ライツアウト」を作っていて、どこが点灯しているか、どこが消灯しているかを分かりやすく把握するためです。あと、プログラミング自体に関しても初心者なので、知識不足です。 できれば、タイムライン上にまとめて書きたいのです。 ライツアウトとは http://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%83%84%E3%82%A2%E3%82%A6%E3%83%88 回答ありがとうございました。

  • suzuki-_-
  • ベストアンサー率77% (152/195)
回答No.4

#3の方が挙げられているjoin()の配列連結による区切りの問題点について、 全て1だった際に、nijigen.join() は 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 となり、一見どこで二次元配列が区切られていたのかわからないですが、 これは基本的に配列を ,(カンマ)区切りでString化されるので起こってしまうもので、 下記の形で見るとわかりやすいと思いますが、 (1,1,1,1),(1,1,1,1),(1,1,1,1),(1,1,1,1) 今回の nijigen.join() で連結させたのは括弧で挟まれていないカンマの部分です join()は引数でセパレータを記述することにより、 連結時の区切りを指定することができます なので、区切りをカンマ以外にすることで、#3の方が仰っている、 配列の長さ等による区別の付かない問題点を解決することができます 例: Ar1 = [[0, 1, 2], [3, 4, 5]]; Ar2 = [[0, 1], [2, 3, 4, 5]]; trace(Ar1.join() + " == " + Ar2.join() + " / " + (Ar1.join() == Ar2.join())); trace(Ar1.join("+") + " == " + Ar2.join("+") + " / " + (Ar1.join("+") == Ar2.join("+"))); 今回の内容で言えば、「↓こうなった時に検知したいのです」と 記述されている二次元配列を別の配列で用意(例 kounatta) kounatta = new Array(); kounatta[0] = new Array(1, 1, 1, 1); kounatta[1] = new Array(1, 1, 1, 1); kounatta[2] = new Array(1, 1, 1, 1); kounatta[3] = new Array(1, 1, 1, 1); そして、条件判別は if(nijigen.join("+") == kounatta.join("+")){ //nijigenとkounattaが同じ内容になっている時はここを通るよ } のようにすると、視覚的にもわかりやすいかなと思います (今回に限っては配列の長さが一定なので"+"の効果は得られませんが・・・) 他には前途二次元配列同士を直で if(nijigen eq kounatta){~} なんて方法もあります ただ 区切りの判別が付かない問題点は有りで、 更に eq の使用はFlashPlayer5以降は非推奨なので、 こんなのもあるんですよ程度で。 他にわかりやすい方法を挙げると、今回はボタンが16個なので、 「配列の内容が全て1になった時」ではなく、 「カウントが16になったら」という考え方もあります nijigenの定義と一緒に、 clickCnt = 0;//カウント clickMax = 16;//カウント最大数 のように変数を用意してあげて、ボタン押下時のアクションは if(nijigen[自分の担当][自分の担当] != 1){ //自分の担当部分が1じゃなかった時はここを通る nijigen[自分の担当][自分の担当] = 1;//自分の担当部分を1にして clickCnt++;//カウントを1プラスする } あとはカウント最大数に達しているかどうか調べることで、 if(clickCnt == clickMax){ //ここを通る時は全て押されてる時だよ } 「配列の内容が全て1になった時」の検知と同じ目的を果たせます nijigenをカウントアップの判別用フラグとして使用するという感じです ちなみにこれは別に2次元配列ではなくてもいいですね 個別のアクション時にできるだけ処理を軽くするための一例です

noname#77303
質問者

お礼

カウントアップの方法もあったのですね。 ボタンを離した時には1を減算すれば元に戻るでしょうし、一人で考えるよりも相談したほうが、いろいろな事が見つかるものですね。 回答ありがとうございました。

  • BlurFiltan
  • ベストアンサー率91% (1611/1754)
回答No.3

面白い(興味をそそられる)ご質問ですね。 スクールなどの課題などで出題されると良い問題だと思います。 私もまず, #1の方の書かれていらしゃる方法を思いつきました。 #1の方の方法とは,つまり,  「for文 か while 文などで,   if文 か case文 判定を総当たりで連続実行して,   1以外のものが発見できたら brake させれば良い。」 という意味のことだと思います。 しかし..., もちょっと楽な方法はないかと思案してみましたら, なんとか,「一刀両断バサッ!」 的な方法を考えつきました。 こんな方法はいかがでしょうか↓。 --------------------------------- nijigen = new Array(); nijigen[0] = new Array(1, 1, 1, 1); nijigen[1] = new Array(1, 1, 1, 1); nijigen[2] = new Array(1, 1, 1, 1); nijigen[3] = new Array(1, 1, 1, 1); // 照合用文字列を用意 mojiretsu = "1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1"; // if文 照合判定 if (nijigen.join() == mojiretsu) { trace("nijigen の要素は全て1"); } --------------------------------- join() は 配列のエレメントを全て 「,」 で区切って結合します。 この方法だと,1回のif文使用で判定ができます。 また,toString() を使って, if (nijigen.toString() == mojiretsu) { trace("nijigen の要素は全て1"); } のようにしても良いと思います。 if文の等価演算子は,2つある別の配列の等価を判定しません。 case文でも同じです。 したがって, 1,1,1,… という文字列をあらかじめ用意しておいて, その文字列と,一度文字列に変換した配列とが,等価であるかどうか判定させています。 またこの方法だと,「全て1」とかいう限定条件でなく, 配列が ,  1,2,3,4  3,4,5,6  9,8,7,6  2,4,6,8 になっているかどうか? というような複雑な判定も簡単にできます。 ただし, この方法のままでは,  1,2,3,4  3,4,5,6  9,8,7,6  2,4,6,8 と  1,2,3,4,3,4,5,6  9,8,7,6,2,4,6,8 などとの区別ができません。これが問題点です。 しかし,「すべて1」くらいの判定には使えると思います。 === 別案1 ============= ◎ concat() を使う Array.concat() は,パラメータで指定された複数の配列連結して,新しい配列を作成します。 あと Array.sort() も使います。 --------------------------------- nijigen = new Array(); nijigen[0] = new Array(1, 1, 1, 1); nijigen[1] = new Array(1, 1, 1, 1); nijigen[2] = new Array(1, 1, 1, 1); nijigen[3] = new Array(1, 1, 1, 1); // 配列を結合して 一次元配列 ichijigen を作成 ichijigen = nijigen[0].concat(nijigen[1], nijigen[2], nijigen[3]); // 配列 ichijigen を昇り順でソート ichijigen.sort(); // ichijigen の最初の要素と最後の要素がともに1であれば if (ichijigen[0] == 1 && ichijigen[ichijigen.length-1] == 1) { trace("nijigen の要素は全て1"); } --------------------------------- 二次元配列を一次元配列にしてしまって, 中身のエレメントを昇り順にソートしています。 最後の要素も1で,最初の要素も1であれば,全ての要素は1になります。 つまり,最大値も最小値も1なのであれば,全てが1じゃないか。 ということです。 本当は Array.sort() ではm数値は文字列としてソートされます。 したがって, // 配列 ichijigen を昇り順でソート ichijigen.sort(); で本当の数値の大小関係で並ばない場合もありますが, 最初と最後が 1 だったら他も 1 でしょう。 したがって, これも「すべて1」くらいの判定には使えると思います。 「すべて1」くらいの判定に使うのではなく, もっとちゃんとソートしたい場合は次のURLが参考になるかもしれません。 GAC「テキストの最後尾にフォーカスしたい」 http://www.gac.jp/article/index.php?stats=question&category=7&id=6593&command=msg === 別案2? ============= ◎ Math.max() と Math.min() を使う??? AS2 以下では, Math.max() と Math.min() の ( ) 内の引数は2つです。 したがって次のようにしか使えません。 Math.max(a, b); これでは,a と b のうち大きい方の数しか得ることができません。 しかし,AS3 では, Math.max() と Math.min() の ( ) 内の引数に,3つ以上の数を入れられるようになったらしいです。 Math.max(a, b, c, d,…); これで,a , b , c , d ,… のうち一番大きな数を得ることができるそうです。 AS2 では, Math.max() と Math.min() を使って簡単に判定させるには, ----------------------------------- // エレメント2つの一次元配列を作成 var ichijigen:Array = new Array(1, 1); // 配列エレメント中の最大値を a に代入 var a:Number = Math.max.apply(null, ichijigen); // 配列エレメント中の最小値を b に代入 var b:Number = Math.min.apply(null, ichijigen); // a , b ともに 1 であれば if (a == 1 && b == 1) { trace("ichijigen の要素は全て1"); } ----------------------------------- と,これくらいのことしかできません。 しかし AS3 では,同じ "ような" 方法で,二次元配列の最高値と最低値も取れるのではないの??? と思います(未検証です)。 こういう方法もアリかもしれないという別案でした。 =================== 良いですね。 Flash を動かす前の純粋ASという感じのご質問で, 「他にも簡単な方法はないものか…!?」 みたいな感じで,ちょっと思考に燃えてしましました。

noname#77303
質問者

お礼

この1の方法は非常によさそうですね。 joinの存在は知っていたのですが、やはりひらめき力の違いでしょうね^^; 「すべてが1」と言うのはそれほど複雑な条件では無いので、おそらく使えると思います。 一次元配列にしてからsort()を使う方法、Math.max()とMath.min()の方法、は「最小が1、最大が1」のとき、内容は全て1。という考え方なのですね。 僕自身も初心者ながら考えさせてもらった回答でした。 ありがとうございました。

  • is_may
  • ベストアンサー率65% (58/89)
回答No.2

配列を一つ展開するとその中にまた配列があるので2重のforになりますね。 kenshutu=true; for(var i=0;i<4;i++){ for(var t=0;t<4;t++){ if(!nijigen[i][t]){ //一つでも「0」があれば、 kenshutu=false; //「kenshutu」変数を負にして、 break; //forを抜ける。 } } } 二次元配列なので二重forになるのは仕方ないと思います。 パフォーマンスですが、単純なif評価なのでほとんど無に等しいでしょう。 少し前にfor回数が500回、その中の処理が入れ子ifが8回ぐらいあるFlashを作りましたが、立て続けに行っても毎回瞬時に処理されました。 ついでに二次元のように「new Array();」を何回も書くのが面倒な場合は以下のように宣言することも出来ます。スクリプトの自動フォーマットをすると改行がなくなっちゃいますけどね^^; nijigen = [ [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0] ];

noname#77303
質問者

お礼

回答ありがとうございます。 実際にサンプルまで書いていただき、ありがとうございます。 AS自体、初めたばかりなので経験を積んで行きたいと思います。 大変参考になりました。

  • taku2888
  • ベストアンサー率60% (15/25)
回答No.1

やはり、LOOP回すしかないでしょうか。 もし、関数があったとしても結局中身は回してるはず。 ボタンが配置できる数以内で、 押された時だけチェックすればよいのだから パフォーマンスも目に見えるほど落ちないでしょう。 LOOP中、1以外なら抜ければよいので、もっと落ちないと思いますが。

noname#77303
質問者

お礼

回答ありがとうございます。 二次元配列の内容のチェック、ということでforなりwhileなり、の形でループさせる事は想定していました。 ボタンは16個だけなので、パフォーマンスの心配は無いようです。 参考になりました。

関連するQ&A