実戦でオシツブシが決まると気持ちいいね! 囲碁クエスト9路盤本日の対局より。
それで実際の所これ活きてたの?
(注意:↑↑に画面内に入ったらigo.js等を読み込んで、読み込み終わったら盤面を表示するスクリプトが仕込んであります。表示まで時間がかかるのはサーバが重いんじゃないかな……?)
実戦でオシツブシが決まると気持ちいいね! 囲碁クエスト9路盤本日の対局より。
それで実際の所これ活きてたの?
(注意:↑↑に画面内に入ったらigo.js等を読み込んで、読み込み終わったら盤面を表示するスクリプトが仕込んであります。表示まで時間がかかるのはサーバが重いんじゃないかな……?)
JavaScript碁盤を書きました。
最近囲碁クエストにはまっているのですが、ブラウザ版には終局後の検討機能がありません。検討機能は終局後に巻き戻して途中から自分で好きに石を並べられるモードです。終局後に「あそこは何かあったんじゃないかな」「防ぐ手はなかったかな」などと思うことは良くありますが、ブラウザでプレイするとそれができません。Android版にはちゃんと検討機能がありますし過去の対局を振り返って検討することもできるのですが、デスクで作業しているときにいちいちスマホに手を伸ばしたくありません。
幸い棋譜(SGF)のエクスポート機能(ブラウザのテキスト領域に表示してコピーできる)はあるので、何か適当なソフトで読み込めば検討はできます。CGobanを入れてみたのですが、コピーしたSGFテキストを一度ファイルに保存してから読み込まなければならず面倒です。同じブラウザでSGFをペーストして読み込める碁盤があれば便利だなと思ったので作ることにしました。同じようなものは探せばいくらでもあるみたいですが、探すのも面倒ですし作るのも勉強になりますので。
基本的なロジックは10年以上前に作ったものがあったので流用しつつ現代風にアレンジ&機能追加しました。
盤面のデータ構造はUint32Arrayに1交点2ビットずつ詰め込む形を採用。9路盤を2*9*9=162ビット、162/32=5.0625で6dwords(24バイト)で表現出来ます。最小を目指すなら3(空点、黒、白)の倍数で記録していくべきなのですがさすがにそれはやりすぎかなと。盤外を表す点を用意すべきか等色々トレードオフがあってよく分からないのですが、盤面を沢山複製して保持するときにできるだけ小さい方がいいだろう、ということで。ハッシュも計算しやすそうですし。ただ、今回の用途までならどうやっても問題なし。
画像で表示するのも馬鹿らしいのでSVGで表示。グラデーションと影をつけてまあまあの見た目になりました。
履歴は最初からツリー構造を採用。巻き戻してから他の場所に打ったときは別のノードを作って記録します。分岐はA, B, C,…と盤面に表示できるようにしました。他のツールのようにツリー構造全体を図で表示出来たらかっこよかったのですが、これでも十分実用にはなります。
SGFインポート/エクスポート機能は今回の目的では必須。SGFフォーマットのページを見たらEBNFが書いてあったのでそれを元に手書きで解析器を作成。プロパティは、囲碁クエストのSGFを読むだけならSZ, B, Wくらいに対応すれば十分。後から詰碁を表示したいと思ってPL, AB, AW, AE, C, その他マーク等にも対応しました。
詰碁に対応するためにコメント機能、フリー編集機能、先番設定機能を追加。
一応スマホでも使えるようにタッチイベントに対応。19x19はさすがに小さいですね。ボタン類も一緒に小さくなってしまうので盤面部分だけ拡大縮小出来るようになるとよいのですが……。(→追記:対応しました)
あ、結果の判定機能はありません。死活判定しなければならないので難しいですよね……。死に石を指定出来るようにするとか、純碁みたいに全部打ち切るなら判定出来るのですが……。今後の大きな課題です。
純碁と言えば王銘エン先生の「こんなに面白い 世界の囲碁ルール」は面白かったです。オススメ。
ちなみに囲碁クエストをはじめたのは、知り合いが将棋ウォーズをやっていたからです。将棋は子供の頃に父親にボコボコにされたトラウマがあるのでやらないことにしているのです。なので囲碁ウォーズを試してみたのですが、Android版アプリの出来がすこぶる悪い。何回やっても起動しなくなるのです。時々思い出したようにすんなり起動するのですが、アプリを終了するとまた起動しなくなります。何が原因なのかまったく分かりません。調べてみると囲碁クエストという別のアプリの方が安定しているようなのでそちらをはじめました。
どちらもメインは9路盤のようです。19路のオンライン対局は大変なのでとてもやる気が起きませんが、9路なら短時間でプレイできるのでオススメです。
「一問一答! 囲碁・9路盤の手筋 ~基本定石からヨセまで~ (囲碁人ブックス)」という本も買ってみたのですが、これはちょっと難しいですね。難易度が低い問題が分からなくて気を落としていたら難易度が中くらいのものがあっさり分かってしまったり。巻末の引き分け定石は参考になります。手順番号の着いた棋譜を脳内再生するのは苦手なのですが、今回作った碁盤を使って勉強してみようと思います。
Web上も探すと9路の情報が結構ありますね。Youtube動画もあります。
かぐや様2期くらいかなぁ。
| 印象 | 開始日時 | チャネル | タイトル |
|---|---|---|---|
| - | 03/26(木) | Netflix | 7SEEDS 第2期 |
| - | 03/27(金) 24:00 | TOKYO MX | GRANBLUE FANTASY The Animation Season2 |
| - | 03/28(土) 11:00 | YouTube | アイカツオンパレード! |
| - | 03/30(月) 18:10 | NHK Eテレ | 忍たま乱太郎 第28シリーズ |
| △ | 04/01(水) 24:30 | TOKYO MX | 神之塔 -Tower of God- |
| - | 04/01(水) 07:30 | テレビ東京系 | みっちりわんこ!あにめ~しょん |
| - | 04/01(水) 18:00 | NHK Eテレ | おじゃる丸 第23シリーズ |
| - | 04/02(木) 17:20 | NHK Eテレ | のりものまん |
| ○ | 04/02(木) 22:30 | TOKYO MX | 八男って、それはないでしょう! |
| × | 04/02(木) 23:30 | TOKYO MX | 球詠 |
| ○ | 04/02(木) 24:00 | TOKYO MX | かくしごと |
| - | 04/03(金) 16:00 | 公式ポータルサイト | ベイブレードバースト スパーキング |
| - | 04/03(金) 17:00 | YouTube(爆丸公式Ch) | 爆丸アーマードアライアンス |
| - | 04/03(金) 18:20 | NHK Eテレ | あはれ!名作くん シーズン5 |
| × | 04/03(金) 22:00 | TOKYO MX | 新サクラ大戦 the Animation |
| △ | 04/03(金) 25:23 | テレビ東京 | 文豪とアルケミスト |
| ○ | 04/03(金) 25:55 | TBS | LISTENERS リスナーズ |
| △ | 04/03(金) 26:25 | TBS | 波よ聞いてくれ |
| - | 04/04(土) 07:30 | テレビ東京系 | 遊☆戯☆王 SEVENS |
| - | 04/04(土) 09:00 | NHK Eテレ | おしりたんてい 新シリーズ |
| - | 04/04(土) 17:35 | NHK Eテレ | MAJOR 2nd 第2シリーズ |
| ○ | 04/04(土) 22:00 | TOKYO MX | アルテ |
| △ | 04/04(土) 25:00 | TOKYO MX | ギャルと恐竜 |
| ○ | 04/04(土) 25:30 | TOKYO MX | 乙女ゲームの破滅フラグしかない悪役令嬢に転生してしまった… |
| △ | 04/04(土) 25:30 | テレビ朝日 | イエスタデイをうたって |
| - | 04/05(日) 07:30 | テレビ東京系 | ディズニー・サンデー ラプンツェル ザ・シリーズ |
| △ | 04/05(日) 09:00 | フジテレビ | デジモンアドベンチャー: |
| × | 04/05(日) 09:30 | テレビ東京系 | トミカ絆合体 アースグランナー |
| ○ | 04/05(日) 10:30 | テレビ東京系 | ミュークルドリーミー |
| - | 04/05(日) 19:00 | NHK Eテレ | もっと!まじめにふまじめ かいけつゾロリ |
| △ | 04/05(日) 22:00 | TOKYO MX | 継つぐもも |
| - | 04/05(日) 22:30 | TOKYO MX | アイドリッシュセブン Second BEAT! |
| △ | 04/05(日) 23:30 | TOKYO MX | グレイプニル |
| - | 04/05(日) 24:15 | NHK総合 | キングダム 第3シリーズ |
| △ | 04/05(日) 24:30 | TOKYO MX | 社長、バトルの時間です! |
| △ | 04/05(日) 25:00 | TOKYO MX | 俺の指で乱れろ。~閉店後二人きりのサロンで…~ |
| - | 04/06(月) 07:05 | テレビ東京系 | ガル学。~聖ガールズスクエア学院~ |
| × | 04/06(月) 17:55 | テレビ東京系 | ファンファンキティ! |
| ○ | 04/06(月) 22:30 | TOKYO MX | 白猫プロジェクト ZERO CHRONICLE |
| ○ | 04/06(月) 22:50 | NHK Eテレ | 銀河英雄伝説 Die Neue These(NHK版) |
| - | 04/06(月) 23:29 | BS日テレ | ぽっこりーず |
| - | 04/06(月) 24:30 | TOKYO MX | プリンセスコネクト!Re:Dive |
| - | 04/06(月) 25:00 | チバテレビ | あの世のすべては、おばけぐみ |
| - | 04/06(月) 25:15 | TOKYO MX | 邪神ちゃんドロップキック' |
| - | 04/06(月) 25:30 | テレビ東京 | フルーツバスケット 新作 2nd season |
| - | 04/07(火) 07:30 | テレビ東京系 | おりがみにんじゃ コーヤン |
| - | 04/07(火) 17:55 | テレビ東京系 | シャドウバース |
| △ | 04/07(火) 24:30 | TOKYO MX | 放課後ていぼう日誌 |
| ○ | 04/07(火) 25:35 | TOKYO MX | 無限の住人-IMMORTAL- |
| - | 04/08(水) 07:20頃 | テレビ東京系 | おばけずかん |
| △ | 04/08(水) 22:00 | TOKYO MX | 本好きの下剋上 司書になるためには手段を選んでいられません 第2部 |
| - | 04/08(水) 23:29 | BS日テレ | 困ったじいさん |
| △ | 04/08(水) 24:55 | フジテレビ | BNA |
| △ | 04/09(木) 25:10 | フジテレビ | 富豪刑事 Balance:UNLIMITED |
| - | 04/09(木) 25:28 | TBS | ノー・ガンズ・ライフ 第2期 |
| - | 04/09(木) 25:58 | TBS | やはり俺の青春ラブコメはまちがっている。完 |
| 04/10(金) 21:00 | YouTube | 魔神英雄伝ワタル 七魂の龍神丸 | |
| ○ | 04/10(金) 20:00 | ニコニコCh | ざしきわらしのタタミちゃん |
| △ | 04/10(金) 22:30 | TOKYO MX | 天晴爛漫! |
| - | 04/10(金) 24:30 | TOKYO MX | 食戟のソーマ 豪の皿 |
| - | 04/10(金) 25:25 | TBS系 | アルゴナビス from BanG Dream! ANIMATION |
| - | 04/11(土) 17:30 | 日本テレビ系 | ハクション大魔王2020 |
| - | 04/11(土) 19:00 | TOKYO MX | ガンダムビルドダイバーズRe:RISE 2nd Season |
| ○ | 04/11(土) 23:30 | TOKYO MX | かぐや様は告らせたい? |
| ○ | 04/12(日) 23:00 | TOKYO MX | ULTRAMAN |
| △ | 04/13(月) 23:00 | TOKYO MX | 啄木鳥探偵處 |
| △ | 04/16(木) 19:53 | テレビ東京 | テレビ野郎 ナナーナ 怪物クラーケンを追え! |
| △ | 04/20(月) 21:54 | TOKYO MX | オリンピア・キュクロス |
| - | 04/23(木) | Netflix | 攻殻機動隊 SAC_2045 |
| - | 04/25(土) 08:00 | テレビ東京系 | ヴァンガード 外伝 イフ-if- |
| - | 04/25(土) 24:00 | TOKYO MX | ソードアート・オンライン-アリシゼーション- War of Underworld -THE LAST SEASON- |
| - | –/–(日) 08:30 | テレビ東京系 | デュエル・マスターズキング |
org-modeで書かれた文書を org-match-sparse-tree を使ってをタグで検索する(Sparse Tree を作る)とマッチしたヘッドラインだけが表示されるのですが、そのとき本文も全て一括で表示して欲しかったのでどうしたらよいのか調べてみました。
例えば DIARY タグで検索して日記を通して読みたいときにいちいち一つ一つのヘッドラインをTABキーで展開してまわるのは面倒です。かといってS-TABだとマッチしていない部分の本文も展開されてしまいます。
マニュアルのSparse Treeの項目を見てもマッチしたエントリを移動する操作くらいしか書いていませんでした。
しかたがないので org-match-sparse-tree 関数(org.el内)を調べてみることにしました。
org-match-sparse-tree は内部で org-scan-tags を呼んでいます。 org-scan-tags はその名の通りの文書をスキャンしてマッチする部分を巡回する関数です。マッチしたところで何をするかは引数 action で指定することになっていて、 org-match-sparse-tree では 'sparse-tree を渡してマッチした場所をハイライトしたりヘッドラインを表示したりしています。 action には任意の関数も渡せるようになっているので、そこにサブツリーを表示する関数を渡してマッチした部分を子孫も含めて全て表示させることにしました。
org-match-sparse-tree のコードをベースに修正してみます。
;; org-match-sparse-treeをベースに修正 (defun org-match-sparse-tree-show-subtree (&optional todo-only match) "Create a sparse tree according to tags string MATCH. MATCH is a string with match syntax. It can contain a selection of tags (\"+work+urgent-boss\"), properties (\"LEVEL>3\"), and TODO keywords (\"TODO=\\\"WAITING\\\"\") or a combination of those. See the manual for details. If optional argument TODO-ONLY is non-nil, only select lines that are also TODO tasks." (interactive "P") (org-agenda-prepare-buffers (list (current-buffer))) (let ((org--matcher-tags-todo-only todo-only)) ;; 修正点ここから ;; まずはトップレベル以外は全て非表示にする。ハイライトももしあれば解除する。 (org-overview) (org-remove-occur-highlights) ;; 修正点ここまで (org-scan-tags ;; 修正点ここから ;; マッチしたところをハイライトしてからサブツリーを表示する (lambda () (and org-highlight-sparse-tree-matches (org-get-heading) (match-end 0) (org-highlight-new-match (match-beginning 1) (match-end 1))) (org-show-subtree) ) ;; 修正点ここまで (cdr (org-make-tags-matcher match)) org--matcher-tags-todo-only)))
元々の (org-scan-tags 'sparse-tree ....) では、マッチした場所で (org-show-context 'tags-tree) を呼び出しており、 (org-show-context) は (org-show-set-visibility) を呼び出してその場所を表示状態にします。どのような表示を行うかは org-show-context-detail 変数でカスタマイズできるのですがサブツリー全体を表示するというオプションはありません。 'local が一番近いのですが、なぜか次のエントリーのヘッドラインまで表示してしまいます(ドキュメントにもそう書いてあるので意図した動作のようです)。他のオプションには if point is not on headline, also show entry and all children 等と書いてあり、ヘッドライン外にポイントがないと内容は表示してくれません。しかたがないので (org-show-context) の代わりに直接 (org-show-subtree) を呼ぶようにしてあります。
org-match-sparse-tree の実行中、 (org-show-context) を一時的にオーバーライドしても結果は同じです。こちらの方がシンプルでしょうか?
(require 'cl) (defun org-match-sparse-tree-show-subtree (&optional todo-only match) (interactive "P") (flet ((org-show-context (&optional key) (org-show-subtree))) (org-match-sparse-tree todo-only match)))
fletで一時的にorg-show-contextをオーバーライドしてみました。
これでマッチした全エントリーの中身(サブツリー)を表示出来たのですが通常の動作との使い分けが面倒なことに気がつきました。最初から本文を全部読みたいと思って検索するなら上で定義した (org-match-sparse-tree-show-subtree) を使えば良いのですが「とりあえずタグで検索 → 一覧が出る → 一覧を全部展開」という手順の方が自然な気がします。
本当に欲しかったのはS-TABでマッチした(ハイライトした)エントリだけをvisibility cyclingすることなのではないでしょうか。
マッチした部分は (org-occur-next-match) という関数で巡回出来ます(M-g n や M-g p で移動するときに使われます)。マッチした部分のハイライトは (org-highlight-new-match beg end) で行われています。これは org-occur のためにある関数のようですが指定された範囲にオーバーレイを適用して、オーバーレイを org-occur-highlights リストに追加します。検索後にC-c C-cでハイライトが消えるのは (org-ctrl-c-ctrl-c) 内で org-occur-highlights リストに何か入っているときは (org-remove-occur-highlights) を呼んでいるからだったりします。 (org-occur-next-match) で次のマッチ、前のマッチに移動出来るのはオーバーレイの 'org-type プロパティに値 'org-occur が設定されているからで、その値を検索して移動しているようです。
なので (org-occur-next-match) でハイライト部分を巡回して (org-show-subtree) しまくればマッチしたエントリーの内容をサブツリーを含めて表示出来ます。
(defun org-show-occur-highlights-subtree () (interactive) (save-excursion (goto-char (point-min)) (while (ignore-errors (org-occur-next-match 1)) (org-show-subtree))))
本当は S-TAB をオーバーライドしてハイライトがあるときはハイライトされている部分だけを展開したり折りたたんだりできればいいのですが現在の展開状態を調べる必要があるので面倒そうなので今日はここまでにしておきます。
ここまでで一応検索結果(Sparse Tree)の本文を一括で表示(展開)できるようになったのですが、やっぱり S-TAB に割り当てて検索結果を開いたり閉じたりしたい! ということで引き続き調べてみました。
まず、既存の S-TAB がどのようになっているのか調べる必要があります。
調べてみた結果、次のようなフローになっていることが分かりました。複雑ですね。
S-TAB => org-shifttab => org-global-cycle => (org-cycle '(4)) => org-cycle-internal-global
C-u 引数によって多少流れが変わることもあるかもしれませんが最終的には org-cycle-internal-global が呼ばれるようです。
org-cycle-internal-global関数は切り替えの処理を行いますが最初はOVERVIEWで表示し、同じコマンドが連続して呼ばれたときに => CONTENTS => ALL => OVERVIEW … の順で表示を切り替える実装になっています。私はこれまで気がついていなかったのですが現在の状態から次の状態へ変わるわけではないんですね。例えば他の作業をした後にS-TABを3回押せばどんな状態からでも必ずALLになるわけです。
実際の表示状態の変更は次の関数が行います。
ハイライトがあるとき(Sparse Tree表示時)の動作はこれらの関数を修正すべきでしょう。S-TABだけでなくこれらの関数を直接呼び出すこともあるでしょうし。
……と思ったのですが、これらの関数がハイライトがあるときにどのような動作をすべきか考えたのですがなかなか一貫性のある挙動を決めることができませんでした。
org-occurがあるのでハイライトされるのはヘッドラインだけとは限らないところが問題を難しくします。Sparse Treeを表示した直後に1回S-TABを押したときに同じ表示になる(何も変わらない)のも避けたいところです。
仕方が無いので既存の動作との一貫性は脇に置いてSparse Tree表示時の特殊な挙動として仕様を決めることにしました。
ハイライトがあるときは、この二つと既存の三つをS-TABで切り替えることにします。
OVERVIEW => SPARSE-TREE-SHOW-MATCHED => SPARSE-TREE-SHOW-ALL => CONTENTS => ALL
つまり2回S-TABすればマッチした箇所(タグ検索の場合はヘッドラインのみ)が、3回S-TABすれば本文まで展開してくれるわけです。既存の操作感覚に割と近くなっていると思います。マッチした部分に限定されたくない場合に備えて、4回S-TABすればCONTENTS、5回S-TABすればALLとなるようにしました。
;; org-sparse-tree-cycle.el ;; https://misohena.jp/blog/2020-04-15-show-contents-of-entry-when-sparse-tree-in-org-mode.html ;; (defun org-sparse-tree-show-all () "マッチした部分をサブツリーを含めて表示します。" (interactive) (save-excursion (org-overview) (goto-char (point-min)) (while (ignore-errors (org-occur-next-match 1)) (org-show-context 'tags-tree) (org-show-subtree)))) (defun org-sparse-tree-show-matched () "マッチした部分を表示します。" (interactive) (save-excursion (org-overview) (goto-char (point-min)) (while (ignore-errors (org-occur-next-match 1)) (org-show-context 'tags-tree)))) ;; この関数は org-cycle-internal-global を元に修正しています。 (defun org-sparse-tree-cycle-internal () "Do the sparse tree cycling action." ;; Rotate overview => sparse-tree-show-matched => sparse-tree-show-all => contents => show all ;; Hack to avoid display of messages for .org attachments in Gnus (let ((ga (string-match "\\*fontification" (buffer-name)))) (cond ((and (eq last-command this-command) (eq org-cycle-global-status 'sparse-tree-show-all)) ;; We just created the overview - now do table of contents ;; This can be slow in very large buffers, so indicate action (run-hook-with-args 'org-pre-cycle-hook 'contents) (unless ga (org-unlogged-message "CONTENTS...")) (org-content) (unless ga (org-unlogged-message "CONTENTS...done")) (setq org-cycle-global-status 'contents) (run-hook-with-args 'org-cycle-hook 'contents)) ((and (eq last-command this-command) (eq org-cycle-global-status 'contents)) ;; We just showed the table of contents - now show everything (run-hook-with-args 'org-pre-cycle-hook 'all) (org-show-all '(headings blocks)) (unless ga (org-unlogged-message "SHOW ALL")) (setq org-cycle-global-status 'all) (run-hook-with-args 'org-cycle-hook 'all)) ((and (eq last-command this-command) ;; sparse tree show matched (eq org-cycle-global-status 'overview)) (org-sparse-tree-show-matched) (setq org-cycle-global-status 'sparse-tree-show-matched)) ((and (eq last-command this-command) (eq org-cycle-global-status 'sparse-tree-show-matched)) ;; sparse tree show all (org-sparse-tree-show-all) (setq org-cycle-global-status 'sparse-tree-show-all)) (t ;; overview (run-hook-with-args 'org-pre-cycle-hook 'overview) (org-overview) (unless ga (org-unlogged-message "OVERVIEW")) (setq org-cycle-global-status 'overview) (run-hook-with-args 'org-cycle-hook 'overview))))) (defun org-sparse-tree-shifttab-advice (oldfun arg) (if org-occur-highlights ;; ハイライトがある (org-sparse-tree-cycle-internal) ;; 元の関数を呼ぶ (funcall oldfun arg))) ;; org-shifttabをオーバーライドする (advice-add 'org-shifttab :around #'org-sparse-tree-shifttab-advice)
うん、これでSparse Treeが大分使いやすくなりました。