≫ EDIT

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。





↓ランキングに参加しています!お手数ですが、よろしければ3つとも1日1回クリックをお願いしますm(__)m↓
にほんブログ村 大学生日記ブログ 理系大学生へ
にほんブログ村


【カテゴリー:スポンサー広告 comments(-)

≫ EDIT

【C言語】私が作った弱小コンピュータリバーシ(オセロ)のソースコード



今日は私が書いた弱小コンピュータリバーシ(オセロ)のソースコードを貼りたいと思います。


C言語対応のコンパイラーでコンパイルすれば多分、動くと思います笑






//コンピューターリバーシ(まだまだ発展途中)

#include

/******************************* 根幹部分に関する記述 *******************************************/
//マスの状態を定義
#define NONE 0 //空きマス
#define BLACK 1 //黒駒
#define WHITE 2 //白駒
//ボード(左上を[0][0])
int board[8][8];
//マスの状態の数
int NONE_NUMBER; //空きマス
int BLACK_NUMBER; //黒駒
int WHITE_NUMBER; //白駒
//ターン
int turn = 1; //1が先手、2が後手
//コンピュータ・人間のターン
int human, computer;
//方向ベクトルを定義
int vec_x[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
//方向ベクトルを定義
int vec_y[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
//変換関数のプロトタイプ宣言
int Trans_x(int place); //多次元配列のx要素に変換
int Trans_y(int place); //多次元配列のy要素に変換
int Trans_place(int x, int y); //多次元配列のx,y要素を人間が入力する値に変換
int EnemyColor(int turn); //相手の色を出力
int StretchVec(int a); //その方向のベクトルを1つ伸ばす(成分を入力すると、1つ伸ばした成分を出力する)
//重要関数のプロトタイプ宣言
void Initia(void); //初期化
void Disp(void); //現在のボードを描画
int CanGo(int place, int turn); //有効な場所かどうかを判断(有効な場合は1、有効でない場合は0を返却)
int CanPut(int turn); //そのターンが駒を置けるかどうかを判別(置ける場合は1、置けない場合は0を返却)
void Count(const int ThinkBoard[8][8]); //空きマス、黒駒、白駒の数をカウント
void Flip(int place, int turn); //駒を裏返す
void Change(void); //ターンを入れ替える
void Judge(void); //勝敗を決定

/* ***************************探索アルゴリズムに関する記述************************************* */
void BestPlace(int ThinkBoard[8][8], int turn); //コンピューターの評価点を表示し最善の場所をグローバル変数bestplaceに保存
int minimax(int ThinkBoard[8][8], int turn, int depth); //minimax法で評価点を出力



//探索アルゴリズムに関する設定
#define DEPTH 5 //読みの深さを設定

//グローバル宣言
int bestplace; //最善の場所を保存する変数
int saveboard[8][8][DEPTH]; //局面を保存する変数
long count = 0; //読み込んだ局面数を保存する変数

/* ***************************評価関数に関する記述********************************************* */
//評価関数のプロトタイプ宣言
void ChangeStage(const int ThinkBoard[8][8]); //局面を判断して評価関数のパラメーターを変化
int Evalu_Total(const int ThinkBoard[8][8], int turn); //評価値の合計を計算(評価する局面とターンを値渡し)
int Evalu_HowMany(const int ThinkBoard[8][8], int turn); //着手可能手数を計算して返却(評価する局面とター73ンを値渡し)
int Evalu_BoardScore(const int ThinkBoard[8][8], int turn); //マスの評価値の合計を計算して数値を返却
int Evalu_HowManyStones(const int ThinkBoard[8][8], int turn); //駒の数を計算して数値を返却
//評価点の数(現在、【着手可能手数】と【マスの評価値の合計】、【自分の駒の数】、【相手の駒の数】の4つ
#define EVALUATION_POINTS 4

//評価関数の設定に関する記述
//Evalu_BoardScore()のマスの得点を定義
const int CellScore[8][8] = {

{ 50, -12, 0, -1, -1, 0, -12, 50 },

{ -12, -15, -5, -5, -5, -5, -15, -12 },

{ 0, -5, 0, -1, -1, 0, -3, 0 },

{ -1, -5, -1, -1, -1, -1, -3, -1 },

{ -1, -5, -1, -1, -1, -1, -3, -1 },

{ 0, -5, 0, -1, -1, 0, -3, 0 },

{ -12, -15, -5, -5, -5, -5, -15, -12 },

{ 50, -12, 0, -1, -1, 0, -12, 50 },

};

//序盤、中盤、終盤を定義(その空きマスの数まで)
#define FIRST 50 //64 - 50マスまでが序盤
#define MIDDLE 30 //51 - 30マスまでが中盤
#define FINAL 0 //31 - 0マスまでが終盤

//グローバル変数の定義
int w[EVALUATION_POINTS]; //各々の評価関数のウェイト
//序盤、中盤、終盤の各々の評価関数のパラメーター
//左から【着手可能手数】、【マスの評価値の合計】、【自分の駒の数】、【相手の駒の数】
const int w_first[EVALUATION_POINTS] = { 3, 10, 0, 0 }; //序盤
const int w_middle[EVALUATION_POINTS] = { 3, 7, 1, -1 }; //中盤
const int w_final[EVALUATION_POINTS] = { 1, 0, 5, -5 }; //終盤



int main(void){

//コンティニューするかどうかを保存する変数
int retry;

//リトライする時はここでループ
while (1){

//初期化
Initia();

//人間が先手・後手を決める
while (1){
printf("先手が黒、後手が白です。\n");
printf("あなたは先手ですか?後手ですか?\n先手なら1、後手なら2を入力してください。\n");
printf("入力:"); scanf_s("%d", &human);

if (human == 1){
break;
}
else if (human == 2){
break;
}
printf("無効な値です。\a\a\a\n\n");
}
if (human == 1){
computer = 2;
}
else{
computer = 1;
}
printf("\n\n");

//空きマスがある・パスの時はここでループ
while (1){

//ボードを描画
Disp();
printf("\n");

//人間の手
if (turn == human){
//有効な手であるまでループ
while (1){

//場所を入力
int human_place;
printf("場所:"); scanf_s("%d", &human_place);
if (!(CanGo(human_place, human))){
printf("無効な数値です。もう一度入力してください。\a\a\a\n\n");
continue;
}
Flip(human_place, human);
break;
}
}

//コンピュータの手
if (turn == computer){
printf("思考中・・・\n\n\n");
//コンピューターの評価点を表示し最善の場所をグローバル変数bestplaceに保存
BestPlace(board, computer);
printf("読み込んだ局面数:%d\n", count);
printf("コンピュータは%dに置きました。\n\n", bestplace);
//エラー判定
if (!(CanGo(bestplace, computer))){
printf("エラー3\n\n\a\a\a");
return(0);
}

//裏返す
Flip(bestplace, computer);
}

//黒駒・白駒・空きマスをカウント
Count(board);

//終了の時はループをブレイク
if (NONE_NUMBER == 0){
printf("空きマスが無くなったので終了です。\n\n");
break;
}

//一色になったら終了
if (BLACK_NUMBER == 0){
printf("ボード上に白しかありません。\n\n");
break;
}
else if (WHITE_NUMBER == 0){
printf("ボード上に黒しかありません。\n\n");
break;
}

//パスの場合はここでループ
if (!(CanPut(EnemyColor(turn)))){
if (EnemyColor(turn) == human){
puts("人間は置く場所がありません。");
}
else if (EnemyColor(turn) == computer){
puts("コンピューターは置く場所がありません。");
}
else{
puts("エラー1");
}
continue;
}

//ターンチェンジ
Change();



}

//ボードを描画
Disp();

//結果を出力
Judge();

printf("\n\n\n");
puts("リトライするときは1、そうでない場合は2を入力してください。");
printf("入力:"); scanf_s("%d", &retry);

if (retry == 1){
continue;
}
else{
break;
}

}

return(0);
}





/******************************************************************************** 評価関数 ******************************************************************************************/

//評価値の合計を計算
int Evalu_Total(const int ThinkBoard[8][8], int turn){

//評価値の合計
int totalscore = 0;

//評価関数のパラメーターを変化させる
ChangeStage(ThinkBoard);

//各々の評価値
//上から順に【着手可能手数】、【マスの評価値の合計】
int e[EVALUATION_POINTS] = { Evalu_HowMany(ThinkBoard, turn),
Evalu_BoardScore(ThinkBoard, turn), Evalu_HowManyStones(ThinkBoard, turn), Evalu_HowManyStones(ThinkBoard, EnemyColor(turn)) };

for (int i = 0; i < EVALUATION_POINTS; i++){
totalscore += w[i] * e[i];
}

return(totalscore);
}


//着手可能手数を計算して返却(評価する局面とターンを値渡し)
int Evalu_HowMany(const int ThinkBoard[8][8], int turn){

//着手可能手数を保存する変数
int count = 0;

//総当りで解析
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
if (CanGo(Trans_place(j, i), turn) == 1){
count++;
}
}
}

return(count);
}

//マスの評価値の合計を計算して数値を返却
int Evalu_BoardScore(const int ThinkBoard[8][8], int turn){

//評価値を保存する変数
int score = 0;

for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
if (ThinkBoard[i][j] == turn){
score += CellScore[i][j];
}
}
}

return(score);
}

//自分の駒の数を計算して数値を返却
int Evalu_HowManyStones(const int ThinkBoard[8][8], int turn){

//カウントする
Count(ThinkBoard);

if (turn == 1){
return(BLACK_NUMBER);
}
else if (turn == 2){
return(WHITE_NUMBER);
}

//エラー
return(-10000);
}

//局面を判断して評価関数のパラメーターを変化
void ChangeStage(const int ThinkBoard[8][8]){

//左から【着手可能手数】、【マスの評価値の合計】、【自分の駒の数】、【相手の駒の数】
//各々の評価関数のウェイトを書き換える
if (NONE_NUMBER >= FIRST){ //【序盤】
for (int i = 0; i < EVALUATION_POINTS; i++){
w[i] = w_first[i];
}
}
else if (FIRST > NONE_NUMBER && NONE_NUMBER >= MIDDLE){ //【中盤】
for (int i = 0; i < EVALUATION_POINTS; i++){
w[i] = w_middle[i];
}
}
else if (MIDDLE > NONE_NUMBER && NONE_NUMBER >= FINAL){ //【終盤】
for (int i = 0; i < EVALUATION_POINTS; i++){
w[i] = w_final[i];
}
}
else{
for (int i = 0; i < EVALUATION_POINTS; i++){ //エラー
w[i] = -10000;
}
printf("エラー2\n\n");
}

}


/******************************************************************************** 探索アルゴリズム ******************************************************************************************/

//コンピューターの評価点を表示し最善の場所をグローバル変数bestplaceに保存
void BestPlace(int ThinkBoard[8][8], int turn){

//局面数カウントをリセット
count = 0;


//minimax()で評価点を記録し最善手をグローバル変数bestplaceに記録
int score = minimax(board, turn, DEPTH);

//評価点を出力
if (turn == human) printf("\n人間の評価点は%dです。\n", score);
if(turn == computer) printf("\nコンピューターの評価点:%d\n", score);

//ボード上のカウントを戻す
Count(ThinkBoard);

}

//minimax法で評価点を出力
int minimax(int ThinkBoard[8][8], int ThinkTurn, int depth){


//ボード上をカウント
Count(ThinkBoard);

//即死局面
if (BLACK_NUMBER == 0 || WHITE_NUMBER == 0){

//削除する表現
printf("即死局面\n\n");

if (ThinkTurn == turn) return(-32768); //自分が即死局面
if (ThinkTurn == EnemyColor(turn)) return(32768); //相手が即死局面
}

//空きマスが無い場合
if (NONE_NUMBER == 0){
return(Evalu_Total(ThinkBoard, turn));
}

//リーフの場合は評価値を返却する
if (depth == 0) return(Evalu_Total(ThinkBoard, turn));

int score = -32768;
int var;

//局面を保存する
for (int k = 0; k < 8; k++){
for (int l = 0; l < 8; l++){
saveboard[k][l][depth - 1] = ThinkBoard[k][l];
}
}

for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){

if (CanGo(Trans_place(j, i), ThinkTurn)){

//実際に裏返す
Flip(Trans_place(j, i), ThinkTurn);

//局面数をカウント
count++;

//再帰
if (CanPut(EnemyColor(ThinkTurn))) var = minimax(ThinkBoard, EnemyColor(ThinkTurn), depth - 1); //着手可能な場所がある
if (!(CanPut(EnemyColor(ThinkTurn)))) var = minimax(ThinkBoard, ThinkTurn, depth - 1); //着手可能な場所がない


//得点と最善手を更新
if (ThinkTurn == turn && score < var){
score = var;
if (depth == DEPTH){
bestplace = Trans_place(j, i);
}
}
if (ThinkTurn == EnemyColor(turn) && score < -var){
score = -var;
}

//局面を1手戻す
for (int k = 0; k < 8; k++){
for (int l = 0; l < 8; l++){
ThinkBoard[k][l] = saveboard[k][l][depth - 1];
}
}

}
}
}

//最大値を返却
if (ThinkTurn == turn){
return(score);
}
//最小値を返却
if (ThinkTurn == EnemyColor(turn)){
return(-score);
}

//エラー判定
printf("\a\a\aエラー4");
return(99999);


}




/********************************************************* 根幹部分の関数(基本的に改変する必要が無い) ******************************************************************************************/

//多次元配列のx要素に変換
int Trans_x(int place){
return((place % 10) - 1);
}

//多次元配列のy要素に変換
int Trans_y(int place){
return((place / 10) - 1);
}

//多次元配列のx,y要素を人間が入力する値に変換
int Trans_place(int x, int y){
return((1 + y) * 10 + (x + 1));
}


//敵の色を出力
int EnemyColor(int turn){
if (turn == 1){
return(2);
}
else if (turn == 2){
return(1);
}
//エラー
return(-1);
}

//その方向のベクトルを1つ伸ばす
int StretchVec(int a){

int b = a;

if (a > 0){
b++;
}
else if (a < 0){
b--;
}
return(b);
}

//初期化
void Initia(void){

//全てのマスを空きにする
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
board[i][j] = NONE;
}
}

//最初の4マスを定義
board[4][3] = BLACK;
board[3][4] = BLACK;
board[3][3] = WHITE;
board[4][4] = WHITE;

}

//ボードを描画
void Disp(void){

//行を描画
printf(" ");
for (int i = 1; i <= 8; i++){
printf("%2d", i);
}
putchar('\n');
//マスを描画
for (int i = 0; i < 8; i++){
//列を描画
printf("%2d", (i + 1) * 10);
for (int j = 0; j < 8; j++){
switch (board[i][j]){
case NONE: printf("□"); break;
case BLACK: printf("○"); break;
case WHITE: printf("●"); break;
default: printf("\a\a\aerror"); break;
}
}
putchar('\n');
}
}

//有効な場所かどうかを判断(有効な場合は1、有効でない場合は0を返却)
int CanGo(int place, int turn){

//相手の色を定義
int enemy = EnemyColor(turn);
//x,yの要素を取り出す
int x = Trans_x(place);
int y = Trans_y(place);

//指定された場所がボード上でない
if (x < 0 || y < 0 || x > 7 || y > 7){
return(0);
}

//指定された場所に既に駒がある場合
if (board[y][x] != NONE){
return(0);
}

for (int i = 0; i < 8; i++){
//周りに相手の駒があるかどうかを判別
if (board[y + vec_y[i]][x + vec_x[i]] == enemy){

//ベクトルの成分をコピー
int vec_xx = vec_x[i];
int vec_yy = vec_y[i];

//その方向を詳しく調べる
while (1){
//ベクトルを1つ伸ばす
vec_xx = StretchVec(vec_xx);
vec_yy = StretchVec(vec_yy);

//探索ベクトルがボード外
if ((x + vec_xx) < 0 || (y + vec_yy) < 0 || (x + vec_xx) > 7 || (y + vec_yy) > 7){
break;
}//空きマスに出た
else if (board[y + vec_yy][x + vec_xx] == NONE){
break;
}//自分の駒に出た
else if (board[y + vec_yy][x + vec_xx] == turn){
return(1);
}
}
}
}
return(0);
}

//駒を置けるかどうかを判別(置ける場合は1、置けない場合は0を返却)
int CanPut(int turn){

//総当りで解析
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
if (CanGo((i + 1) * 10 + (j + 1), turn) == 1){
return(1);
}
}
}
return(0);
}

//マスの状態の数
void Count(const int ThinkBoard[8][8]){

//マスの状態の数をリセット
NONE_NUMBER = 0;
BLACK_NUMBER = 0;
WHITE_NUMBER = 0;

for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
switch (ThinkBoard[i][j]){
case NONE: ++NONE_NUMBER; break;
case BLACK: ++BLACK_NUMBER; break;
case WHITE: ++WHITE_NUMBER; break;
default: printf("\a\a\aerror"); break;
}
}
}
}

//駒を裏返す
void Flip(int place, int turn){

//相手の色を定義
int enemy = EnemyColor(turn);
//x,yの要素を取り出す
int x = Trans_x(place);
int y = Trans_y(place);

//指定した場所に駒を置く
board[y][x] = turn;

for (int i = 0; i < 8; i++){
//周りに相手の駒があるかどうかを判別
if (board[y + vec_y[i]][x + vec_x[i]] == enemy){

//ベクトルの成分をコピー
int vec_xx = vec_x[i];
int vec_yy = vec_y[i];
//マスをカウントする変数
int count = 0;

//その方向を詳しく調べる
while (1){

//探索ベクトルがボード外
if ((x + vec_xx) < 0 || (y + vec_yy) < 0 || (x + vec_xx) > 7 || (y + vec_yy) > 7){
break;
}//空きマスに出た
else if (board[y + vec_yy][x + vec_xx] == NONE){
break;
}//自分の駒に出た
else if (board[y + vec_yy][x + vec_xx] == turn){
//ベクトルの成分をコピー
int vec_xxx = vec_x[i];
int vec_yyy = vec_y[i];
//裏返す
for (int j = 0; j < count; j++){
//マスの書き換え
board[y + vec_yyy][x + vec_xxx] = turn;
//ベクトルを1つ伸ばす
vec_xxx = StretchVec(vec_xxx);
vec_yyy = StretchVec(vec_yyy);
}
break;
}
//ベクトルを1つ伸ばす
vec_xx = StretchVec(vec_xx);
vec_yy = StretchVec(vec_yy);
//カウントアップ
count++;
}
}
}
}

//ターンを入れ替える
void Change(void){

if (turn == 1){
turn = 2;
}
else{
turn = 1;
}
}

//勝敗を決定
void Judge(void){

//ボード上をカウント
Count(board);

printf("黒駒の数:%d個\n", BLACK_NUMBER);
printf("白駒の数:%d個\n\n", WHITE_NUMBER);

if (BLACK_NUMBER > WHITE_NUMBER){
if (human == BLACK) printf("人間の勝ちです。\n\n");
if (computer == BLACK) printf("コンピュータの勝ちです。\n\n");
}
else if (WHITE_NUMBER > BLACK_NUMBER){
if (human == WHITE) printf("人間の勝ちです。\n\n");
if (computer == BLACK) printf("コンピュータの勝ちです。\n\n");
}
else{
printf("引き分けです。\n\n");
}

}



ちょっと解説します。


まず、評価関数が4つしかありません←wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww


それと各々の評価関数のパラメーターも調節してなくてデタラメなのでいじればもう少し強くなるかも知れませんね。



それでは



ノシ

関連記事



↓ランキングに参加しています!お手数ですが、よろしければ3つとも1日1回クリックをお願いしますm(__)m↓
にほんブログ村 大学生日記ブログ 理系大学生へ
にほんブログ村


【カテゴリー:開発・発明系 コメント(0)

コメント







(コメントの削除依頼等は右下の「連絡先」にて承ります。)

前ページ || 次ページ

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。