地鳳(Nostr麻雀)
ソース
bot
ゲーム進行を司るサーバーbotおよびゲームをプレイするクライアント(プレイヤー)botのリポジトリ。
本来はプレイヤーbotは分けるべきだけど面倒くさいので同居させている。
自摸切りするだけのbot。ゲームをプレイするために必要最小限の機能を搭載したもの。何を切るかを考える賢い思考エンジンを実装すればまともな対戦相手になるはず。
UI
パブリックチャットで直接コマンドを投稿することでゲーム進行は可能だが、それは厳しいので専用のUIを用意している。それが地鳳。
仕様
プロトコル
Nostrのパブリックチャット(NIP-28)を利用していることは先に述べた通りで、イベントのcontentのテキスト部分を利用して独自の麻雀プロトコルを乗せている。
見ての通りすべての情報が丸見えであり、いくらでもズルすることができる。
地鳳ではログイン状態では他者の手牌が見えないようになっており、擬似的に通常の対局を演出している。
Nostrの仕様にテキストの暗号化があるのでそれを使用することもできるが、面倒くさいので現状ではオープンにしている。
伺か麻雀プロトコル(UKAJONG/0.2)
地鳳のプロトコルは伺か麻雀プロトコル(UKAJONG/0.2)を移植して作られている。
UKAJONG/0.2はSSTP通信により麻雀対局を実現している。
Nostr麻雀ではすべてをプレーンテキストに乗せるため、Referenceヘッダは省略して半角スペース区切りで送信している。具体的な内容はパブリックチャットのログを参照されたい。
特殊事情
Renderの無料枠で24時間稼働する
botは Render の無料枠で動かしている。15分間アクセスが無いと停止してしまうので、外部からアクセスし続けないといけない。現状のシステムでは nostr-webhook から10分ごとにアクセスしてもらっている。
また、10分ごとにアクセスして起こし続けても(インスタンスは起き続けるが) WebSocket 接続が寝ることがある。この挙動については運用中に気付いたものである。対策として kind1イベントを受信し続け、毎分ログを出力するようにしたら WebSocket 接続も切れないようになった。
created_at + 1 を続けると未来へ行ってしまう問題
Nostrの通信イベントは後から振り返った時に時刻順でソートされていなければならない。リプライ先イベントの created_at に +1 することでリプライツリーは時刻順にソートされるが、大量のイベントログが生成されるため高速で処理すると容易に未来へ吹っ飛んで行ってしまう。
ゲームサーバーbotは複数イベントを同時に送信する場合は id をマイニングしてid順にソートするように工夫して無駄に created_at が増えていかないようにしている(created_atが同じ場合はid順にソートするという仕様がある)
それでもゲーム進行が早すぎると未来へ行ってしまうため、現状のシステムでは created_at 時刻が来るまで sleep して送信を保留するという対策を施している。そのためbotだらけの対局でもゲーム進行は人間並みにゆっくりである。
また、人間がゲームに参加する場合に正直に現在時刻を created_at に設定してはいけない。イベントログの最終投稿の created_at を確認し、現在時刻がその時刻に達していなかったら created_at + 1 に時刻を設定すべきである。(地鳳のUIはこの方法を採用しているが、パブリックチャット等、通常のNostrクライアントで投稿する場合はそのようにならないため注意)
イベント投げすぎでリレーからブロックされる問題
投稿頻度がすごい。スパムに等しい。当然、普通のリレーからはブロックされる。
前述の事情のためゆっくりめにイベントを投げるようになったが、それでも厳しい。
以下のリレーは比較的寛容であるため地鳳の接続先として利用している。
(それでもbotが暴走していると判断されるほど投稿頻度が高すぎるとブロックされる)
- wss://relay.nostr.wirednet.jp/
- wss://yabu.me/
前述の通りbotはRenderで動かしているため、海外からのアクセスとなる。
yabu.me は日本以外からの投稿を許可しないが、管理botにお願いすることで一定期間海外からの投稿を許可してもらうことができる。定期的に管理botにお願いするプログラムを組むことで利用を続けている。