2020-05-31に更新

とろろ碁 を高速に開発しようぜ☆(^~^)<その3>

ほびょびょびょろへべっべ☆(^~^) とろろ碁 公開下書き

前記事その2に戻る

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 PC重……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ぴーー しーーー おーーーー もーーーーー ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なーーー んーーーー でーーーー おーーーー そーーーー いーーーー のーーーーー」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 顔の出現位置を決めるアルゴリズムの仕様を固めようぜ☆」

20200527go115a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ボール型なら 簡単だな☆
その前に、餅の形状を 調べる必要があるんじゃないか☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 顔の出現位置と 関係のある形状だけ リスト しなさい!」

20200527go116.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ どこに出現してもおかしくないよな、人面疽☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 人面疽ではない☆!」

20200527go116a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ もう少し 正確に描くと こうか……☆
どこに出現するか、ではなく、よっぽど変わったところでなければ……、
正方形の形をしている石の上なら  出したいところに 出せる、という感じだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 独立しているものと、従属しているものに分けて 法則を解き明かしていきましょう!」

  • 他の石から独立しているもの
    • 生き石
    • 死に石

KIFUWARABE_80x100x8_01_Futu.gif
「 独立しているものは、生き石と、死に石の二種類だぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 独立しているのなら、周りの石に関わらず、自分の好きな所に 顔を出せばいいのよ!」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 誘目性の強さや 視点誘導の先を 位置ごとに 数値化できるならいいんだが……☆
調べてみるかだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 独立しているものは、ワイルド・カードとして使えるのだから、後回しでいいんじゃないか☆?
回りに影響されて 決まってしまうやつ を先に決めたらどうだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 それもそうね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 とりあえず 石のつながりを 切り出してみるか……☆」

20200527go117a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ コンピューター囲碁では 餅 とは呼ばず、連(Ren, String, Group) と呼ぶ☆
上図では 27個の連が 確認できるし、検出するための再帰アルゴリズムも 探せば出てくるだろう☆
liberty みたいな感じの名前の関数になっていると思うぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今日はここまでだぜ☆」

2020-05-28(thu) 17:40 (着目から 78:25 経過 - ) - 翌00:30

◆◆◆◆ ↓挿入

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 liberty のアルゴリズム知りたいやつがいるかもしれないな……☆
ちょっと 書いておくかだぜ☆」

20200528go121a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 将棋盤のように描くが、13路盤のときは、枠を付けて 15路盤 のメモリを用意しろだぜ☆」

20200528go121a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 枠の内側の 盤の左上から 英文読みで スキャンをして、最初に 白石に ぶつかったとしようぜ☆」

20200528go121a3b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ そして 現在位置から 左、上、右、下 の順に 隣に進もうとするとしよう☆
今いる所の石の色と同じ色が 隣にあれば 進めるぜ☆」

20200528go121a3b2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 一度通ったところは、二度と通らないようにチェックを入れておけだぜ☆ この探索は ツリー構造になる☆
次は下に行けそうだな☆」

20200528go121a3b3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ この調子で 進めていくだけだぜ☆ 進める所がなくなったら、ツリーを一段戻れだぜ☆
まだ進んでいない枝があれば、その先を再開しろだぜ☆」

def search(current):
    for direction in [1, -15, -1, 15]: # 左、上、右、下
        next = current + direction
        if !board_check[next] and board[next] == my_color:
            board_check[next] = True # Check

            # TODO 石の形を覚えるなら、ここでリストに入れるなり何なり 書けだぜ☆(^~^)

            search(next): # Recursive.

    # 帰り道に書いてもいいな☆(^~^)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 再帰関数 の書き方を覚えると それは簡単にできる☆
ただ、上のサンプルは ビール飲んだあとに書いたので 合ってるのか知らん☆ 調べろだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こんなところだな……☆」

◆◆◆◆ ↑挿入

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 顔の 指し手一覧 をリストしましょう!」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 はいはい……☆」

20200528go118a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ここが 顔の生えてくる可能性のある一覧だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 この半歩ずれてるやつは 計算で 算出できるものなのかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 簡単にできるぜ☆ その算法をお見せしよう☆」

20200528go118a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ まあ、まずは 塗り絵でも しようぜ☆?」

20200528go118a5.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ブロックの右端とか、下端とかを チョンプ しようぜ☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 いじきたないなあ☆」

20200528go118a5b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 白抜きした方が 見やすいか……☆」

20200528go118a5b2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ で、あとは 半歩ずらした気分 になれだぜ☆ ほんとに ずらすと手間になる☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 これで 顔の指し手一覧 をリストする手段も確立したわね」

KIFUWARABE_80x100x8_01_Futu.gif
「 このマスに 評価値を入れていって、一番大きなところに 顔を出せばいいんだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 出現位置評価と、顔の向き評価な☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 あっ、レクタングルが無いぜ☆!」

20200528go118a6.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 忘れていたぜ☆
右端をチョンプ、下端をチョンプ、これだけでは消し残っているのがあるから、 右下から見て ナナメ1つ上もチョンプしろだぜ☆」

20200528go118a7.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ しかし 石に顔が付いているとか どっちを向いているとか 考えたことがなかったので、
思考が ツルツル 滑り落ちていってしまうぜ☆
考え方の フック が欲しいよな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 距離は?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あるかも☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 石は 相手の石を 見てるんだろ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 なるほど☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 死に石は魅力的で、顔の向きは アームに影響を受けるわよ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 顔の位置と、顔の向き は どちらが先に決まるべきだぜ☆?」

20200528go119a1.png

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ↑ 石の顔は 向き合うのだから、どの石を見合うか 1対N の組みが先に決まり、
そのときの N は 基本的に 同じ方向を 向いているのよ。例外もあるけど」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そうなんだぜ、例外があるんだぜ☆ ヒューリスティックだよな☆
とろろ が どういう考えを持って 石の顔を この方向に向けたのか説明できるのなら
わたしは とろろ評論家になるぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 遠くにある死に石と、近くにある相手の石なら、どちらを優先して向くんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そんな、遠くの親戚より 近所の他人みたいな話し……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 どれぐらいの遠くの、という問いが解決しないわよ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 近くの石について、と決めつけるか……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あと、鳩ノ巣原理……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 えっ☆? 鳩がどうしたって☆? 腹でも減ったか☆?」

20200528go119a2b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 石が近くにあることを優先するか、ペアを作ることを優先するか、どっちだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 まず、近くの石の方を向いて、その次に、余剰の顔があれば、ペアを作ることに ちからを回すのよ」

20200528go119a3b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ どちらの石が 余剰だぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 スキャンした順で うしろのやつが 余剰でいいんじゃない?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 それだと 割りくうやつが いそう……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 近くの石というが……☆」

20200528go119a4.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑ この黒石は どっち向けばいいんだぜ☆?」

20200528go119a4b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ N対1 のルールから、周りの顔から見て 利便性のあるところに顔があるといいよな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 埼玉県民と 千葉県民と 神奈川県民 が集まる場所は 東京ね」

20200528go119a4b3.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 位置が定まれば、距離のルールで 向きも定まるか……☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 正方形だったら☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 見栄え優先で 南 向いてりゃいいんじゃないか☆?」

20200528go122.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 点 なら すぐ距離を測れるが、変な餅の形してると 中心がまず 分からんしな……☆
堂々巡りだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 鶏と 卵よね」

KIFUWARABE_80x100x8_01_Futu.gif
「 距離が先か、顔が先か☆?」

20200528go123.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 双頭、三叉頭 だったら どうするんだぜ☆? エアロゾルだったら☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 ゲリマンダーみたいな真似して……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 顔は 相手の石の数に影響を受けるのかしらねぇ?」

KIFUWARABE_80x100x8_01_Futu.gif
「 個体という考えでは ダメ なのでは☆?
人口密度を求めてはどうだろうか☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 マルサスのトラップ!?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 13路盤の 13 は 素数 だからな~☆
面積を 平等に分割するのは どうやるかだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 天元 を原点にしましょう!
ウィンドウ・サイズは 1辺を奇数 3、5 にしましょう!」

20200528go123a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ なるほど……、どこがが割を食うのを避けられないのであれば 端に割を食わせ、欠損も まあまあ 押さえるサイズかだぜ☆」 

KIFUWARABE_80x100x8_01_Futu.gif
「 願いましては☆!」

20200528go123a3.png

KIFUWARABE_80x100x8_01_Futu.gif
「 御破算☆!」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 各マス 重なった人口密度を 足し合わせて、周辺に人の多い地点の目安としましょう」

KIFUWARABE_80x100x8_01_Futu.gif
「 お父ん 頼む☆」

20200528go123a4b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 7、5、5、7……、やっと終わったぜ、へぇーつら☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 今度は逆に、相手の石の人口密度も 見てみましょう!」

20200528go123a5b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ へぇーっ、つら☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 人間様の直観に合っているのは どっちだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ふたつの数を足したものも 一応 見ておきましょう!」

20200528go123a6b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ へぇーっ、つら☆!」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 なんか 説得力があるふりをしている数に なってきたんじゃない?」

KIFUWARABE_80x100x8_01_Futu.gif
「 とろろ のサンプル画像でも 見てみたいよな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 へぇーっ☆!」

20200528go124a2.png

20200528go124a3b1.png

20200528go124a4b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ つらーーーっ☆!」

KIFUWARABE_80x100x8_01_Futu.gif
「 レクタングルも 見てみたいぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 やだ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 顔の出現位置アルゴリズムは これでいいんじゃない?
やってみりゃ だいたい 動くでしょ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 きょうは ここまでだぜ☆」

2020-05-29(fri) 17:40 (着目から 85:15 経過 - ) - 翌01:00

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 レクタングルも 計算しとくか……☆」

20200528go125.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ なるべく広い範囲で 一様 にしようと思うと、端っこは 切り捨てちゃうぐらい 割り食うんだが……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 囲碁の 盤端で戦うことを欲する人は とろろ碁 なんか遊ばないのよ!
ゲーム開発は 割り食う石を見切って捨てて 助かるところを 助けることなのよ! 100点を目指したら 終わんないの!」

KIFUWARABE_80x100x8_01_Futu.gif
「 お父んの切り方、得票数の公平性は 保たれているのかだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 1票の格差かだぜ☆? そんなん考えてないぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 考え直せ☆!」

20200528go125a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ すべてのマスを調べる必要はなくて、上下反転と45°回転を使えば 同型になるところは省けるぜ☆」

20200528go125a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 人口密度とは 面積分の人口 なのだから、各マスごとに割り当てられた 面積を測ればいいんだぜ☆」

20200528go125a3b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 横長や、レクタングルは 出にくそうだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 1.7倍すればいいのでは☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 他に しわ寄せ が行くぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 右とか 下とかに ずらすと もっと 均等にできるわよ?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 点対象の囲碁盤に、右とか 下とか 差を付けたくないぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 この切り分け方なら、コンボリューション すれば 内側の方は 均等 になるわよ?」

20200528go125a4.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ コンボリューションの方が マシそうな見た目をしているな……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ウィンドウを大きくしたコンボリューションは 端は捨てるという前提なんだな☆」

20200528go126a1.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑ レクタングルの方は カウントが大きくならないか☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 1マス 0.25 にしたらどうだぜ☆ それでも 差あるけどマシだろ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 仮に 顔の出現位置アルゴリズムは 24近傍の和 をコンボリューションして求めることにしましょう。
実装して ダメだったら 変えましょう。
次は 向きよ」

20200528go127.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ めんどくさいから こういう人口密度を 8方向 求めて 多い方向こうぜ☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 雑になってきた……☆ そろそろ タイムアウトか☆」

20200529go128a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 枠の太さは 2マス にしようぜ☆ 24近傍のサーチを 回すんだぜ☆」

20200529go128a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ と思ったが レクタングル を考慮すると 左と下は 枠の太さを 3マス にした方がいいか……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 範囲内の石を 数えているだけで 顔の出現位置と、顔の向き の評価値になるという算段かだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 盤のマスに付いた評価値を 目視確認でテストすることを考えると、小数点第2位までの4桁の数(25.00とか)を表示する方法が 欲しいわねぇ」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 HTML をちょっくら書いてみるかだぜ……☆」

https://github.com/muzudho/tororo-go-js

20200529go129.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 数を出す準備は オッケーだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 じゃあ 全てのマスに石が置いてあると思って、探索を回しなさい」

  • 石密度(各ノード)
    • 盤13×13
    • ウィンドウ5x5
  • 石密度(各スクウェア)
    • 盤12×12
    • ウィンドウ5x5
      *8方向石密度(各ノード)
    • 北東
    • 北西
    • 西
    • 南西
    • 南東
  • 8方向石密度(各スクウェア)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ だいたい こんな感じか……☆
関数名を考えるかだぜ☆」

  • stone_density_node
  • stone_density_sq
  • stone_density_node_east
    • 以下略
  • stone_density_sq_east

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑ 東西南北を関数名に付けるのは かっこわるいな……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 じゃあ directed_stone_density みたいな感じで☆」

20200529go130.png

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 こういう図形って どうやって プログラミングに落とし込むの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ふつうに 黄色を原点 (0, 0) として、 (1,1) とか座標指定しろだぜ☆
他の方法でも いいけど……☆」

20200529go131a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 枠も含めて 17×17 の盤だと分かっていれば、プラス・マイナスだけで行けるぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 これって 回転できるの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 x と y に分ければ、行列演算で回転できるぜ☆」

20200529go131a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ x と y を入れ替えて、 xの符号を反転すれば 反時計回りに90°回転したのと同じだぜ☆
これを 4回 繰り返せば 4方向もOKだぜ☆
これが 四則演算で 計算でできるぜ☆」

20200529go131a3b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ タテ、ヨコ どっちだっけ……☆ 原理は覚えられるが タテ、ヨコ は覚えられないぜ☆
各マス毎に 行列演算 すれば出るぜ☆
興味を持ったら アフィン変換 で調べろだぜ☆ 全パターン解説されてる☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 xとyをひっくり返して、xの符号を反転するのでいいのなら、こんな行列演算しなくていいのでは☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 簡単だと思う方を使えだぜ☆ やりたいことがないうちは 道具の使い方は見えないものだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 90°じゃなくて 45°回転 したくない?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 できるが やりたいのは ただの回転ではなく 荷物がスライドするような回転なんで、
複雑なんで、45°スタートのパターンを作っておいて、90°回転しろだぜ☆」

20200529go131a4.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑ フム、90°回転しそうだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 うたぐり深いやつだな……☆」

20200529go131a5.png

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑ あれっ、180°回転が やりにくいぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 x、y は 90°で交差しているからな☆
真実の回転とは 2つの軸の 90°の間で起こることを言う☆ 円というのは 4分の1の回転を4回しているに過ぎない☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 じゃあ 左右反転か上下反転か いちいち調べるぐらいなら 90°回転を 2回やった方がマシか……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あっ、 y は 上が小さくて、下が大きいように 逆転しておいてくれだぜ☆

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 13×13 の盤に 太さ2の枠を付けて、 17×17 にリサイズするのは どうやったらいいの?」

20200529go132.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 分解すると こうなってるから、地道に編集してくれだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今日は ここまで……☆」

2020-05-30(sat) 23:00 (着目から 92:35 経過 - ) - 翌07:15

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 寝てた……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 太さ2の枠ではなくて、太さ2と、太さ3のところがある枠なんだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 見直しからか……☆」

20200530go133.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ こうだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ループで回すから、頭の1行だけ切り取って 12回繰り返すのではなく、 まるっと 13回繰り返すようにしてほしいんだけど」

20200530go133a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 頭から切り取っても、尻から切り取っても どちらも同じだが、こうだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 インド式掛け算で検算してるの わらう☆」

def resize13to18(a: list, default=0):
    """13×13サイズのベクトルを、18×18サイズのベクトルに変換します。"""
    b = [default] * 38
    for row in range(13):
        b.append(a[row*13:(row+1)*13])
        b += [default] * 13

    return b

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ↑ こんなんで 動くかな~?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 プリントしてみようぜ☆?」

[ '.', '.', '.', '.']

KIFUWARABE_80x100x8_01_Futu.gif
「 例えば [.] * 4 と書くと上記のようになってしまい、 '....' にはならないぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ''.join(x) のパラメーター部にリストを挟んでおいてちょうだい」

KIFUWARABE_80x100x8_01_Futu.gif
「 最後に 52個の . を付けるのを忘れているぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 付けといてちょうだい」

KIFUWARABE_80x100x8_01_Futu.gif
「 ×13 ではなくて ×5 だろ☆ 間違いだらけだな☆ 直すか……☆」

def resize13to18str(a: list, default='.'):
    """13×13サイズのベクトルを、18×18サイズのベクトルに変換します。"""
    s = ''.join([default] * 38)
    for row in range(13):
        s += f"{''.join(a[row*13:(row+1)*13])}{''.join([default] * 5)}"

    s += ''.join([default] * 52)

    return s

KIFUWARABE_80x100x8_01_Futu.gif
「 Python のベスト・プラクティスは知らないが、上記で動くぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 18文字ごとに 改行を挟むのって どうやんの?」

KIFUWARABE_80x100x8_01_Futu.gif
「 どうやるんだぜ、お父ん☆?」

    board18 = resize13to18str(stone_board1, '.')
    rows = [board18[i: i+18] for i in range(0, len(board18), 18)]
    board18_text = '\n'.join(rows)
    print(board18_text)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ こう書くみたいだが、Pythonの内包表記、読みにくいぜ☆
英語が母国語なら 読みやすいんだろうけど☆」

..................
..................
...x...o..........
..ooxxxxoo........
...oox..x..o......
..o.oox..x........
..oooox....o......
..xxxx..x.o.......
..................
.........x.o.o....
...x........ox....
..ox.x...x.oxx....
...ox....xooxxo...
..oox....xxxoo....
...o..............
..................
..................
..................

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ イケてる、イケてる☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 リストを 表形式で表示するの よく使うから 関数化できないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 つまり 一次元配列を 二次元配列 にする 次元上げ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 言葉をかみ砕いて さらに難度を上げるお父ん わらう☆」

def line_to_table_str(s: str, width: int):
    """1行の文字列を、複数行にします。"""
    rows = [s[i: i+width] for i in range(0, len(s), width)]
    return '\n'.join(rows)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ こんなもんで十分だろ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 数の配列版も作ってちょうだい」

def line_to_table(li: list, width: int):
    """1行の配列を、二次元配列にします。"""
    return [li[i: i+width] for i in range(0, len(li), width)]

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 文字列型と、それ以外の型で 関数2つ作るの イケてないな……☆」

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 24, 0, 0, 
24, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 24, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 24, 24, 24, 24, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0], [0, 0, 0, 14, 0, 5, 0, 0, 0, 19, 0, 0, 24, 24, 0, 0, 0, 0], [0, 0, 0, 0, 5, 0, 0, 0, 0, 19, 0, 0, 19, 19, 0, 0, 0, 0], [0, 0, 0, 0, 3, 0, 0, 0, 0, 14, 14, 14, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0]]

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 二次元配列って、行で折り返して見ることはできないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 pretty がやりたいんだろ☆ 調べるか……☆
Google で python pretty print list as table とか検索すれば出てくるだろ……☆」

    row_format = "{:>3}" * 18
    for row in num_board18x18:
        print(row_format.format(*row))

Output:

  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0 14  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0 19 19 19 19  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0 24  0  0 24  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0 24  0  0 14  0  0  0  0  0  0  0  0
  0  0  0  0  0  0 24  0  0  0  0  0  0  0  0  0  0  0
  0  0 24 24 24 24  0  0 10  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0 10  0  0  0  0  0  0  0  0
  0  0  0 19  0  0  0  0  0  0  0  0  0 24  0  0  0  0
  0  0  0 14  0  5  0  0  0 19  0  0 24 24  0  0  0  0
  0  0  0  0  5  0  0  0  0 19  0  0 19 19  0  0  0  0
  0  0  0  0  3  0  0  0  0 14 14 14  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 十分だな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 pretty とか どこで覚えてくるんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 プログラミングの語彙は、プログラマーのブログか、ソースコードのコメントを読めだぜ☆
海外のメーリング・リストもあり☆
生まれた場所とか近所の中だけで調べてもガラパゴスなんで 学習のソースは その時代でイケてるコミュニティを探しとけだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 これで 石の密度を 調べる準備が整ったわね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Python のラムダ計算って 書きにくいよな……☆
高階関数を用いて手軽に コンビネーターがどのような形をしているか見る、というラムダ計算の構文の簡潔性を失っている……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 Python に何も期待するなだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 分かったぜ☆ Python に何も期待しないぜ☆」

20200531go134.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 内側のテーブルをループするのって めんどくさいよな☆
めんどくさくないと思ってるやつは めんどくさがれだぜ☆」

def scan_pure_board(f):
    """枠を除いた、盤上を1マスずつイテレートして座標を返します。Y座標は行番号です。"""
    for x in range(2, 15):
        for y in range(2, 15):
            f((x, y))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 計算機科学は ここから始まったと言ってもいい ラムダ計算 の考え方を Pythonコードに落とし込むと こうだぜ☆
チューリング・マシンと ラムダ計算 を自在に操ることは 計算機科学の古典に親しむことだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 パラメーターに 変数ではなくて 関数が入ってるだけよね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 数学でよく目にするのは 四則演算 だろ☆ 1+2+3 とか☆ こいつらは 横に並んでいるだけだぜ☆
ラムダ計算 は f(g(h)) のように入れ子できる☆ すると ツリー構造 を作れる☆
その違いを お見せしよう☆」

20200531go134a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 なんか 惑星が回っていて、回っている惑星の周りを衛星が回っている みたいなこと、あるだろ☆
↑ 上図は 4重ループになってるよな☆ 黒線で示した盤のx、yループ、赤線で示したウィンドウのx、yループ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 4重ループで いいのでは☆?」

def scan_window_5x5(f, p):
    """中心を p とする 5x5 のウィンドウの各マスをイテレートして座標を返します。"""
    for x in range(-2, 3):
        for y in range(-2, 3):
            f((p[0]+x, p[1]+y))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ Python でこんな書き方でいいのか知らないが、ループも 自分に分かりやすいところで 小分けにしろだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 うーん☆」

pure_board_map = []
for y in range(2, 15):
    for x in range(2, 15):
        pure_board_map.append((x, y))

window_5x5 = []
for y in range(-2, 3):
    for x in range(-2, 3):
        window_5x5.append((x, y))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 二重ループが いつも同じ値を返すのに 二重ループするのが 無駄くさいぜ☆
テーブルにしといたろ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ラムダ計算の話しの途中だったのに……☆ 良いデータ構造を与えると プログラムは不要になることがあるのに……☆」

def scan_pure_board(f):
    """枠を除いた、盤上を1マスずつイテレートして座標を返します。Y座標は行番号です。"""
    for p in pure_board_map:
        f(p)


def scan_window_5x5(f, p):
    """中心を p とする 5x5 のウィンドウの各マスをイテレートして座標を返します。"""
    for pp in window_5x5:
        f((p[0]+pp[0], p[1]+pp[1]))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 単ループになった……☆」

    scan_pure_board(lambda p: scan_window_5x5(
        lambda pp: xxxxxxxx(pp), p))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 例えば ラムダ計算では 関数を合成できるんだぜ☆ 何この 可読性の低い Python のラムダ計算の構文☆?」

    scan_pure_board(|p| scan_window_5x5(|pp| xxxxxxxx(pp), p))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ Rust言語なら こう書くところだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 別に たいして見やすくなかった☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 本場のラムダ計算ならどう書くの?」

    λp.(λq.(q 1) p) λx.(x+1)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 例えば結果に 2 を出したけりゃ こうだぜ☆
ラムダ計算は プログラムを書くのには向いてないぜ☆ どのような動きになるかシミュレーションして、考え方だけ プログラムに使うんで☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 何が 見やすいのか 分からなかった☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ラムダ計算が プログラムだったら意味ないんだぜ☆
枝葉末節を 全部削って、入り組んだプログラムは フローとして どうなるか、を手短に見せてくれるものなんだぜ☆
ライフ・ゲームとか見たことあるかだぜ☆? あれを数式のノーテーションでやったみたいなの☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 何を言っているか分からないから 話を進めましょう!」

20200531go134a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 点p と 点pp が どういう動きをしているか 頭の中で アニメーションできるだろうか☆?
アニメーションを作ってやれば すぐ分かると思うんだが めんどいんで作らないぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ところで変数名についてなんだが、 点pp でも 点q でも 点p2 でも なんでもいい☆
特に決まりはない☆ pの次だ、ということが分かれば みな同じ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 お父んは p と q のどっちを向いているのが ぴー で、もう片方が きゅー なのか瞬間に判別付かないから p と pp なんだろ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 一般化してしまうよな☆ 左右反転してるだけで同じものなのだ、みたいな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 大混乱よね。面白い頭 してるわよね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 色弱はユニバーサル・デザインで知られるようになったが、アフィン変換頭は知られていないぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 さんぽ と 裏返っている ちんぽ は瞬時に判別できるのよね」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 無理☆ どっちも ちんぽ☆」

  • 黒石p から見た、
    • 黒石pp
    • 白石pp
  • 白石p から見た、
    • 黒石pp
    • 白石pp

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 で、石の密度なんだが、 どうせ最後に総和するのなら色を区別する意味ない のでは……☆

KIFUWARABE_80x100x8_01_Futu.gif
「 ブログを読み返してみるかだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 確かに 総和 してるな……☆ これでは 石の多いところに 顔が出てくるだけだぜ☆
なぜ それで もっともらしさがあるのか……☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 相手の顔を見るために お互い集まっているのなら、 石の密度が高いところに 顔があるのも
もっともらしいんじゃない?」

KIFUWARABE_80x100x8_01_Futu.gif
「 うーん計算が合わんな……☆」

window_5x5 = []
for y in range(-2, 3):
    for x in range(-2, 3):
        if y != 0 and x != 0: # 原点を数えない
            window_5x5.append((x, y))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 自分を数えてはいけないのでは☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 数えてしまえだぜ☆ 結果に影響はない☆」

def stone_density_node(stone_board):
    """盤サイズは 太さ2と3の枠が付いた 計18x18 にしてください。"""
    num_board = [0] * (18*18)

    def count_up(stone_board, p, pp):
        nonlocal num_board
        if stone_board[to_addr18x18(pp)] != '.':
            num_board[to_addr18x18(p)] += 1

    scan_pure_board(lambda p: scan_window_5x5(
        lambda pp: count_up(stone_board, p, pp), p))
    return num_board

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ すると プログラムは こんな感じだぜ☆」

Trace   | Start.
..................
..................
...x...o..........
..ooxxxxoo........
...oox..x..o......
..o.oox..x........
..oooox....o......
..xxxx..x.o.......
..................
.........x.o.o....
...x........ox....
..ox.x...x.oxx....
...ox....xooxxo...
..oox....xxxoo....
...o..............
..................
..................
..................
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  6  8  9 10  9  8  6  6  4  2  1  1  0  0  0  0
  0  0  8 11 13 13 12 11  8  7  5  3  1  1  0  0  0  0
  0  0 11 15 18 17 15 13  9  8  6  4  2  2  0  0  0  0
  0  0 13 18 21 18 17 14 10  9  8  5  3  2  0  0  0  0
  0  0 10 14 16 13 12  9  6  6  6  4  3  2  0  0  0  0
  0  0  8 11 13 10  9  8  6  6  6  6  4  3  1  0  0  0
  0  0  7  9 10  8  6  5  4  5  6  7  6  5  3  0  0  0
  0  0  6  8  8  6  4  5  4  6  8 10  8  7  5  0  0  0
  0  0  5  6  6  5  2  4  4  7 10 14 12 11  8  0  0  0
  0  0  8  9  9  7  3  5  6 10 14 19 16 14 10  0  0  0
  0  0  9 10 10  8  3  4  5  8 12 16 14 12  9  0  0  0
  0  0  8  9  9  7  3  4  5  8 11 14 12 10  7  0  0  0
  0  0  6  6  6  5  2  2  4  6  8 10  9  7  5  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
Trace   | Finished.

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 多分 石密度 出てるだろ☆ それっぽい数が入ってるぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 他のパターンも作っていこうぜ☆」

def stone_density_sq(stone_board):
    """盤サイズは 太さ2と3の枠が付いた 計18x18 にしてください。"""
    num_board = [0] * (18*18)

    def count_up(stone_board, p, pp):
        nonlocal num_board
        if stone_board[to_addr18x18(pp)] != '.':
            num_board[to_addr18x18(p)] += 0.25
            num_board[to_addr18x18((p[0]+1, p[1]))] += 0.25
            num_board[to_addr18x18((p[0], p[1]+1))] += 0.25
            num_board[to_addr18x18((p[0]+1, p[1]+1))] += 0.25

    scan_pure_board(lambda p: scan_window_6x6(
        lambda pp: count_up(stone_board, p, pp), p))
    return num_board
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     6     8     9    10     9     8     6     6     4     2     1     1     0     0     0     0
     0     0     8    11    13    13    12    11     8     7     5     3     1     1     0     0     0     0
     0     0    11    15    18    17    15    13     9     8     6     4     2     2     0     0     0     0
     0     0    13    18    21    18    17    14    10     9     8     5     3     2     0     0     0     0
     0     0    10    14    16    13    12     9     6     6     6     4     3     2     0     0     0     0
     0     0     8    11    13    10     9     8     6     6     6     6     4     3     1     0     0     0
     0     0     7     9    10     8     6     5     4     5     6     7     6     5     3     0     0     0
     0     0     6     8     8     6     4     5     4     6     8    10     8     7     5     0     0     0
     0     0     5     6     6     5     2     4     4     7    10    14    12    11     8     0     0     0
     0     0     8     9     9     7     3     5     6    10    14    19    16    14    10     0     0     0
     0     0     9    10    10     8     3     4     5     8    12    16    14    12     9     0     0     0
     0     0     8     9     9     7     3     4     5     8    11    14    12    10     7     0     0     0
     0     0     6     6     6     5     2     2     4     6     8    10     9     7     5     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0     0
  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00
  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00
  0.00  0.00  2.75  6.00  7.00  7.50  7.25  6.25  5.00  4.00  3.00  2.00  1.00  0.50  0.25  0.00  0.00  0.00
  0.00  0.00  6.50 14.25 16.50 17.25 16.25 13.75 11.00  8.75  6.50  4.50  2.50  1.50  0.75  0.00  0.00  0.00
  0.00  0.00  8.50 18.50 21.00 21.50 19.75 16.50 13.25 10.50  8.00  5.75  3.50  2.25  1.00  0.00  0.00  0.00
  0.00  0.00  9.25 20.00 22.25 22.50 20.75 17.50 14.00 11.00  8.75  6.50  4.00  2.50  1.00  0.00  0.00  0.00
  0.00  0.00  8.00 17.25 18.75 18.50 17.25 14.75 12.00  9.75  8.50  7.25  5.00  3.50  1.75  0.25  0.00  0.00
  0.00  0.00  6.50 14.00 15.00 14.25 13.00 11.25  9.50  8.25  8.25  8.25  6.50  5.00  3.25  1.00  0.00  0.00
  0.00  0.00  6.00 12.75 13.50 12.50 10.75  9.25  8.25  8.00  9.25 10.00  8.50  7.00  5.25  2.00  0.00  0.00
  0.00  0.00  5.50 11.25 11.50 10.75  9.25  8.25  8.25  9.50 12.25 13.75 12.25 10.25  8.00  3.25  0.00  0.00
  0.00  0.00  4.75  9.50  9.50  8.75  7.75  7.50  8.50 11.25 15.25 17.75 16.25 13.50 10.75  4.50  0.00  0.00
  0.00  0.00  4.75  9.50  9.50  8.50  7.25  7.00  8.50 12.00 16.50 19.50 18.00 15.00 12.00  5.00  0.00  0.00
  0.00  0.00  5.00 10.00 10.00  9.00  7.25  6.50  7.75 11.00 15.25 18.00 16.75 14.00 11.25  4.75  0.00  0.00
  0.00  0.00  4.75  9.50  9.50  8.50  6.75  6.00  7.00  9.75 13.25 15.50 14.50 12.00  9.50  4.00  0.00  0.00
  0.00  0.00  3.75  7.50  7.50  6.75  5.50  5.00  6.00  8.25 10.75 12.50 11.75  9.50  7.25  3.00  0.00  0.00
  0.00  0.00  1.50  3.00  3.00  2.75  2.25  2.00  2.50  3.50  4.50  5.25  5.00  4.00  3.00  1.25  0.00  0.00
  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00
  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 上のはノードで、囲碁の石を置くとこの石密度評価値だぜ☆
下のはスクウェアで、将棋の駒を置くとこの石密度評価値だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 次は8方向も☆」

def ccw90(p):
    """反時計回りに90°回転"""
    return (-p[1], p[0])


direction_map = []

"""
西向きを手動で作ります。
-2 . . x
-1 . x x
 0 @ x x
 1 . x x
 2 . . x
   0 1 2
"""
direction_map.append([(2, -2), (1, -1), (2, -1), (1, 0),
                      (2, 0), (1, 1), (2, 1), (2, 2)])
"""
北西を手動で作ります。
-2 x x x
-1 x x x
 0 @ x x
 1 . . .
 2 . . .
   0 1 2
"""
direction_map.append([(0, -2), (1, -2), (2, -2), (0, -1),
                      (1, -1), (2, -1), (1, 0), (2, 0)])

# あとは90°回転で作るぜ☆(^~^)
direction_map.append(list(map(lambda p: ccw90(p), direction_map[0])))
direction_map.append(list(map(lambda p: ccw90(p), direction_map[1])))
direction_map.append(list(map(lambda p: ccw90(p), direction_map[2])))
direction_map.append(list(map(lambda p: ccw90(p), direction_map[3])))
direction_map.append(list(map(lambda p: ccw90(p), direction_map[4])))
direction_map.append(list(map(lambda p: ccw90(p), direction_map[5])))
# print(f'direction_map=len:{len(direction_map)} {direction_map}')

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こんな感じで ループ文を書かなくても済むように テーブルを作成するんだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 こんなことするから このプログラムは何をやっているんですか? とマニュアルを書くことになるわけじゃない、
数式なしの日本語で」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 アウトソーシングしろだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 他の人が読めないプログラムを書いていいのか☆!」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 議論は要らない☆ アウトソーシングしろだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 原理的に お金の話しの輪の中から あんたが 外へ、外へ 行かない?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そら、業務のワークフローを構想するのが仕事の上部組織は わたしのブログや、こんなプログラム記事なんか 見ていてはいけないんだぜ☆
ここは 実装 なのだから☆ 実装をしないのが 構想なのだから☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 他の人の実装と、あなたの実装。どこで価格の差が付くの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 時間給だぜ☆ 契約時点でおわり☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 だいたい、お金を稼ぐスキルではないんだぜ、プログラミングは☆
お金を稼ぐスキルを持ってるやつが プログラミング覚えたら ブーストかかるだけで☆
プログラミング・スキルは 空っぽの配管みたいなもんだぜ☆ 中を 水なり ガスなりが通って 使い物になるんだぜ☆」

def scan_window_dir_node(f, ccw45, p):
    """起点を p とする方向性のあるウィンドウの各マスをイテレートして座標を返します。"""
    for pp in direction_map[ccw45]:
        f((p[0]+pp[0], p[1]+pp[1]))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ウィンドウのループも簡単だな☆」

def directed_stone_density_node(stone_board, ccw45: int):
    """
    Parameters
    ----------
    ccw45:
        0 - 東
        1 - 北東
        2 - 北
        3 - 北西
        4 - 西
        5 - 南西
        6 - 南
        7 - 南東
    """
    num_board = [0] * (18*18)

    def count_up(stone_board, p, pp):
        nonlocal num_board
        if stone_board[to_addr18x18(pp)] != '.':
            num_board[to_addr18x18(p)] += 1

    scan_pure_board(lambda p: scan_window_dir_node(
        lambda pp: count_up(stone_board, p, pp), ccw45, p))
    return num_board

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ボードのループも簡単だろ☆ これで8方向対応終わり☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 8方向にも 将棋盤座標 があるだろ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 めんどくさ……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 しかも 回転が 同型にならないやつだぜ☆ 右側と下側に1多いという……☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ドロップ・シャドウを付けるみたいな動きよね」

def drop_shadow(p):
    """右下に影が落ちるような座標"""
    return (p, (p[0]+1, p[1]), (p[0], p[1]+1), (p[0]+1, p[1]+1))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ じゃあ ドロップ・シャドウの動きを作ってしまえだぜ☆」

def flat(m):
    """重複とネストを解消します。順はばらばらになります。"""
    s = set()
    for items in m:
        # items は ((),(),(),()) みたいに丸かっこがネストしている。
        for item in items:
            s.add(item)
    return list(s)


dir_sq_map = []
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[0])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[1])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[2])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[3])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[4])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[5])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[6])))
dir_sq_map.append(flat(map(lambda p: drop_shadow(p), dir_node_map[7])))
# print(f'dir_sq_map=len:{len(dir_sq_map)} {dir_sq_map}')

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 被ったところは set() で省けだぜ☆ 変数名も長くなったから短くしようぜ☆」

def scan_window_dir_sq(f, ccw45, p):
    """起点を p とする方向性のあるウィンドウの各マスをイテレートして座標を返します。"""
    for pp in dir_sq_map[ccw45]:
        f((p[0]+pp[0], p[1]+pp[1]))

def directed_stone_density_sq(stone_board, ccw45: int):
    """
    Parameters
    ----------
    ccw45:
        0 - 東
        1 - 北東
        2 - 北
        3 - 北西
        4 - 西
        5 - 南西
        6 - 南
        7 - 南東
    """
    num_board = [0] * (18*18)

    def count_up(stone_board, p, pp):
        nonlocal num_board
        if stone_board[to_addr18x18(pp)] != '.':
            num_board[to_addr18x18(p)] += 0.25
            num_board[to_addr18x18((p[0]+1, p[1]))] += 0.25
            num_board[to_addr18x18((p[0], p[1]+1))] += 0.25
            num_board[to_addr18x18((p[0]+1, p[1]+1))] += 0.25

    scan_pure_board(lambda p: scan_window_dir_sq(
        lambda pp: count_up(stone_board, p, pp), ccw45, p))
    return num_board

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 似たような関数を作るのはイケてないが、動きが細かく違うんで 分けてしまうのも しかたないぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これで コードは書けたつもりだが、テストが手間だぜ☆
次に進もうぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 次は連(れん)だな☆」

06時56分

20200531go135a2.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こんな風に 切り分け(parse)たいわけだぜ☆
パースというのは 使いやすい形に 変えることで 1つ1つの部品が よく見えるようになる、ぐらいの意味で、深い意味のある言葉ではないぜ☆
シリアライズは 1列にして 連番振って 先頭から順に 読み取りやすくすることだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 連は英語で string だし、文字列も英語で string だし、紐も英語で string よ?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 stone string みたいな名前にすれば分かるだろ……☆」

7時16分

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 今日はここまでだぜ☆」

2020-05-31(sun) 14:45 (着目から 100:50 経過 - ) - 18:00

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 生活リズムが狂った……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 原因は 昨日の夕方に食った ラーメンとビールだけどな☆」

20200531go136.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 連(Ren)を作るアルゴリズムというよりは、呼吸点(Liberty)を調べるアルゴリズムと認識されていると思うんで、
Liberty アルゴリズムなんだが……☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 差分更新と、盤面更新の手法があって、これから説明するのは 1995年頃からある古典的な 盤面更新の方だぜ☆
これは 実装が簡単というメリットがあり、最初に手を付けるには よい方法だぜ☆
ただし、2020年の コンピューター囲碁の強いやつを作ろうと思う時は、差分更新も 別に調べて作って 使いどころに合わせて利用しろだぜ☆」

20200531go136a1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 盤面をスキャンするときは、一度読み終わった 連 は もう再チェックしないようにする仕組みが必要だぜ☆
チェックが終わった石は 印でも付けておけだぜ☆
そして スキャンのとき 印が付いているところは 飛ばせだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 スキャンの関数に 別バージョンがいるな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ラムダ計算は そういうこと しなくて済むんだぜ☆」

def scan_pure_board(f):
    """枠を除いた、盤上を1マスずつイテレートして座標を返します。Y座標は行番号です。"""
    for p in pure_board_map:
        f(p)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 盤面をスキャンするのは もう作ったわけだぜ☆」

def scan_pure_board_with_dirty(f):
    """チェックされていない番地だけ、関数を実行します。"""
    dirty_board = [False] * (18*18)  # 読み取ったらフラグを立てていきます。

    def check(p):
        if not dirty_board[to_addr18x18(p)]:
            dirty_board[to_addr18x18(p)] = True  # 読取済みのチェックをします。
            f(p)

    scan_pure_board(lambda p: check(p))

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ じゃあ あとは 発火条件で切り分ける関数を追加すればいいんだぜ☆ こんな短いの関数化しなくていいと思うが、
Python のラムダ関数 書きにくいんで 関数にしてしまおう☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ラムダ計算で書かれると 読みにくいなあ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Python のラムダ計算の構文が 読みにくいだけなんで☆
ラムダ関数は、こっちから渡した関数に 引数を入れてくれると思えだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コンピューター将棋の方やるんで、今日はここまでだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 もうタイムアウトだな☆」

<書きかけ>

何度でもクリック!→

むずでょ

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

むずでょ の最近の記事