どうも、かけるです!
大学時代、ゲームにおける最適手の探索を卒業研究のテーマとしており、その過程でリバーシのプログラムを作成していました。
そのときのソースコードは凄まじいレベルの汚コードで人に見せられるようなものではありません。――が、大学を卒業した今も気が向いたときに研究活動を趣味として続けており、プログラムもで一から見直し中だったりします。
今回は、そんな見直し中のリバーシのプログラムを一部公開してみようと思います。
ソースコード
解説よりソースコード……っていう人も多い気がするので、とにかく先にソースコードを出します。
定義値ほか
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef unsigned long long int BitBoard;
/**** 定義値 ****/
#define BOARD_SIZE 0x08
#define INIT_BOARD_STONE_BLACK 0x0000000810000000
#define INIT_BOARD_STONE_WHITE 0x0000001008000000
/**** 列挙型 ****/
/* ターンプレイヤーID */
typedef enum _EN_CommonFunc_TurnId{
TURNID_PLAYER = 0,
TURNID_CPU,
TURNID_MAX
}EN_CommonFunc_TurnId;
/**** 構造体 ****/
/* 盤面情報 */
typedef struct _ST_CommonFunc_BoardInfo{
BitBoard ullPlayerBoard; // Player盤面
BitBoard ullCpuBoard; // CPU盤面
BitBoard ullValidPlace; // 合法手
BitBoard ullSetPlace; // 着手位置
BitBoard ullReversePlace; // 直前に裏返した石の位置情報
}ST_CommonFunc_BoardInfo;
/* ステータス情報 */
typedef struct _ST_CommonFunc_StateInfo{
EN_CommonFunc_TurnId eFirstTurn; // 先攻プレイヤー情報
EN_CommonFunc_TurnId eTurnId; // ターンプレイヤー
unsigned char ucPassFlag; // パスフラグ
}ST_CommonFunc_StateInfo;
/* 対局情報 */
typedef struct _ST_CommonFunc_GameInfo{
ST_CommonFunc_BoardInfo stBoardInfo;
ST_CommonFunc_StateInfo stStateInfo;
}ST_CommonFunc_GameInfo;
/**** プロトタイプ宣言 ****/
void GameSetup_getFirstTurnPlayer( EN_CommonFunc_TurnId *peFirstTurn );
void GameSetup_initBoard(ST_CommonFunc_BoardInfo *pstBoardInfo, const EN_CommonFunc_TurnId eFirstTurn);
void GameSetup_initState( ST_CommonFunc_StateInfo *pstStateInfo );
void GameCtrl_main( ST_CommonFunc_GameInfo *pstGameInfo );
BitBoard CommonFunc_getValidPoint( const BitBoard ullTurnPlayerBoard, const BitBoard ullWaitPlayerBoard );
BitBoard CommonFunc_execReverseStone( BitBoard *pullTurnPlayerBoard, BitBoard *pullWaitPlayerBoard, const BitBoard ullSetPlace);
BitBoard GamaCtrl_getPlayerSetPlace( const BitBoard ullValidPlace );
BitBoard GamaCtrl_getCpuSetPlace( const BitBoard ullValidPlace );
int CommonFunc_checkSetPlace( const BitBoard ullValidBoard, const BitBoard ullSetPlace );
void Debug_printBoard( const BitBoard ullPlayerBoard, const BitBoard ullCpuBoard, const EN_CommonFunc_TurnId eFirstTurn );
void GameResult_show( const BitBoard ullPlayerBoard, const BitBoard ullCpuBoard );
int CommonFunc_getStoneCount( BitBoard ullTargetBoard );
対局の準備
int main( void ){
ST_CommonFunc_GameInfo stGameInfo;
ST_CommonFunc_GameInfo *pstGameInfo = &stGameInfo;
/* 先攻後攻の情報取得 */
GameSetup_getFirstTurnPlayer( &pstGameInfo->stStateInfo.eFirstTurn );
/* 盤面生成 */
GameSetup_initBoard( &pstGameInfo->stBoardInfo, pstGameInfo->stStateInfo.eFirstTurn );
/* ステータス情報の初期化 */
GameSetup_initState( &pstGameInfo->stStateInfo );
/* 対局開始 */
GameCtrl_main( pstGameInfo );
return 0;
}
void GameSetup_getFirstTurnPlayer( EN_CommonFunc_TurnId *peFirstTurn ){
do{
printf( "先攻はどっち??? [Player:0 / CPU:1] >>> " );
scanf( "%d", peFirstTurn );
}while( ( TURNID_PLAYER > *peFirstTurn ) || ( TURNID_CPU < *peFirstTurn ) );
}
void GameSetup_initBoard(ST_CommonFunc_BoardInfo *pstBoardInfo, const EN_CommonFunc_TurnId eFirstTurn){
switch(eFirstTurn){
case TURNID_PLAYER:
pstBoardInfo->ullPlayerBoard = INIT_BOARD_STONE_BLACK;
pstBoardInfo->ullCpuBoard = INIT_BOARD_STONE_WHITE;
break;
case TURNID_CPU:
pstBoardInfo->ullPlayerBoard = INIT_BOARD_STONE_WHITE;
pstBoardInfo->ullCpuBoard = INIT_BOARD_STONE_BLACK;
break;
default:
// エラー
break;
}
}
void GameSetup_initState( ST_CommonFunc_StateInfo *pstStateInfo ){
/* 先攻プレイヤーを設定 */
pstStateInfo->eTurnId = pstStateInfo->eFirstTurn;
/* パスフラグ初期化 */
pstStateInfo->ucPassFlag = 0x00;
srand((unsigned int)time(NULL));
}
ゲームの進行
void GameCtrl_main( ST_CommonFunc_GameInfo *pstGameInfo ){
ST_CommonFunc_BoardInfo *pstBoardInfo;
ST_CommonFunc_StateInfo *pstStateInfo;
pstBoardInfo = &pstGameInfo->stBoardInfo;
pstStateInfo = &pstGameInfo->stStateInfo;
do{
Debug_printBoard( pstBoardInfo->ullPlayerBoard, pstBoardInfo->ullCpuBoard, pstStateInfo->eFirstTurn );
switch( pstStateInfo->eTurnId ){
case TURNID_PLAYER:
/* 合法手を取得 */
pstBoardInfo->ullValidPlace = CommonFunc_getValidPoint( pstBoardInfo->ullPlayerBoard, pstBoardInfo->ullCpuBoard );
/* パス判定 */
if( pstBoardInfo->ullValidPlace ){
/**** Player着手可能 ****/
/* パスフラグ初期化 */
pstStateInfo->ucPassFlag = 0;
/* 着手位置を取得 */
pstBoardInfo->ullSetPlace = GamaCtrl_getPlayerSetPlace( pstBoardInfo->ullValidPlace );
/* 石を置く/返す */
pstBoardInfo->ullReversePlace = CommonFunc_execReverseStone( &pstBoardInfo->ullPlayerBoard, &pstBoardInfo->ullCpuBoard, pstBoardInfo->ullSetPlace );
/* ターンプレイヤー情報の更新 */
pstStateInfo->eTurnId = TURNID_CPU;
}else{
if( pstStateInfo->ucPassFlag ){
/**** 両者着手不可ケース ****/
pstStateInfo->eTurnId = TURNID_MAX;
}else{
/* Player→CPUにターン移行 */
pstStateInfo->ucPassFlag++;
pstStateInfo->eTurnId = TURNID_CPU;
}
}
break;
case TURNID_CPU:
/* 合法手を取得 */
pstBoardInfo->ullValidPlace = CommonFunc_getValidPoint( pstBoardInfo->ullCpuBoard, pstBoardInfo->ullPlayerBoard );
/* パス判定 */
if( pstBoardInfo->ullValidPlace ){
/**** CPU着手可能 ****/
/* パスフラグ初期化 */
pstStateInfo->ucPassFlag = 0;
/* 着手位置を取得 */
pstBoardInfo->ullSetPlace = GamaCtrl_getCpuSetPlace( pstBoardInfo->ullValidPlace );
/* 石を置く/返す */
pstBoardInfo->ullReversePlace = CommonFunc_execReverseStone( &pstBoardInfo->ullCpuBoard, &pstBoardInfo->ullPlayerBoard, pstBoardInfo->ullSetPlace );
/* ターンプレイヤー情報の更新 */
pstStateInfo->eTurnId = TURNID_PLAYER;
}else{
if( pstStateInfo->ucPassFlag ){
/**** 両者着手不可ケース ****/
pstStateInfo->eTurnId = TURNID_MAX;
}else{
/* Player→CPUにターン移行 */
pstStateInfo->ucPassFlag++;
pstStateInfo->eTurnId = TURNID_PLAYER;
}
}
break;
default:
break;
}
}while( TURNID_MAX != pstStateInfo->eTurnId );
/* 対局結果表示 */
GameResult_show( pstBoardInfo->ullPlayerBoard, pstBoardInfo->ullCpuBoard );
}
合法手(石を置ける位置)を取得
/* 合法手の取得 */
BitBoard CommonFunc_getValidPoint( const BitBoard ullTurnPlayerBoard, const BitBoard ullWaitPlayerBoard ){
BitBoard ullValidPlace = 0x0000000000000000;
BitBoard ullBrankBoard = ~( ullTurnPlayerBoard | ullWaitPlayerBoard ); /* 空きマス情報 */
BitBoard ullWaitPlayerBoardMask;
BitBoard ullTempValidPlace;
/**** 左方向の合法手をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x7e7e7e7e7e7e7e7e;
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard << 1 );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << 1 );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace << 1 );
}
/**** 右方向の合法手をチェック ****/
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard >> 1 );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> 1 );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> 1 );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace >> 1 );
}
/**** 上方向の合法手をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x00ffffffffffff00;
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard << BOARD_SIZE );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << BOARD_SIZE );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace << BOARD_SIZE );
}
/**** 下方向の合法手をチェック ****/
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard >> BOARD_SIZE );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> BOARD_SIZE );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> BOARD_SIZE );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace >> BOARD_SIZE );
}
/**** 左上方向の合法手をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x007e7e7e7e7e7e00;
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard << ( BOARD_SIZE + 1 ) );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace << ( BOARD_SIZE + 1 ) );
}
/**** 右上方向の合法手をチェック ****/
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard << ( BOARD_SIZE - 1 ) );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace << ( BOARD_SIZE - 1 ) );
}
/**** 左下方向の合法手をチェック ****/
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard >> ( BOARD_SIZE - 1 ) );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace >> ( BOARD_SIZE - 1 ) );
}
/**** 右下方向の合法手をチェック ****/
ullTempValidPlace = ullWaitPlayerBoardMask & ( ullTurnPlayerBoard >> ( BOARD_SIZE + 1 ) );
if( ullTempValidPlace ){
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
ullTempValidPlace |= ullWaitPlayerBoardMask & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
ullValidPlace |= ullBrankBoard & ( ullTempValidPlace >> ( BOARD_SIZE + 1 ) );
}
return ullValidPlace;
}
着手位置の取得
BitBoard GamaCtrl_getPlayerSetPlace( const BitBoard ullValidPlace ){
BitBoard ullSetPlace;
int row, col;
do{
/* 位置情報の初期化 */
ullSetPlace = 0x8000000000000000;
/* 着手位置の入力受付 */
printf( "(行, 列) >>> " );
scanf( "%d %d", &row, &col );
/* 入力情報をビットシフトで変換 */
ullSetPlace >>= ( row - 1 ) * BOARD_SIZE + col - 1;
}while( CommonFunc_checkSetPlace( ullValidPlace, ullSetPlace ) );
return ullSetPlace;
}
BitBoard GamaCtrl_getCpuSetPlace( const BitBoard ullValidPlace ){
BitBoard ullSetPlace;
int row, col;
do{
/* 位置情報の初期化 */
ullSetPlace = 0x8000000000000000;
/* 乱数を用いて着手位置を決定 */
row = 1 + ( double )rand() / RAND_MAX * BOARD_SIZE;
col = 1 + ( double )rand() / RAND_MAX * BOARD_SIZE;
/* 入力情報をビットシフトで変換 */
ullSetPlace >>= ( row - 1 ) * BOARD_SIZE + col - 1;
}while( CommonFunc_checkSetPlace( ullValidPlace, ullSetPlace ) );
return ullSetPlace;
}
合法手判定
int CommonFunc_checkSetPlace( const BitBoard ullValidBoard, const BitBoard ullSetPlace ){
if( ullValidBoard & ullSetPlace ) return 0;
else return -1;
}
石を返す
/* 返る石の取得 */
BitBoard CommonFunc_getReverseStone( const BitBoard ullTurnPlayerBoard, const BitBoard ullWaitPlayerBoard, const BitBoard ullSetPlace ){
BitBoard ullReversePlaceFix = 0x0000000000000000;
BitBoard ullTempReversePlace;
BitBoard ullWaitPlayerBoardMask;
BitBoard ullWorkBoard;
/**** 左方向に返る石をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x7e7e7e7e7e7e7e7e;
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace << 1 );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard <<= 1;
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 右方向に返る石をチェック ****/
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace >> 1 );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard >>= 1;
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 上方向に返る石をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x00ffffffffffff00;
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace << BOARD_SIZE );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard <<= BOARD_SIZE;
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 下方向に返る石をチェック ****/
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace >> BOARD_SIZE );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard >>= BOARD_SIZE;
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 左上方向に返る石をチェック ****/
ullWaitPlayerBoardMask = ullWaitPlayerBoard & 0x007e7e7e7e7e7e00;
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace << ( BOARD_SIZE + 1 ) );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard <<= ( BOARD_SIZE + 1 );
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 右上方向に返る石をチェック ****/
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace << ( BOARD_SIZE - 1 ) );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard <<= ( BOARD_SIZE - 1 );
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 左下方向に返る石をチェック ****/
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace >> ( BOARD_SIZE - 1 ) );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard >>= ( BOARD_SIZE - 1 );
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
/**** 右下方向に返る石をチェック ****/
ullWorkBoard = ullWaitPlayerBoardMask & ( ullSetPlace >> ( BOARD_SIZE + 1 ) );
if( ullWorkBoard ){
ullTempReversePlace = 0x0000000000000000;
while( ullWaitPlayerBoardMask & ullWorkBoard ){
ullTempReversePlace |= ullWorkBoard;
ullWorkBoard >>= ( BOARD_SIZE + 1 );
}
if( ullTurnPlayerBoard & ullWorkBoard ) ullReversePlaceFix |= ullTempReversePlace;
}
return ullReversePlaceFix;
}
/* 石を返す */
void CommonFunc_execReverseStone( BitBoard *pullTurnPlayerBoard, BitBoard *pullWaitPlayerBoard, const BitBoard ullSetPlace, const BitBoard ullReversePlace ){
*pullTurnPlayerBoard |= ( ullSetPlace | ullReversePlace );
*pullWaitPlayerBoard ^= ullReversePlace;
}
盤上の表示
/* 呼び出し時の盤面を表示 */
void Debug_printBoard( const BitBoard ullPlayerBoard, const BitBoard ullCpuBoard, const EN_CommonFunc_TurnId eFirstTurn ){
BitBoard *pullBrackStone;
BitBoard *pullWhiteStone;
BitBoard ullBoradMask = 0x8000000000000000;
int i, j;
/* プレイヤーと石色の対応付け */
if( TURNID_PLAYER == eFirstTurn ){
pullBrackStone = &ullPlayerBoard;
pullWhiteStone = &ullCpuBoard;
}else{
pullBrackStone = &ullCpuBoard;
pullWhiteStone = &ullPlayerBoard;
}
/* 盤面表示処理 */
printf(" ABCDEFGH\n");
for( i=0; i<BOARD_SIZE; i++){
printf("%d", i+1);
for( j=0; j<BOARD_SIZE; j++){
if( *pullBrackStone & ullBoradMask ){
printf("●");
}else if( *pullWhiteStone & ullBoradMask ){
printf("○");
}else printf("+");
ullBoradMask >>= 1;
}
printf("\n");
}
}
対局結果表示
void GameResult_show( const BitBoard ullPlayerBoard, const BitBoard ullCpuBoard ){
int iPlayerCount = CommonFunc_getStoneCount( ullPlayerBoard );
int iCpuCount = CommonFunc_getStoneCount( ullCpuBoard );
printf("Player: %d, CPU: %d\n", iPlayerCount, iCpuCount);
printf("勝者:");
if( iPlayerCount > iCpuCount ) printf("Player!!\n");
else if( iPlayerCount < iCpuCount ) printf("CPU!!\n");
else printf("引き分け~\n");
}
int CommonFunc_getStoneCount( BitBoard ullTargetBoard ){
ullTargetBoard -= ( ullTargetBoard >> 1 & 0x5555555555555555 );
ullTargetBoard = ( ullTargetBoard & 0x3333333333333333 ) + ( ullTargetBoard >> 2 & 0x3333333333333333 );
ullTargetBoard = ( ullTargetBoard & 0x0f0f0f0f0f0f0f0f ) + ( ullTargetBoard >> 4 & 0x0f0f0f0f0f0f0f0f );
ullTargetBoard = ( ullTargetBoard & 0x00ff00ff00ff00ff ) + ( ullTargetBoard >> 8 & 0x00ff00ff00ff00ff );
ullTargetBoard = ( ullTargetBoard & 0x0000ffff0000ffff ) + ( ullTargetBoard >> 16 & 0x0000ffff0000ffff );
ullTargetBoard = ( ullTargetBoard & 0xffffffffffffffff ) + ( ullTargetBoard >> 32 & 0xffffffffffffffff );
return ( int ) ullTargetBoard;
}
ざっくり解説
ビットボード
リバーシプログラムにおいて、盤面の情報は「配列」を用いて管理する方法と「ビットボード」と呼ばれる考え方で管理する方法があり、上記のソースコードはビットボードで実装したものになります。
ビットボードとは、リバーシゲームが8×8の64マスで行われるゲームである点を利用し、白石・黒石の位置をそれぞれ64bit変数のビット情報で表現する手法です。
これを採用することで得られる一番のメリットは、石を返すなどの石を操作する処理にビット演算が使用できるため、配列による実装と比べて処理を大幅に高速化することができる点が挙げられます。
一方でビットボード採用のデメリットについてですが、リバーシに限って言えばビットボードを使用することでのデメリットは特にありません。これが将棋や囲碁であった場合、64bitでは足りないため表現できないため、不都合になる場面も一部あるらしいですが……。
強いて言えば、配列と比べて処理が複雑化してしまうため、必要な知識が増えてしまうこと……ですかね。
つまり、リバーシプログラムにおいて、ビットボードを採用しない理由はないと言っても過言ありません。
CPUの思考ルーチン
GamaCtrl_getCpuSetPlace()でCPUが打つ箇所を決定しています。上記のソースコードは「打てる箇所からランダムに選出して打ち返す」実装になっています。
強いプログラムを作りたいという場合は、最適な一手を決めるための処理をこの関数に追加することになります。
有名な探索アルゴリズムとしてはMini-Max法、Alpha-Beta法などが挙げられますが、これらについてはに【リバーシ】強いAIを目指すための探索アルゴリズムの記事でまとめています!
以上、最後まで読んでいただきありがとうございます!それでは、また(・ω・)ノシ
コメント