2024/01/15 Katan-O 道路建設可能か?の続き
今日は宿直AなのでいつもはRacketが使えないんだけど事情によりLinuxPCを持ち込んだので道路建設の準備をする。
前回までで、自軍道路の続きに道を作る準備が出来た。
道路は自軍町村からも建設できるので、その関数を・・準備として、まず全体マップから自軍町村の座標を取り出す関数を。テストOK
任意のポイントから上下左右に道が伸びてるか?って述語を前に書いたけど、それを改造して「上下左右に道が伸びてないか?」伸びてなければその道路座標を、いずれかの軍の道路があれば#fを返すものに書き換える。
で、書き始めると下方向と右方向、つまり座標の+方向に調べるときにはそのまま書けば良いんだけどー方向に探す時の計算が複雑になってしまう・・うーん?
あ、そうか。まず+方向のを作っておいて、そこから左なら−1、上なら−9すれば良いじゃないか。ということで完成。
上で作ったクロージャをリストにして、自軍町村の座標ごとに上下左右でチェックして道路建設可能な座標を返す、と。#fはFilterでキレイに取り除けるので楽だ〜
道路が建設可能なのは12と14なので大正解(^o^)
アッサリと書いてるけど例によって苦戦した・・単体テストで道路マップと間違えてポイントマップを入れてしまって数時間悩んだりw
最後にちょっと時間があったのでマップのオブジェクトに黒枠をつけて見やすくしてみたり。
今夜もRacetが使えるので、道路関係を終わらせよう!
2024/01/14 Katan-O 道路を少し
道路が建設可能か?という事を調べる述語を書く・・。これは無様だけど大量の条件式を使って場合分けすればリストの該当箇所をチェックするだけだから楽勝だろう・・と、思ったらこれがうまく動かない。
一部だけを取り出せばちゃんと動いてるのに
実際のコードでは#tとなるべきところが#fとなってしまう。いや、絶対におかしい・・どこかにとんでもないミスが潜んでいるのに違いない。が、エラーが出ないもんだから延々2時間もチェックを続ける
Tate&yokoがおかしいのか?それとも・・んん?仮引数にご注目!PLYAERだと・・・?ゲーッ!まさか!?
質の悪いことに条件式の中にも3箇所ほど同様のスペルミスがあったためにエラーが出ずに妙な挙動をしてるのであった。いや〜恐ろしい
条件式全体は恐ろしく長いので一部だけ貼り付けるとこういう感じ・・この4倍くらいの長さがあります(-_-;)
で、MapとLambdaで実験。はい、道が建設可能な道路座標を見事取り出すことが出来るようになりました。はぁ〜・・スペルミスが無かったらもっと進んでたのに。
ということで、後は「村・町」が隣接してる場合も建設可能って部分、必要資材も条件に入れて(折角なので連想型にするか?)、村から町へのレベルアップもリストアップ出来るようにして、ようやくREPLのRead部分、Phase最初での行動選択メニューを表示する準備が整うな!あ、その前にPhase頭のサイコロ振りとカード分配があるか・・
2024/01/12 宿直Bが無い日はJSICP
やる気だったんですけど・・・
なるほど、以前途中でやめた理由がわかった。「フェルマーテスト」とか、そもそも数学的素養(高校レベル)の無い僕にはソコがキツかったんだな、と。
う〜ん・・Land of Lispの内容をSchemeに移植するってのも面白そうだけど・・
とりあえず「数学的に分からんw」って部分は飛ばして最後まで読み通して見るか・・・その後に演習を答えをカンニングして勉強するって感じで行きますわ。
2024/01/10 Katan-Oの道が作れる述語が思ったより面倒
前回AI生成の画像で戸惑っていた。考えたらイラストやで良いじゃないか・・
するとこうなるわけだけど・・ちょっと味気ないけど統一感はあるし無難かなぁ
前回、村が作れる場所をリストで返す関数が楽勝だったので道も大丈夫だろう!と思ってたんだけど・・Dokohe?関数は交点を起点にしてどこに道が伸びているのかをチェックする仕組みだったので、任意の道ポイントからどこへ道が延びているかをチェックする用に転用出来ない(こともないかもだけどやたらヤヤコシい)と判断。専用で作るか・・
道は横に延びてるのと縦に延びてるのがあるので、道マップの4つで横、5つで縦・・と分けないといけない。これは以前作ってたんだけど、こいつは交点用に作ってて、しかも0からじゃなくて1から数えるようにしてるもんだから一々変換しないといけなくなっててゲンナリ。あ~・・0から数えるようにしておくべきだった(-_-;)
んでまあ、雛形としてはこういう感じ?計算式で上下左右端を判定するか・・と思ったんだけど、もう逆に面倒なのでカッコ悪いけど該当する道(13と22は左端でなおかつ上下端ではない)で場合分けしていくか・・。
カッコ悪くて冗長だけど、コンピューターってそういのを処理するのが役目じゃないか!と開き直る。
2024/01/07 Katan-Oのメインを書き始める
Phaseの更新を行うためにList-ref用のIndex(?)として循環リストを使うのが良さそうな気がする。ということで動作の復習
構想としては、Phaseの初めに行動の選択肢を表示しないといけない(あ、その前にカードを配らないといけないが(-_-;))、選択肢として(可能であれば)「村を作る」「町を作る」「道を作る」が考えられるので(カードの交換は後回し)、それぞれが可能かどうかをチェックするところから書いていくか・・
過去コードを読むと、必要な部分は数ヶ月前に書いていた模様。隣の座標に村や町があったら作れないので、そのチェック
道が繋がってないと村や町を作れないので同じくチェック
上の2つを組み合わせて特定の座標に施設を作れるか?をチェックするCan-build?を作る。述語なので、これをMapに噛ませて建設可能座標リストを作る予定。
Can-build?を組み込んで村が作れる座標一覧をリストで返すVillage-ok?を作成。実験で使ってるマップは道がアホみたいに長いので作れる場所もえらく多くなってるが・・。
これに必要な資材が揃ってるか?をチェックするのを作って組み合わせて完成となる予定。なんか疲れてて食後寝てしまったので、あんまり進まなかった・・。
次回はRoad-ok?を書いて、それぞれの資材チェック部分を書いて、Phase最初のカード追加部分を書いて、ダイス部分を書いて・・うーん、COMアルゴリズムは別として、ほぼ出来上がったようなものかな?面倒なのは画面表示部分か・・作成可能な座標に数字を表示して選択出来るようにしないといかんし・・
2024/01/04 Katan-O、表示をちょっとイジる
実際に画面を表示しようとすると描画のみではシンドい・・ということでAIに画像生成を頼もうとしたところまで進んだ・・
BARDなんかも役に立たなかったので昨年登録したLineの画像生成Botに頼んで見る。が!嘘だろ・・一日3枚しか(無料では)生成出来ないなんて・・。
イラスト風に羊をお願いします・・と指定したらこうなった。怖い・・
というわけで、鉄鉱石、羊、木材・・と3枚だけ生成成功。待ってられないので粘土ブロックはネットで正方形の画像を適当に拾って80x80に縮小。
描画部分を画像表示に置き換えて・・
マップ表示はこうなった。おおっw なんか一気に印象変わったな!
数字が見にくいなぁ・・
ってことで、Number表示の下に一枚レイヤーをかませて・・
見やすくする、と。羊も口元が隠れてちょうど良かったかな
後は右枠のステータス表示部分を全プレイヤー表示できるようにしないと。
なんとなくForでPlayerを回して一気に描画出来そうな気がする。これが一人分なので・・
Forを入れると・・あ、駄目か~・・。Place-imagesは最初に画像リスト、その後に座標リストと分けられてるので、そのままForで繰り返すのは無理か。けど、PlayersをForで回すと、その後のMatch-letと相まって非常にエレガントな書き方に出来そうな気がするので諦められない・・どうすっかな~
For部分で一気にListを作ってしまう部分と、その後にリストを利用してPlace-imagesする部分を分けるとうまくいく気がする。挙動の実験をして・・
関数内で変数を設定してForで回してConsをSet!してReverseで順番を正してApply appendで合体させてPlace-imagesに渡すと・・
ムホ~出来た!黄色文字の見難さ以外はイメージ通りだ!
これでようやくお楽しみのシステム部分に取り掛かることが出来るわ~
今回のPlace-statusのスタイルはMinimaxで散々にらめっこしたForと破壊的変更のコード無しには、こうスンナリ思いつかなかったと思うので・・無駄にはならんかったな!という印象
2024/01/03 Katan-O、とうとう続く
まずは前回のロンゲストロード関数をもうちょっと見栄え良くするか・・
前回、大域変数にしていたFinalだけど関数内に入れて処理する事にする
検証用の表示部分を削除して・・と副作用部分を全部削ったらBodyが無い!とエラー。うーん、'()だけでも置いとくか?ってことで解決。
じゃあそもそも副作用必要ないな・・って事でFor/listに変えてLongestを返すようにする。良いじゃない!
ロンゲストロードをMap化する実験。全座標から自軍カラーのHazi?をFilterして出来たリストにロンゲストロード。え?8とか9とかあるんだけど・・おかしくない?
おかしくない!あ、別の地点から始めたら確かに9になるわ!
出来たリストからMaxで抜き出して・・ようやく完成した!
これでバッチリ・・だ!?ああっ!ちょっと待てよ・・自軍の道が円環状態で繋がった場合どうすんだ!
あらら~・・これはそれまでの座標を記録しておいてMemberでNext-pをチェック、存在してたらそこで終了って処理を書かないとイカンか・・。
まあ、とりあえず現時点でのロンゲストロード関数。円環処理以外ではちゃんと動いてるということで・・(-_-;)
気分転換に表示部分の続きを書く。ザッと今まで書いたところを復習して・・仮の処理で一人分しか表示してなかったのを四人打ちにするためにデータを作る。
で、困ったのがマップタイルを単色のSquareで描いてるので、道や町を表示させるのが困難であるということ。出来れば画像データを使わずに描画のみでやりたかった(←コードを貼り付けるだけで全実行できるので)けど、思い切ってJpeg画像でタイルを作るか・・
WEBのAI画像生成サービスを適当に選んで依頼してみる。うわ、気持ち悪い・・けどまあ、洋ボードゲーの雰囲気が出てる気もする。こんな感じで、ブロック、鉄、木材なども作るか・・
と思ったら生成数上限に達する。勝手に4枚作って2セットで終わりかよ!もう来ねぇよ!
しょうがないのでClaude先生に頼んでみる。おお・・心強い!
が、30分経っても出来上がらない。これは使い物になりませんわ・・
じゃあGoogleのBARDなら出力できるか?
ファファファ、ファーック!文字しか表示できてないじゃないか・・調べたらまだ画像表示は実装されてないらしい。じゃあ正直に言えよ!
・・Lineの画像生成サービスがあったのでそっちで作るか・・。サイズ指定出来たっけかなぁ・・
というわけで続きは次回!
追記
微妙に時間があったので適当に書いてみる。Loopに過去ポイント記録用のスロットを増やして、Loop時に現在のポイントをConsするようにして終了条件にMemberで新たなC-pointがK-pointに含まれてたらってのをOrで足して・・普通に考えたらこんな感じだけど・・どうせまたおかしくなるんでしょ?
えっ!?ちゃんと動いた!俄然やる気が漲ってきたw
2023/12/31 Katan-O ロンゲストロードを探索で
Dokohe?で返ってくる座標リストからもと来た方向のみを削除したい。リストから特定の要素を排除するのにRemoveが使えそうだが・・?なぜかエラーが出る。Procedureだと・・?remove 1 '(1 7)は問題なく動くのに変だな?
しょうがないのでMy-removeを作る。
で、とりあえずこういう感じかなぁ・・というのを書いてみる。出力結果は・・・うーん?Valはむちゃくちゃだが・・Pointは何か意味がある数字が並んでいる
おおっ? 座標1,2,7,6,11,12・・・などなど、ちゃんと道で繋がってる座標を返してるじゃないか!ということは基本的な構造は間違ってないな。
ちょっと改造。Longest-r内でLongestとCountを初期化してるので0に戻ってしまって数値が累積できないと見た。Forを使ってるのに非破壊的な書き方になってるのは・・プロトタイプだから許していただこう。
おおっ!?Valがそれっぽい数値になった。だけど、肝心の返り値が0のままである。うーん、スコープがFor内で完結してるからなぁ・・。となると、大域変数としてFinalを設定し、そこをFor内で破壊的変更するとか?
Longest-rの終了時の処理、CountやValの数値には影響しないと判明。そうか・・オセロの探索の場合、一番浅いところに戻ってくる過程で数値が変化したけど、こっちは潜っていく過程で数値が変化って違いがあるから同じような書き方では無理なのか?とにかくちょっと反則かもだけどココで破壊的行動をしてみるか・・・
(if (> longest final) (set! final longest) '()))ってな感じで大域変数にLongestの数値を更新
実行後にリスナーでFinalの数値を見ると・・オッケイ、ロンゲストロードがちゃんと出力された!
後はこれをちゃんとゲーム内で使えるようにお化粧しないと・・だけど、まあ狙った結果は出るようになった。探索レベルが0から1くらいへは上がったんじゃないだろうか!
次回は・・基本的な流れは分かったので、もうちょっとマシな書き方に挑戦してみるか?Let loopを使ったりとか・・それかもう先に進むかも知れません。ま、年内の目標が達成できて良かった!
2023/12/27 RacketのKatan-O復習
自分で書いたのに完全に忘れてるのでコメントをつけながら見て・・ロンゲストロードまで到達。前はこのような形で出力して、内部がある()だけを取り出すようにしたってわけです。これをやめてちゃんと「探索」でやってみようと
ポイントになるのは現在のポイントから、枝分かれした方向を調べるDokohe?かな。これを使って再帰を使って探索をしてくことになりそう
全方向に対して、自軍の道が伸びてるポイントを調べるわけだが・・
・Hazi?でスタート地点を特定、そこから開始するけど開始ポイント方向に戻ってはいけないので、このHazi?地点と再帰時に開始するポイントを保存しておいて、Dokohe?で返ってくるリストから排除しないといけない。で、枝分かれしてる場合は、それをリストにしてForかLet loopのイート用リストにして、Count変数を設定してLongest変数を更新していく・・って感じかな。ま、それは次回になるんですが。
2023/12/25 五目並べで脱出(Gauche)
Scheme系でLoopから脱出するとなると・・大域脱出って事になるんだろうか?と思って思い出すために検索。うーん、ただでさえややこしい自作のコードにこれを組み込むとなると面倒だなぁ・・じゃあClaude先生に聞いてみるか
なんと3つも考えてくれました。1つ目はBreak。超簡単じゃない?これこれ、こういうのが欲しかったんですよ。
2つ目はエラー処理の応用。ん〜なんだかよく分からない。’Breakを返したらエラーが起きて#tになって抜けられるのか?
いや、それよりも!Loop()とすることで無限ループが起こせるってこと?この表記のほうが僕にとっては重要なんだけど!
お〜これは破壊的だけどエレガント!ちょっと形として覚えておきたい
ま、結局一番簡単な1番のBreak使うんですけどね
という訳で書いてみました。
当たり前のごとく不具合。うーん、なんで0?あ、ループのBest-move初期値を0にしてるからだ
Car MovesでOtonari-pで出てきた座標を入れるようにする。おお
例のBreak処理はこんな感じで・・・Racketにもあれば良いのにね。
というわけでRacketでForを使った場合とLet loopを使った場合も(多分)αβカットが出来た!と思う。破壊的でない方法で書くというのもエエけど・・もう蛇足みたいなものかな。
宿直BではRacketが使えるのでKatan-Oに戻るけど、宿直Aや図書館ではChromebookしか使えないので引き続きGauche(Paiza.io)を使って基本的な事をやっていこうと。適当にテキストベースの謎ゲームを作ってもいいけど、人間からの入力が出来ないからなぁ(Read-lineとか)。
実用Commonlispの続きを・・とも思ったけど、競技オセロのためにタイムを計測するとか、他の環境だとどうしていいやら見当もつかないものだったり自然言語処理だったりとあんまり興味が湧かないのでSICPをやっていこうかな、と。これだったら翻訳せずに読めるし書けるので。
じゃあ2024年は、もう一度SICPに挑戦するということで!シックプシックプ!
2023/12/24 オセロのαβカット、これでどないだ?
昼間にClaude師匠にGuacheでのLoop脱出を教えてもらった時にはこういう感じでイケまっせ!との事だったけど。Racketでは駄目でした。ForのBody内に、Breakを配置するには・・?
LetでValを再帰束縛した後にBreak‐Clauseを置いたら文法的に間違いだと言われるし
文法的にOKな場所に置いたらVal束縛前なのでこれまた駄目
となると・・新たな変数を用意して一個前のValをSet!で更新して持ち越すようにしてはどうだろうか?そしてBreak判定に使用
お互い打つ手がなくなった場合のFinal-valueも謎仕様だったので自分なりに変更する。オリジナル版は何度読んでも意味が分からない
Body部分はSet!の嵐。まあFor構文だからしょうがない。自ターンはPlyが奇数、敵ターンはPlyが偶数と決めてしまうことで謎の(- (alpha-beta ...)のマイナス部分を回避、AchievableとCutoffは入れ替えることなく、自ターンはAchi、敵ターンはCutとして明確に分けてしまった。オリジナルは何度も読んでも意味が分からないので(-_-;)
全体的にはこう言う感じでどう?
実行結果はこう・・確かにえらく早く結果が出る・・気がする
アテに出来るかわからないけどClaude先生にチェックをしてもらう。αβカットになってますやろか?と。
一応出来てると言う判定だったけど・・Lisp系では頼りにならないからなぁ・・まあ、いつまでもやってたってしょうがないので「出来た」って事にしておくか!
というわけで、ようやく!Katan-Oのロンゲストロードを深さ探索で書く準備が出来た!数カ月ぶりで再帰してきた訳だ。
が、ザッと自作コードを見たんだけど・・全然意味が分からないのであった(やっぱりな)
2023/12/21 オセロのαβカットか、うーん
というわけでαβカット。とりあえずMinimaxで使った方法、Consで型を揃えて・・というのを書くが・・・。Cutoff と Achievableだけど、入れ替えて再起するのは何となく分かる、が、マイナスするってのが分からない。もしかしてオリジナルは文頭で全部にマイナスしてるから、それを打ち消すためかな?
ここから自分なりに納得の行く形に書き換えていくか~
Forの強制中止オプションは#:breakで、置き場所はFor((... )ココ)なんだけど、そこに置くとValが束縛できてない部分だからエラーが出るんだよなぁ・・かと言ってBody部分に置こうとすると、これまたエラー。なんか書き方があると思うんだけど。
もしかして()でくくってしまうのか?と思ったけど駄目
駄目だと思うけどWhenを代わりに使えないか?と考えて実験。動いてるか分からないのでDisplayで表示するようにしてみる。が、表示されてるからと言ってそこで終わってるのかは分からない。右下の表示行数に注目してみるか?仮説ではDisplayよりも脱出的に(cons achievable best-move)を出したほうが行数が短くなればカットできてると思うんだけど。Displayだと600弱
Consでの場合は・・1800!全然駄目w
Unlessも使えるので書いてみるが・・Breakと同じでやっぱり束縛が出来てない。うーん、Valを使わない方法で判定をしないといけないのか・・?
1,原作でAchievableとCutoffを交互に入れ替えてるのが解決の可能性
2,ForのBody内にBreakを発生させる事が出来る可能性
のどっちか・・かな?もっと根本的な問題かも知れないが。
というわけで引き続き考えていきますか・・
2023/12/20 オセロの方のMinimaxもなんとかしたい
先日五目並べで多分成功したっぽい?Minimaxをオセロの応用してみるべくRacketを起動。書いてるうちに気づいてしまった・・あ、五目でOrizinal-pとして今のターンが黒か白かを判定してるつもりになってたけど、再帰のたびに再設定されるから意味ねぇやwと。となると・・
1,外部にPlayer変数を持っておく
2,ストラテジーを使うPlayerを固定してしまう
3,深さ設定Plyを固定して(例えば3レベルまで探索なら)自ターンは奇数、敵は偶数としてしまう
画面では2で実験してるけど、3が妥当かな?
まあどっちみちちゃんとした出力は得られてないわけですがw
余計な条件を省いて(when (> (car val) best-val) (set! best-move (car val) ...とやった場合はこのようにそれらしい値が返るのだが・・。
あ、そうか・・敵ターンの際に(when (< (car val) best-val) ... としてるのだけど、Best-valの初期値がループ開始時に0になってるのでそれ以下にならないのが原因か。ってことは敵ターン時にはデッカイ数を初期値をしないといえけないな?
というわけでForを使ったMinimax、これでどうだ!敵ターン時には初期値10000、味方ターン時には初期値-10000(別に0でエエけど対称美のため)にしておくと。
結果はちゃんとそれらしい数値が返ってきた、コレでエエやろ!五目並べの方もちゃんと修正しておかないと。
しかしCL版の(val (- (minimax player board ply eva-fn)...がマジで謎だな?Valuesに対してLet-values的なもの(Multipleなんとか)を使わなくても大丈夫なところも納得いかんし。
ま、とにかくこれでLoop版も全部書き換えて次回はとうとうαβカットに挑むで