オセロAIの組み込み

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ひーやん

オセロAIの組み込み

#1

投稿記事 by ひーやん » 11年前

http://idehideout.fc2web.com/p/rev/00.html
このサイトを参考にオセロゲームを作りました。
ですができたのは対人戦と乱数で適当に相手をするコンピュータだけです。

今考えているのはミニマックス法で
http://hitsujiai.blog48.fc2.com/blog-entry-26.html
を参考にしようと考えているのですが
実際のところこのeval関数はどういったところで使用するのでしょうか。

回答よろしくお願いします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#2

投稿記事 by softya(ソフト屋) » 11年前

そもそも、最初のサイトの内容を十分に理解できているのでしょうか?
(対人戦を行うオセロをサンプル無しで書けるかと言う意味です)
それが出来て、その上に簡単なAIを自分で実装してみて(4つ角を狙う、それ以外は乱数みたいな)。そこまで勉強積み重ねた上のミニマックス法だと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
asd
記事: 319
登録日時: 14年前

Re: オセロAIの組み込み

#3

投稿記事 by asd » 11年前

これからの派生でしょうか?
http://dixq.net/forum/viewtopic.php?f=3&t=15388
ひーやん さんが書きました: 今考えているのはミニマックス法で
http://hitsujiai.blog48.fc2.com/blog-entry-26.html
を参考にしようと考えているのですが
実際のところこのeval関数はどういったところで使用するのでしょうか。

回答よろしくお願いします。
まずミニマックス法がどのようなアルゴリズムかは説明できますか?
またそのURLのページ内にて黒番または白番から見た盤面の評価値を算出する関数であると
記載があります。

上記の2つからどういう場合に使われるものかはある程度想像がつくかと思いますが。
オフトピック
前トピックの書き込みもみましたがどうもアルゴリズムを理解されていないようなので、
仮にその関数の使い方を教えたところで、次に具体的な組み込み方を質問されるだけのような気がします。
現状ではサイトを参考にプログラムを作ったのではなく、打ち込んだだけのような印象が強いです。
Advanced Supporting Developer
無理やりこじつけ(ぉ

ひーやん

Re: オセロAIの組み込み

#4

投稿記事 by ひーやん » 11年前

どうやら自分は参考にしたのではなく、ただ書き写したレベルだったようですね。
みなさんはどうやってC言語を学習したのですか…
正直、何から手をつければ良いのかわかりません。
入門書は関数まで学習したと思っていたのですがどうやらまだまだなんですね。
ポインタや構造体は使いどきが見つからず学習がイマイチできていません。
自分のイマイチなのでみなさんからすれば学習していない同然のレベルだと思います。

使えるようなったと感じたためオセロを作ろうしたのですが…
どうしたらC言語を勉強できるのですか…
大学にいかないとダメなんですか…

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#5

投稿記事 by softya(ソフト屋) » 11年前

どんな人も段階を踏まずに難しいことは出来ません。
例えると鉄棒で逆上がりが出来る様になったからといって、大車輪で月面宙返りできるとは思わないですよね。まぁ、この例は極端ですが。
つまり、プログラムも同じで難しいのは間を飛ばしまくっているわけです。
入門書をさらっとやってプログラムがいきなり書けるわけがありません。

一番簡単なゲームプログラムだと、じゃんけんですかね。
グー、チョキ、パーを0,1,2などの数値で入力して、コンピューターも乱数でグー、チョキ、パーを出します。アレンジはお任せします。
これをサンプルなしに独自に組んでみてください。C言語の文法書は読んでも可です。

> 大学にいかないとダメなんですか…
大学に行くだけで組めたら苦労しないのですね。
結局自分でやらないとダメだと思います。
※ 一部の天才除く。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
asd
記事: 319
登録日時: 14年前

Re: オセロAIの組み込み

#6

投稿記事 by asd » 11年前

ひーやん さんが書きました:どうやら自分は参考にしたのではなく、ただ書き写したレベルだったようですね。
みなさんはどうやってC言語を学習したのですか…
正直、何から手をつければ良いのかわかりません。
ご自分で提示しているURLの内容をまず理解するところから始めませんか?
処理の流れや仕組みが理解できているのならば、
敵側(AI)の打つ場所を変更するのもできるはずです。
今がランダムになっているのであれば、その処理を行っている場所を理解し、
その都度その時点でたくさん取れる場所に石を置くようにしようとか、
盤面の評価値が高い場所に置こうなど改良点が出てくるはずです。

最初は打ち込むだけでもOKですが、そこから処理の流れを理解し、
本当に理解できたかを手を加えて確認するという流れもありですよ。
ひーやん さんが書きました: 使えるようなったと感じたためオセロを作ろうしたのですが…
どうしたらC言語を勉強できるのですか…
大学にいかないとダメなんですか…
大学は貴方の代わりに努力をしてくれるモノではありません。
仮に大学に行ったとしても、自分で理解し努力しようとしない人はプログラムができるようにはなりません。

逆に大学に行かなくても独学でプログラムができるようになる人もいます。

入門書を自分で探し、不明点をここのようなサイトで質問し理解を深めるのと、
大学で教材を用意してもらい、不明点を教授やTAに質問し理解を深めるのは
本人のやる気次第でほぼ同じ内容になります。

大学に行けばエスカレーターに乗るかのように楽に階上まで行けると思ったら大間違いです。
#楽に物にできるなら留年なんてしませんよね?
Advanced Supporting Developer
無理やりこじつけ(ぉ

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#7

投稿記事 by softya(ソフト屋) » 11年前

asdさんの提案と私の提案のどちらを選んでも構いません。自分のやりたいようにやるべきだと思います。
両方ともやっても構いません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
asd
記事: 319
登録日時: 14年前

Re: オセロAIの組み込み

#8

投稿記事 by asd » 11年前

せっかくなので両方チャレンジしてみませんか?
softyaさんのじゃんけんゲームを作るには、全体の処理を考え、各処理を細分化してプログラム処理に変換する必要があります。
オセロの方はすでにプログラムが出来上がっているのでその処理を検証し、追いかけていくことになります。

どちらも勉強になりますよ(*´ヮ`)
不明点が出てきたらこのサイトを活用すればいいわけですしね。
Advanced Supporting Developer
無理やりこじつけ(ぉ

ひーやん

Re: オセロAIの組み込み

#9

投稿記事 by ひーやん » 11年前

多くの回答、提案ありがとうございます。
大学を出すのはよくなかったですね。
つい、自信をなくしてしまい発言してしまいました。すみません。

そこで回答してくださった方の提案どおり
じゃんけんを作ってみました。
ほぼ何も見てません。コード、アルゴリズム、インシデントなどどんなことでもご指摘ください。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int play = 0,cp = 0,i = 0,game = 0;

void hantei(void){//cpの手を判定(表示)する関数
    if(cp == 1)printf("グー\n");
    else if(cp == 2)printf("チョキ\n");
    else if(cp == 3)printf("パー\n");
}

void katimake(void){//勝ち負けを判定する関数
    if(play == 1&&cp == 2){
        printf("あなたの勝ちです\n");
        i = 1;
    }else if(play == 2&&cp == 3){
        printf("あなたの勝ちです\n");
        i = 1;
    }else if(play == 3&&cp == 1){
        printf("あなたの勝ちです\n");
        i = 1;
    }else if(play == cp){
        printf("あいこ\n");
        play = cp = 0;
        i = 0;
    }else{
        printf("あなたの負けです\n");
        i = 1;
    }
}

int main(){
    while(game == 0){
        printf("自分の手を入力\nグー:1\nチョキ:2\nパー:3\n");
        do{
            scanf("%d",&play);
            if(play != 1&&play != 2&&play != 3){
                printf("正しく入力してください\n");
                break;
            }
            srand((unsigned)time(NULL));//ここは調べました
            cp = rand()%3 + 1;
            printf("相手の手は");
            hantei();
    
            katimake();
        
        }while(i == 0);
        printf("じゃんけんを続けますか?続ける場合は0をやめる場合は0以外を入力してください\n");
        scanf("%d",&game);
    }
}
また、オセロのほうは時間ができ次第、回答通りに手をつけていこうと思います。

万が一、じゃんけんプログラムがこれでよければ新しい課題を頂く思います。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#10

投稿記事 by softya(ソフト屋) » 11年前

十分組めていると思いますよ。

ただ少し残念な所を書きますね。
・グローバル変数にする必要な無いものまでグローバル化している。
・関数の引数や戻り値にすべきものもグローバルにされている。
・play やcpやiやgameなど単体で意味が通らない変数名である事。playerHand cpuHand loopEnd gameOver などわかり易い名前を選ぶべきです。
・srand((unsigned)time(NULL));は毎ループすべきものではありません。
・while(game == 0){とdo{}while(i == 0);で使い方の統一性がありません。同じような用途で使い分けている意味がないのかと思います。
・hantei();の関数名が中の処理と完全に一致しないことですね。これは表示であって勝敗判定のような名前はふさわしくないと思います。
これらを気をつけないと大規模なコードになるとプログラムが破綻します。

【追記】
次にやるとしたら3目並べでしょうか。オセロに通じるものが有るますしね。
「三目並べ - Wikipedia」
http://ja.wikipedia.org/wiki/%E4%B8%89% ... 6%E3%81%B9
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: オセロAIの組み込み

#11

投稿記事 by usao » 11年前

オフトピック
自分の手を入力するところで
1=グー,2=チョキ,3=パー,9=終了
みたいに終了も選べるようにすればすっきりしそうですね

ひーやん

Re: オセロAIの組み込み

#12

投稿記事 by ひーやん » 11年前

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int gameEnd = 0;//このままでよいのでしょうか。グローバルにしないやり方がわかりません。

void cpuHand(int hand){//cpの手を判定(表示)する関数
    if(hand == 1)printf("グー\n");
    else if(hand == 2)printf("チョキ\n");
    else if(hand == 3)printf("パー\n");
}

void battle(int play,int cpu){//勝ち負けを判定する関数
    if(play == 1&&cpu == 2){
        printf("あなたの勝ちです\n");
        gameEnd = 1;
    }else if(play == 2&&cpu == 3){
        printf("あなたの勝ちです\n");
        gameEnd = 1;
    }else if(play == 3&&cpu == 1){
        printf("あなたの勝ちです\n");
        gameEnd = 1;
    }else if(play == cpu){
        printf("あいこ\n");
        play = cpu = 0;
        gameEnd = 0;
    }else{
        printf("あなたの負けです\n");
        gameEnd = 1;
    }
}

int main(){
    
    int mainLoop = 0,play = 0,cpu = 0;
    
    srand((unsigned)time(NULL));//ここは調べました
    
    while(mainLoop == 0){//メインループ
        printf("自分の手を入力\nグー:1\nチョキ:2\nパー:3\n");
        while(gameEnd == 0){//初回・あいこのみ
            scanf("%d",&play);
            //エラー
            if(play != 1&&play != 2&&play != 3){
                printf("正しく入力してください\n");
                break;
            }
            //コンピュータ
            cpu = rand()%3 + 1;
            printf("相手の手は");
            cpuHand(cpu);
            //勝敗を決める
            battle(play,cpu);
        }
        printf("じゃんけんを\n続ける:0を入力\nやめる:0以外を入力\n");
        scanf("%d",&mainLoop);
        gameEnd = 0;
    }
}
softya(ソフト屋) さんが書きました: ・グローバル変数にする必要な無いものまでグローバル化している。
・関数の引数や戻り値にすべきものもグローバルにされている。
・play やcpやiやgameなど単体で意味が通らない変数名である事。playerHand cpuHand loopEnd gameOver などわかり易い名前を選ぶべきです。
・srand((unsigned)time(NULL));は毎ループすべきものではありません。
・while(game == 0){とdo{}while(i == 0);で使い方の統一性がありません。同じような用途で使い分けている意味がないのかと思います。
・hantei();の関数名が中の処理と完全に一致しないことですね。これは表示であって勝敗判定のような名前はふさわしくないと思います。
これらを気をつけないと大規模なコードになるとプログラムが破綻します。
とりあえず指摘に従って書き換えてみました。
おそらくまだまだ残念な点、あるいは自分が勘違いしてしまっている点、理解し得ていない点があるかと思います。
どんどん指摘ください。
usao さんが書きました:
オフトピック
自分の手を入力するところで
1=グー,2=チョキ,3=パー,9=終了
みたいに終了も選べるようにすればすっきりしそうですね
こちらも取り入れようと考えたのですがとりあえず今の問題を解決出来次第にしたいと思います。

p.s 三目並べは対人戦だけでしょうか。コンピュータもつけたほうがよいのでしょうか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#13

投稿記事 by softya(ソフト屋) » 11年前

まだ少し気になる点を。
・関数名が名詞だけだと意味不明ですね。動詞も含めてみてください。cpuHandよりは、displayCpuHandの方が機能が明確です。
・gameEndはbattleでしか変化しないのなら戻り値に出来るはずです。
・battleでplay = cpu = 0;は意味が無いです。mainのplayやcpu値は変化しませんし変化させる必要もないです。変数のスコープを調べてみてください。
こんなところでしょうか。本当はもっと良くなるんですが考えてみてください。

> p.s 三目並べは対人戦だけでしょうか。コンピュータもつけたほうがよいのでしょうか。

こちらとしては「ご自由に」なのですが、どちらが難しくなると思いますか?
突き放すようですが、こちらがテーマを与え続ける訳には行かないので、ご自分で判断すべきところはして下さい。
少しづつ今ちょっと難しいことにチャレンジしていけば、やがてはすごく難しいことも出来るようになります。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#14

投稿記事 by ひーやん » 11年前

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


void displayCpuHand(int hand){//cpの手を判定(表示)する関数
    if(hand == 1)printf("グー\n");
    else if(hand == 2)printf("チョキ\n");
    else if(hand == 3)printf("パー\n");
}

int battleJudge(int play,int cpu){//勝ち負けを判定する関数
    if( (play == 1&&cpu == 2) || (play == 2&&cpu == 3) || (play == 3&&cpu == 1) ){
        printf("あなたの勝ちです\n");
        return 1;
    }else if(play == cpu){
        printf("あいこ\n");
        return 0;
    }else{
        printf("あなたの負けです\n");
        return 1;
    }
}

int main(){
    
    int mainLoop = 0,play = 0,cpu = 0;
    
    srand((unsigned)time(NULL));//ここは調べました
        
    while(mainLoop == 0){//初回・あいこのみ
        //プレイヤー
        if(play == 0)//初回のみ表示したい
        printf("自分の手を入力\nグー:1\nチョキ:2\nパー:3\n");
        scanf("%d",&play);
            
        //エラー
        if(play != 1&&play != 2&&play != 3){
            printf("正しく入力してください\n");
            break;
        }
            
        //コンピュータ
        cpu = rand()%3 + 1;
        printf("相手の手は");
        displayCpuHand(cpu);
            
        //勝敗を決める
        mainLoop = battleJudge(play,cpu);
        
        //ゲーム終了
        if(mainLoop == 1){
            printf("じゃんけんを\n続ける:0を入力\nやめる:0以外を入力\n");
            scanf("%d",&mainLoop);
            play = 0;
        }
    
    }
}
グローバル変数をなくし、戻り値などを使ってみました。自分的にはだいぶスッキリしたかと思います。
まだ気になる点がありましたら、指摘ください。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#15

投稿記事 by softya(ソフト屋) » 11年前

だいぶ良くなったと思いますので次に行きましょう。
後日に見ると改善点も見えてくると思いますが、今はこれで十分です。
ここでこだわり過ぎると先に進めませんから。

【補足】
十分に理解しているので順調ですよ。。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#16

投稿記事 by ひーやん » 11年前

三目並べをつくることにしたんですがうまく進みません(泣
なにかヒントください。三つならんでいるかの判定がつくれません。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: オセロAIの組み込み

#17

投稿記事 by みけCAT » 11年前

ひーやん さんが書きました:三目並べをつくることにしたんですがうまく進みません(泣
なにかヒントください。三つならんでいるかの判定がつくれません。
今できているコードを貼るべきだと思います。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: オセロAIの組み込み

#18

投稿記事 by usao » 11年前

先に 2目ならべ(!) とかを作ってみると(考えてみるだけでもいいのかも)良いかもしれませんね.
2*2の4マスしかなくて,先に2つ並んだら勝ち.
(どう考えても先攻後攻決まった時点で終了ですがそういう事柄は置いといて)
各手番において,勝利判定を行うにはどうすればいいのか,というのが想像つけば 3目並べでも一緒かな,と.

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#19

投稿記事 by softya(ソフト屋) » 11年前

最終的には直すとしても、全部if文で組むことも出来ませんか? 3+3+2パターンぐらいしか無いと思いますが。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
asd
記事: 319
登録日時: 14年前

Re: オセロAIの組み込み

#20

投稿記事 by asd » 11年前

みけCATさんの言う通りプログラムを提示してもらわないと、ゲームの体はできていて判定だけができていないのか、
いきなり判定文だけ作ろうとしているのかわからないです。

質問文の通り三つ並んでいるかの判定だけができていないならば、盤面を走査してどこに石が置いてあるかの判定はできていて、
その石から各方向に三つ石が並んでいるかの判定ができないということでしょうか?
Advanced Supporting Developer
無理やりこじつけ(ぉ

ひーやん

Re: オセロAIの組み込み

#21

投稿記事 by ひーやん » 11年前

なかなか返信できずすみません。
時間が取れなくて・・・。以後もこんなペースでやっていくかもしれませんがよろしくお願いします。
とりあえず書けるところを書いてみました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char ban[5][5] = {{0,2,0,2,0},//盤
                    {1,2,1,2,1},
                    {0,2,0,2,0},
                    {1,2,1,2,1},
                    {0,2,0,2,0}};

void showDisplay(void){//画面表示関数
    int i,j;
    
    printf("  ");//左上の空白
    
    for(i = 0; i < 5; i++)
        printf("%d",(i+1));//番号の表示
    printf("\n");
    for(i = 0; i < 5; i++){
        printf("%d",(i+1)*10);//番号
        for(j = 0; j < 5; j++){
            switch(ban[i][j]){
                case 0:
                    printf(" ");
                    break;
                case 1:
                    printf("-");
                    break;
                case 2:
                    printf("|");
                    break;
                case 3:
                    printf("o");//プレイヤー
                    break;
                case 4:
                    printf("x");//コンピュータ
                    break;
                default:
                    printf("er");
            }
        }
        printf("\n");
    }
}


int check(int x,int y){
    if(ban[x][y] != 0){
        printf("正しく入力してください\n");
        return 1;
    }
    return 0;
}


void input(int turn){//入力関数
    int place = 0,x,y;
    while(1){
        
        if(turn == 0)//プレイヤー
            scanf("%d",&place);
        else if(turn == 1)//コンピュータ
            place = rand()%5+1;
        else printf("error\n");
    
        if(place < 11 || place > 66){//例外処理
            printf("Error\n");
            place = 0;
            continue;//以下を無視
        }
        
        //入力された値を変換
        x = place / 10;
        y = place % 10;
        
        if(check(x,y) == 1)break;
        
        if(turn == 0)ban[x][y] = 3;
        else if(turn == 1)ban[x][y] = 4;
    }
    place = 0;
}


int main(){
    
    int mainLoop = 0,play = 0,turn = 0;
    
    srand((unsigned)time(NULL));
    
    while(mainLoop == 0){
        //画面表示
        showDisplay();
        //プレイヤー
        if(play == 0)//初回のみ
        printf("自分の手を入力\n縦と横の番号を足した値を入力する\n");
        input(turn);
        //ターン入れ替え
        turn = 1 - turn;
        //ゲーム終了
        //if((ban[1][1] == 3 && ban[1][3] == 3 && ban[1][5] == 3)
        //↑こんな感じで判定を書くのでしょうか?
    }
        //ゲーム終了
        if(mainLoop == 1){
            printf("続ける:0を入力\nやめる:0以外を入力\n");
            scanf("%d",&mainLoop);
        }
}
ゲーム終了判定はどう書いたらよいのでしょうか。すべての通りを書いてみるべきですか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#22

投稿記事 by softya(ソフト屋) » 11年前

すべてを書いてみるべきでしょうね。
その前に33と入力してもエラーが無限ループしますが、これは? 
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#23

投稿記事 by ひーやん » 11年前

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char ban[5][5] = {{0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0}};

void showDisplay(void){//画面表示関数
    int i,j;
    
    printf(" ");//左上の空白
    
    for(i = 0; i < 5; i++)
        printf("%d",(i+1));
    printf("\n");
    for(i = 0; i < 5; i++){
        printf("%d",(i+1));
        for(j = 0; j < 5; j++){
            switch(ban[i][j]){
                case 0:
                    printf(" ");
                    break;
                case 1:
                    printf("-");
                    break;
                case 2:
                    printf("|");
                    break;
                case 3:
                    printf("o");//プレイヤー
                    break;
                case 4:
                    printf("x");//コンピュータ
                    break;
                default:
                    printf("er");
            }
        }
        printf("\n");
    }
}


int check(int x,int y,int turn){
    if(ban[x-1][y-1] != 0 && turn == 0){
        printf("正しく入力してください\n");
        return 1;
    }else if(ban[x-1][y-1] != 0 && turn == 1){
        return 2;
    }
    return 0;
}


void input(int turn){//入力関数
    int place = 0,x,y;
    while(1){
        
        if(turn == 0)//プレイヤー
            scanf("%d",&place);
        else if(turn == 1)//コンピュータ
            place = rand()%5+1*10 + rand()%5+1;
        else printf("error\n");
    
        if(place < 11 || place > 66){//例外処理
            printf("Error\n");
            place = 0;
        }
        
        //入力された値を変換
        x = place / 10;
        y = place % 10;
        
        if(check(x-1,y-1,turn) == 1 || check(x-1,y-1,turn) == 2){
            place = 0;
            x = 0;
            y = 0;
            continue;
        }
        
        if(turn == 0){//プレイヤーターン
            ban[x-1][y-1] = 3;
            break;
        }
        else if(turn == 1){//コンピュータターン
            ban[x-1][y-1] = 4;
            break;
        }
    }
    place = 0;
}


int checkEnd(void){
    //プレイヤー
    if(ban[0][0] == 3 && ban[3][0] == 3 && ban[4][0] == 3)return 1;
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 1;
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 1;
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 1;
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 1;
    else return 0;
    //コンピュータ
    if(ban[0][0] == 3 && ban[3][0] == 3 && ban[4][0] == 3)return 2;
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 2;
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 2;
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 2;
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 2;
    else return 0;
}

int main(){
    
    int mainLoop = 0,gameStart = 0,turn = 0,gameEnd = 0;
    
    srand((unsigned)time(NULL));
    
    while(mainLoop == 0){
        
        //画面表示
        showDisplay();
        
        //プレイヤー
        if(gameStart == 0){//初回のみ
        printf("三目並べ\no:あなた\nx:相手\n自分の手を入力\n横の番号と縦の番号を10倍した値を入力\n例)(1,1) => 11\n");
        gameStart = 1;
        }
        
        //入力
        input(turn);
        
        //ターン入れ替え
        turn = 1 - turn;
        
        //ゲーム終了
        mainLoop = checkEnd();
        if(mainLoop == 1)printf("あなたの勝ちです\n");
        else if(mainLoop == 2)printf("あなたの負けです\n");
        
        //ゲーム終了
        if(gameEnd == 1){
            printf("続ける:0を入力\nやめる:0以外を入力\n");
            scanf("%d",&gameEnd);
            gameStart = 0;
        }
        
    }
}
こんな感じで書き換えてみましたが空白でないのにマルバツが書けたり、
逆に空白なのにマルバツが書けなかったりしました。

どこがおかしいのでしょうか。教えてください。

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: オセロAIの組み込み

#24

投稿記事 by usao » 11年前

気のせいかもしれませんが,以下の2つがミスマッチ状態というかそんな感.

・ユーザ入力の座標指定は X,Y共に{1,3,5}であるような形にしたい?
 (実際のデータ(ban)の持ち方と関係ない形を考えているなら,{0,1,2}とか{1,2,3}とかの方がわかりやすいと思うけど)
・C言語の配列(ban)で それに対応する添え字は {0,2,4}

……と書いてたら修正版が来ましたね.
しかしそれだと Check()の中と外とで2回-1されちゃうような.


あと,コンピュータの手の式が間違ってるかな

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#25

投稿記事 by softya(ソフト屋) » 11年前

これは、一気に作りすぎでしょうね。
1.人間の手の入力チェックを完璧にする
2.人間の手を板に置けるようにする。
3.人間同士で対戦できる仕組みを作る
4.勝敗判定を付ける。
を1から順番にクリアしていくべきでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#26

投稿記事 by ひーやん » 11年前

コンピュータの手の式など改善した結果ほぼ遊べるようになりました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char ban[5][5] = {{0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0}};

void showDisplay(void){//画面表示関数
    int i,j;
    
    printf(" ");//左上の空白
    
    for(i = 0; i < 5; i++)
        printf("%d",(i+1));
    printf("\n");
    for(i = 0; i < 5; i++){
        printf("%d",(i+1));
        for(j = 0; j < 5; j++){
            switch(ban[i][j]){
                case 0:
                    printf(" ");
                    break;
                case 1:
                    printf("-");
                    break;
                case 2:
                    printf("|");
                    break;
                case 3:
                    printf("o");//プレイヤー
                    break;
                case 4:
                    printf("x");//コンピュータ
                    break;
                default:
                    printf("er");
            }
        }
        printf("\n");
    }
}


int check(int x,int y,int turn){
    if(ban[x][y] != 0 && turn == 0){
        printf("正しく入力してください\n");
        return 1;
    }else if(ban[x][y] != 0 && turn == 1){
        return 2;
    }
    return 0;
}


void input(int turn){//入力関数
    int place = 0,x = 0,y = 0;
    while(1){
        
        if(turn == 0)//プレイヤー
            scanf("%d",&place);
        else if(turn == 1)//コンピュータ
            place = (rand()%5+1)*10 + rand()%5+1;
        else printf("error\n");
    
        if(place < 11 || place > 55){//例外処理
            printf("正しく入力してください\n");
            place = 0;
            continue;
        }
        
        //入力された値を変換
        x = place / 10;
        y = place % 10;
        
        if(check(x-1,y-1,turn) == 1 || check(x-1,y-1,turn) == 2){
            place = 0;
            x = 0;
            y = 0;
            continue;
        }
        
        if(turn == 0){//プレイヤーターン
            ban[x-1][y-1] = 3;
            break;
        }
        else if(turn == 1){//コンピュータターン
            ban[x-1][y-1] = 4;
            break;
        }
    }
    place = 0;
}


int checkEnd(void){
    //プレイヤー
    if(ban[0][0] == 3 && ban[2][0] == 3 && ban[4][0] == 3)return 1;
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 1;
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 1;
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 1;
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 1;
    else return 0;
    //コンピュータ
    if(ban[0][0] == 3 && ban[2][0] == 3 && ban[4][0] == 3)return 2;
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 2;
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 2;
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 2;
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 2;
    else return 0;
}

int main(){
    
    int mainLoop = 0,gameStart = 0,turn = 0,gameEnd = 0;
    
    srand((unsigned)time(NULL));
    
    while(mainLoop == 0){
        
        //画面表示
        showDisplay();
        
        //プレイヤー
        if(gameStart == 0){//初回のみ
        printf("三目並べ\no:あなた\nx:相手\n自分の手を入力\n横の番号+縦の番号を10倍した値を入力\n例)(1,3) => 31\n");
        gameStart = 1;
        }
        
        //入力
        input(turn);
        
        //ターン入れ替え
        turn = 1 - turn;
        
        //ゲーム終了
        mainLoop = checkEnd();
        if(mainLoop == 1){printf("あなたの勝ちです\n");showDisplay();}
        else if(mainLoop == 2){printf("あなたの負けです\n");showDisplay();}
        
        //ゲーム終了
        if(gameEnd == 1){
            printf("続ける:0を入力\nやめる:0以外を入力\n");
            scanf("%d",&gameEnd);
            gameStart = 0;
        }
        
    }
}
ただ、引き分けの判定がつくれていません。なにか簡単に引き分けを判定する方法ありませんか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#27

投稿記事 by softya(ソフト屋) » 11年前

簡単以前に、引分の条件を厳密に定義してみてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#28

投稿記事 by ひーやん » 11年前

条件としては「同じものが3つ並ぶ事なく、すべてのマスが埋まった場合」ではないでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#29

投稿記事 by softya(ソフト屋) » 11年前

そうですね。その条件の通りをプログラムに書くだけですよ。
「同じものが3つ並ぶ事なく」は勝利条件のnotですよね。あとは「すべてのマスが埋まった場合」を判定するだけですね。
所でcheckEnd()がバグっているように見えますが、本当にCPUは勝利できますか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#30

投稿記事 by ひーやん » 11年前

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char ban[5][5] = {{0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0}};

void showDisplay(void){//画面表示関数
    int i,j;
    
    printf(" ");//左上の空白
    
    for(i = 0; i < 5; i++)
        printf("%d",(i+1));
    printf("\n");
    for(i = 0; i < 5; i++){
        printf("%d",(i+1));
        for(j = 0; j < 5; j++){
            switch(ban[i][j]){
                case 0:
                    printf(" ");
                    break;
                case 1:
                    printf("-");
                    break;
                case 2:
                    printf("|");
                    break;
                case 3:
                    printf("o");//プレイヤー
                    break;
                case 4:
                    printf("x");//コンピュータ
                    break;
                default:
                    printf("er");
            }
        }
        printf("\n");
    }
}


int check(int x,int y,int turn){
    if(ban[x][y] != 0 && turn == 0){
        printf("正しく入力してください\n");
        return 1;
    }else if(ban[x][y] != 0 && turn == 1){
        return 2;
    }
    return 0;
}


void input(int turn){//入力関数
    int place = 0,x = 0,y = 0;
    while(1){
        
        if(turn == 0){//プレイヤー
            scanf("%d",&place);
            
            //数値以外を入力された場合の処理がわかりません。
            
        }else if(turn == 1)//コンピュータ
            place = (rand()%5+1)*10 + rand()%5+1;
        else printf("error\n");
    
        if(place < 11 || place > 55){//例外処理
            printf("正しく入力してください\n");
            place = 0;
            continue;
        }
        
        //入力された値を変換
        x = place / 10;
        y = place % 10;
        
        if(check(x-1,y-1,turn) == 1 || check(x-1,y-1,turn) == 2){
            place = 0;
            x = 0;
            y = 0;
            continue;
        }
        
        if(turn == 0){//プレイヤーターン
            ban[x-1][y-1] = 3;
            break;
        }
        else if(turn == 1){//コンピュータターン
            ban[x-1][y-1] = 4;
            break;
        }
    }
    place = 0;
}


int checkEnd(void){
    int i,j,count = 0;
    
    //プレイヤーの勝ち
    if(ban[0][0] == 3 && ban[2][0] == 3 && ban[4][0] == 3)return 1;
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 1;
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 1;
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 1;
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 1;
    
    //コンピュータの勝ち
    if(ban[0][0] == 4 && ban[2][0] == 4 && ban[4][0] == 4)return 2;
    else if(ban[0][0] == 4 && ban[2][2] == 4 && ban[4][4] == 4)return 2;
    else if(ban[0][0] == 4 && ban[0][2] == 4 && ban[0][4] == 4)return 2;
    else if(ban[0][4] == 4 && ban[2][4] == 4 && ban[4][4] == 4)return 2;
    else if(ban[2][0] == 4 && ban[2][2] == 4 && ban[2][4] == 4)return 2;
    
    for(i= 0; i < 6; i++){
        for(j = 0; j < 6; j++){
            if(ban[i][j] == 0)break;
            if(ban[i][j] == 1 || ban[i][j] == 2 || ban[i][j] == 3 || ban[i][j] == 4){
                count++;
                if(count == 25){return 3;continue;}//時々空白が2つほどあっても引き分けになってしまいます。
            }
        }
    }
    
    //初期化
    count = 0;
    return 0;
}


int main(){
    
    int mainLoop = 0,gameStart = 0,turn = 0;
    
    srand((unsigned)time(NULL));
    
    while(mainLoop == 0){
        
        //画面表示
        showDisplay();
        
        //プレイヤー
        if(gameStart == 0){//初回のみ
        printf("三目並べ\no:あなた\nx:相手\n自分の手を入力\n横の番号+縦の番号を10倍した値を入力\n例)(1,3) => 31\n");
        gameStart = 1;
        }
        
        //入力
        input(turn);
        
        //ターン入れ替え
        turn = 1 - turn;
        
        //ゲーム終了
        mainLoop = checkEnd();
        if(mainLoop == 1){printf("あなたの勝ちです\n");showDisplay();}
        else if(mainLoop == 2){printf("あなたの負けです\n");showDisplay();}
        else if(mainLoop == 3){printf("引き分けです\n");showDisplay();}
        
    }
    
}
CPUが勝つようにはできました。引き分けも大半はうまくいきます。
ですが、コード中にもあるように時々空白があっても引き分けになってしまいます。
なにがいけないのでしょうか。
また、試しに文字を入力したところ無限ループしました。文字が入力されたらループの前に初期化するようにしたいのですがどうしたらよいのでしょうか。0を代入しても意味がありませんよね?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#31

投稿記事 by softya(ソフト屋) » 11年前

添字は0,2,4のところしか調べなくて良いのなら、ループを変更すると良いでしょう。もうすこし簡単になります。
あと勝利パターンが8パターンづつあるはずなので、全パターン無いような。

>また、試しに文字を入力したところ無限ループしました。文字が入力されたらループの前に初期化するようにしたいのですがどうしたらよいのでしょうか。0を代入しても意味がありませんよね?

scanfの戻り値でエラーが分かりますので、それで検知して文字入力をキャンセルして下さい。
数値入力でエラー→文字で取り込み捨てる→再び数値入力へ
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひーやん

Re: オセロAIの組み込み

#32

投稿記事 by ひーやん » 11年前

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

char ban[5][5] = {{0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0},
                  {1,2,1,2,1},
                  {0,2,0,2,0}};

void showDisplay(void){//画面表示関数
    int i,j;
    
    printf(" ");//左上の空白
    
    for(i = 0; i < 5; i++)
        printf("%d",(i+1));
    printf("\n");
    for(i = 0; i < 5; i++){
        printf("%d",(i+1));
        for(j = 0; j < 5; j++){
            switch(ban[i][j]){
                case 0:
                    printf(" ");
                    break;
                case 1:
                    printf("-");
                    break;
                case 2:
                    printf("|");
                    break;
                case 3:
                    printf("o");//プレイヤー
                    break;
                case 4:
                    printf("x");//コンピュータ
                    break;
                default:
                    printf("er");
            }
        }
        printf("\n");
    }
}


int check(int x,int y,int turn){
    if(ban[x][y] != 0 && turn == 0){
        printf("正しく入力してください\n");
        return 1;
    }else if(ban[x][y] != 0 && turn == 1){
        return 2;
    }
    return 0;
}


void input(int turn){//入力関数
    int place = 0,x = 0,y = 0;
    while(1){
        
        if(turn == 0){//プレイヤー
            if(scanf("%d",&place) != 1){//数字なら1を返す
                printf("正しく入力してください\n");
                scanf("%*s");//中身を破棄
                continue;
            }
            
        }else if(turn == 1)//コンピュータ
            place = (rand()%5+1)*10 + rand()%5+1;
        else printf("error\n");
    
        if(place < 11 || place > 55){//例外処理
            printf("正しく入力してください\n");
            place = 0;
            continue;
        }
        
        //入力された値を変換
        x = place / 10;
        y = place % 10;
        
        if(check(x-1,y-1,turn) == 1 || check(x-1,y-1,turn) == 2){
            place = 0;
            x = 0;
            y = 0;
            continue;
        }
        
        if(turn == 0){//プレイヤーターン
            ban[x-1][y-1] = 3;
            break;
        }
        else if(turn == 1){//コンピュータターン
            ban[x-1][y-1] = 4;
            break;
        }
    }
    place = 0;
}


int checkEnd(void){
    int i,j,count = 0;
    
    //プレイヤーの勝ち
    if(ban[0][0] == 3 && ban[2][0] == 3 && ban[4][0] == 3)return 1;//11-15横
    else if(ban[0][0] == 3 && ban[2][2] == 3 && ban[4][4] == 3)return 1;//11-55斜め
    else if(ban[0][0] == 3 && ban[0][2] == 3 && ban[0][4] == 3)return 1;//11-51縦
    else if(ban[0][4] == 3 && ban[2][4] == 3 && ban[4][4] == 3)return 1;//51-55横
    else if(ban[2][0] == 3 && ban[2][2] == 3 && ban[2][4] == 3)return 1;//13-53縦
    else if(ban[0][2] == 3 && ban[2][2] == 3 && ban[4][2] == 3)return 1;//31-35横
    else if(ban[4][0] == 3 && ban[4][2] == 3 && ban[4][4] == 3)return 1;//15-55縦
    else if(ban[4][0] == 3 && ban[2][2] == 3 && ban[0][4] == 3)return 1;//15-51斜め
    
    //コンピュータの勝ち
    if(ban[0][0] == 3 && ban[2][0] == 3 && ban[4][0] == 3)return 2;//11-15横
    else if(ban[0][0] == 4 && ban[2][2] == 4 && ban[4][4] == 4)return 2;//11-55斜め
    else if(ban[0][0] == 4 && ban[0][2] == 4 && ban[0][4] == 4)return 2;//11-51縦
    else if(ban[0][4] == 4 && ban[2][4] == 4 && ban[4][4] == 4)return 2;//51-55横
    else if(ban[2][0] == 4 && ban[2][2] == 4 && ban[2][4] == 4)return 2;//13-53縦
    else if(ban[0][2] == 4 && ban[2][2] == 4 && ban[4][2] == 4)return 2;//31-35横
    else if(ban[4][0] == 4 && ban[4][2] == 4 && ban[4][4] == 4)return 2;//15-55縦
    else if(ban[4][0] == 4 && ban[2][2] == 4 && ban[0][4] == 4)return 2;//15-51斜め

    for(i= 0; i < 6; i=i+2){
        for(j = 0; j < 6; j=j+2){
            if(ban[i][j] == 0)break;
            if(ban[i][j] == 1 || ban[i][j] == 2 || ban[i][j] == 3 || ban[i][j] == 4){
                count++;
                if(count == 9){return 3;continue;}
            }
        }
    }
    
    //初期化
    count = 0;
    return 0;
}


int main(){
    
    int mainLoop = 0,gameStart = 0,turn = 0;
    
    srand((unsigned)time(NULL));
    
    while(mainLoop == 0){
        
        //画面表示
        showDisplay();
        
        //プレイヤー
        if(gameStart == 0){//初回のみ
        printf("三目並べ\no:あなた\nx:相手\n自分の手を入力\n横の番号+縦の番号を10倍した値を入力\n例)(1,3) => 31\n");
        gameStart = 1;
        }
        
        //入力
        input(turn);
        
        //ターン入れ替え
        turn = 1 - turn;
        
        //ゲーム終了
        mainLoop = checkEnd();
        if(mainLoop == 1){printf("あなたの勝ちです\n");showDisplay();}
        else if(mainLoop == 2){printf("あなたの負けです\n");showDisplay();}
        else if(mainLoop == 3){printf("引き分けです\n");showDisplay();}
        
    }
    
}
こんな感じで書き換えてみました。おそらくエラーはないかと・・・。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: オセロAIの組み込み

#33

投稿記事 by softya(ソフト屋) » 11年前

エラーが有るかある程度はテストはしてみましたか? テストも重要なプログラミングの一貫です。
本当は勝敗パターン8+8は自動テストとかしないといけないのですが、今回は省略しましょうか。
何回ぐらい、どういうテストをしたか書いておくべきかなと。

なお、変数名と関数名がやはり意味不明が多いので考えてもらうと良いと思います。
(例)showDisplayは確かに画面表示ですが何が表示されるかわからない名前です。画面に表示すると言うのはわざわざソースコードを見る人に伝えるべきものとして情報不足です。Displayが余分で代わりの言葉が必要だと思います。

あとは勝敗判定のループ化に挑戦して、その後三目並べの思考ルーチンを考えてみましょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

“C言語何でも質問掲示板” へ戻る