アップル、「iPhone」アプリ向けNDAを緩和--開発者の不満に対応

ということで、チンパンジーゲームは iPhone用としてダイレクトに作成決定~!
いや~、めでたい。やっとiPhoneプロジェクト公開できるね。
みんな、次回までにiPhone Developerに登録してSDKをゲットだぜ。いや、まじで、これダウンロードしてくれんことには心が通じ合えん。

詳しくはこれ→iPhone買ったMacユーザーなら

それでは前回の続き。

ランダムな座標の決定方法
 例えば、縦と横、別々に乱数を割り当てていく方法が一番素直なわけだけど、偶然同じ座標になる可能性を確認するのに、縦と横の2つを比較しなければならないところがいまいちなわけですよ。
 そこで二次元座標を、下の図のように折り返された一次元座標と考え、

Fig.1

 出てきた一次元座標を横のマス目数で割った商を縦のマス目、余りを横のマス目と考えることで2次元座標に変換するようにする。

Fig.2

こうすれば、ランダム座標作成自体は単純に同じ数字が重ならないようにするだけでよくなるわけだ。

 例えば 3 x 3マスのうち3つのランダムな座標を作るなら以下のようになる。

 static int H_BLOCK_COUNT = 3; // 横3マス
 static int V_BLOCK_COUNT = 3; // 縦3マス
 // こうやって変数にしておけば、後から簡単にマス目の数を変更できる。

 int x[3]; // 求める3つの座標
 for (int i = 0; i < 3; i++) { // 3つ埋まるまでループ
  retry:
  x[i] = rand() % (H_BLOCK_COUNT * V_BLOCK_COUNT);
  // すでに設定された iの1つ前までの値と重なっていないかチェック
  for (int j = 0; j < (i - 1); j++) { 
   if (x[i] == x[j]) {
    goto retry; // 重なっていたのでやり直し retryと書かれた位置にジャンプ
   }
  }
 }


ちなみにgotoは使うなとか言う人いるけど、私はこんな場合バンバン使う。

 でも、これだと、3つくらいならいいけど、3 x 3マス全部とか、1000 x 1000マス全部とかになると、最後に行けば行くほど比較ループが大変になるし、乱数だけに、へたしたら永遠にループから抜け出せなくなるかもしれない。

 で、またまた発想を変えて、乱数は用意された座標配列のインディックスに利用するという方法を考えてみる。

 int leftCount = H_BLOCK_COUNT * V_BLOCK_COUNT; // 全マス目数
 // 全マス目の座標を入れる配列を作成
 int* loc = new int[leftCount];
 for (int i = 0; i < leftCount; i++) { // 全マス目座標をすべて用意。
  loc[i] = i;
 }
 int x[3]; // 求める3つの座標
 for (int i = 0; i < 3; i++) { // 3つ埋まるまでループ
  int index = rand() % leftCount;
  x[i] = loc[index];
  loc[index] = loc[leftCount - 1];
  leftCount--;
 }
 delete[] loc; // 用が済んだ全マス目座標配列を破棄


絵で描くとこんな感じ。
Fig.3
あとはできた一次元ランダム座標を二次元座標に変換して返せばいい。

 for (int i = 0; i < 3; i++) { // 3つ変換
  locations[i].h = x[i] % H_BLOCK_COUNT; // 横マス数で割った余りが縦座標
  locations[i].v = x[i] / H_BLOCK_COUNT; // 横マス数で割った商が縦座標
 }

実際のstartGameでは引数countOfNumbersを使い以下のようになる。こまかい説明は次回。
ではでは。

@implementation monkey

static int H_BLOCK_COUNT = 3;
static int V_BLOCK_COUNT = 3;

-(void)startGame:(int)countOfNumbers:(SNumberLocation*)locations
{
  int leftCount = H_BLOCK_COUNT * V_BLOCK_COUNT;  //  全マス目数
  //  全マス目の座標を入れる配列を作成
  int* loc = (int*)malloc(sizeof(int) * leftCount);
  for (int i = 0; i < leftCount; i++) {  //  全マス目座標をすべて用意。
    loc[i] = i;
  }
  int* x = (int*)malloc(sizeof(int) * countOfNumbers); // 求めるcountOfNumbersの座標配列を確保
  for (int i = 0; i < countOfNumbers; i++) {  //  countOfNumbers分埋まるまでループ
    int index = rand() % leftCount;
    x[i] = loc[index];
    loc[index] = loc[leftCount - 1];
    leftCount--;
  }
  free(loc);  //  用が済んだ全マス目座標配列を破棄
  for (int i = 0; i < countOfNumbers; i++) {  //  3つ変換
    locations[i].h = x[i] % H_BLOCK_COUNT;  //  横マス数で割った余りが縦座標
    locations[i].v = x[i] / H_BLOCK_COUNT;  //  横マス数で割った商が縦座標
  }
  free(x);  //  用が済んだランダム一次元マス目座標配列を破棄
  self->locations = locations;
  curtNumber = 0;
}

-(BOOL)judge:(SNumberLocation)location
{
  if (locations[curtNumber].h != location.h)
    return FALSE;
  if (locations[curtNumber].v != location.v)
    return FALSE;
  curtNumber++;
  return TRUE;
}
@end