NNCardDeck version0.4(3)
さて、必要なメソッドと、その実装を行なっていきます。
その前に思ったのですが。
現実のカードの山はシャッフルされてバラバラに並んでいるので、順番に取っていくので充分なのですが、このNNCardDeckについては、初期化の都合上カードがスーツごとに整然と並んでしまいます。ここから「シャッフルされた状態」を演出するために、ランダムにカードを抜き出す、というメソッドを実装させるわけですが、逆に言うと配列から位置を指定してカードを抜き出す、といったようなメソッドは、「カードの山」という性格上必要のないメソッドなのかも知れません。つまり、あえてNSMutableArrayのサブクラスとして設計する意味はあまり無いのかも知れない(笑)、などと思う、台風前夜(笑)。
だとしたら、NNCardDeckクラスは、NSObjectのサブクラスで充分なのかも。
これまでの苦労は……(笑)。ま、それが試行錯誤というもの(笑)。
そんなことも考えつつ、まずは、オーバーライドさせるメソッドについて。
前回記した7つのプリミティブ・メソッドについては、「Cocoa基礎ガイド」の「複合サブクラス」のページの解説そのままに、インスタンス変数cardArrayへの処理として書き換えます。こんな感じになります。
- (id)objectAtIndex:(unsigned)index; { return [cardArray objectAtIndex:index]; }
他のプリミティブ・メソッドについても、同様にオーバーライドしていきます。値を返さないメソッドについては、return命令も必要ありません。cardArrayに対して、同じメソッドを投げるだけ。ここまでは簡単。
で、次に初期化メソッドを考えるわけですが、これが大変(笑)。
前バージョンと同じく、アプリケーションコントローラからゲーム内容に合わせた変数を受け取り、それをNNCardオブジェクトに埋め込んでいって、それを配列に加えていく、という作業になるのですが、さて、どうしたものか。
基本は、カードが持つ変数やフラグの値はゲームによって左右されるので、アプリケーションコントローラからその値を受け取って、その値をもとにNNCardオブジェクトを生成し、配列に加えていく、という作業になるのですが、ゲームによってはカードのインスタンス変数のうち必要のないものもあるでしょうし、だったら必要のない変数は与えなくても初期化がうまく行くように考えておいた方がいいでしょう。
カードのフラグ関係の変数については「0(またはNO)」を埋め込んでおけばいいとして、ふたつのint値、stdValueとaltValueに渡す値がない場合に備えて、初期値のデータを用意しておきます。
// Card Suits Symbols & numbers. Last parameter used for Joker card. static const char charSuits[] = {'d', 'h', 'k', 's', 'j'}; static const char charNumbers[] = {'a','2','3','4','5','6','7','8','9','x','j','q','k','0'}; // if no argument recieved, use these array. static const int initialStdValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 99}; static const int initialAltValues[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0};
今回、初期値のデータとしてint配列をふたつ用意したのですが、前バージョンと違って53枚分の数値の配列にしました。前回はスートが違っても数字が同じなら同じ数字を埋め込んでいたのですが(BlackJackならそれで充分ですが)、スートによって直を変えた方がいいゲームがあるかも知れませんし、もともと一枚々々のカードには固有の値が埋め込まれている、という前提で考えた方がいいのではないか、と思い、53枚分の値を配列として用意しました。
「スートに関わらず同じ数字に同じ値が埋め込まれていても構わないようなゲームなら、13個の値の配列だけ渡せば済むようにしようか」などと、いろいろ考えてみたのですが、そこまでやるとなるともう何が何だかな状態になってしまう(笑)ので、考えるのは止めました(笑)。
とりあえず、外部から変数の値が渡されない場合、自分で変数の値を確保しておかなくてはならないわけで。というさまざまな状況を考慮した上、このクラスでカードに渡す値を配列で保持することにしました。
お。そういうことなら、再度カードの山を作り直す(リシャッフル)場合には、わざわざもう一度最初から初期化する必要がなくなるのでは? データは自分自身が持っているのだから、そのデータを使って配列を作り直してしまえばいいじゃないか。じゃあ、そういうメソッドも用意しておきましょう。
などなど、寄り道をしたりしながらも初期化メソッドを作るのですが、まずはプリミティブ・メソッドから。
これはNNCardの場合と同じく、全ての値を外部から受け取ってカード配列を作るメソッドです。値はNSArrayオブジェクトで受け取ります。
- (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) { cardArray = [[NSMutableArray alloc] init]; stdValues = [[NSArray alloc] initWithArray:sValues]; altValues = [[NSArray alloc] initWithArray:aValues]; valueFlags = [[NSArray alloc] initWithArray:vFlags]; boolFlags = [[NSArray alloc] initWithArray:bFlags]; intFlags = [[NSArray alloc] initWithArray:iFlags]; deckValue = dValue; jokerValue = jValue; randomPosition = 0; 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 (0 < jokerValue) { 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++; } } } return self; }
あとはこのバリエーションとして、渡す変数を省略したものも用意します。
// フラグデータを省略 - (id)initWithStdValues:(NSArray *)sValues altValues:(NSArray *)aValues deckValue:(unsigned)dValue jokerValue:(unsigned)jValue { // フラグデータの配列を初期化 int i; NSMutableArray *tempValueFlags; NSMutableArray *tempBoolFlags; NSMutableArray *tempIntFlags; tempValueFlags = [[NSMutableArray arrayWithCapacity:53]; for (i=0; i < 53; i++) { id object; object = [[NSNumber alloc] initWithBool:NO]; [tempValueFlags addObject:object]; } valueFlags = [[NSArray alloc] initWithArray:tempVlaueFlags]; tempBoolFlags = [[NSMutableArray arrayWithCapacity:53]; for (i=0; i < 53; i++) { id object; object = [[NSNumber alloc] initWithBool:NO]; [tempBoolFlags addObject:object]; } boolFlags = [[NSArray alloc] initWithArray:tempBoolFlags]; tempIntFlags = [[NSMutableArray arrayWithCapacity:53]; for (i=0; i < 53; i++) { id object; object = [[NSNumber alloc] initWithBool:NO]; [tempIntFlags addObject:object]; } intFlags = [[NSArray alloc] initWithArray:tempIntFlags]; deckValue = dValue; jokerValue = jValue; // プリミティブ・メソッドの呼び出し return [self initWithStdValues:sValues altValues:aValues valueFlags:valueFlags boolFlags:boolFlags intFlags:intFlags deckValue:deckValue jokerValue:jokerValue]; }
こんな感じで作ってはみたのですが、初期化メソッドのバリエーションを増やすたびに、外から渡されないデータ配列をメソッドごとに初期化することになってしまいまして(笑)、上の「フラグデータの初期化」の部分のソースを何度も書くことに(笑)。
「こりゃ、くど過ぎる」(笑)と思って、修正することにしました。
修正したものと、その他のメソッドについては、次回。
ソースコードを入れると、やたら長くなりますな。書くのも一苦労(笑)。