乱反射の光跡 in hatenablog

なみへいのブログです。hatenablogヴァージョン。

制作ノート その16

NNCardDeck version0.4(5)


いつまでやっているんでしょうか、ひとつのクラスの設計を(笑)。……自分でも早く次へ取りかかりたい、と思いつつ(笑)。
できれば先送りできる問題は先送りして(笑)、とりあえず「BlackJack version0.4」を完成させたいとは思っているのですが。……いやいや、これでも未完成だが(笑)。とにかく、問題なく動くレベルまでは早くもって行きたい、と。
さっさと片づけてしまいましょう。
次はカード配列を作るメソッド。


さて、内部で保持するカード用の値の配列が整理できた所で、今度はカードオブジェクトを保持するNSMutableArray *cardArrayに、カードオブジェクトを初期化して埋め込んで行くメソッド。
これ自体はヴァージョン0.3でもやっていたことで、見かけ上は複雑ですが、中身は単純作業をカードの枚数だけ繰り返すだけ。

ただし、今回はNNCardDeckオブジェクトにカードがもつ値を保持しています。ゲーム中にカードの固有値を変える必要がないのなら、この内部定数でカードオブジェクトを作り直すことが可能。
つまり、いちいち初期化しないで、内部定数でカードオブジェクトの生成が可能です。
という訳で、初期化メソッドに加えて、同じNNCardDeckオブジェクトを保持したままカード配列を作り直すメソッドを追加することに。

具体的には、メモリーの確保からカードオブジェクトの埋め込みまで行なう初期化メソッドとは別に、現在のオブジェクトにカード配列だけを作り直すメソッドを加えます。
両方ともカード配列を作る部分は一緒で、初期化メソッド(プリミティブ・メソッド)の場合は定数配列を初期化してそこからカード配列を作るのに対し、「作り直し」メソッドではすでに作られた定数配列を利用します。
これは内部のカード配列carrdArrayから全てのカードオブジェクトを取り除き、新たに生成したカードを埋め込むことになります。


で、この場合カードオブジェクトの生成は両方で共通のコードになりますので、この部分を別のメソッドとして用意します。
ヴァージョン0.3とは違い、今回は53枚それぞれに個別のデータを埋め込みます。ある程度の汎用性を持たせるなら、それぞれのカードに独自の値を振り分けられるようにするためです。そのために、1セット53枚ごとにリセットされるvalueIndex変数を用意してあります。

- (void)makeCardArray
{
   int i,j,k;
   int l = 0;
   unsigned valueIndex;
   
   id object;
   
   for (i=0; i < deckValue; i++) {
      valueIndex = 0;
      for (j=0; j < 4; j++) {
         for (k=0; k < 13; k++) {
            char suit = charSuits[j];
            char num = charNumbers[k];
            int stdValue = [[stdValues objectAtIndex:valueIndex] intValue];
            int altValue = [[altValues objectAtIndex:valueIndex] intValue];
            BOOL valueFlag = [[valueFlags objectAtIndex:valueIndex] boolValue];
            BOOL boolFlag = [[boolFlags objectAtIndex:valueIndex] boolValue];
            int intFlag = [[intFlags objectAtIndex:valueIndex] intValue];
            
            object = [[NNCard alloc] initWithSuit:suit 
                                   charNum:num 
                                  stdValue:stdValue 
                                  altValue:altValue 
                                 valueFlag:valueFlag 
                                  boolFlag:boolFlag 
                                   intFlag:intFlag];
            if (object != nil) {
               [cardArray addObject:object];
            }
            valueIndex++;
         }
      }
      if (jokerValue > l) {
        // ジョーカーカードの初期化メソッド
         object = [[NNCard alloc] initWithSuit:charSuits[4]
                                charNum:charNumbers[13]
                               stdValue:[[stdValues objectAtIndex:52] intValue]
                               altValue:[[altValues objectAtIndex:52] intValue]];
         if (object != nil) {
            [cardArray addObject:[object autorelease]];
         }
         l++;
      }
   }
}


このメソッドをcardArrayを初期化したあと(あるいは全てのカードオブジェクトを取り除いたあと)に呼び出すことになるわけです。
これまでの試行錯誤(笑)を踏まえた初期化メソッド(プリミティブ・メソッド)は、こうなります。

- (id)initWithStdValues:(NSArray *)sValues 
           altValues:(NSArray *)aValues 
          valueFlags:(NSArray *)vFlags 
           boolFlags:(NSArray *)bFlags 
            intFlags:(NSArray *)iFlags 
           deckValue:(unsigned)dValue 
          jokerValue:(unsigned)jValue
{
   self = [super init];
   if (self != nil) {
   
      randomPosition = 0;
      deckValue = dValue;
      jokerValue = jValue;
      cardArray = [[NSMutableArray alloc] init];
      int i;
      
      // 内部の定数配列の初期化
      if (sValues == nil) {
         NSMutableArray *tempArray;
         tempArray = [NSMutableArray arrayWithCapacity:53];
         for (i=0; i < 53; i++) {
            id object;
            
            object = [[NSNumber alloc] initWithInt:initialValues[i]];
            [tempArray addObject:object];
         }
         stdValues = [[NSArray alloc] initWithArray:tempArray];
      } else {
         stdValues = [[NSArray alloc] initWithArray:sValues];
      }

      if (aValues == nil) {
         NSMutableArray *tempArray;
         tempArray = [NSMutableArray arrayWithCapacity:53];
         for (i=0; i < 53; i++) {
            id object;
            
            object = [[NSNumber alloc] initWithInt:initialValues[i]];
            [tempArray addObject:object];
         }
         altValues = [[NSArray alloc] initWithArray:tempArray];
      } else {
         altValues = [[NSArray alloc] initWithArray:aValues];
      }

      if (vFlags == nil) {
         NSMutableArray *tempArray;
         tempArray = [[NSMutableArray alloc] initWithCapacity:53];
         for (i=0; i < 53; i++) {
            id object;
            object = [[NSNumber alloc] initWithBool:NO];
            [tempArray addObject:object];
         }
         valueFlags = [[NSArray alloc] initWithArray:tempArray];
      } else {
         valueFlags = [[NSArray alloc] initWithArray:vFlags];
      }

      if (bFlags == nil) {
         NSMutableArray *tempArray;
         tempArray = [[NSMutableArray alloc] initWithCapacity:53];
         for (i=0; i <53; i++) {
            id object;
            
            object = [[NSNumber alloc] initWithBool:NO];
            [tempArray addObject:object];
         }
         boolFlags = [[NSArray alloc] initWithArray:tempArray];
      } else {
         boolFlags = [[NSArray alloc] initWithArray:bFlags];
      }   

      if (iFlags == nil) {
         NSMutableArray *tempArray;
         tempArray = [[NSMutableArray alloc] initWithCapacity:53];
         for (i=0; i < 53; i++) {
            id object;
            
            object = [[NSNumber alloc] initWithInt:0];
            [tempArray addObject:object];
         }
         intFlags = [[NSArray alloc] initWithArray:tempArray];
      } else {
         intFlags = [[NSMutableArray alloc] initWithArray:iFlags];
      }
      // カード生成
      [self makeCardArray];
   }
   return self;
}

さらにカードオブジェクトの再配置メソッドとして、-(void)reshuffleメソッドを加えます。
これは単純(笑)

- (void)reShuffle
{
   [cardArray removeAllObjects];
   [self makeCardArray];
}

あとは、独自のメソッド。
ランダムに一枚カードを引くメソッドですが、ランダムなカードを渡すメソッドと、渡したカードを配列から抜いてしまうメソッドを作ってみました。-(id)objectAtRandomは、ランダムに選んだカードを返すメソッド。cardArray配列からカードを抜くことはありません。-(id)oneCardは以前のヴァージョンからあった名前ですが、ランダムに選んだカードを返し、そのカード(オブジェクト)を配列から抜き取るメソッドです。

- (id)objectAtRandom
{
   id object;
   
   srand((unsigned)time(0));
   randomPosition = rand() % [cardArray count];
   object = [cardArray objectAtIndex:randomPosition];
   return object;
}

- (id)oneCard
{
   id object;
   
   object =[[self objectAtRandom] retain];
   [self removeObjectAtIndex:randomPosition];
   
   return [object autorelease];
}

returnでメソッドを終了する前にremoveObjectAtIndex:を実行するわけですが、そうするとカードオブジェクトにreleaseが送られてしまいます(そのはず)。なので、一旦retainしてから、autoreleaseプールへ渡します。これで、コントローラでオブジェクトを解放した時に、メモリーも解放されるはず。
ここまでで、やっとソースコードがひと通り完了したことになるでしょうか。


ついでと言っては何ですが、ついでに-(id)initメソッドもオーバーライドし(一組のカードセットを作る)、さらに+cardArrayメソッドも作ってしまいました。必要かどうかはともかく(笑)。
その辺の全貌は、公開するソースコードを参照してください。……って、ただいま公開準備中(笑)。