Python(オセロ01_Geminiでの対戦相手)

■Geminiでオセロの対戦相手を作る。
前にやったPythonのオセロのコードを少し修正して、Geminiへの問い合わせから石を置く場所を求める。
久しぶりなので、前回のコードの中身を思い出しながら修正。前回はランダム同士で対戦させていたけど、ロジックは次のようなものだった。
置くことができる場所をリストにして、その中からランダムで場所をチョイス(このときリストから削除)。その場所で相手の石をひっくり返すことができたら、それを採用。できなかったら、残ったリストから再度ランダムでチョイス。これを繰り返して最後までひっくり返せなかったらパス。
下のように"○"の場合はランダム(このロジック)、それ以外("●")の場合はGeminiへの問い合わせで場所を取得する。

while len(leftBlankList) > 0:
    if turn_ == "○":
        stone_pos = rd.choice(tempBlankList)
        tempBlankList.remove(stone_pos)
    else:
        stone_pos_str = gemini(board)
        print("gemini:"+str(stone_pos_str))
        if stone_pos_str == None:
            tempBlankList = ""
        stone_pos = ast.literal_eval(stone_pos_str)

    stoneList_tobeChanged = checkReversible(board, stone_pos)
    if len(stoneList_tobeChanged) > 0:
    <以下省略>

肝心のGeminiのコードが下(まだ試行錯誤中だけど)。

def gemini(board):
    # 優先順位が高い順にモデルをリスト化
    models_to_try = ["gemini-2.5-flash"]
    max_retries = 3
    client = genai.Client(api_key="(省略、APIキー部分)")

    for model_id in models_to_try:
        for i in range(max_retries):
            try:
                print(f"モデル {model_id} で試行中... ({i + 1}/{max_retries})")

                response = client.models.generate_content(
                    model=model_id,
                    contents=f"盤面: {board}",
                    config=types.GenerateContentConfig(
                        system_instruction=(
                            "オセロAI。回答は[行,列]形式のみ。例:[3,5]。"
                            "回答は-が入っているところから選択すること。"
                            "解説不要。1-8の範囲で選択。あなたは●。あなたはプロのオセロプレイヤーです。"
                            "置くところがないときはNoneを返却"
                        ),
                        temperature=0.1,
                    ),
                )

                # 正常に取得できたら返却
                if response and response.text:
                    return response.text.strip()

            except exceptions.ResourceExhausted:
                print(f"モデル {model_id} の制限に達しました。")
                if model_id == models_to_try[-1]:
                    # 最後のモデルでも制限なら待機
                    print("全モデル制限中。60秒待機します...")
                    time.sleep(60)
                else:
                    # 次のモデル(1.5 Flash等)へ切り替え
                    print("次のモデルへ切り替えます。")
                    break

            except Exception as e:
                print(f"予期せぬエラー ({model_id}): {e}")
                break

    return None

回答のフォーマットを指定しないと出てきた回答をうまく処理できないので、[3, 5]のように回答を指定。これで何手かはうまくいった。下の実行結果では[5, 3] の回答が返ってきて、置くことができた(赤丸部分)。

ただ、画面から分かる通り、先に[4, 7]と置くことができない回答もしている。また、既に石が置いてあるところを回答したりもする。この辺りは、質問の工夫でもう少し何とかなるかも。

上の実行では、gemini-2.5-flashのモデルを使っているけど、現在RPD(1日のリクエスト数)は無料枠では20が上限になっている。オセロの対戦では盤の状態を送って適切な場所を求めるなら、30のリクエストは必要で明らかに不十分。Google AI Studioでは、Gemini 2 Flashとか他のバージョンの記載もあるけど、実際にAPIで使おうとすると、上限が0に設定されているといった例外が発生して使えない。この辺りは、有料枠の設定とかすれば何とかなるかな。

とりあえずの目標は、どれかのモデルでオセロの1ゲームを達成することにする。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です