コンピュータに手を選択させる(1)

コンピュータに手を選択させるように変更してみましょう。

乱数で手を選択

単純な選択方法として、乱数を使って選択させる方法があります。知能のかけらもありませんが。^^;

手順としては

  1. 0〜8までの乱数を一つ発生させる。
  2. 発生した乱数のマスがすでに入力されている場合には1に戻る。
  3. 発生した乱数を返す。

となります。

サブルーチン
# 手の選択サブルーチン
sub select {
	my $b=@_;
	while (1) {
		$number=int(rand(8)); # 0から8の乱数を発生
		if ($b[$number]==0) { return $number } # 重複チェック
	}
}
メインルーチンの修正
# 乱数初期化(Perlのバージョンによっては必要ない
srand(time());
# メインループ
for ($i=0; $i<9; $i++) {
	if ( ($i % 2) ==0 ) {
		$b[&select(@b)]=1; # 先手を&selectで選ぶように変更
	} else {
		$b[&input(@b)]=-1; # 後手
	}
	&print_board(@b);
	if (&check_winner(@b)!=0) {
		last;
	}	
}

戦略的規則の導入

ゲームの勝敗のルールから、「3目並べれば勝ち。3目並べられれば負け。」なのですから、戦略として

  1. 自分で3目並べられる場所には、その場所に置く。
  2. 相手に3目並べられる場所には、その場所に置く。

の2つが上げられます。このような規則を戦略的規則といいます。

手順としては

  1. 勝ちになるマスがあるかマス目を順に調べる。
    1. 空きマスかどうか調べ、空いてなければ次のマスに。
    2. ○を置いた状態で勝敗チェックをし、ボードから取り除く。勝ちならばマス目の番号を返す。
    3. 次のマスに。
  2. 負けになるマスがあるかマス目を順に調べる。
    1. 空きマスかどうか調べ、空いてなければ次のマスに。
    2. ×を置いた状態で勝敗チェックをし、ボードから取り除く。負けならばマス目の番号を返す。
    3. 次のマスに。
  3. 1.、2.で決まらなければとりあえず乱数で。^^;

となります。

# 手の選択サブルーチン
sub select {
	my @b=@_;
	my $i;
	for ($i=0; $i<9; $i++) { # 勝ちマスチェック
		if ($b[$i]==0) {
			$b[$i]=1;
			if (&check_winner(@b)==1) {
				$b[$i]=0;
				return $i;
			} else {
				$b[$i]=0;
			}
		}
	}
	for ($i=0; $i<9; $i++) { # 負けマスチェック
		if ($b[$i]==0) {
			$b[$i]=-1;
			if (&check_winner(@b)==-1) {
				$b[$i]=0;
				return $i;
			} else {
				$b[$i]=0;
			}
		}
	}
	while (1) {
		$number=int(rand(8)); # 0から8の乱数を発生
		if ($b[$number]==0) { return $number } # 重複チェック
	}
}

ここまでのプログラム

# 盤面初期化
for ($i=0; $i<9; $i++) {
	$b[$i]=0;
}

# メインループ
for ($i=0; $i<9; $i++) {
	if ( ($i % 2) ==0 ) {
		$b[&select(@b)]=1; # 先手
	} else {
		$b[&input(@b)]=-1; # 後手
	}
	&print_board(@b);
	if (&check_winner(@b)!=0) {
		last;
	}	
}

# 勝敗表示
if (&check_winner(@b)==1) {
	print "○の勝ち!\n";
} elsif (&check_winner(@b)==-1) {
	print "×の勝ち!\n";
} else {
	print "引き分け!\n";
}

# 手の選択サブルーチン
sub select {
	my @b=@_;
	my $i;
	for ($i=0; $i<9; $i++) { # 勝ちマスチェック
		if ($b[$i]==0) {
			$b[$i]=1;
			if (&check_winner(@b)==1) {
				$b[$i]=0;
				return $i;
			} else {
				$b[$i]=0;
			}
		}
	}
	for ($i=0; $i<9; $i++) { # 負けマスチェック
		if ($b[$i]==0) {
			$b[$i]=-1;
			if (&check_winner(@b)==-1) {
				$b[$i]=0;
				return $i;
			} else {
				$b[$i]=0;
			}
		}
	}

	while (1) {
		$number=int(rand(8)); # 0から8の乱数を発生
		if ($b[$number]==0) { return $number } # 重複チェック
	}
}

# 盤面表示サブルーチン
sub print_board {
	my @b=@_;
	my $i;
	for ( $i=0; $i<9; $i++ ) {
		if ( $b[$i]==1 ) {
			print "○";
		} elsif ( $b[$i]==-1 ) {
			print "×";
		} else {
			print " ";
		}
		if ( ($i==2) || ($i==5) || ($i==8) ) {
			print "\n";
		} 
	}
}

# 勝敗チェックサブルーチン
sub check_winner {
	my @b=@_;
	my $i;
	for ( $i=0; $i<3; $i++ ) {
		if ( $b[$i*3+0]+$b[$i*3+1]+$b[$i*3+2]==3 ) {
			return 1;
		} elsif ( $b[$i*3+0]+$b[$i*3+1]+$b[$i*3+2]==-3 ) {
			return -1;
		}
	}
	for ( $i=0; $i<3; $i++ ) {
		if ( $b[0+$i]+$b[3+$i]+$b[6+$i]==3 ) {
			return 1;
		} elsif ( $b[0+$i]+$b[3+$i]+$b[6+$i]==-3 ) {
			return -1;
		}
	}
	if ( $b[0]+$b[4]+$b[8]==3 ) {
		return 1;
	} elsif ( $b[0]+$b[4]+$b[8]==-3 ) {
		return -1;
	}
	if ( $b[2]+$b[4]+$b[6]==3 ) {
		return 1;
	} elsif ( $b[2]+$b[4]+$b[6]==-3 ) {
		return -1;
	}
	return 0;
}

# 入力サブルーチン
sub input {
	my @b=@_;
	my $number;
	while () { # 無限ループ
		print "012\n";
		print "345\n";
		print "678\n";
		print "0-8の数字をいれてください。";
		$number=<stdin>;
		chomp($number);
		if ($number>=0 && $number<=8) { # 数字チェック。文字列も0として通ってしまう。TT
			if ($b[$number]!=0) { # 重複チェック
				print "そのマスはすでに入力済みです。\n";
			} else {
				return $number;
			}
		} else {
			print "入力が正しくありません。";
		}
	}
}