🏍️ ポンコツ夫婦のBIKE日記 with Insta360 Vol.5:馬飼ビーチ編

monkey125

🏍️ ポンコツ夫婦のBIKE日記 with Insta360 Vol.5:馬飼ビーチ編

私たちR60のポンコツ夫婦の趣味の柱であるツーリング。今回は、愛車にInsta360 Ace Proを搭載し、Dr.takodemousの青春の地馬飼ビーチ周辺へと足を延ばしました。

相変わらずRedkabagonさんは、公道デビューに向けて秘密の練習を続けているため、今回のメイン走行はDr.takodemousが担当しています。しかし、その練習の模様も、近いうちに特別公開の予定です(ネタ的に)。

今回Insta360 Ace Proが捉えたのは、岐阜県と愛知県の県境に位置する馬飼ビーチ(木曽川の河川敷)周辺の雄大な景色です。

この地は、ウインドサーフィンのメッカとして知られ、水面を滑るカラフルなセイル(帆)が、ツーリングの景色に最高のアクセントを加えてくれます。Insta360 Ace Proのクリアな映像が、この土地特有の開放感を余すことなく捉えてくれました。

実は私Dr.takodemousにとって、この馬飼ビーチは青春そのものの場所です。かつて私は、ここでウインドサーフィンに熱中し、水面を滑る快感に没頭していました。

バイクでこの地に再び立ち、多くのウインドサーファーたちが楽しむ姿を見るのは、非常に感慨深いものがあります。**「R60が頑張ってチャレンジしてみた」**という当ブログのテーマは、この青春時代の挑戦心と共通していると再認識しました。

動画の最後には、おまけ編として、ウインドサーフィンを楽しむ人々の様子をInsta360 Ace Proで撮影しました。彼らが水面を滑るダイナミックな動きは、バイクに乗る私たちに、再び新たな挑戦への活力を与えてくれます。


Redkabagonさんは、安全への配慮から公道デビューにはまだ慎重です。現在も、夫(Dr.takodemous)の厳しい指導のもと、広い敷地内で基本的な操作や走行技術の秘密の練習に励んでいます。

しかし、夫婦で仲良くツーリングを楽しむという目標に向け、彼女の挑戦は決して止まることはありません。着実に成長している姿を見るのは、私にとっても大きな喜びです。

さて、そんなRedkabagonさんの練習風景を、皆様に特別編として公開する準備が整いました。

ネタ的に「R60のポンコツ夫婦」の妻が、公道デビューに向けてどれほど苦戦格闘しているのか、その赤裸々な練習動画を近々お届けする予定です。夫婦で仲良く老いていくための、彼女の涙ぐましい努力をぜひご覧ください。


今回のツーリング記録も、Insta360 Ace Proが担いました。Ace Proの広角と手ブレ補正が、バイクのダイナミックな動きと、河川敷の広大な景色を完璧に両立させています。この機材があるからこそ、R60夫婦のアクティブな趣味を詳細に残すことができています。

Insta360 Ace Proの具体的な機能や、なぜhohem iSteady Pro 4を使用しないのかといった機材の詳細は、**「The Gear」**カテゴリーで詳しくご紹介しています。機材選びの参考として、ぜひご覧ください。

Dr.takodemous(筆者)

「AIコーダー」を目指すR60世代の夫。プログラミング無知ながら、Gemini(AI)と格闘しながらアプリ開発に挑むロジック担当。趣味はAIとの対話と、昔取った杵柄の格闘技。最近は妻と楽しむ近郊への夫婦旅の企画と、インドア・アウトドア活動の経費化を目的とした生活研究に熱中しています。**R60が頑張ってチャレンジしてみた!**というテーマで、新しい発見を共有します。

Redkabagon(ポンコツ夫婦の妻)

夫と共にブログを運営するR60世代の妻。得意分野は、脳内活性(ナンプレ、ゲーム)と、夫婦の活動を支える生活の知恵。G5ゲームや趣味の音楽動画(Remix)の制作にも挑戦中。特に、**特技を活かしたレンタル企画(観葉植物など)の実現に向けて奮闘中です。夫の奇抜なアイデアに振り回されつつ、ポンコツ夫婦が仲良く老いていくための「絆と癒やしのロジック」**を担当しています。

ホーム » ブログ

ポンコツ夫婦のGame Trial Log Vol.3:将棋編

Pythonでゲーム作成

I. 挑戦:AIと格闘!Pythonで将棋ゲームを作成

「Game Trial Log」シリーズ Vol.3へようこそ。

本シリーズの核となるテーマは、プログラミング無知な筆者Dr.takodemousが、AI(Gemini)の力を借りて『AIコーダー』としてどこまで通用するか?という挑戦です。

Vol.1(オセロ)、Vol.2(ナンプレ)に続き、今回はPythonで将棋ゲームの作成に挑みました。

GeminiとDr.takodemousが共同制作した将棋アプリの完成版を、動画で公開します。

しかし、完成までの道のりは、これまでのゲーム開発を遥かに超える難題の連続でした。具体的には、駒の動かし方、禁手、将棋内容に意味のある動きといったルール設定の壁、棋譜の導入、そしてAIの強さを決めるdepthの設定の難解さ、さらにはGUI設定の苦労(駒の漢字が読み込めない)など、数々の難問をクリアして出来上がっています。

そして筆者とAIとの対局は、なんと2時間に及ぶ格闘の末、Dr.takodemousが勝利しました!編集を経て動画は45分に凝縮していますが、まさに激闘でした。

したがって、この苦戦格闘こそが、プログラミング初心者の醍醐味です。

そこで、Vol.2での煽りテロップに応えるべく、Redkabagonに挑戦を促しましたが、彼女は将棋の難解さを前に撮影開始前にギブアップを宣言しました。これにより、「Redkabagonギブアップ」という前回の公約が、挑戦以前の降参という形で果たされることとなりました。

そして、今回は限定公開動画が存在しません。彼女の迅速な潔さを通して、将棋というゲームの奥深さ、そして難解さが伝わるかもしれません。


将棋のような複雑なロジックを持つゲームは、AIの力を借りても一筋縄ではいきませんでした。そのため、コードは何度もデバッグに遭遇しています。

筆者が今回のアプリ制作でR60世代に向けて強くメッセージしたいのは、AIを駆使すれば、複雑なゲームであっても、知識ゼロからその**「外殻」「ルール設定」「depth設定」「GUI設定」といった構造を学ぶこと**ができる、ということです。一緒にこの未知の世界へ飛び込みましょう。

今回も前回同様、PR TIMESさんのサイトよりプロのコーダーが作成した脳トレWebゲームを紹介します。したがって、将棋アプリ版の完成度と、筆者(AIと共同制作)のコードの難解さを対比させることで、AIサポートの凄さが浮き彫りになります。

したがって、プロのアプリの機能と対比しながら、筆者のコードを紹介します。プログラミング未経験以前の無知な筆者でも、AIを駆使すれば、コーダーと言えるレベルのプログラムがつくれるという事実は驚きです。

今回はデバッグの経緯を追体験していただくために、外殻、ルール設定、depth設定、GUI設定の4つの段階のコードを紹介します。

して、このプロンプト No.3までで、AIは「駒の基本価値」「駒の位置(PST)」「反則チェック」「 玉の堅さ 」の4つの要素を考慮して指し手を選ぶようになりました 。しかし、AIはさらに強化が可能です。例えば、探索深さを3や4に増やしたり 、王手や駒の取り合いが発生している局面で探索を深くする静的探索の導入 、大局的な戦型評価の追加 といったステップが考えられます 。読者の皆さんの好奇心に合わせて、ぜひカスタマイズに挑戦してみてください。


一方で、Redkabagonは、これまで通りG5 Entertainment提供のアイテム探し&3マッチゲームをクリアする動画を投稿し、ブログにリンクしています。彼女の着実なゲームクリアも、本シリーズの隠れた見どころです。

アイテム探し&3マッチ

本シリーズは、今後も継続的に新しいアプリ開発に挑んでいきます。

Redkabagonさんの公道デビューはネタ化していますが、プログラミング初心者の筆者も、AIの力を借りて『AIコーダー』としてデビューできるのか、どうか!?という挑戦は続きます。

次回は別のパズルゲームアプリの作成、またはプロの脳トレアプリの機能との比較検証を予定しています。

どうぞ、Geminiのプロンプトの世界に飛び込んでみてください。


本動画の撮影機材や新しい動画編集ソフト「PowerDirector 365」に関する情報、アフィリエイト情報については、「The Gear」カテゴリーで詳しくご紹介しています。

Dr.takodemous(筆者)

「AIコーダー」を目指すR60世代の夫。プログラミング無知ながら、Gemini(AI)と格闘しながらアプリ開発に挑むロジック担当。趣味はAIとの対話と、昔取った杵柄の格闘技。最近は妻と楽しむ近郊への夫婦旅の企画と、インドア・アウトドア活動の経費化を目的とした生活研究に熱中しています。**R60が頑張ってチャレンジしてみた!**というテーマで、新しい発見を共有します。

Redkabagon(ポンコツ夫婦の妻)
Redkabagon(ポンコツ夫婦の妻)

夫と共にブログを運営するR60世代の妻。得意分野は、脳内活性(ナンプレ、ゲーム)と、夫婦の活動を支える生活の知恵。G5ゲームや趣味の音楽動画(Remix)の制作にも挑戦中。特に、**特技を活かしたレンタル企画(観葉植物など)の実現に向けて奮闘中です。夫の奇抜なアイデアに振り回されつつ、ポンコツ夫婦が仲良く老いていくための「絆と癒やしのロジック」**を担当しています。

ホーム » ブログ

Fire TV Stick Vol.20:R60夫婦の「NCIS: LA シーズン3」

NCIS: LA シーズン3

📺 R60夫婦が震えた「NCIS: LA シーズン3」の秘密

私たちR60のポンコツ夫婦は、アウトドア活動(BIKE日記)とインドア活動のバランスを大切にしています。夫婦仲良く老いていくには、共通のインドア趣味が欠かせません。その中心にあるのが、Fire TV Stickを使ってリビングで楽しむ海外ドラマ鑑賞です。

今回は、私の「アクション・潜入捜査もの」好きと、redkabagonの「謎解きとチームの絆」好き、両方を満たしてくれた傑作、『NCIS: LA ~極秘潜入捜査班』のシーズン3を深く掘り下げます。単なる事件捜査ドラマという枠を超え、シリーズ全体を貫く縦軸のストーリーが、ついに大きな転換期を迎えるシーズンです。


『NCIS: LA』シーズン3は、これまでの平穏が一転し、主人公たちの過去の因縁がチーム全体を揺るがす、まさに「タイトルマッチ」のような緊迫感で幕を開けます。このシーズンの核は、前シーズン終盤で消息を絶った支部長ヘティ(ヘンリエッタ・ラング)の救出作戦です。

ルーマニアに単身潜入したヘティを救うため、カレン、サム、ケンジー、ディークスのチームは辞職覚悟で敵地へ向かいます。この一連の出来事の背後には、ヘティの過去に関わる「コメスク作戦」という巨大な秘密が隠されていました。

私Dr.takodemousが特に熱狂したのは、主人公カレンの冷静沈着な潜入能力と、元Navy SEALsであるサムとの完璧な連携です。これは、私の長年の趣向である格闘技や武術における「高度な戦略と技術」に通じるものがあり、終始熱狂して鑑賞しました。

そして、このシーズンで描かれる過去の因縁の敵や組織との戦いは、ただのアクションではなく、知的なサバイバルゲームとして機能しています。妻はあまり興味を示しませんが、このドラマを通じて、命懸けの作戦の裏側にある戦術の美しさを共有したいと密かに願っています。

この激動のミッションを通じて、カレンは自分がルーマニアにいた記憶や、作戦との深いつながりがあることを知ります。彼の「G」という名前の意味、そして彼を巡る組織的な陰謀の核心が徐々に明らかになっていきます。

シーズンを通して、カレンは複雑な家族背景と向き合い、自らのアイデンティティに一つの区切りをつけることになります。この、主人公が過去と決着をつけるという展開は、シリーズファンにとって最大のクライマックスであり、非常に見応えがありました。


シーズン3を語る上で絶対に外せないのが、シリーズ最大の見どころの一つであるクロスオーバーエピソードです。

特に、このシーズン3 第21話は、同系列の超人気ドラマ『HAWAII FIVE-0』シーズン2 第21話と連動しています。これは、NCIS: LAのチームがハワイへ飛び、FIVE-0チームと合同でテロ事件を捜査するという、ドラマファンにとって夢のような展開です。

なぜなら、普段はLAの街で活動するカレンやサムたちが、常夏のハワイでFIVE-0のメンバーと連携する様子は、異なる環境とロジックを持つチーム同士が、同じ目標のために協力し合うという点で、非常に興奮させられるからです。この大規模な連携作戦は、物語に大きな厚みをもたらしました。

シーズン3の序盤、ヘティの不在中に登場し、チームの指揮を執ったのがハンター(ハンター・ショウ)です。彼女は一時的にチームに緊張感をもたらしましたが、その冷静さと有能さでチームを危機から救いました。

そこで、今回、この重要な役割を担ったピックアップアクターを紹介します。俳優の詳細は、当ブログの「勝手にアンバサダー」カテゴリー、または外部の専門サイトにて詳しく掘り下げています。

クレア・フォーラニ(Claire Forlani)

👤 クレア・フォーラニ(Claire Forlani)

  • 役名: ローレン・ハンター(Lauren Hunter)
  • 登場: シーズン2終盤から登場し、ヘティが不在となったシーズン3でOSPの指揮官を務めました。
  • 日本語吹替: 加藤優子(かとう ゆうこ)

彼女は、映画『ミート・ジョー・ブラック』や『ザ・ロック』など、多くの作品に出演している女優です


一方で、妻Redkabagonさんは、私が熱中する激しいアクションや格闘描写よりも、チーム内の温かい絆登場人物の個人的な感情の機微に深く共感していました。

特に、ディークスとケンジーのコンビ、通称「ディークジー」の関係性は、このシーズンで大きく進展します。相棒(パートナー)としての信頼関係はもちろん、お互いへの個人的な感情が芽生え始める兆しが見え始めます。これは、R60の私たち夫婦にとって、若かりし頃の微妙な距離感を思い出す、非常に微笑ましい展開でした。

また、ヘティ不在の間に登場するハンター(ハンター・ショウ)や、NCIS副局長グレンジャー(オーウェン・グレンジャー)といった新キャラクターがチームにもたらす緊張感も、Redkabagonさんは楽しんでいました。彼らが敵か味方か分からない中で、チームがどう連携を取るのかという人間ドラマの複雑さが、物語に深みを与えています。

R60夫婦にとっての使いやすさ

私たちのドラマ鑑賞を支えているのは、もちろんFire TV Stickです。

高性能な映像体験だけでなく、操作のシンプルさがR60世代の私たちにとって非常に重要です。特に、リモコンの音声認識機能を使うことで、見たいドラマや映画、過去のシーズンにすぐにアクセスできる点は、非常に便利で重宝しています。

そのおかげで、「あのシーンをもう一度見たい」「カレンの過去の事件はいつだったか」といった会話がスムーズになり、夫婦の鑑賞体験を一層楽しいものにしてくれています。


シーズン3は、「ヘティの救出」から始まり、「カレンの過去の秘密の解明」へと焦点が移る、主人公の物語に大きな進展が見られるシーズンでした。

本カテゴリーは、今後も私たち夫婦がインドアで一緒に楽しんでいるドラマや映画、そして私Dr.takodemousの個人的な趣向(格闘技など)に特化したレビューを継続的に投稿していく予定です。

今回、鑑賞を支えたFire TV Stickですが、具体的な設定方法や、なぜこの第3世代を選んだのかといった機材の詳細は、「The Gear」カテゴリーで改めてご紹介する予定です。そちらもぜひご覧ください。

Dr.takodemous(筆者)

「AIコーダー」を目指すR60世代の夫。プログラミング無知ながら、Gemini(AI)と格闘しながらアプリ開発に挑むロジック担当。趣味はAIとの対話と、昔取った杵柄の格闘技。最近は妻と楽しむ近郊への夫婦旅の企画と、インドア・アウトドア活動の経費化を目的とした生活研究に熱中しています。**R60が頑張ってチャレンジしてみた!**というテーマで、新しい発見を共有します。

Redkabagon(ポンコツ夫婦の妻)

夫と共にブログを運営するR60世代の妻。得意分野は、脳内活性(ナンプレ、ゲーム)と、夫婦の活動を支える生活の知恵。G5ゲームや趣味の音楽動画(Remix)の制作にも挑戦中。特に、**特技を活かしたレンタル企画(観葉植物など)の実現に向けて奮闘中です。夫の奇抜なアイデアに振り回されつつ、ポンコツ夫婦が仲良く老いていくための「絆と癒やしのロジック」**を担当しています。

ホーム » ブログ

ポンコツ夫婦のGame Trial Log Vol.2:ナンプレ編

Pythonでゲーム作成

I. 挑戦:AIと格闘!ナンプレアプリ制作

「「Game Trial Log」シリーズ Vol.2へようこそ。 本シリーズの核となるテーマは、プログラミング無知な筆者Dr.takodemousが、AI(Gemini)の力を借りて『AIコーダー』としてどこまで通用するか?という挑戦です。

Vol.1ではオセロの完成版をご紹介しましたが、今回はナンプレ(数独)アプリの制作に挑みました。

GeminiとDr.takodemousが共同制作したナンプレアプリの完成版を、動画で公開します。

ナンプレのルールしか知らない筆者が、Geminiと共同制作した超初級者バージョンの動作確認をする動画です。必勝法など知らない筆者が、デバッグ段階で苦戦格闘する様子をそのまま収録しました。この苦労こそが、プログラミング初心者の醍醐味です。

さらに、動画のアウトロには、「Redkabagonは解けるのか!?」という煽りテロップを入れています。

上記の煽りに応え、Redkabagonがナンプレに挑戦する動画を限定公開でブログ内に挿入します。ナンプレが苦手な方は、彼女の予想外な苦戦ぶりを見て、「夫婦対決」を楽しみつつ、勇気をもらえるかもしれません。(概要欄に限定公開である旨を告知しています)


ナンプレは、ロジックと集中力が試され、認知機能の維持・向上に役立つとされています。特にロジックを組み立てる工程は、海馬の活性化に繋がると言われています。

そして、筆者が今回のアプリ制作でR60世代に向けて強くメッセージしたいのは、AIを駆使すれば、「脳機能を鍛える」という明確な目的を持ったアプリさえも、知識ゼロから作成できるということです。一緒にこの未知の世界へ飛び込みましょう。

今回も前回同様、PR TIMESさんのサイトよりプロのコーダーが作成した脳トレWebゲーム『Dr.脳トレ』のナンプレ版を紹介します。このサイトの完成度と、筆者(AIと共同制作)のコードを対比させることで、AIサポートの凄さが浮き彫りになります。

したがって、プロのアプリの機能と対比しながら、筆者のコードを紹介します。プログラミング未経験以前の無知な筆者でも、AIを駆使すれば、コーダーと言えるレベルのプログラムがつくれるという事実は驚きです。

具体的には、プロンプトには、「ナンプレの盤面生成」「解答の確認機能」「難易度調整」といった複雑な指示が含まれます。その結果、AIが生成してきたコードは、初心者には理解できないほどの論理的な美しさを備えていました。

プログラミングに興味がある方は、このコードの進化の道のりをぜひ参考にしてください。


一方で、Redkabagonは、これまで通りG5 Entertainment提供のアイテム探し&3マッチゲームをクリアする動画を投稿し、ブログにリンクしています。彼女の着実なゲームクリアも、本シリーズの隠れた見どころです。

アイテム探し&3マッチ

本シリーズは、今後も継続的に新しいアプリ開発に挑んでいきます。

Redkabagonさんの公道デビューはネタ化していますが、プログラミング初心者の筆者も、AIの力を借りて『AIコーダー』としてデビューできるのか、どうか!?という挑戦は続きます。

次回は別のパズルゲームアプリの作成、またはプロの脳トレアプリの機能との比較検証を予定しています。

どうぞ、Geminiのプロンプトの世界に飛び込んでみてください。


本動画の撮影機材や新しい動画編集ソフト「PowerDirector 365」に関する情報、アフィリエイト情報については、「The Gear」カテゴリーで詳しくご紹介しています。

ホーム » ブログ

ポンコツ夫婦のGame Trial Log Vol.1

Pythonでゲーム作成

カテゴリーの変更から

カテゴリー変更の説明がフォーマット化している点、ご容赦ください。今回は脱コロナをイメージしての変更ではなく、ゲームに関わる事柄を扱う新しい独立したカテゴリーとして「Game Trial Log」を立ち上げます。

Redkabagonはこれまで通り、G5Entertainmentの提供するアイテム探し&マッチ3を行い、その様子を録画・公開します。筆者Dr.takodemousは新たにゲームアプリを作成し、アプリのコード紹介と動作状況の録画公開に挑戦します

ゲームアプリ作成はGeminiのコーディングサポートを受けますが、時間がかかるため更新頻度は遅くなります。気長にお待ちください。また、機材に関わる内容は「The Gear」とクロスオーバーしますので、ゲームに興味はないけど機材には興味があるという方々のご来場もお待ちしております。

経歴の詳細は割愛しますが、2012年7月にサーティファイWEBクリエイター能力認定試験の上級(合格点65点に対し66点というブービー賞!)を45歳で取得。現在に至るまで、WEB業界で生計を立てられているとは言い難い状況です。他にIllustrator、Photoshop、Javascript、今では使われなくなったflashをかいつまんだ程度に理解している状況です。

Pythonのコード作成は未経験以前の無知。という状況でゲームを作成します。そこでR60世代に向けて是非ともメッセージしたい事があります。AI生成技術は産業革命に匹敵すると言われていますが、(筆者は産業革命を経験していないので匹敵するかどうかは不明!)AIは経験上、未知の世界へ飛び込むことに大いに役立ちます。R60世代の皆様も、一緒に飛び込みましょう!

【動画】AIと共同制作したオセロ、Dr.takodemousが勝利! Geminiと共同制作したオセロの完成版を、筆者がAI相手に打ち勝った動画です。ほぼ強制的に設置されたヒント表示を使用せず勝利したのは、ある程度のゲームのコツを知っていたからです。(コツは本ブログの趣旨と異なるため省略します。)

伝えたいのは、無知なレベルであってもAIを駆使すると、コーダーと言えるレベルのプログラムがつくれるという事です。ゲームやプログラムに興味がある方は是非参考にしてください。

プロンプトの参考になるように、最初に作成したオセロのコードから完成版までの道のりをフォルダに添付しておきます。プログラムに興味のある方は最初から順番に、ゲームに興味のある方は完全版を試してください。全てPythonのコードとなります。

redkabagonは今まで通り、G5Entertainmentの提供するアイテム探し&マッチ3をゲームしてもらい、その状況を録画そしてYou Tubeに投稿&本ブログにリンクする流れを行ってもらっています。

CapCutでの動画編集は現在のチャンネル登録分で最終となります。今後はCyberlinkの提供するPowerDirector 365を使用して作成していきます。PowerDirector 365についても、機材説明の機会に「The Gear」カテゴリーで紹介したいと思います。

映像作成に興味のある方は、是非ご来場ください。

pyramid of mahjong

前回まで五回にわたって記載してきたこの題目を、総括として終了したいと思います。コロナ禍が与えた影響は心身ともに、無意識下にダメージを与えていたと筆者は感じます。

漠然とした日々の不安や拠り所のない希望など、記載内容は意図的であれ無意識であれ、ネガティブな内容であった気がします。その当時はその状態が仕方ないとしても、現在の状況は自己選択が出来る状態です。

総じてカテゴリーの変更を行った後は、楽しみながら老いていくを実践しているようなものです。つまり最近のブログの内容は、R60のポンコツ夫婦が楽しみながら老いていく姿を紹介しているのです。やたら、参考にしてくださいというメッセージが多発するのは、自己選択の結果なような気がします。

やりがいや生きがいは、認知機能や肉体機能を維持するのに役立つと言われています。真意は不明といえばそれまでなのですが、信じてメッセージしていきたいと思います。

PR TIMESさんのサイトより”認知症予防の脳トレWebゲーム『Dr.脳トレ』をリリース。”のページを引用しています

今回からは、無知なAIコーダー(筆者)VSプロのコーダーの決戦の意味合いも含みます。脳科学には触れたことすらない筆者が「脳機能を鍛える」とプロンプトに指示して生まれたコードが、どこまでプロに肉薄できるか、自身でもウキウキしています。

第四回目も海馬を鍛えるシリーズになります。先ずはゲストモードで試してみて、効果を感じた場合アカウントを作成する感じで手軽に始めることが出来ます。このコナーのゲームは記憶の形成、空間学習、感情の制御を鍛えるというゲームとなっています。筆者も同類のアプリの作成にチャレンジするつもりです。

動画に登場しているバージョンは**プロンプト No.6です。DEPTH=4という設定で、レベルは中級者以上となっています。実行するには、Pythonを入手する必要があります。入手は自己責任ですが、世界中で使用されているプログラムを実行するためには必需品です。入手方法や設定方法などのリンクを貼っておきます。

どうぞGeminiのプロンプトの世界に、飛び込んでみてください。

プロンプトno1(外殻の設定と初期動作:相互手動)
# othello.py

# 1. ゲームボードの初期化
def create_board():
    board = []
    for i in range(8):
        row = [' '] * 8
        board.append(row)
    
    board[3][3] = 'W'
    board[3][4] = 'B'
    board[4][3] = 'B'
    board[4][4] = 'W'
    return board

# 2. ボードの表示
def print_board(board):
    print('  a b c d e f g h')
    print(' +-+-+-+-+-+-+-+-+')
    for i in range(8):
        print(f'{i+1}|', end='')
        for j in range(8):
            print(f'{board[i][j]}|', end='')
        print(f' {i+1}')
    print(' +-+-+-+-+-+-+-+-+')
    print('  a b c d e f g h')

# 3. 駒を置ける場所の判定と駒の反転
def is_valid_move(board, row, col, player, opponent):
    if not (0 <= row < 8 and 0 <= col < 8) or board[row][col] != ' ':
        return False

    directions = [
        (-1, -1), (-1, 0), (-1, 1),
        (0, -1),          (0, 1),
        (1, -1),  (1, 0),  (1, 1)
    ]
    
    found_flips = []

    for dr, dc in directions:
        r, c = row + dr, col + dc
        path = []
        while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opponent:
            path.append((r, c))
            r, c = r + dr, c + dc
        
        if 0 <= r < 8 and 0 <= c < 8 and board[r][c] == player and path:
            found_flips.extend(path)
            
    return found_flips

def flip_pieces(board, pieces_to_flip, player):
    for r, c in pieces_to_flip:
        board[r][c] = player

# 4. メインゲームループ
def main():
    board = create_board()
    player_turn = 'B'
    opponent_turn = 'W'

    while True:
        print_board(board)
        print(f'現在のターン: {player_turn}')

        try:
            move_str = input('駒を置く場所を入力してください (例: d3): ')
            col_char = move_str[0].lower()
            row_char = move_str[1:]
            
            # 入力を座標に変換
            col = 'abcdefgh'.index(col_char)
            row = int(row_char) - 1
            
            if not (0 <= row < 8 and 0 <= col < 8):
                print('無効な入力です。')
                continue
            
            # 有効な手か判定
            pieces_to_flip = is_valid_move(board, row, col, player_turn, opponent_turn)
            
            if pieces_to_flip:
                board[row][col] = player_turn
                flip_pieces(board, pieces_to_flip, player_turn)
                
                # ターンを交代
                player_turn, opponent_turn = opponent_turn, player_turn
            else:
                print('その場所には置けません。有効な手を打ってください。')
        
        except (ValueError, IndexError):
            print('無効な入力です。例: d3 のように入力してください。')

# プログラムの実行
if __name__ == '__main__':
    main()
プロンプトno2(GUIの設定1と初期動作:相互手動)
# 1. ゲームボードの初期化
# 盤面を表すための定数を定義します
# '・' : 空, '⚫' : 黒, '⚪' : 白
EMPTY = '・'
BLACK = '⚫'
WHITE = '⚪'

def create_board():
    """
    8x8の空の盤面を作成し、初期配置の石を配置します。
    """
    board = []
    for _ in range(8):
        row = [EMPTY] * 8
        board.append(row)
    
    board[3][3] = WHITE
    board[3][4] = BLACK
    board[4][3] = BLACK
    board[4][4] = WHITE
    return board

# 2. ボードの表示
def print_board(board):
    """
    現在の盤面をきれいに表示します。
    """
    # 列の表記を修正
    print('    a    b    c    d    e    f    g    h')
    # 罫線の表示
    print('  +---+---+---+---+---+---+---+---+---+---')
    for i in range(8):
        # 行の表示と升目の調整
        row_str = f'{i+1} |'
        for j in range(8):
            row_str += f' {board[i][j]} |'
        print(row_str)
        print('  +---+---+---+---+---+---+---+---+---+---')
    
# 3. 駒を置ける場所の判定と駒の反転
def is_valid_move(board, row, col, player, opponent):
    """
    指定された位置に石を置くことができるか判定し、
    ひっくり返せる石のリストを返します。
    """
    if not (0 <= row < 8 and 0 <= col < 8) or board[row][col] != EMPTY:
        return None

    # 8つの方向を定義 (行の変化, 列の変化)
    directions = [
        (-1, -1), (-1, 0), (-1, 1),
        (0, -1),            (0, 1),
        (1, -1),  (1, 0),  (1, 1)
    ]
    
    pieces_to_flip = []

    for dr, dc in directions:
        r, c = row + dr, col + dc
        path = []
        while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opponent:
            path.append((r, c))
            r, c = r + dr, c + dc
        
        # 挟み込んでいるかチェック
        if 0 <= r < 8 and 0 <= c < 8 and board[r][c] == player and path:
            pieces_to_flip.extend(path)
            
    return pieces_to_flip if pieces_to_flip else None

def flip_pieces(board, pieces_to_flip, player):
    """
    ひっくり返す石のリストを、指定されたプレイヤーの石に変換します。
    """
    for r, c in pieces_to_flip:
        board[r][c] = player

def get_valid_moves(board, player, opponent):
    """
    現在のプレイヤーが石を置けるすべての場所(有効な手)のリストを返します。
    """
    valid_moves = []
    for r in range(8):
        for c in range(8):
            if board[r][c] == EMPTY:
                if is_valid_move(board, r, c, player, opponent):
                    valid_moves.append((r, c))
    return valid_moves

def count_pieces(board):
    """
    盤面上の石の数を数え、勝敗を表示します。
    """
    black_count = 0
    white_count = 0
    for r in range(8):
        for c in range(8):
            if board[r][c] == BLACK:
                black_count += 1
            elif board[r][c] == WHITE:
                white_count += 1
    
    print(f"最終結果:黒 = {black_count}、白 = {white_count}")
    
    if black_count > white_count:
        print("黒の勝ちです!")
    elif white_count > black_count:
        print("白の勝ちです!")
    else:
        print("引き分けです。")

# 4. メインゲームループ
def main():
    """
    ゲームのメイン処理を実行します。
    """
    board = create_board()
    player_turn = BLACK
    opponent_turn = WHITE
    pass_count = 0  # 連続でパスした回数をカウント

    while True:
        print_board(board)
        
        valid_moves = get_valid_moves(board, player_turn, opponent_turn)
        
        if not valid_moves:
            pass_count += 1
            print(f'現在のターン: {"黒" if player_turn == BLACK else "白"}')
            print("置ける場所がありません。パスします。")
            
            if pass_count == 2:
                print("両プレイヤーがパスしました。ゲームを終了します。")
                break
            
            player_turn, opponent_turn = opponent_turn, player_turn
            continue
        else:
            pass_count = 0
            
        print(f'現在のターン: {"黒" if player_turn == BLACK else "白"}')
        
        try:
            move_str = input('駒を置く場所を入力してください (例: d3): ')
            
            if move_str.lower() == 'q':
                print("ゲームを終了します。")
                break

            col_char = move_str[0].lower()
            row_char = move_str[1:]
            
            col = 'abcdefgh'.index(col_char)
            row = int(row_char) - 1
            
            if (row, col) not in valid_moves:
                print('その場所には置けません。有効な手を打ってください。')
                continue
            
            pieces_to_flip = is_valid_move(board, row, col, player_turn, opponent_turn)
            board[row][col] = player_turn
            flip_pieces(board, pieces_to_flip, player_turn)
            
            player_turn, opponent_turn = opponent_turn, player_turn
        
        except (ValueError, IndexError):
            print('無効な入力です。例: d3 のように入力してください。')
    
    print("ゲーム終了!")
    count_pieces(board)
    
    input("結果を確認後、何かキーを押して終了してください...")

# プログラムの実行
if __name__ == '__main__':
    main()
プロンプトno3(GUIの設定2と初期動作:相互手動:盤面ほぼ完成)
import tkinter as tk
from tkinter import messagebox

# 盤面を表すための定数
EMPTY = '・'
BLACK = '⚫'
WHITE = '⚪'

# ウィンドウとキャンバスのサイズ
BOARD_SIZE = 400
CELL_SIZE = BOARD_SIZE // 8

# グローバル変数としてボードの状態を保持
game_board = None
canvas = None
current_player = BLACK
pass_count = 0

def create_board():
    """
    8x8の空の盤面を作成し、初期配置の石を配置します。
    """
    board = []
    for _ in range(8):
        row = [EMPTY] * 8
        board.append(row)
    
    board[3][3] = WHITE
    board[3][4] = BLACK
    board[4][3] = BLACK
    board[4][4] = WHITE
    return board

def is_valid_move(board, row, col, player, opponent):
    """
    指定された位置に石を置くことができるか判定し、
    ひっくり返せる石のリストを返します。
    """
    if not (0 <= row < 8 and 0 <= col < 8) or board[row][col] != EMPTY:
        return None

    directions = [
        (-1, -1), (-1, 0), (-1, 1),
        (0, -1),            (0, 1),
        (1, -1),  (1, 0),  (1, 1)
    ]
    
    pieces_to_flip = []

    for dr, dc in directions:
        r, c = row + dr, col + dc
        path = []
        while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opponent:
            path.append((r, c))
            r, c = r + dr, c + dc
        
        if 0 <= r < 8 and 0 <= c < 8 and board[r][c] == player and path:
            pieces_to_flip.extend(path)
            
    return pieces_to_flip if pieces_to_flip else None

def flip_pieces(board, pieces_to_flip, player):
    """
    ひっくり返す石のリストを、指定されたプレイヤーの石に変換します。
    """
    for r, c in pieces_to_flip:
        board[r][c] = player

def get_valid_moves(board, player, opponent):
    """
    現在のプレイヤーが石を置けるすべての場所(有効な手)のリストを返します。
    """
    valid_moves = []
    for r in range(8):
        for c in range(8):
            if board[r][c] == EMPTY:
                if is_valid_move(board, r, c, player, opponent):
                    valid_moves.append((r, c))
    return valid_moves

def count_pieces(board):
    """
    盤面上の石の数を数え、勝敗を決定します。
    """
    black_count = sum(row.count(BLACK) for row in board)
    white_count = sum(row.count(WHITE) for row in board)
    return black_count, white_count

def end_game_check():
    """
    ゲームが終了したかチェックし、終了していれば勝敗を表示します。
    """
    global pass_count
    
    black_count, white_count = count_pieces(game_board)
    
    if pass_count == 2 or (black_count + white_count) == 64:
        # ゲーム終了
        if black_count > white_count:
            message = f"ゲーム終了!\n黒の勝ちです!\n(黒: {black_count}, 白: {white_count})"
        elif white_count > black_count:
            message = f"ゲーム終了!\n白の勝ちです!\n(黒: {black_count}, 白: {white_count})"
        else:
            message = f"ゲーム終了!\n引き分けです!\n(黒: {black_count}, 白: {white_count})"
            
        messagebox.showinfo("ゲーム終了", message)
        return True
    return False

def draw_pieces():
    """
    Pythonのボード情報に基づいて、canvas上に石を描画します。
    """
    canvas.delete("all")
    
    # 盤面の升目を再描画
    for i in range(8):
        for j in range(8):
            x1 = j * CELL_SIZE
            y1 = i * CELL_SIZE
            x2 = x1 + CELL_SIZE
            y2 = y1 + CELL_SIZE
            canvas.create_rectangle(x1, y1, x2, y2, outline="black", fill="green")
            
    # 石を描画
    for row_idx in range(8):
        for col_idx in range(8):
            piece = game_board[row_idx][col_idx]
            if piece == BLACK:
                color = "black"
            elif piece == WHITE:
                color = "white"
            else:
                continue
            
            x1 = col_idx * CELL_SIZE + 5
            y1 = row_idx * CELL_SIZE + 5
            x2 = x1 + CELL_SIZE - 10
            y2 = y1 + CELL_SIZE - 10
            
            canvas.create_oval(x1, y1, x2, y2, fill=color, outline="black")

def on_click(event):
    """
    マウスがクリックされたときに呼び出される関数です。
    """
    global current_player, pass_count
    
    col = event.x // CELL_SIZE
    row = event.y // CELL_SIZE
    
    opponent_player = WHITE if current_player == BLACK else BLACK
    
    pieces_to_flip = is_valid_move(game_board, row, col, current_player, opponent_player)

    if pieces_to_flip:
        # 有効な手であれば、石を置く
        game_board[row][col] = current_player
        flip_pieces(game_board, pieces_to_flip, current_player)
        
        # プレイヤーを交代
        current_player = opponent_player
        pass_count = 0  # パスカウントをリセット

    else:
        # 無効な手の場合、何もしない
        return
        
    draw_pieces()
    
    # 相手プレイヤーに有効な手があるかチェック
    if not get_valid_moves(game_board, current_player, opponent_player):
        print(f'{current_player}は置ける場所がありません。パスします。')
        pass_count += 1
        current_player = opponent_player  # プレイヤーを再度交代
        
        # 連続パスの場合、ゲーム終了チェック
        if pass_count == 2:
            end_game_check()
        else:
            # パスしたので、盤面を再描画して次のプレイヤーの番
            draw_pieces()
            
    # ゲーム終了条件を常にチェック
    if not end_game_check():
        pass
    else:
        print("ゲームが終了しました。")

def create_gui_board():
    """
    tkinterのウィンドウとオセロ盤のGUIを作成します。
    """
    global game_board, canvas
    
    root = tk.Tk()
    root.title("オセロゲーム")

    canvas = tk.Canvas(root, width=BOARD_SIZE, height=BOARD_SIZE, bg="green")
    canvas.pack()
    
    canvas.bind("<Button-1>", on_click)
            
    game_board = create_board()
    draw_pieces()

    root.mainloop()

# プログラムの実行
if __name__ == "__main__":
    create_gui_board()
プロンプトno4(AI対戦相手のプログラムと初期動作)
import tkinter as tk
from tkinter import messagebox
import random # <--- ここを追加

# 盤面を表すための定数
EMPTY = '・'
BLACK = '⚫'
WHITE = '⚪'

# ウィンドウとキャンバスのサイズ
BOARD_SIZE = 400
CELL_SIZE = BOARD_SIZE // 8

# グローバル変数としてボードの状態を保持
game_board = None
canvas = None
current_player = BLACK
pass_count = 0

# AIの設定
AI_PLAYER = WHITE # AIを白のプレイヤーに設定
IS_AI_TURN = False # AIのターンかどうかを示すフラグ

def create_board():
    """
    8x8の空の盤面を作成し、初期配置の石を配置します。
    """
    board = []
    for _ in range(8):
        row = [EMPTY] * 8
        board.append(row)
    
    board[3][3] = WHITE
    board[3][4] = BLACK
    board[4][3] = BLACK
    board[4][4] = WHITE
    return board

# ----------------- AIロジックの追加 -----------------
def get_ai_move(valid_moves):
    """
    有効な手の中からランダムに手を選択します。
    """
    return random.choice(valid_moves)

def handle_ai_turn():
    """
    AIのターンを処理します。
    """
    global current_player, pass_count, IS_AI_TURN
    
    opponent_player = BLACK # AIは白なので、相手は黒
    
    valid_moves = get_valid_moves(game_board, AI_PLAYER, opponent_player)

    if not valid_moves:
        pass_count += 1
        print("AIは置ける場所がありません。パスします。")
        current_player = BLACK # プレイヤーを交代
        IS_AI_TURN = False
        end_game_check()
        draw_pieces()
    else:
        pass_count = 0
        
        # AIがランダムに手を選択
        row, col = get_ai_move(valid_moves)
        
        # 石を置く処理
        pieces_to_flip = is_valid_move(game_board, row, col, AI_PLAYER, opponent_player)
        game_board[row][col] = AI_PLAYER
        flip_pieces(game_board, pieces_to_flip, AI_PLAYER)
        
        current_player = BLACK # プレイヤーを交代
        IS_AI_TURN = False
        
        draw_pieces()
        
        if not end_game_check():
            pass
        else:
            print("ゲームが終了しました。")
            
def on_click(event):
    """
    マウスがクリックされたときに呼び出される関数です。
    """
    global current_player, pass_count, IS_AI_TURN
    
    if IS_AI_TURN:
        return # AIのターン中はプレイヤーの入力を無視
    
    col = event.x // CELL_SIZE
    row = event.y // CELL_SIZE
    
    opponent_player = WHITE # プレイヤーは黒なので、相手は白
    
    pieces_to_flip = is_valid_move(game_board, row, col, current_player, opponent_player)

    if pieces_to_flip:
        game_board[row][col] = current_player
        flip_pieces(game_board, pieces_to_flip, current_player)
        
        pass_count = 0
        current_player = opponent_player
        IS_AI_TURN = True # 次のターンはAI
        
        draw_pieces()
        
        if not end_game_check():
            canvas.after(500, handle_ai_turn) # 0.5秒後にAIを動かす
        else:
            print("ゲームが終了しました。")
    else:
        print("その場所には置けません。有効な手を打ってください。")


# ----------------- ここから下の関数は変更なし -----------------
def is_valid_move(board, row, col, player, opponent):
    if not (0 <= row < 8 and 0 <= col < 8) or board[row][col] != EMPTY:
        return None
    directions = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]
    pieces_to_flip = []
    for dr, dc in directions:
        r, c = row + dr, col + dc
        path = []
        while 0 <= r < 8 and 0 <= c < 8 and board[r][c] == opponent:
            path.append((r, c))
            r, c = r + dr, c + dc
        if 0 <= r < 8 and 0 <= c < 8 and board[r][c] == player and path:
            pieces_to_flip.extend(path)
    return pieces_to_flip if pieces_to_flip else None

def flip_pieces(board, pieces_to_flip, player):
    for r, c in pieces_to_flip:
        board[r][c] = player

def get_valid_moves(board, player, opponent):
    valid_moves = []
    for r in range(8):
        for c in range(8):
            if board[r][c] == EMPTY:
                if is_valid_move(board, r, c, player, opponent):
                    valid_moves.append((r, c))
    return valid_moves

def count_pieces(board):
    black_count = sum(row.count(BLACK) for row in board)
    white_count = sum(row.count(WHITE) for row in board)
    return black_count, white_count

def end_game_check():
    global pass_count
    black_count, white_count = count_pieces(game_board)
    if pass_count == 2 or (black_count + white_count) == 64:
        if black_count > white_count:
            message = f"ゲーム終了!\n黒の勝ちです!\n(黒: {black_count}, 白: {white_count})"
        elif white_count > black_count:
            message = f"ゲーム終了!\n白の勝ちです!\n(黒: {black_count}, 白: {white_count})"
        else:
            message = f"ゲーム終了!\n引き分けです!\n(黒: {black_count}, 白: {white_count})"
        messagebox.showinfo("ゲーム終了", message)
        return True
    return False

def draw_pieces():
    canvas.delete("all")
    for i in range(8):
        for j in range(8):
            x1 = j * CELL_SIZE
            y1 = i * CELL_SIZE
            x2 = x1 + CELL_SIZE
            y2 = y1 + CELL_SIZE
            canvas.create_rectangle(x1, y1, x2, y2, outline="black", fill="green")
    for row_idx in range(8):
        for col_idx in range(8):
            piece = game_board[row_idx][col_idx]
            if piece == BLACK:
                color = "black"
            elif piece == WHITE:
                color = "white"
            else:
                continue
            x1 = col_idx * CELL_SIZE + 5
            y1 = row_idx * CELL_SIZE + 5
            x2 = x1 + CELL_SIZE - 10
            y2 = y1 + CELL_SIZE - 10
            canvas.create_oval(x1, y1, x2, y2, fill=color, outline="black")

def create_gui_board():
    global game_board, canvas
    root = tk.Tk()
    root.title("オセロゲーム")
    canvas = tk.Canvas(root, width=BOARD_SIZE, height=BOARD_SIZE, bg="green")
    canvas.pack()
    canvas.bind("<Button-1>", on_click)
    game_board = create_board()
    draw_pieces()
    root.mainloop()

if __name__ == "__main__":
    create_gui_board()
プロンプトno5(AI対戦相手のGUI改善:ヒント表示と初期動作)
import tkinter as tk
from tkinter import messagebox
import copy
import time 
import sys

# --- ゲーム定数 ---
BOARD_SIZE = 8
CELL_SIZE = 60
WINDOW_SIZE = BOARD_SIZE * CELL_SIZE
SIDE_PANEL_WIDTH = 150
AI_PLAYER = 2 # 2: 黒 (後攻) / 1: 白 (先攻)
SEARCH_DEPTH = 4 # AIの探索深さ

# --- グローバル変数 ---
root = None
canvas = None
board = None
current_player = 1 # 1:白, 2:黒
game_running = True
score_label = None
turn_label = None
history = [] 

# --- 初期化 ---

def create_initial_board():
    """初期盤面を作成 (0:空, 1:白, 2:黒)"""
    new_board = [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)]
    new_board[3][3] = 1 # 白
    new_board[3][4] = 2 # 黒
    new_board[4][3] = 2 # 黒
    new_board[4][4] = 1 # 白
    return new_board

# --- ルールとAIロジック ---

def is_valid_move(r, c, player, current_board):
    """指定されたマスに駒を置けるかチェックし、裏返せる駒のリストを返す"""
    if current_board[r][c] != 0:
        return []

    opponent = 3 - player
    flips = []

    # 8方向をチェック
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]

    for dr, dc in directions:
        line_flips = []
        rr, cc = r + dr, c + dc

        # 相手の駒が続く限り進む
        while 0 <= rr < BOARD_SIZE and 0 <= cc < BOARD_SIZE and current_board[rr][cc] == opponent:
            line_flips.append((rr, cc))
            rr, cc = rr + dr, cc + dc
        
        # 終点が自分の駒で、間に裏返せる駒がある場合
        if line_flips and 0 <= rr < BOARD_SIZE and 0 <= cc < BOARD_SIZE and current_board[rr][cc] == player:
            flips.extend(line_flips)

    return flips

def get_valid_moves(player, current_board):
    """合法手の一覧を (r, c) のリストで返す"""
    valid_moves = []
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            if is_valid_move(r, c, player, current_board):
                valid_moves.append((r, c))
    return valid_moves

def apply_move(r, c, player, current_board):
    """実際に駒を置き、盤面を更新する (新しい盤面を返す)"""
    new_board = [row[:] for row in current_board]
    flips = is_valid_move(r, c, player, current_board)
    
    if not flips:
        return new_board # 無効な手の場合は元の盤面を返す

    new_board[r][c] = player
    for fr, fc in flips:
        new_board[fr][fc] = player
        
    return new_board

def evaluate_board(current_board, player):
    """評価関数: 駒の数の差を計算"""
    p1_count = sum(row.count(1) for row in current_board)
    p2_count = sum(row.count(2) for row in current_board)
    
    if player == 1:
        return p1_count - p2_count
    else:
        return p2_count - p1_count

def minimax(current_board, depth, is_maximizing_player, alpha, beta):
    """Minimax with Alpha-Beta Pruning"""
    current_player_minimax = 2 if is_maximizing_player else 1
    
    # 終端条件
    valid_moves = get_valid_moves(current_player_minimax, current_board)
    if depth == 0 or (not valid_moves and not get_valid_moves(3 - current_player_minimax, current_board)):
        return evaluate_board(current_board, 2), None # AI(黒=2)視点の評価を返す

    best_move = None
    
    if is_maximizing_player: # AI (黒=2) の手番
        max_eval = -float('inf')
        for r, c in valid_moves:
            new_board = apply_move(r, c, 2, current_board)
            current_eval, _ = minimax(new_board, depth - 1, False, alpha, beta)
            
            if current_eval > max_eval:
                max_eval = current_eval
                best_move = (r, c)
            
            alpha = max(alpha, max_eval)
            if beta <= alpha:
                break
        return max_eval, best_move
        
    else: # プレイヤー (白=1) の手番
        min_eval = float('inf')
        for r, c in valid_moves:
            new_board = apply_move(r, c, 1, current_board)
            current_eval, _ = minimax(new_board, depth - 1, True, alpha, beta)
            
            if current_eval < min_eval:
                min_eval = current_eval
                best_move = (r, c)
                
            beta = min(beta, min_eval)
            if beta <= alpha:
                break
        return min_eval, best_move

def find_best_move(current_board, depth):
    start_time = time.time()
    _, best_move = minimax(current_board, depth, True, -float('inf'), float('inf'))
    end_time = time.time()
    print(f"Minimax探索時間: {end_time - start_time:.2f}秒 (深さ: {depth})")
    return best_move

# --- UIと描画 ---

def draw_board():
    """盤面を描画し、駒を配置する"""
    canvas.delete("all")
    
    # 盤面のグリッド線を描画
    for i in range(BOARD_SIZE + 1):
        # 垂直線
        canvas.create_line(i * CELL_SIZE, 0, i * CELL_SIZE, WINDOW_SIZE, fill="black")
        # 水平線
        canvas.create_line(0, i * CELL_SIZE, WINDOW_SIZE, i * CELL_SIZE, fill="black")

    # 駒の配置
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            player = board[r][c]
            if player != 0:
                x1 = c * CELL_SIZE + 5
                y1 = r * CELL_SIZE + 5
                x2 = (c + 1) * CELL_SIZE - 5
                y2 = (r + 1) * CELL_SIZE - 5
                color = "white" if player == 1 else "black"
                canvas.create_oval(x1, y1, x2, y2, fill=color, outline="black")

    update_score_display()

def update_score_display():
    """スコアとターン表示を更新する"""
    p1_count = sum(row.count(1) for row in board)
    p2_count = sum(row.count(2) for row in board)
    
    score_text = f"白: {p1_count}\n黒: {p2_count}"
    turn_text = f"現在のターン: {'白' if current_player == 1 else '黒'}"
    
    if score_label and turn_label:
        score_label.config(text=score_text)
        turn_label.config(text=turn_text)

def show_hint():
    """合法手があるマスをハイライト表示する"""
    canvas.delete("hint")
    valid_moves = get_valid_moves(current_player, board)
    
    for r, c in valid_moves:
        x1 = c * CELL_SIZE + 2
        y1 = r * CELL_SIZE + 2
        x2 = (c + 1) * CELL_SIZE - 2
        y2 = (r + 1) * CELL_SIZE - 2
        canvas.create_rectangle(x1, y1, x2, y2, outline="yellow", width=3, tags="hint")

# --- イベントハンドラ ---

def handle_click(event):
    """マウスがクリックされたときの処理"""
    global board, current_player, game_running
    
    if not game_running or current_player == AI_PLAYER:
        return

    c = event.x // CELL_SIZE
    r = event.y // CELL_SIZE
    
    if not (0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE):
        return

    flips = is_valid_move(r, c, current_player, board)
    
    if flips:
        save_state() # 履歴保存
        
        board = apply_move(r, c, current_player, board)
        
        next_player = 3 - current_player
        end_turn(next_player)
    else:
        messagebox.showinfo("無効な手", "そこには置けません。")

def end_turn(next_player):
    """ターン終了時の処理"""
    global current_player, game_running
    current_player = next_player
    
    draw_board()
    
    p1_moves = get_valid_moves(1, board)
    p2_moves = get_valid_moves(2, board)
    
    if not p1_moves and not p2_moves:
        # 両者とも置けない = ゲーム終了
        game_running = False
        p1_count = sum(row.count(1) for row in board)
        p2_count = sum(row.count(2) for row in board)
        
        winner = "引き分け"
        if p1_count > p2_count: winner = "白の勝利"
        elif p2_count > p1_count: winner = "黒の勝利"
            
        messagebox.showinfo("ゲーム終了", f"最終スコア 白:{p1_count}, 黒:{p2_count}\n{winner}")
        return
        
    elif not get_valid_moves(current_player, board):
        # パス (合法手が無い場合は相手のターンに戻る)
        messagebox.showinfo("パス", f"{'白' if current_player == 1 else '黒'}は置く場所がないためパスします。")
        current_player = 3 - current_player
        draw_board() # プレイヤーが変わったことを表示

    show_hint()
    
    if current_player == AI_PLAYER and game_running:
        root.after(500, make_ai_move) # 0.5秒後にAIを起動

def make_ai_move():
    """AIが指し手を決定し、実行する"""
    global board, current_player, game_running
    
    if not game_running or current_player != AI_PLAYER:
        return

    save_state() # 履歴保存
    
    best_move = find_best_move(board, SEARCH_DEPTH)
    
    if best_move:
        r, c = best_move
        board = apply_move(r, c, AI_PLAYER, board)
        
        next_player = 3 - AI_PLAYER
        end_turn(next_player)
    else:
        # AIが置けない場合もパス処理で end_turn が実行される
        end_turn(3 - AI_PLAYER)

def save_state():
    """現在のゲーム状態を履歴に保存する (Undo用)"""
    global history
    state = (
        [row[:] for row in board],
        current_player
    )
    history.append(state)

def undo_move():
    """一手前の状態に戻す"""
    global board, current_player, game_running, history
    
    if current_player == AI_PLAYER:
        messagebox.showinfo("警告", "AIの手番中はUndoできません。")
        return
        
    if len(history) <= 1:
        messagebox.showinfo("警告", "初期状態のため、これ以上戻せません。")
        return
    
    # プレイヤーとAIの2手分を戻す
    if len(history) >= 2:
        history.pop() # AIの手
        history.pop() # プレイヤーの手
    else:
        # 初期盤面直後の履歴の場合
        history.pop() 

    prev_board, prev_player = history[-1]
    
    board = [row[:] for row in prev_board]
    current_player = prev_player
    game_running = True
    
    draw_board()
    show_hint()
    
    if current_player == AI_PLAYER and game_running:
        root.after(500, make_ai_move)
        
# --- GUIメイン関数 ---

def create_board_ui():
    global root, canvas, board, score_label, turn_label
    
    root = tk.Tk()
    root.title(f"オセロゲーム (AI深さ: {SEARCH_DEPTH})")
    
    # --- UI中央配置のための改善 ---
    
    # 1. すべての要素を保持するメインフレームを作成
    main_frame = tk.Frame(root)
    
    # 2. main_frameを親ウィンドウの中央に配置
    #    expand=True で利用可能なスペースを広げ、anchor='center' で中央に寄せます。
    main_frame.pack(expand=True, anchor='center') 
    
    # --------------------------------

    # 3. ボード(キャンバス)を main_frame の中に配置 (親を root から main_frame に変更)
    canvas = tk.Canvas(main_frame, width=WINDOW_SIZE, height=WINDOW_SIZE, bg="green")
    canvas.pack(side=tk.LEFT)
    
    # 4. スコアパネル(サイドパネル)を main_frame の中に配置 (親を root から main_frame に変更)
    score_frame = tk.Frame(main_frame, width=SIDE_PANEL_WIDTH)
    score_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10, pady=10)
    
    # --- スコアとターン表示 ---
    
    score_label = tk.Label(score_frame, text="スコア", font=("Arial", 16))
    score_label.pack(pady=20)
    
    turn_label = tk.Label(score_frame, text="ターン", font=("Arial", 14))
    turn_label.pack(pady=10)

    # --- ボタン ---
    
    undo_button = tk.Button(score_frame, text="一手戻る (Undo)", command=undo_move, 
                            height=2, bg="#B0C4DE", fg="black")
    undo_button.pack(pady=20)
    
    hint_button = tk.Button(score_frame, text="ヒント表示", command=show_hint,
                            height=2, bg="#FFFACD", fg="black")
    hint_button.pack(pady=10)
    
    # --- ゲームの起動 ---
    
    board = create_initial_board()
    canvas.bind("<Button-1>", handle_click)
    
    # 初期状態を履歴に保存
    save_state()
    
    draw_board()
    show_hint()
    
    def on_closing():
        root.destroy()
        sys.exit()
        
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    create_board_ui()
プロンプトno6(AI対戦相手の完全版ヒント表示の選択機能の追加と初期動作)
import tkinter as tk
from tkinter import messagebox
import copy
import time 
import sys

# --- ゲーム定数 ---
BOARD_SIZE = 8
CELL_SIZE = 60
WINDOW_SIZE = BOARD_SIZE * CELL_SIZE
SIDE_PANEL_WIDTH = 150
AI_PLAYER = 2 # 2: 黒 (後攻) / 1: 白 (先攻)
SEARCH_DEPTH = 4 # AIの探索深さ

# --- グローバル変数 ---
root = None
canvas = None
board = None
current_player = 1 # 1:白, 2:黒
game_running = True
score_label = None
turn_label = None
history = [] 

# --- 初期化 ---

def create_initial_board():
    """初期盤面を作成 (0:空, 1:白, 2:黒)"""
    new_board = [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)]
    new_board[3][3] = 1 # 白
    new_board[3][4] = 2 # 黒
    new_board[4][3] = 2 # 黒
    new_board[4][4] = 1 # 白
    return new_board

# --- ルールとAIロジック ---

def is_valid_move(r, c, player, current_board):
    """指定されたマスに駒を置けるかチェックし、裏返せる駒のリストを返す"""
    if current_board[r][c] != 0:
        return []

    opponent = 3 - player
    flips = []

    # 8方向をチェック
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]

    for dr, dc in directions:
        line_flips = []
        rr, cc = r + dr, c + dc

        # 相手の駒が続く限り進む
        while 0 <= rr < BOARD_SIZE and 0 <= cc < BOARD_SIZE and current_board[rr][cc] == opponent:
            line_flips.append((rr, cc))
            rr, cc = rr + dr, cc + dc
        
        # 終点が自分の駒で、間に裏返せる駒がある場合
        if line_flips and 0 <= rr < BOARD_SIZE and 0 <= cc < BOARD_SIZE and current_board[rr][cc] == player:
            flips.extend(line_flips)

    return flips

def get_valid_moves(player, current_board):
    """合法手の一覧を (r, c) のリストで返す"""
    valid_moves = []
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            if is_valid_move(r, c, player, current_board):
                valid_moves.append((r, c))
    return valid_moves

def apply_move(r, c, player, current_board):
    """実際に駒を置き、盤面を更新する (新しい盤面を返す)"""
    new_board = [row[:] for row in current_board]
    flips = is_valid_move(r, c, player, current_board)
    
    if not flips:
        return new_board # 無効な手の場合は元の盤面を返す

    new_board[r][c] = player
    for fr, fc in flips:
        new_board[fr][fc] = player
        
    return new_board

def evaluate_board(current_board, player):
    """評価関数: 駒の数の差を計算"""
    p1_count = sum(row.count(1) for row in current_board)
    p2_count = sum(row.count(2) for row in current_board)
    
    if player == 1:
        return p1_count - p2_count
    else:
        return p2_count - p1_count

def minimax(current_board, depth, is_maximizing_player, alpha, beta):
    """Minimax with Alpha-Beta Pruning"""
    current_player_minimax = 2 if is_maximizing_player else 1
    
    # 終端条件
    valid_moves = get_valid_moves(current_player_minimax, current_board)
    if depth == 0 or (not valid_moves and not get_valid_moves(3 - current_player_minimax, current_board)):
        return evaluate_board(current_board, 2), None # AI(黒=2)視点の評価を返す

    best_move = None
    
    if is_maximizing_player: # AI (黒=2) の手番
        max_eval = -float('inf')
        for r, c in valid_moves:
            new_board = apply_move(r, c, 2, current_board)
            current_eval, _ = minimax(new_board, depth - 1, False, alpha, beta)
            
            if current_eval > max_eval:
                max_eval = current_eval
                best_move = (r, c)
            
            alpha = max(alpha, max_eval)
            if beta <= alpha:
                break
        return max_eval, best_move
        
    else: # プレイヤー (白=1) の手番
        min_eval = float('inf')
        for r, c in valid_moves:
            new_board = apply_move(r, c, 1, current_board)
            current_eval, _ = minimax(new_board, depth - 1, True, alpha, beta)
            
            if current_eval < min_eval:
                min_eval = current_eval
                best_move = (r, c)
                
            beta = min(beta, min_eval)
            if beta <= alpha:
                break
        return min_eval, best_move

def find_best_move(current_board, depth):
    start_time = time.time()
    _, best_move = minimax(current_board, depth, True, -float('inf'), float('inf'))
    end_time = time.time()
    print(f"Minimax探索時間: {end_time - start_time:.2f}秒 (深さ: {depth})")
    return best_move

# --- UIと描画 ---

def draw_board():
    """盤面を描画し、駒を配置する"""
    canvas.delete("all")
    
    # 盤面のグリッド線を描画
    for i in range(BOARD_SIZE + 1):
        # 垂直線
        canvas.create_line(i * CELL_SIZE, 0, i * CELL_SIZE, WINDOW_SIZE, fill="black")
        # 水平線
        canvas.create_line(0, i * CELL_SIZE, WINDOW_SIZE, i * CELL_SIZE, fill="black")

    # 駒の配置
    for r in range(BOARD_SIZE):
        for c in range(BOARD_SIZE):
            player = board[r][c]
            if player != 0:
                x1 = c * CELL_SIZE + 5
                y1 = r * CELL_SIZE + 5
                x2 = (c + 1) * CELL_SIZE - 5
                y2 = (r + 1) * CELL_SIZE - 5
                color = "white" if player == 1 else "black"
                canvas.create_oval(x1, y1, x2, y2, fill=color, outline="black")

    update_score_display()

def update_score_display():
    """スコアとターン表示を更新する"""
    p1_count = sum(row.count(1) for row in board)
    p2_count = sum(row.count(2) for row in board)
    
    score_text = f"白: {p1_count}\n黒: {p2_count}"
    turn_text = f"現在のターン: {'白' if current_player == 1 else '黒'}"
    
    if score_label and turn_label:
        score_label.config(text=score_text)
        turn_label.config(text=turn_text)

def show_hint():
    """合法手があるマスをハイライト表示する"""
    canvas.delete("hint")
    valid_moves = get_valid_moves(current_player, board)
    
    for r, c in valid_moves:
        x1 = c * CELL_SIZE + 2
        y1 = r * CELL_SIZE + 2
        x2 = (c + 1) * CELL_SIZE - 2
        y2 = (r + 1) * CELL_SIZE - 2
        canvas.create_rectangle(x1, y1, x2, y2, outline="yellow", width=3, tags="hint")

# --- イベントハンドラ ---

def handle_click(event):
    """マウスがクリックされたときの処理"""
    global board, current_player, game_running
    
    if not game_running or current_player == AI_PLAYER:
        return

    c = event.x // CELL_SIZE
    r = event.y // CELL_SIZE
    
    if not (0 <= r < BOARD_SIZE and 0 <= c < BOARD_SIZE):
        return

    flips = is_valid_move(r, c, current_player, board)
    
    if flips:
        save_state() # 履歴保存
        
        board = apply_move(r, c, current_player, board)
        
        next_player = 3 - current_player
        end_turn(next_player)
    else:
        messagebox.showinfo("無効な手", "そこには置けません。")

def end_turn(next_player):
    """ターン終了時の処理"""
    global current_player, game_running
    current_player = next_player
    
    draw_board()
    
    p1_moves = get_valid_moves(1, board)
    p2_moves = get_valid_moves(2, board)
    
    if not p1_moves and not p2_moves:
        # 両者とも置けない = ゲーム終了
        game_running = False
        p1_count = sum(row.count(1) for row in board)
        p2_count = sum(row.count(2) for row in board)
        
        winner = "引き分け"
        if p1_count > p2_count: winner = "白の勝利"
        elif p2_count > p1_count: winner = "黒の勝利"
            
        messagebox.showinfo("ゲーム終了", f"最終スコア 白:{p1_count}, 黒:{p2_count}\n{winner}")
        return
        
    elif not get_valid_moves(current_player, board):
        # パス (合法手が無い場合は相手のターンに戻る)
        messagebox.showinfo("パス", f"{'白' if current_player == 1 else '黒'}は置く場所がないためパスします。")
        current_player = 3 - current_player
        draw_board() # プレイヤーが変わったことを表示

    show_hint()
    
    if current_player == AI_PLAYER and game_running:
        root.after(500, make_ai_move) # 0.5秒後にAIを起動

def make_ai_move():
    """AIが指し手を決定し、実行する"""
    global board, current_player, game_running
    
    if not game_running or current_player != AI_PLAYER:
        return

    save_state() # 履歴保存
    
    best_move = find_best_move(board, SEARCH_DEPTH)
    
    if best_move:
        r, c = best_move
        board = apply_move(r, c, AI_PLAYER, board)
        
        next_player = 3 - AI_PLAYER
        end_turn(next_player)
    else:
        # AIが置けない場合もパス処理で end_turn が実行される
        end_turn(3 - AI_PLAYER)

def save_state():
    """現在のゲーム状態を履歴に保存する (Undo用)"""
    global history
    state = (
        [row[:] for row in board],
        current_player
    )
    history.append(state)

def undo_move():
    """一手前の状態に戻す"""
    global board, current_player, game_running, history
    
    if current_player == AI_PLAYER:
        messagebox.showinfo("警告", "AIの手番中はUndoできません。")
        return
        
    if len(history) <= 1:
        messagebox.showinfo("警告", "初期状態のため、これ以上戻せません。")
        return
    
    # プレイヤーとAIの2手分を戻す
    if len(history) >= 2:
        history.pop() # AIの手
        history.pop() # プレイヤーの手
    else:
        # 初期盤面直後の履歴の場合
        history.pop() 

    prev_board, prev_player = history[-1]
    
    board = [row[:] for row in prev_board]
    current_player = prev_player
    game_running = True
    
    draw_board()
    show_hint()
    
    if current_player == AI_PLAYER and game_running:
        root.after(500, make_ai_move)
        
# --- GUIメイン関数 ---

def create_board_ui():
    global root, canvas, board, score_label, turn_label
    
    root = tk.Tk()
    root.title(f"オセロゲーム (AI深さ: {SEARCH_DEPTH})")
    
    # --- UI中央配置のための改善 ---
    
    # 1. すべての要素を保持するメインフレームを作成
    main_frame = tk.Frame(root)
    
    # 2. main_frameを親ウィンドウの中央に配置
    #    expand=True で利用可能なスペースを広げ、anchor='center' で中央に寄せます。
    main_frame.pack(expand=True, anchor='center') 
    
    # --------------------------------

    # 3. ボード(キャンバス)を main_frame の中に配置 (親を root から main_frame に変更)
    canvas = tk.Canvas(main_frame, width=WINDOW_SIZE, height=WINDOW_SIZE, bg="green")
    canvas.pack(side=tk.LEFT)
    
    # 4. スコアパネル(サイドパネル)を main_frame の中に配置 (親を root から main_frame に変更)
    score_frame = tk.Frame(main_frame, width=SIDE_PANEL_WIDTH)
    score_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10, pady=10)
    
    # --- スコアとターン表示 ---
    
    score_label = tk.Label(score_frame, text="スコア", font=("Arial", 16))
    score_label.pack(pady=20)
    
    turn_label = tk.Label(score_frame, text="ターン", font=("Arial", 14))
    turn_label.pack(pady=10)

    # --- ボタン ---
    
    undo_button = tk.Button(score_frame, text="一手戻る (Undo)", command=undo_move, 
                            height=2, bg="#B0C4DE", fg="black")
    undo_button.pack(pady=20)
    
    hint_button = tk.Button(score_frame, text="ヒント表示", command=show_hint,
                            height=2, bg="#FFFACD", fg="black")
    hint_button.pack(pady=10)
    
    # --- ゲームの起動 ---
    
    board = create_initial_board()
    canvas.bind("<Button-1>", handle_click)
    
    # 初期状態を履歴に保存
    save_state()
    
    draw_board()
    show_hint()
    
    def on_closing():
        root.destroy()
        sys.exit()
        
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    create_board_ui()

次回はナンプレ(数独)のアプリを作成と動作確認を予定しています。楽しみにお待ちください。

ホーム » ブログ