演習

二人のプレイヤーでじゃんけんを行うようなクラスを作成すること考えます。 「プレイヤー」は「戦略」を持ち、その戦略に従って「手」を出します。 手には Gu, Choki, Pa があります。 プレイヤーは「ボックス」において双方の手を出し、一回の勝敗を決めます。

このようなクラスを作成するため、以下の小問に答えなさい。

問1-1

以下のプログラムが動作するように、 Hand.m を作成しなさい。 GNUmakefile は最後を参照すること。 なお、 dealloc メソッドも作成すること。

Hand.h


#import <Foundation/Foundation.h>
@interface Hand : NSObject
{
  int _type;
}
- (int) wins: (Hand *) h;
- (int) getType;
- (id) initWithType: (int)t;
@end

testHand.m


#import <Foundation/Foundation.h>
#import "Hand.h"
int main(void){
  Hand* hands[3];
  int i;
  int j;
  for(i=0; i<3; i++){
    hands[i] = [[ Hand alloc] initWithType: i];
  }
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      NSLog(@"%@ wins %@ values %d\n",hands[i], hands[j], [hands[i] wins: hands[j]] );
    }
  }
  return 0;
}
実行結果例
2013-07-04 09:00:35.877 testHand[8652] Gu wins Gu values 0
2013-07-04 09:00:35.877 testHand[8652] Gu wins Choki values 1
2013-07-04 09:00:35.877 testHand[8652] Gu wins Pa values -1
2013-07-04 09:00:35.877 testHand[8652] Choki wins Gu values -1
2013-07-04 09:00:35.877 testHand[8652] Choki wins Choki values 0
2013-07-04 09:00:35.877 testHand[8652] Choki wins Pa values 1
2013-07-04 09:00:35.878 testHand[8652] Pa wins Gu values 1
2013-07-04 09:00:35.878 testHand[8652] Pa wins Choki values -1
2013-07-04 09:00:35.878 testHand[8652] Pa wins Pa values 0

(但し、 NSLog の仕様により、各文字列の左側に時刻やプロセス番号などの情報が表 示される)

問1-2

問 1-1 で作成した Hand のコンビニエンスコンストラクタとして HandBuilder クラスを下記のように実装しなさい。

getHand: (int) type
type で指定された Hand インスタンスを返す

そして、下記のテストプログラムを実行し、出力が予想と一致しているか確か めなさい。

HandBuilder.h

#import <Foundation/Foundation.h>
#import "Hand.h"
@interface HandBuilder: NSObject
{
  Hand* hands[3];
}
- (Hand*) getHandOfType: (int) type;
@end
testHandBuilder.m

#import <Foundation/Foundation.h>
#import "Hand.h"
#import "HandBuilder.h"
int main(void){
  Hand* hands[3];
  int i;
  int j;
  HandBuilder* hb;
  hb = [[HandBuilder alloc] init];
  NSLog(@"HandBuilder version\n");

  for(i=0; i<3; i++){
    hands[i] = [hb getHandOfType: i];
    [hands[i] retain];
  }
  for(i=0; i<3; i++){
    for(j=0; j<3; j++){
      NSLog(@"%@ wins %@ values %d\n",hands[i], hands[j], [hands[i] wins: hands[j]] );
    }
  }
  for(i=0; i<3; i++){
    [hands[i] release];
  }
  return 0;
}
出力
2013-07-04 09:04:30.324 testHandBuilder[9015] HandBuilder version
2013-07-04 09:04:30.325 testHandBuilder[9015] Gu wins Gu values 0
2013-07-04 09:04:30.326 testHandBuilder[9015] Gu wins Choki values 1
2013-07-04 09:04:30.326 testHandBuilder[9015] Gu wins Pa values -1
2013-07-04 09:04:30.326 testHandBuilder[9015] Choki wins Gu values -1
2013-07-04 09:04:30.326 testHandBuilder[9015] Choki wins Choki values 0
2013-07-04 09:04:30.326 testHandBuilder[9015] Choki wins Pa values 1
2013-07-04 09:04:30.326 testHandBuilder[9015] Pa wins Gu values 1
2013-07-04 09:04:30.326 testHandBuilder[9015] Pa wins Choki values -1
2013-07-04 09:04:30.326 testHandBuilder[9015] Pa wins Pa values 0

問1-3

さらに、下記のクラスを追加します。

Tactics.h


#import <Foundation/Foundation.h>
#import "Hand.h"
#import "HandBuilder.h"

@interface Tactics : NSObject
{
  HandBuilder* _handbuilder;
}
- (id) initWithHandBuilder: (HandBuilder*) hb;
- (Hand*) getHand;
@end
Tactics.m

#import "Tactics.h"
@implementation Tactics
- (id) initWithHandBuilder: (HandBuilder*) hb
{
  if((self=[super init])!=nil){
    _handbuilder = hb;
    [_handbuilder retain];
  }
  return self;
}
- (Hand*) getHand
{
  return [_handbuilder getHandOfType: 0];
}
- (void) dealloc 
{
  [_handbuilder release];
  [super dealloc];
}
@end

この Tactics を継承し、次の条件を満たすクラス Tactics1, Tactics2 を作 成しなさい。

Tactics1
インスタンスに対して getHand を行うと、毎回 Gu, Choki, Pa のどれかをそ れぞれ確率 1/3 で出す(ヒント time.h, stdlib.h をインクルードして srand(time(NULL)) をコンストラクタで実行後、 getHand で rand() を使用する)
Tactics2
インスタンスに対して getHand を行うと、毎回 Gu, Choki, Pa を順番に出す

作成した Tactics1, Tactics2 に対して、下記のテストプログラムを実行し、 正常に動作していることを示しなさい。

テストプログラム testTactics.m


#import <Foundation/Foundation.h>
#import "Hand.h"
#import "HandBuilder.h"
#import "Tactics.h"
#import "Tactics1.h"
#import "Tactics2.h"
BOOL testTactics(Tactics* tactics){
  int i;
  for(i=0; i<100; i++){
    Hand* hand = [tactics getHand];
    if([hand getType]!=0) return NO;
  }
  return YES;
}
BOOL testTactics1(Tactics* tactics){
  int i,j;
  double average;
  int stat[3][3]={{0},{0},{0}};
  Hand* previous;

  previous = nil;
  for(i=0; i<1000; i++){
    Hand* hand = [tactics getHand];
    if(previous!=nil)
      stat[[previous getType]][[hand getType]] ++;
    previous = hand;
  }
  average=0;
  for(i=0; i<3; i++)
    for(j=0; j<3; j++)
      average += (double) stat[i][j];
  average /= 9;
  for(i=0; i<3; i++)
    for(j=0; j<3; j++){
      NSLog(@"%d,%d = %d",i,j,stat[i][j]);
      if(stat[i][j]>2*average || 2*stat[i][j] < average) return NO;
    }
  return YES;
}
BOOL testTactics2(Tactics* tactics){
  int i;
  Hand* previous;
  previous = nil;
  for(i=0; i<100; i++){
    Hand* hand = [tactics getHand];
    if(previous!=nil){
      NSLog(@"%@ wins %@ = %d",hand, previous, [hand wins: previous]);
      if([hand wins: previous]!=-1) return NO;
    }
    previous = hand;
  }
  return YES;
}
int main(void){
  NSAutoreleasePool* pool;
  HandBuilder* hb = [[HandBuilder alloc] init];
  Tactics* tactics;

  pool = [[NSAutoreleasePool alloc] init];

  tactics = [[Tactics alloc] initWithHandBuilder: hb];
  NSLog(@"Tactics %@",testTactics(tactics)?@"Ok":@"NG");
  [tactics release];

  tactics = [[Tactics1 alloc] initWithHandBuilder: hb];
  NSLog(@"Tactics1 %@",testTactics1(tactics)?@"Ok":@"NG");
  [tactics release];

  tactics = [[Tactics2 alloc] initWithHandBuilder: hb];
  NSLog(@"Tactics2 %@",testTactics2(tactics)?@"Ok":@"NG");
  [tactics release];

  [hb release];
  [pool release];
  return 0;
}
出力例
2013-07-04 09:08:28.635 testTactics[9278] Tactics Ok
2013-07-04 09:08:28.637 testTactics[9278] 0,0 = 101
2013-07-04 09:08:28.637 testTactics[9278] 0,1 = 118
2013-07-04 09:08:28.637 testTactics[9278] 0,2 = 109
2013-07-04 09:08:28.637 testTactics[9278] 1,0 = 118
2013-07-04 09:08:28.637 testTactics[9278] 1,1 = 122
2013-07-04 09:08:28.637 testTactics[9278] 1,2 = 111
2013-07-04 09:08:28.637 testTactics[9278] 2,0 = 110
2013-07-04 09:08:28.637 testTactics[9278] 2,1 = 110
2013-07-04 09:08:28.637 testTactics[9278] 2,2 = 100
2013-07-04 09:08:28.637 testTactics[9278] Tactics1 Ok
2013-07-04 09:08:28.637 testTactics[9278] Choki wins Gu = -1
2013-07-04 09:08:28.637 testTactics[9278] Pa wins Choki = -1
(中略)
2013-07-04 09:08:28.639 testTactics[9278] Pa wins Choki = -1
2013-07-04 09:08:28.639 testTactics[9278] Gu wins Pa = -1
2013-07-04 09:08:28.639 testTactics[9278] Tactics2 Ok

問1-4

下記のクラス Box を追加します。

追加クラス

Box.h

#import <Foundation/Foundation.h>
@class Player;
@interface Box:NSObject
{
  Player* _player1;
  Player* _player2;
}
- (id) initWithPlayer1:(Player*) player1 Player2:(Player*) player2;
- (NSArray*)getGame;
@end
Box.m

#import <Foundation/Foundation.h>
#import "Hand.h"
#import "Player.h"
#import "Box.h"
@implementation Box
- (id) initWithPlayer1:(Player*) player1 Player2: (Player*) player2
{
  if((self=[super init])!=nil){
    _player1 = [player1 retain];
    _player2 = [player2 retain];
  }
  return self;
}
- (NSArray*)getGame{
  NSMutableArray* result = [[NSMutableArray alloc] initWithCapacity:2];
  Hand* h1 = [_player1 getHand];
  Hand* h2 = [_player2 getHand];
  [result addObject: h1];
  [result addObject: h2];
  switch([h1 wins:h2]){
  case -1:
    [_player1 lose];
    [_player2 win];
    break;
  case 0:
    [_player1 draw];
    [_player2 draw];
    break;
  case 1:
    [_player1 win];
    [_player2 lose];
    break;
  }
  [result autorelease];
  return result;
}
-(void) dealloc
{
  [_player1 release];
  [_player2 release];
  [super dealloc];
}
@end

このプログラムが動作するように Player クラスを作成しなさい。 Player クラスには、 getHand, win, lose, draw メソッドを実装し ます。 さらに、次の条件にも合致するようにメソッドを実装して下さい。

-(id) initWithString: (NSString*) name
文字列 name を引数にするとその文字列を名前として記憶し、オブジェクト を初期化します
-(void) setTactics:(Tactics*) t
Tactics オブジェクトを受け取り、記憶します。
- (Hand*) getHand
Tactics オブジェクトへの getHand メソッドの結果をそのまま返します。
- (NSString*) result
勝敗結果を文字列で返します。書式は次の通りです:
名前 : 勝った数 win(s), 負けた数 lose(s), 引き分けの数 draw(s)

Player.h


#import <Foundation/Foundation.h>
#import "Tactics.h"
#import "Hand.h"
@interface Player:NSObject
{
  NSString* name;
  Tactics* tactics;
  int win,lose,draw;
}
- (id) initWithString:(NSString*) n;
- (void) setTactics:(Tactics*) t;
- (Hand*) getHand;
- (void) win;
- (void) lose;
- (void) draw;
- (NSString*) result;
@end

そして、下記のテストプログラムで動作を確認しなさい。

テストプログラム


#import <Foundation/Foundation.h>
#import "Hand.h"
#import "HandBuilder.h"
#import "Tactics.h"
#import "Tactics1.h"
#import "Tactics2.h"
#import "Box.h"
#import "Player.h"

int main(int argc, char* argv[]){
  NSAutoreleasePool* pool;
  pool = [[NSAutoreleasePool alloc] init];

  HandBuilder* hb = [[HandBuilder alloc] init];
  int i;
  Player* player1 = [[Player alloc] initWithString:@"Kaiji"];
  Tactics* t1 = [[Tactics1 alloc] initWithHandBuilder: hb];
  [player1 setTactics: t1];

  Player* player2 = [[Player alloc] initWithString:@"Funai"];
  Tactics* t2 = [[Tactics2 alloc] initWithHandBuilder: hb];
  [player2 setTactics: t2];
    
  Box* box = [[Box alloc] initWithPlayer1: player1 Player2: player2];

  NSMutableArray* list = [[NSMutableArray alloc] init];
  for(i=1; i<=1000000; i++){
    [list addObject: [box getGame]];
  }
  NSLog(@"%@",[player1 result]);
  NSLog(@"%@",[player2 result]);
  [box release];
  [player1 release];
  [player2 release];
  [t1 release];
  [t2 release];
  [list release];
  [hb release];
  [pool release];

  return 0;
}
出力例
2013-07-04 09:30:14.455 testPlayer[10901] Kaiji: 333709 win(s), 333149 lose(s), 333142 draw(s)
2013-07-04 09:30:14.456 testPlayer[10901] Funai: 333149 win(s), 333709 lose(s), 333142 draw(s)

なお、上記の勝敗の数字の一部は乱数によって変化しますので、この通りの出力が必ず出るわけではありません。 自ら行った各テストがそれぞれ成功しているかどうか、理由をつけて説明しなさい。 また、レポート作成時には、テスト結果全てを添付して下さい。

GNUmakefile


#
# An example GNUmakefile
#

GNUSTEP_MAKEFILES = C:\GNUstep\GNUstep\System\Library\Makefiles
#GNUSTEP_MAKEFILES = /usr/share/GNUstep/Makefiles
#GNUSTEP_MAKEFILES = /GNUstep/System/Library/Makefiles

FOUNDATION_LIB=gnu
# Include the common variables defined by the Makefile Package
include $(GNUSTEP_MAKEFILES)/common.make

# Build a simple Objective-C program

# The Objective-C files to compile

TOOL_NAME = testHand
testHand_OBJC_FILES =  Hand.m testHand.m

#TOOL_NAME = testHandBuilder
#testHandBuilder_OBJC_FILES =  Hand.m HandBuilder.m testHandBuilder.m

#TOOL_NAME = testTactics
#testTactics_OBJC_FILES =  Hand.m HandBuilder.m Tactics.m Tactics1.m Tactics2.m testTactics.m

#TOOL_NAME = testPlayer
#testPlayer_OBJC_FILES =  Hand.m HandBuilder.m Tactics.m Tactics1.m Tactics2.m Box.m Player.m testPlayer.m

-include GNUmakefile.preamble

# Include in the rules for making GNUstep command-line programs
include $(GNUSTEP_MAKEFILES)/tool.make

-include GNUmakefile.postamble

坂本直志 <[email protected]>
東京電機大学工学部情報通信工学科