asterism-games 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +109 -0
- data/README.md +385 -14
- data/VERSION +1 -1
- data/bin/asterism-games +8 -0
- data/lib/asterism_games/ai.rb +64 -0
- data/lib/asterism_games/commentate/ai_comment.rb +633 -0
- data/lib/asterism_games/commentate/assets/TILES_LICENSE.txt +42 -0
- data/lib/asterism_games/commentate/assets/commentate.css +100 -0
- data/lib/asterism_games/commentate/assets/commentate.js +735 -0
- data/lib/asterism_games/commentate/assets/index.html +26 -0
- data/lib/asterism_games/commentate/assets/tiles_hand.bmp +0 -0
- data/lib/asterism_games/commentate/assets/tiles_river.bmp +0 -0
- data/lib/asterism_games/commentate/mahjong.rb +400 -0
- data/lib/asterism_games/commentate/mahjong_events.rb +320 -0
- data/lib/asterism_games/commentate/mahjong_phrases.rb +535 -0
- data/lib/asterism_games/commentate/mahjong_voice.yml +93 -0
- data/lib/asterism_games/commentate/server.rb +204 -0
- data/lib/asterism_games/commentate/voice.rb +394 -0
- data/lib/asterism_games/commentate/voicevox.rb +110 -0
- data/lib/asterism_games/commentate.rb +223 -0
- data/lib/asterism_games/host.rb +1 -1
- data/lib/asterism_games/othello/table.rb +13 -10
- data/lib/asterism_games/play/mahjong.rb +23 -10
- data/lib/asterism_games/play/mahjong_screen.rb +33 -12
- data/lib/asterism_games/play/mahjong_text.rb +47 -6
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a35db1346246e5d20505026e701fef357536cc75fa53f22bf47878398fb01c3
|
|
4
|
+
data.tar.gz: 79c4c46763e2104c3e4d03ba521327e9ceb5fa77fc74521cec6041eb3ca48b89
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2b31fdedf12d610dca0c8cbd4f1ae060b5b42eeab54dee46d62f0ced96d8895468b48e84db2eae2904489448329a19ef5888cf9cb2a514684c2610d4e0937d87
|
|
7
|
+
data.tar.gz: 9908df82204210223cecd09b651da9d12e3a27ee832433d0e57b74d4d895a7a9753c83c20b75cadfeff88ef94eb91f6564290be193b1e501a37ce209124b67d3
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,115 @@
|
|
|
3
3
|
Versions before 0.11.1 are noted in the README where each feature is
|
|
4
4
|
described ("games 0.10.0" and the like).
|
|
5
5
|
|
|
6
|
+
## 0.20.0
|
|
7
|
+
|
|
8
|
+
- **Watch games** (README "Watch games"): the owner of a mahjong table
|
|
9
|
+
may start a game of CPUs and AI players only, and watch it with the
|
|
10
|
+
others at the table (and a commentary, `commentate mahjong`). The owner
|
|
11
|
+
need not be seated: a member that joins a table where nobody is seated
|
|
12
|
+
and there is no owner is the owner (`join`'s `"owner"`, the new `"mo"`
|
|
13
|
+
of the member's own view); once somebody sits the first seated is the
|
|
14
|
+
owner as before, so a game with people seated goes as it did. The new
|
|
15
|
+
method `watch(token)` starts it when the four seats are CPUs or AI
|
|
16
|
+
players, nobody is seated and at most one seat is Claude's (Jev may take
|
|
17
|
+
all four; `"human"`, `"ai limit"`, `"seats"`, `"not owner"` otherwise).
|
|
18
|
+
The lobby view's `"wc"` says whether it may start (1), is over the AI
|
|
19
|
+
limit (2) or not (0); views during it have `"wm" => 1` and the host's
|
|
20
|
+
`@status` `"watch" => true`. Nobody is seated, so the table's
|
|
21
|
+
abandoning does not apply; instead the game stops (unrecorded) when
|
|
22
|
+
every member has left. One game, then the lobby with the same seats.
|
|
23
|
+
The record is kept with `"watch" => true` (also on `records`' entries)
|
|
24
|
+
and counts in the stats for the CPU and the AI kinds only, never for a
|
|
25
|
+
person. The AI players' daily limits work as before.
|
|
26
|
+
- **The game's AI decisions are each seat's own**: `ASTERISM_GAMES_AI_CALLS_PER_GAME`
|
|
27
|
+
(400) is now how many decisions each AI seat may ask in a game (mahjong
|
|
28
|
+
and othello, every game, watch games or not); a seat past it becomes a
|
|
29
|
+
CPU alone. Before, the seats of a game shared it, so a game with three
|
|
30
|
+
or four AI seats stopped them all part way through a tonpuu. A game's
|
|
31
|
+
most is now 400 times its AI seats; the daily limits are as before.
|
|
32
|
+
- `play mahjong`: `W` (and the command `watch`) starts a watch game, for
|
|
33
|
+
the owner with or without a seat; `A` then a seat's number turns that
|
|
34
|
+
seat into a CPU, Claude, Jev and back (the kinds the lobby offers); the
|
|
35
|
+
owner's commands (`cpu`, `ai`, `watch`, `--commands`) work for an owner
|
|
36
|
+
without a seat; the lobby marks the owner among those watching and says
|
|
37
|
+
the AI limit when the seats are over it; a watch game is headed
|
|
38
|
+
`観戦対局`.
|
|
39
|
+
- Family mruby's mahjong app (fmruby-core) has a `観戦で始める[W]` button
|
|
40
|
+
for the owner when the host says it may start (`"wc"`); with an older
|
|
41
|
+
host it is not shown.
|
|
42
|
+
|
|
43
|
+
## 0.19.1
|
|
44
|
+
|
|
45
|
+
- The commentary tells where to get VOICEVOX when it finds no engine:
|
|
46
|
+
the terminal says it is a free app installed and started apart from the
|
|
47
|
+
gem (with its home page), the page's bar says the same in Japanese, and
|
|
48
|
+
`commentate --help` and the README's "The voice" say it up front.
|
|
49
|
+
|
|
50
|
+
## 0.19.0
|
|
51
|
+
|
|
52
|
+
- **Commentary** (README "Commentary"): `asterism-games commentate
|
|
53
|
+
mahjong` watches a table from its spectate key (0.18.0) and shows it in
|
|
54
|
+
the browser: a small web server on `127.0.0.1` (HTTP and Server-Sent
|
|
55
|
+
Events by hand on `socket`, no new gem; port 7470 or the next free one),
|
|
56
|
+
a page that draws the table in a canvas with the hand-drawn tile sheets
|
|
57
|
+
of Family mruby's mahjong app, and commentary in Japanese made by rules
|
|
58
|
+
from what changed between two views (a round's start, the oorasu,
|
|
59
|
+
riichi and its waits, calls, a dora or red five discarded, a new dora,
|
|
60
|
+
the wall at 10, a tenpai, a win with its yaku and points, a draw, the
|
|
61
|
+
lead changing hands, the end), shown as captions and read by the
|
|
62
|
+
browser's voice (Web Speech API) in a queue that drops the old small
|
|
63
|
+
lines first. Space pauses, M silences, H hides the hands; `--public`
|
|
64
|
+
keeps the hands from the page altogether; `--json` gives the lines on
|
|
65
|
+
standard output; `--table`, `--namespace` (a room's tables), `--port`,
|
|
66
|
+
`--no-open`. It only reads (never joins or calls a table) and goes on
|
|
67
|
+
when the host leaves and comes back.
|
|
68
|
+
- **The voice** (README "Commentary", "The voice"): the commentary speaks
|
|
69
|
+
with VOICEVOX on this machine (`--voicevox URL`, `VOICEVOX_URL`, default
|
|
70
|
+
`http://127.0.0.1:50021`), by default as ずんだもん (`--speaker`,
|
|
71
|
+
`--style`; the numbers looked up by name in `/speakers`). The fixed
|
|
72
|
+
phrases (riichi, the calls, ron and tsumo, the rounds, 満貫 to 役満, ...)
|
|
73
|
+
are WAV files made by the user's own engine from the gem's list
|
|
74
|
+
(`commentate/mahjong_voice.yml`) into
|
|
75
|
+
`$XDG_CACHE_HOME/asterism-games/voices/<speaker>-<style>/`, named by the
|
|
76
|
+
phrasing's digest (`asterism-games commentate voices --make`, and the
|
|
77
|
+
missing ones at the start; `voices --list` lists the characters). The
|
|
78
|
+
rest of each line is synthesised as it comes and served from the
|
|
79
|
+
command's web server, kept in a bounded cache. The page says it all in
|
|
80
|
+
one queue (the fixed phrase, then the line; captions as the voice
|
|
81
|
+
starts; the old small lines dropped first) and shows the credit
|
|
82
|
+
`VOICEVOX:ずんだもん`. ずんだもん's lines are in her tone (〜なのだ),
|
|
83
|
+
kept as a tone of the phrasings (`MahjongPhrases::TONES`). Without an
|
|
84
|
+
engine the browser's voice reads as before. The engine gets the speech
|
|
85
|
+
only (サンマン, 30フ 2ハン), with the words it misreads written out
|
|
86
|
+
(流局, 起家, 跳満: `MahjongPhrases::SPEECH_READ`). No sound is in the gem;
|
|
87
|
+
`net/http` only, no new gem.
|
|
88
|
+
- **The AI's one-liners** (README "Commentary", "The AI's one-liners"):
|
|
89
|
+
at a riichi, a win, a draw, the lead changing hands, the oorasu, the end
|
|
90
|
+
and after a quiet minute, Claude (`claude-haiku-5-5` by default,
|
|
91
|
+
`--ai-model`) adds a short line after the fixed commentary, in the
|
|
92
|
+
voice's tone, marked AI on the page. At most one call every 20 s (8 s
|
|
93
|
+
for a scene bigger than the last one asked about: a win after a riichi),
|
|
94
|
+
one at a time, only while a page is open. A quiet minute is about one
|
|
95
|
+
topic the command picks (the lead, a dama tenpai with the hands shown,
|
|
96
|
+
the dora, the wall, the last discard). Thinking off, effort low,
|
|
97
|
+
`max_tokens` 200; a cut answer is dropped with its stop reason in the
|
|
98
|
+
log. The fixed part of the ask (the role,
|
|
99
|
+
the rules in short, the tone, the length, whether the hands may be
|
|
100
|
+
told) is cached; the table now and the lines that just came follow.
|
|
101
|
+
With `--public` or a page's H the AI gets no hand. An answer whose round
|
|
102
|
+
or phase is over, or that comes after 15 s, is dropped; on the page an
|
|
103
|
+
AI's line comes after the fixed ones and goes first when the voice
|
|
104
|
+
falls behind. The money is counted from the tokens (cache writes and
|
|
105
|
+
reads too) in `$XDG_STATE_HOME/asterism-games/commentary_ai_usage.json`
|
|
106
|
+
(`AI::Budget`), shown in the bar, and capped at `--ai-daily-usd`
|
|
107
|
+
(default 3 USD a day). `ANTHROPIC_API_KEY`; the anthropic gem stays
|
|
108
|
+
outside the gem's dependencies (loaded only with a key);
|
|
109
|
+
`ASTERISM_GAMES_AI_FAKE=1` tries it without one. `--ai off` turns it off.
|
|
110
|
+
- The gem holds the page (HTML, JS, CSS) and the two tile sheets
|
|
111
|
+
`tiles_hand.bmp` and `tiles_river.bmp`, copied byte for byte from
|
|
112
|
+
fmruby-core; in the gem they are MIT (`TILES_LICENSE.txt`). The gem's
|
|
113
|
+
test checks they are there and unchanged.
|
|
114
|
+
|
|
6
115
|
## 0.18.0
|
|
7
116
|
|
|
8
117
|
- **Spectate** (README "Spectate"): the host puts a table's spectate view
|
data/README.md
CHANGED
|
@@ -60,9 +60,10 @@ the console ("Permanent tables"; the server's setup: docs/install.md).
|
|
|
60
60
|
| Path | What |
|
|
61
61
|
|---|---|
|
|
62
62
|
| `asterism-games.gemspec` | the gem: what it holds and needs ("The gem and the image") |
|
|
63
|
-
| `bin/asterism-games` | the host (loads the framework and the games); `status`; `play <game>`, a table in this terminal ("Playing in a terminal"); `help` |
|
|
63
|
+
| `bin/asterism-games` | the host (loads the framework and the games); `status`; `play <game>`, a table in this terminal ("Playing in a terminal"); `commentate <game>`, a table in the browser with commentary ("Commentary"); `help` |
|
|
64
64
|
| `bin/mahjong-bots` | players without screens, for trying a mahjong table (one process each; not in the gem) |
|
|
65
65
|
| `lib/asterism_games/play.rb`, `lib/asterism_games/play/` | the terminal clients ("Playing in a terminal"): the options, `othello.rb`, `mahjong.rb` with `mahjong_text.rb` (tiles as text, widths, reading the input) and `mahjong_screen.rb` (the screen); they need neither the framework nor riichi_engine |
|
|
66
|
+
| `lib/asterism_games/commentate.rb`, `lib/asterism_games/commentate/` | `asterism-games commentate mahjong` ("Commentary"): the events and their words (`mahjong_events.rb`, `mahjong_phrases.rb`), the hub and the runner (`mahjong.rb`), the web server (`server.rb`), the voice (`voicevox.rb`, VOICEVOX's HTTP; `voice.rb`, the fixed phrases' files and the lines' sound; `mahjong_voice.yml`, the fixed phrases), the page and the tile sheets (`assets/`) |
|
|
66
67
|
| `lib/asterism_games.rb` | the framework, without any game |
|
|
67
68
|
| `lib/asterism_games/game.rb` | the base of a game: the registry, the members and tokens, who joins in a room, the views, the time, saving ("Writing a game") |
|
|
68
69
|
| `lib/asterism_games/remote.rb` | the base of the exposed object: the methods and their numbers of arguments, the caller (`from:`), `join` / `mine` / `leave` |
|
|
@@ -89,7 +90,9 @@ the console ("Permanent tables"; the server's setup: docs/install.md).
|
|
|
89
90
|
game host of your own: the framework, the two sample games that need
|
|
90
91
|
nothing else (othello, the drawing board), `bin/asterism-games` (with
|
|
91
92
|
`play`, the terminal clients of othello and mahjong: a mahjong table needs
|
|
92
|
-
riichi_engine, its client does not
|
|
93
|
+
riichi_engine, its client does not; and `commentate`, a mahjong table watched
|
|
94
|
+
in the browser with commentary, with the tile sheets of Family mruby's
|
|
95
|
+
mahjong app). It depends on
|
|
93
96
|
the asterism gem only (0.7.1 or later in
|
|
94
97
|
0.x; what came after 0.7.1, such as `@status` and `on_app_join`, is used
|
|
95
98
|
when there). Not in it: mahjong (`app/`, it needs the riichi_engine fork
|
|
@@ -473,7 +476,8 @@ asterism/games/games/@status
|
|
|
473
476
|
|
|
474
477
|
`last` is the four in rank order as `[name, rank, score]`, by the names
|
|
475
478
|
the table shows its members anyway (a table app shows them to whoever
|
|
476
|
-
joins); it is there once a game has ended.
|
|
479
|
+
joins); it is there once a game has ended. During a watch game the
|
|
480
|
+
table's entry has `"watch" => true` (games 0.20.0). The rooms' status file
|
|
477
481
|
(`GET /status` of the rooms' host) stays counts only.
|
|
478
482
|
|
|
479
483
|
`ais` counts the AI players' seats (they are counted in `cpus` too), and
|
|
@@ -698,6 +702,7 @@ connects again after 3 s; while a client of a router only reconnects
|
|
|
698
702
|
| `stand` | token | `"ok"` or why not (lobby only) |
|
|
699
703
|
| `cpu` | token, seat, on | the owner: a CPU into an empty seat (on) or out of it (a CPU or an AI player); seat -1 with on fills every empty seat; on as a name the lobby offers (`"claude"`, `"jev"`) puts that AI player in ("AI players"), `"no ai"` when it is not offered; on one seat that holds a CPU or an AI player of another kind, on turns it into the one asked for (games 0.12.1) |
|
|
700
704
|
| `start` | token | the owner, once the four seats are taken and one is a member: the seats are drawn (who deals first) and the game starts |
|
|
705
|
+
| `watch` | token | the owner: a watch game ("Watch games", games 0.20.0): the four seats CPUs or AI players, nobody seated, Claude in one seat at most; `"ok"` or why not (`"not owner"`, `"not waiting"`, `"seats"`, `"human"`, `"ai limit"`) |
|
|
701
706
|
| `rule` | token, item, value | the owner, in the lobby: a local rule ("Local rules"); `"ok"` or why not (`"not owner"`, `"not waiting"`, `"no rule"`, `"value"`, `"no change"`) |
|
|
702
707
|
| `view` | token, assist | the member's view (below), or `{"error" => "token"}`; assist: the help its screen shows (Assist, 0-3) |
|
|
703
708
|
| `act` | token, sv, type, tiles | `"ok"`, or why not: `"token"`, `"not seated"`, `"stale"` (sv is not the current one), `"not offered"`, `"not playing"` |
|
|
@@ -709,8 +714,9 @@ connects again after 3 s; while a client of a router only reconnects
|
|
|
709
714
|
|
|
710
715
|
- People join a table and wait in its lobby (`"st" => "wait"`): a member
|
|
711
716
|
sits in a seat or watches. The first member who sat is the owner (when the
|
|
712
|
-
owner stands or leaves, the member seated longest takes over).
|
|
713
|
-
|
|
717
|
+
owner stands or leaves, the member seated longest takes over). While
|
|
718
|
+
nobody is seated the owner may be a member without a seat (games 0.20.0,
|
|
719
|
+
"Watch games"). The owner fills empty seats with CPUs and starts. At the end of a game the owner's
|
|
714
720
|
next (or `over_seconds`) brings the table back to the lobby with the same
|
|
715
721
|
seats, for another game.
|
|
716
722
|
- The node that calls is the caller in the call's key (`from:`, filled
|
|
@@ -970,6 +976,47 @@ going for everyone (the other away seats are played for as before). A
|
|
|
970
976
|
table of CPUs only (`humans: 0`, the permanent spectator tables) is never
|
|
971
977
|
abandoned.
|
|
972
978
|
|
|
979
|
+
### Watch games
|
|
980
|
+
|
|
981
|
+
A game of CPUs and AI players only, which the people at the table watch
|
|
982
|
+
(games 0.20.0): the owner fills the four seats and starts it without
|
|
983
|
+
sitting down, and a commentary (`commentate mahjong`, "Commentary") can
|
|
984
|
+
follow it like any other table.
|
|
985
|
+
|
|
986
|
+
- **The owner without a seat.** A member that joins a table where nobody
|
|
987
|
+
is seated and nobody is the owner is the owner (`join`'s `"owner"`, the
|
|
988
|
+
member's own view's `"mo"`). When somebody sits, the first to sit is the
|
|
989
|
+
owner as before: whenever a member is seated the role is where it always
|
|
990
|
+
was (the seated member who sat first, "Players that drop out"). An owner
|
|
991
|
+
who stands up with nobody else seated keeps the role; an owner who
|
|
992
|
+
leaves with nobody seated hands it to the member who joined first, of
|
|
993
|
+
those there. An older table app, which knows only `"ow"` (the owner's
|
|
994
|
+
seat), shows an owner without a seat as no owner, as it showed a lobby
|
|
995
|
+
without one before.
|
|
996
|
+
- **Starting.** `watch(token)`, the owner, in the lobby: the four seats
|
|
997
|
+
are CPUs or AI players (`cpu`), no member is seated, and at most one seat
|
|
998
|
+
is Claude's (`WATCH_AI_MAX`: Claude 1, Jev 4). The lobby view's `"wc"`
|
|
999
|
+
tells the screens whether it may start (1), or the seats are all CPUs
|
|
1000
|
+
and AI players but over the AI limit (2), or not (0); a host before
|
|
1001
|
+
0.20.0 has no `"wc"`, and the screens offer no watch game then. The
|
|
1002
|
+
seats are drawn as for `start`. The AI players' money is as for any
|
|
1003
|
+
game ("AI players"): a kind past its day's limit becomes a CPU.
|
|
1004
|
+
- **During it.** Nobody is seated, so nobody is played for and the
|
|
1005
|
+
table's abandoning does not apply ("Players that drop out"); the results
|
|
1006
|
+
move on by themselves as at a table of CPUs (`result_seconds`), and the
|
|
1007
|
+
end of the game after `over_seconds`. The members watch with the four
|
|
1008
|
+
hands ("Spectators", the spectator delay too). Views have `"wm" => 1`
|
|
1009
|
+
and the host's `@status` has `"watch" => true` for the table ("Status").
|
|
1010
|
+
When every member has left the table (`leave`, or forgotten after being
|
|
1011
|
+
away six times `away_seconds`) the game stops: back to the lobby, an
|
|
1012
|
+
unfinished game not recorded. The log says `watch game started (...)`
|
|
1013
|
+
and `watch game stopped: nobody at the table (not recorded)`.
|
|
1014
|
+
- **One game.** At the end the table goes back to the lobby with the same
|
|
1015
|
+
seats; the owner starts another with `watch` again.
|
|
1016
|
+
- **Records.** The game is recorded as any other with `"watch" => true`
|
|
1017
|
+
(in `records`' entries and in `record(id)`). Its seats count in the
|
|
1018
|
+
stats for their kinds (CPU, Claude, Jev), never for a person ("Stats").
|
|
1019
|
+
|
|
973
1020
|
### Views
|
|
974
1021
|
|
|
975
1022
|
A Hash (MessagePack); tiles are one byte each in binary strings (bin).
|
|
@@ -983,7 +1030,9 @@ A Hash (MessagePack); tiles are one byte each in binary strings (bin).
|
|
|
983
1030
|
| `sk` | the four seats: 0 empty, 1 a member, 2 a CPU (an AI player too) |
|
|
984
1031
|
| `ai`, `ak` | when an AI player sits: each seat's kind (`"claude"`, `"jev"`, `""` for the others); in the lobby, the AI players the owner may seat ("AI players") |
|
|
985
1032
|
| `aw` | the seats whose member is away, as bits |
|
|
986
|
-
| `ow` | the owner's seat (-1: nobody seated) |
|
|
1033
|
+
| `ow` | the owner's seat (-1: nobody seated, or an owner without a seat) |
|
|
1034
|
+
| `mo` | the member's own view only: 1 when this member is the owner, seated or not (games 0.20.0, "Watch games") |
|
|
1035
|
+
| `wc`, `wm` | in the lobby, whether a watch game may start: 1 yes, 2 the seats are all CPUs and AI players but over the AI limit, 0 no; during a watch game `wm` 1 ("Watch games", games 0.20.0) |
|
|
987
1036
|
| `mb` | the members in the order they joined: `[name, seat (-1 none), flags (1 away, 2 owner)]` |
|
|
988
1037
|
| `r` | `[bakaze (0 east, 1 south, 2 west), kyoku, honba, riichi sticks, dealer seat, tiles left, seat to act, phase (0 draw, 1 discard, 2 calls, 3 end)]` |
|
|
989
1038
|
| `sc`, `ri` | the scores; riichi as bits |
|
|
@@ -1168,7 +1217,7 @@ assistant through `asterism mcp`).
|
|
|
1168
1217
|
|
|
1169
1218
|
| Method | Arguments | Returns |
|
|
1170
1219
|
|---|---|---|
|
|
1171
|
-
| `records` | from (optional, 0) | `{"records" => [...], "total", "next"}`: the games, newest first, from the from-th, as many as fit one message; `next` is the from of the next page (none: the end). Each: `id`, `at` (the host's local time, `"2026-10-18 21:05"`), `game` (`hanchan` / `tonpuu`), `names`, `ranks`, `scores` (in seat order) |
|
|
1220
|
+
| `records` | from (optional, 0) | `{"records" => [...], "total", "next"}`: the games, newest first, from the from-th, as many as fit one message; `next` is the from of the next page (none: the end). Each: `id`, `at` (the host's local time, `"2026-10-18 21:05"`), `game` (`hanchan` / `tonpuu`), `names`, `ranks`, `scores` (in seat order), `watch` (`true` for a watch game, "Watch games"; else absent) |
|
|
1172
1221
|
| `record` | id | the game's head: `id`, `at`, `time` (Unix seconds), `table`, `game`, `rules` (as `look`'s), `names`, `kinds` (human / cpu / ai), `final` (`[[seat, rank, score, points x 10], ...]`), `reason` (as the view's `fr`), `rounds` (`[[wind, number, honba, kind, [four changes]], ...]`) |
|
|
1173
1222
|
| `record` | id, round | round `round` (from 0) as below, with `id`, `round`, `of` (how many rounds), `part`, `parts` |
|
|
1174
1223
|
| `record` | id, round, part | a round too big for one message comes in parts (`parts` > 1): part 0 has everything with the first moves, part k the moves from `move_from` on |
|
|
@@ -1328,7 +1377,9 @@ pipe or a file (and the tests) they read lines as before, and
|
|
|
1328
1377
|
| `1`..`4` | the lobby: sit there (an empty seat or a CPU's) |
|
|
1329
1378
|
| `S` | the lobby: stand |
|
|
1330
1379
|
| `C` | the lobby, the owner: CPUs into the empty seats |
|
|
1380
|
+
| `A`, then `1`..`4` | the lobby, the owner: that seat's next player: an empty seat a CPU, a CPU the first AI player offered, an AI player the next one, the last back a CPU (games 0.20.0; another key gives it up) |
|
|
1331
1381
|
| Enter | the lobby, the owner: start (four seats taken) |
|
|
1382
|
+
| `W` | the lobby, the owner (seated or not): a watch game, the four seats CPUs or AI players ("Watch games", games 0.20.0) |
|
|
1332
1383
|
|
|
1333
1384
|
| Othello | |
|
|
1334
1385
|
|---|---|
|
|
@@ -1457,12 +1508,297 @@ Typed (in a pipe or a file each line; on a terminal after `:`):
|
|
|
1457
1508
|
| `cpu [1-4]`, `nocpu 1-4`, `ai 1-4 KIND` | the owner: CPUs into the empty seats (or one), a CPU out, an AI player in when the lobby offers one ("AI players") |
|
|
1458
1509
|
| `tonpuu`, `hanchan`, `rule ITEM VALUE` | the owner: the game's length, any local rule ("Local rules") |
|
|
1459
1510
|
| `start` | the owner: start |
|
|
1511
|
+
| `watch` | the owner, seated or not: a watch game ("Watch games", games 0.20.0); `--commands "cpu,ai 1 claude,watch"` from a client that does not sit |
|
|
1460
1512
|
| `auto` | the client plays for you: wins when offered, passes calls, discards the drawn tile, OKs the results (again to stop) |
|
|
1461
1513
|
| `tables`, `view`, `help`, `quit` | |
|
|
1462
1514
|
|
|
1463
1515
|
The host's answers are shown in the table app's words (`今はできない`,
|
|
1464
1516
|
`卓の主だけができる` ...).
|
|
1465
1517
|
|
|
1518
|
+
## Commentary
|
|
1519
|
+
|
|
1520
|
+
(games 0.19.0) `asterism-games commentate mahjong` watches a mahjong table
|
|
1521
|
+
from outside, the way a sports broadcast does: it reads the table's
|
|
1522
|
+
spectate key ("Spectate", with the four hands), draws the table in a
|
|
1523
|
+
browser with the tiles of Family mruby's mahjong app, and comments on what
|
|
1524
|
+
happens in Japanese, as captions under the table and in a voice: a
|
|
1525
|
+
VOICEVOX character's (ずんだもん by default) when a VOICEVOX engine runs on
|
|
1526
|
+
this machine, else the browser's own (the Web Speech API; "The voice"
|
|
1527
|
+
below). It only reads: it never joins the table or calls it. CRuby and
|
|
1528
|
+
the asterism gem only; it runs on WSL and on macOS.
|
|
1529
|
+
|
|
1530
|
+
```
|
|
1531
|
+
asterism-games commentate mahjong --router tcp/127.0.0.1:7447 # a host on this machine or the home network
|
|
1532
|
+
asterism-games commentate mahjong --credentials viewer.tar # a console's network (a viewer's or a spectator's .tar)
|
|
1533
|
+
asterism-games commentate mahjong --credentials viewer.tar --namespace room/01 # a room's tables
|
|
1534
|
+
asterism-games commentate mahjong --public # for a stream: the hands never reach the page
|
|
1535
|
+
asterism-games commentate mahjong --json | tee commentary.jsonl # the lines for another tool too
|
|
1536
|
+
```
|
|
1537
|
+
|
|
1538
|
+
It starts a small web server on `127.0.0.1` (port 7470, the next free one
|
|
1539
|
+
when it is taken), prints the address and opens it in the browser (macOS
|
|
1540
|
+
`open`; WSL `wslview`, else Windows' `start`; elsewhere `xdg-open`).
|
|
1541
|
+
|
|
1542
|
+
| Option | |
|
|
1543
|
+
|---|---|
|
|
1544
|
+
| `--router LOCATOR`, `--credentials FILE` | as `play` (with neither, `ASTERISM_ROUTER` / `ASTERISM_CREDENTIALS`, all or nothing; with nothing, `tcp/127.0.0.1:7447`) |
|
|
1545
|
+
| `--namespace PREFIX` | a room's tables: `room/01` shows `room01-<table>` (the spectate keys are outside the room, so the connection has no namespace) |
|
|
1546
|
+
| `--table TABLE` | the table to show first: `mahjong1`, `games/mahjong1` or `games/spectate/mahjong1`; without it, every table that puts its spectate key is offered and the first one shown |
|
|
1547
|
+
| `--node NODE` | the node ID (default `commentate-<host>-<pid>`, or the `.tar`'s name) |
|
|
1548
|
+
| `--port N` | the page's port (default 7470) |
|
|
1549
|
+
| `--public` | hide the hands: the views sent to the page lose `hand` and `drawn`, and no line tells a hand |
|
|
1550
|
+
| `--no-open` | do not open the browser |
|
|
1551
|
+
| `--json` | each line of commentary as a JSON line on standard output (`at`, `table`, `type`, `prio`, `text`; an AI's line has `type` `ai` and `ai: true`) |
|
|
1552
|
+
| `--voicevox URL` | the VOICEVOX engine (default `VOICEVOX_URL`, else `http://127.0.0.1:50021`) |
|
|
1553
|
+
| `--speaker NAME`, `--style NAME` | the VOICEVOX character and its style (default `ずんだもん`, `ノーマル`) |
|
|
1554
|
+
| `--ai on\|off` | the AI's one-liners (default `on`; they need `ANTHROPIC_API_KEY` and the anthropic gem; "The AI's one-liners") |
|
|
1555
|
+
| `--ai-model MODEL` | the model they ask (default `claude-haiku-5-5`) |
|
|
1556
|
+
| `--ai-daily-usd USD` | what they may cost a day (default 3); then the fixed commentary alone |
|
|
1557
|
+
|
|
1558
|
+
- **The role.** The spectate keys are `asterism/<host>/spectate/<table>/state`.
|
|
1559
|
+
On a console's network a `viewer` (Read `asterism/**`) or a `spectator`
|
|
1560
|
+
(Read `asterism/*/spectate` and `asterism/*/spectate/**`) reads them; the
|
|
1561
|
+
players' role `games` does not. On a plain zenohd anyone does.
|
|
1562
|
+
- **The page.** A bar of states on top (the table, the voice, the delay the
|
|
1563
|
+
host puts on the table, a host that is not there), the table in a
|
|
1564
|
+
canvas (the round, honba, riichi sticks, the wall and the dora
|
|
1565
|
+
indicators; for each seat its wind, name, points, riichi, river with the
|
|
1566
|
+
riichi tile sideways and the called tiles pale, hand and drawn tile,
|
|
1567
|
+
melds; the seat to act framed), the captions below with the time of
|
|
1568
|
+
each, the newest at the bottom. Two seats to a row in a wide window, one
|
|
1569
|
+
in a narrow one (a phone). Keys: **Space** pauses (what comes meanwhile
|
|
1570
|
+
is held and shown when it goes on), **M** turns the voice off and on,
|
|
1571
|
+
**H** hides the hands on that page (backs instead of the tiles, and the
|
|
1572
|
+
lines that tell a hand left out). A table that several hosts put is
|
|
1573
|
+
chosen in the bar.
|
|
1574
|
+
- **The AI's one-liners.** Claude's short lines at the big moments,
|
|
1575
|
+
marked AI, after the fixed ones; the bar shows the model and today's
|
|
1576
|
+
money. "The AI's one-liners" below.
|
|
1577
|
+
- **The voice.** One queue on the page says the lines in turn, each
|
|
1578
|
+
caption coming up as its voice starts; when the voice falls behind, the
|
|
1579
|
+
old small lines are dropped (their captions still come up) and a win or
|
|
1580
|
+
a big moment is said next. With VOICEVOX, a line plays its fixed phrase
|
|
1581
|
+
first (リーチ, ポン, ロン, the round's name ...) and then the rest of it,
|
|
1582
|
+
synthesised meanwhile; without, the browser reads the lines with a
|
|
1583
|
+
Japanese voice when it has one (the bar says which voice, or that it has
|
|
1584
|
+
none). Browsers play sound only after the page has been clicked once:
|
|
1585
|
+
the page says so until then. "The voice" below.
|
|
1586
|
+
- **The commentary.** Made by rules from the difference between one view
|
|
1587
|
+
and the one before (pure functions, `commentate/mahjong_events.rb` and
|
|
1588
|
+
`mahjong_phrases.rb`, so the host can take them over later), each event
|
|
1589
|
+
in two to three phrasings chosen at random, the big moments in strong
|
|
1590
|
+
ones: the first look at the table, a round's start (the dealer and the
|
|
1591
|
+
dora; the game's first; the oorasu, and the oorasu going on), a discard
|
|
1592
|
+
of a dora or a red five, a call (チー, ポン, the kans), a new dora, the
|
|
1593
|
+
wall at 10, riichi with its waits, a tenpai (from the hand, once a round
|
|
1594
|
+
for each seat), a win with its yaku and points (満貫 and over: strong),
|
|
1595
|
+
a draw with who was tenpai, an abortive draw, the lead changing hands,
|
|
1596
|
+
the end with the ranks. The tiles and the yaku are read as a player
|
|
1597
|
+
says them (サンマン, リーチ, タンヤオ). With the hands hidden (`--public`,
|
|
1598
|
+
H) a tenpai is not said and a riichi is said without its waits.
|
|
1599
|
+
- **It does not stop.** A host that leaves (`on_leave`, or no put for 15 s)
|
|
1600
|
+
is shown as gone and comes back with its next put; a new round or a new
|
|
1601
|
+
game goes on as before. The delay of the table is the host's
|
|
1602
|
+
("Spectators"); the page shows it and adds none.
|
|
1603
|
+
- **The web server.** HTTP by hand on the standard library's `socket`
|
|
1604
|
+
(no gem): the page, its script and style, the two tile sheets and
|
|
1605
|
+
`/events`, a stream of Server-Sent Events (`status`, `state`, `line`)
|
|
1606
|
+
that pushes the table and the commentary. It listens on `127.0.0.1`
|
|
1607
|
+
only, answers GET (and HEAD) on those paths only, and refuses a request
|
|
1608
|
+
whose Host is not `127.0.0.1` or `localhost`. The page loads nothing from
|
|
1609
|
+
elsewhere.
|
|
1610
|
+
- **On a stream (OBS).** Add a Browser Source with the address
|
|
1611
|
+
(`http://127.0.0.1:7470/`), 1280 x 720 or larger. `?obs=1` leaves out the
|
|
1612
|
+
bar and the keys' line and makes the background transparent around the
|
|
1613
|
+
captions; `?hands=0` hides the hands on that page, `?voice=0` silences
|
|
1614
|
+
it, `?table=games/mahjong1` picks the table. Start the command with
|
|
1615
|
+
`--public` when the stream must never show a hand. OBS's browser does not
|
|
1616
|
+
always play the Web Speech voice into the stream: for the voice, capture
|
|
1617
|
+
a browser window and the desktop's sound instead.
|
|
1618
|
+
- **The tiles.** `lib/asterism_games/commentate/assets/tiles_hand.bmp` and
|
|
1619
|
+
`tiles_river.bmp` are the hand-drawn sheets of Family mruby's mahjong app
|
|
1620
|
+
(fmruby-core's `flash/app/game/mahjong/`), copied byte for byte; the
|
|
1621
|
+
browser reads the BMPs as they are and only makes their transparent
|
|
1622
|
+
colour (magenta) transparent when it draws them. In this gem they are
|
|
1623
|
+
under the MIT License like the rest of it, by the author's decision
|
|
1624
|
+
(`TILES_LICENSE.txt` next to them).
|
|
1625
|
+
|
|
1626
|
+
### The voice
|
|
1627
|
+
|
|
1628
|
+
(games 0.19.0) The commentary speaks with VOICEVOX,
|
|
1629
|
+
a free Japanese speech synthesiser, when its engine runs on this machine;
|
|
1630
|
+
the default character is ずんだもん (style ノーマル). Nothing of its voice is
|
|
1631
|
+
in this gem: the gem holds the words (`commentate/mahjong_voice.yml`) and
|
|
1632
|
+
the code that has your VOICEVOX say them. **VOICEVOX is a separate app
|
|
1633
|
+
you install and start yourself** (https://voicevox.hiroshiba.jp/, free);
|
|
1634
|
+
without it the commentary still runs, with the browser's voice. When it
|
|
1635
|
+
finds no engine, the command says so on the terminal, with where to get
|
|
1636
|
+
VOICEVOX, and so does the page's bar.
|
|
1637
|
+
|
|
1638
|
+
- **Finding the engine.** At its start the command asks
|
|
1639
|
+
`GET /version` of `--voicevox URL` (default `VOICEVOX_URL`, else
|
|
1640
|
+
`http://127.0.0.1:50021`). When nothing answers, the browser's voice
|
|
1641
|
+
reads, as before, the terminal says where to get VOICEVOX, and the
|
|
1642
|
+
page's bar says 「VOICEVOX が見つからない (無料のアプリ VOICEVOX を入れて
|
|
1643
|
+
起動すると、ずんだもんの声になる): ブラウザの声で読む」.
|
|
1644
|
+
- **The fixed phrases** (リーチ, チー, ポン, カン, ロン, ツモ, the rounds'
|
|
1645
|
+
names, 流局, 途中流局, 満貫 to 役満, オーラス, 逆転, 終局, 対局開始, and
|
|
1646
|
+
「実況を始めます」 when the page is clicked) are WAV files, played at
|
|
1647
|
+
once. The command makes the missing ones at its start (in the
|
|
1648
|
+
background: a phrase not made yet is skipped), or
|
|
1649
|
+
`asterism-games commentate voices --make` makes them all now (about 30
|
|
1650
|
+
files, half a minute on a CPU engine). They go to
|
|
1651
|
+
`$XDG_CACHE_HOME/asterism-games/voices/<speaker>-<style>/`
|
|
1652
|
+
(`~/.cache/asterism-games/voices/ずんだもん-ノーマル/` by default) with a
|
|
1653
|
+
`CREDITS.txt`; each is named by the digest of its phrasing, so a changed
|
|
1654
|
+
list makes the changed ones again and removes the old. **The directory
|
|
1655
|
+
can be deleted at any time**: it is made again.
|
|
1656
|
+
- **The rest of a line** is synthesised as the line comes (the engine's
|
|
1657
|
+
`/audio_query` and `/synthesis`) while its fixed phrase plays, and
|
|
1658
|
+
served to the page from the command's own web server at a temporary
|
|
1659
|
+
address (`/voice/<digest>.wav`; only lines the command made, never a
|
|
1660
|
+
text a page sends). A line that begins with its fixed phrase's word
|
|
1661
|
+
leaves it out (ロン! then 「CPU1、断么九 …」). The same line is
|
|
1662
|
+
synthesised once while it is kept (200 lines and 64 MB at most, the
|
|
1663
|
+
least recently used dropped first). A line the engine fails on is read
|
|
1664
|
+
by the browser's voice.
|
|
1665
|
+
- **What the engine reads** is always a line's speech, never its caption:
|
|
1666
|
+
the tiles, the yaku and the points as a player says them (サンマン,
|
|
1667
|
+
リーチ, 30フ 2ハン), and the words VOICEVOX would read wrongly written
|
|
1668
|
+
out (流局 リューキョク, 起家 チーチャ, 跳満 ハネマン;
|
|
1669
|
+
`MahjongPhrases::SPEECH_READ`, used for the fixed phrases too). No user
|
|
1670
|
+
dictionary is needed.
|
|
1671
|
+
- **Choosing a character.** `asterism-games commentate voices --list`
|
|
1672
|
+
lists the engine's characters and their styles; `--speaker NAME
|
|
1673
|
+
--style NAME` picks one by name (the engine's numbers are looked up,
|
|
1674
|
+
never assumed; a name that is not there is answered with the ones that
|
|
1675
|
+
are). ずんだもん speaks in her own tone: the lines and the fixed phrases
|
|
1676
|
+
end in 〜なのだ (「リーチなのだ!」「満貫、8000点なのだ!」); any other
|
|
1677
|
+
character speaks plainly. The tones are kept with the phrasings
|
|
1678
|
+
(`MahjongPhrases::TONES`), with a note on how to ask an AI to speak so.
|
|
1679
|
+
- **The page** plays the sound itself (never the PC with `paplay` or
|
|
1680
|
+
`afplay`), so a Browser Source in OBS carries it. **M** cuts the voice
|
|
1681
|
+
(the captions go on). When VOICEVOX speaks, the page shows the credit
|
|
1682
|
+
in its corner: `VOICEVOX:ずんだもん`.
|
|
1683
|
+
|
|
1684
|
+
Running VOICEVOX:
|
|
1685
|
+
|
|
1686
|
+
```
|
|
1687
|
+
# Windows (and WSL): install the VOICEVOX app on Windows and start it (its engine
|
|
1688
|
+
# listens on 127.0.0.1:50021). From WSL with networkingMode=mirrored (.wslconfig)
|
|
1689
|
+
# 127.0.0.1 reaches it; otherwise pass Windows' address:
|
|
1690
|
+
asterism-games commentate mahjong --voicevox http://$(ip route show default | awk '{print $3}'):50021
|
|
1691
|
+
|
|
1692
|
+
# macOS: install the VOICEVOX app and start it; the default URL reaches it.
|
|
1693
|
+
|
|
1694
|
+
# docker (Linux, WSL, macOS): the engine alone, CPU version
|
|
1695
|
+
docker run -d --name voicevox -p 127.0.0.1:50021:50021 voicevox/voicevox_engine:cpu-0.25.0
|
|
1696
|
+
asterism-games commentate voices --list
|
|
1697
|
+
asterism-games commentate voices --make # optional: the commentary makes them at its start too
|
|
1698
|
+
asterism-games commentate mahjong --speaker 四国めたん --style ノーマル
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
The engine takes a gigabyte or two of memory; one line of commentary
|
|
1702
|
+
takes it about a second or two on a laptop's CPU.
|
|
1703
|
+
|
|
1704
|
+
**VOICEVOX's terms, in short** (read the current terms of VOICEVOX and of
|
|
1705
|
+
your character before you publish anything; they can change):
|
|
1706
|
+
|
|
1707
|
+
- The software and the voices it makes may be used free of charge,
|
|
1708
|
+
commercially or not, **with a credit** naming the character, such as
|
|
1709
|
+
`VOICEVOX:ずんだもん`. Each character has terms of its own on top of
|
|
1710
|
+
VOICEVOX's (ずんだもん's: a credit where it can be found, in a video's
|
|
1711
|
+
description or on screen; no use against public order, for political or
|
|
1712
|
+
religious campaigns, for deception, and the like).
|
|
1713
|
+
- **When you stream or upload the commentary, put the credit in the
|
|
1714
|
+
description too** (the page shows it in its corner).
|
|
1715
|
+
- Whoever you give generated voices to must follow the same terms, so
|
|
1716
|
+
they cannot be mixed into an MIT package. That is why this gem ships no
|
|
1717
|
+
sound: each user makes the files with their own VOICEVOX, and they stay
|
|
1718
|
+
on that machine.
|
|
1719
|
+
|
|
1720
|
+
### The AI's one-liners
|
|
1721
|
+
|
|
1722
|
+
(games 0.19.0) At the big moments of a table Claude adds a short line of
|
|
1723
|
+
its own after the fixed commentary, in the same voice and tone (ずんだもん's
|
|
1724
|
+
〜なのだ with her voice), marked **AI** in the captions:
|
|
1725
|
+
「この待ち、山にまだ残ってるのだ。勝負はここからなのだ!」. It is also a
|
|
1726
|
+
small demonstration of an AI watching a table on the network and telling
|
|
1727
|
+
what it sees.
|
|
1728
|
+
|
|
1729
|
+
```
|
|
1730
|
+
export ANTHROPIC_API_KEY=sk-ant-... # your own key
|
|
1731
|
+
gem install anthropic # not a dependency of this gem
|
|
1732
|
+
asterism-games commentate mahjong # Claude Haiku, at most 3 USD a day
|
|
1733
|
+
asterism-games commentate mahjong --ai-model claude-sonnet-5-5 --ai-daily-usd 1
|
|
1734
|
+
asterism-games commentate mahjong --ai off # the fixed commentary alone
|
|
1735
|
+
```
|
|
1736
|
+
|
|
1737
|
+
- **The key and the gem.** The key is `ANTHROPIC_API_KEY`; the anthropic
|
|
1738
|
+
gem is loaded only when it is set, and is not a dependency of this gem.
|
|
1739
|
+
Without a key the page's bar says 「AI: off (キーなし)」; with a key and
|
|
1740
|
+
no gem, 「AI: off (anthropic の gem なし)」 and the command says to
|
|
1741
|
+
`gem install anthropic`. `--ai off` turns them off.
|
|
1742
|
+
- **The model.** `claude-haiku-5-5` by default (`--ai-model`), asked with
|
|
1743
|
+
thinking off at effort `low` and `max_tokens` 200 (two short sentences
|
|
1744
|
+
with room; an answer cut there is dropped and the log says its stop
|
|
1745
|
+
reason), one try with no retries, 8 s at most. Every call's line in the
|
|
1746
|
+
log has its seconds, stop reason, tokens and money.
|
|
1747
|
+
- **When.** A riichi, a win (a mangan or more counts as bigger), a draw or
|
|
1748
|
+
an abortive draw, the lead changing hands, the last round (オーラス),
|
|
1749
|
+
the end; and a quiet minute in the middle of a round, about one topic
|
|
1750
|
+
the command picks in turn (the leader's lead, a seat in tenpai without
|
|
1751
|
+
riichi when the hands are shown, the dora, the wall left, the last
|
|
1752
|
+
discard; never the same twice running). One call at a
|
|
1753
|
+
time and **at most one every 20 s**, or every 8 s for a scene bigger
|
|
1754
|
+
than the last one asked about (a win after a riichi, the end after a
|
|
1755
|
+
win; the ranks: the end, a big win and the lead changing hands, a win
|
|
1756
|
+
and the oorasu, a draw, a riichi, a quiet minute). A scene that comes
|
|
1757
|
+
sooner waits (the biggest of those waiting, for 15 s at most) and is let
|
|
1758
|
+
go when its round or phase is over. Calls are made only while a page is
|
|
1759
|
+
open (or with `--json`): nobody watching, nothing is spent. A tonpuu of
|
|
1760
|
+
four CPUs gets about two a round, and a line for its wins.
|
|
1761
|
+
- **What it is told.** A fixed part, the same to the byte every time and
|
|
1762
|
+
cached (prompt caching: the calls after the first read it at a tenth of
|
|
1763
|
+
the price): the commentator's role, mahjong's rules in short, the tone,
|
|
1764
|
+
the length (40 characters, one or two sentences, the line only: no
|
|
1765
|
+
preamble, quotes or emoji) and whether it may speak of the hands. Then
|
|
1766
|
+
the table now (the round, honba, riichi sticks, the wall, the dora; each
|
|
1767
|
+
seat's name, wind, points, riichi, melds and last discards; with the
|
|
1768
|
+
hands shown, each hand and its waits; a win's yaku and points, the
|
|
1769
|
+
ranks at the end), the scene, the fixed lines that just came, and its
|
|
1770
|
+
own last lines (not to repeat itself).
|
|
1771
|
+
- **The hands hidden.** With `--public`, and while a page has **H** on,
|
|
1772
|
+
the AI is not told the hands (nor the waits, nor who is tenpai): it
|
|
1773
|
+
speaks from what everyone can see. A line made while the hands were
|
|
1774
|
+
shown is not shown on a page that hides them.
|
|
1775
|
+
- **Late lines are dropped.** An answer that comes when its round or
|
|
1776
|
+
phase is over, or more than 15 s after it was asked, is not said. On
|
|
1777
|
+
the page an AI's line waits behind the fixed lines (its scene's own come
|
|
1778
|
+
first; a round's start waits behind it), and is dropped when a new scene
|
|
1779
|
+
comes before it is said (a riichi, a win, a draw), first when the voice
|
|
1780
|
+
falls behind, and after 40 s in the queue.
|
|
1781
|
+
- **The voice.** The caption is the line as it came; the voice reads it
|
|
1782
|
+
through the same readings as the fixed lines (`SPEECH_READ` and the
|
|
1783
|
+
tiles: 三萬 サンマン, 流局 リューキョク).
|
|
1784
|
+
- **What it costs.** Counted from the tokens the API reports (input,
|
|
1785
|
+
output, the cache's writes and reads) at the model's published price
|
|
1786
|
+
(Haiku 5.5: 0.10 USD a million input tokens, 0.50 output, 0.125 a cache
|
|
1787
|
+
write, 0.01 a cache read), in
|
|
1788
|
+
`$XDG_STATE_HOME/asterism-games/commentary_ai_usage.json`
|
|
1789
|
+
(`~/.local/state/...` by default; counts and money only, no words). The
|
|
1790
|
+
bar shows today's money and the limit; when today's money reaches
|
|
1791
|
+
`--ai-daily-usd` (default 3 USD) no call is made and the fixed
|
|
1792
|
+
commentary goes on alone; the next day (local date) starts at 0. A
|
|
1793
|
+
call costs about a hundredth of a cent with Haiku (some 1,000 cached
|
|
1794
|
+
tokens, a few hundred new ones, a line out), so a tonpuu costs well
|
|
1795
|
+
under a cent.
|
|
1796
|
+
- **Failures** (no answer in time, an error, a refusal) are one line in
|
|
1797
|
+
the command's output and nothing is said; the key and the words are
|
|
1798
|
+
never written out. `ASTERISM_GAMES_AI_FAKE=1` puts a fake in Claude's
|
|
1799
|
+
place (no API is called; its money is counted apart, in
|
|
1800
|
+
`commentary_ai_usage.fake.json`), to try the page without a key.
|
|
1801
|
+
|
|
1466
1802
|
## Playing from a program
|
|
1467
1803
|
|
|
1468
1804
|
The mahjong table also speaks to programs (games 0.10.0): an AI assistant
|
|
@@ -1538,9 +1874,12 @@ offered), and show the seat by its name (`Claude`, `Jev`, numbered when there ar
|
|
|
1538
1874
|
reports, per day (the host's local date), in `state_dir/ai_usage.json`
|
|
1539
1875
|
(shared by the rooms' hosts and the permanent tables; counts and money
|
|
1540
1876
|
only). When today's total reaches `ASTERISM_GAMES_AI_DAILY_USD` (3.0) no
|
|
1541
|
-
table offers an AI player any more and the seated ones become CPUs;
|
|
1542
|
-
|
|
1543
|
-
then
|
|
1877
|
+
table offers an AI player any more and the seated ones become CPUs; each
|
|
1878
|
+
AI seat asks at most `ASTERISM_GAMES_AI_CALLS_PER_GAME` (400) decisions
|
|
1879
|
+
in a game, then that seat becomes a CPU (games 0.20.0: the count is each
|
|
1880
|
+
seat's own, so a game's most is 400 times its AI seats; before, the
|
|
1881
|
+
seats of a game shared the 400 and every AI seat stopped together). A
|
|
1882
|
+
tonpuu takes about 150-200 decisions a seat, a hanchan about twice that. `bin/asterism-games status` prints the
|
|
1544
1883
|
day's decisions, tokens and money per kind; the host's `@status` has the
|
|
1545
1884
|
totals (`ai_today`).
|
|
1546
1885
|
- **A limit per kind** (games 0.15.0). `ASTERISM_GAMES_AI_DAILY_USD_CLAUDE`
|
|
@@ -1553,7 +1892,7 @@ offered), and show the seat by its name (`Claude`, `Jev`, numbered when there ar
|
|
|
1553
1892
|
0.15.0. A value that is not a number of dollars (0 or more) is ignored,
|
|
1554
1893
|
with a line in the log. The log says the limits at the start
|
|
1555
1894
|
(`games: AI players: claude, jev (at most $10.0 a day for claude, $2.0
|
|
1556
|
-
for jev, 400 decisions a game)`) and once for each kind that stops
|
|
1895
|
+
for jev, 400 decisions a seat in a game)`) and once for each kind that stops
|
|
1557
1896
|
(`today's limit for claude ($10.0) is reached`); a new day lets it play
|
|
1558
1897
|
again. `ai_today` on `@status` then has `kinds` (README "Status").
|
|
1559
1898
|
- **Prices.** USD per million tokens, settings with the published prices
|
|
@@ -1617,8 +1956,9 @@ game between two machines without a host is fmruby-core's
|
|
|
1617
1956
|
"moves" => [{"n", "square", "turns", "text"}]}`; the answer is a move's
|
|
1618
1957
|
`n`. One out of range, one that does not come in `ai_turn_seconds` (30 s)
|
|
1619
1958
|
or a failure is played by the CPU, and the game goes on. When the day's
|
|
1620
|
-
money is spent, or the
|
|
1621
|
-
decisions
|
|
1959
|
+
money is spent, or the seat has asked `ASTERISM_GAMES_AI_CALLS_PER_GAME`
|
|
1960
|
+
decisions in the game (each seat its own count, games 0.20.0), Jev's
|
|
1961
|
+
seat becomes the CPU. The table apps show Jev by its
|
|
1622
1962
|
name with `[AI]` and say when it is thinking; `dl` counts its time down.
|
|
1623
1963
|
- Nothing in othello is hidden, so every member sees the same view: the
|
|
1624
1964
|
table's view (the one the host puts on the state key) and `me`, the
|
|
@@ -1901,6 +2241,34 @@ ASTERISM_TEST_ROUTER=tcp/127.0.0.1:7447 bundle exec rake test # also through a
|
|
|
1901
2241
|
tonpuu game against three CPUs each to the end from scripted input
|
|
1902
2242
|
(letters, tiles, calls, passes, OKs; `PLAY_TRANSCRIPT_DIR` keeps what
|
|
1903
2243
|
they showed).
|
|
2244
|
+
- `test_commentate.rb`: `asterism-games commentate` (in `rake test:gem`
|
|
2245
|
+
too): the waits of a hand (seven pairs, thirteen orphans, with melds),
|
|
2246
|
+
the dora of an indicator, each event from views made by hand (a round's
|
|
2247
|
+
start, the oorasu, riichi with its waits, calls and an added kan, a dora
|
|
2248
|
+
and a red five discarded, the wall, a new dora, a tenpai once a round, a
|
|
2249
|
+
big win and the lead changing hands, a draw, an abortive draw, the end)
|
|
2250
|
+
and their words, a recorded tonpuu of four CPUs
|
|
2251
|
+
(`test/fixtures/commentate_tonpuu.json.gz`) through the events and
|
|
2252
|
+
phrases, nothing of a hand with the hands hidden, the hub (the pages'
|
|
2253
|
+
frames, `--public`, `--json`, a page that opens late, a host that leaves
|
|
2254
|
+
and comes back), the web server (the page, the sheets byte for byte,
|
|
2255
|
+
404 / 405 / a foreign Host refused, 127.0.0.1 only, the events stream, a
|
|
2256
|
+
port taken), the options and `--help`; with the table there, a CPU
|
|
2257
|
+
round played here through the hub.
|
|
2258
|
+
- `test_commentate_voice.rb`: the commentary's voice (in `rake test:gem`
|
|
2259
|
+
too), against a small VOICEVOX engine started in the test: a character
|
|
2260
|
+
and a style by name (and the names offered when one is not there), the
|
|
2261
|
+
synthesis, no engine (the browser's voice, nothing made), the fixed
|
|
2262
|
+
phrases made once and named by their digest, made again when the list
|
|
2263
|
+
changes (the old files removed), the cache directory, every clip a line
|
|
2264
|
+
asks for in the list in both tones, a line synthesised once and kept
|
|
2265
|
+
(the least recently used dropped, made again when asked), a failing
|
|
2266
|
+
engine, the words a fixed phrase said left out of the line, ずんだもん's
|
|
2267
|
+
tone (every phrasing, every line of the recorded game, the clips that
|
|
2268
|
+
tell no hand), the hub's lines with their sound, the web server's
|
|
2269
|
+
`/clip/` and `/voice/` (only what the list and the hub name), the
|
|
2270
|
+
missing phrases made at the start, the options and `voices --list |
|
|
2271
|
+
--make`.
|
|
1904
2272
|
- `test_spectate.rb`: the spectate view (README "Spectate"; its
|
|
1905
2273
|
framework part in `rake test:gem` too): nothing by default (othello, the
|
|
1906
2274
|
board), the keys of the permanent tables and of a room, what the rooms
|
|
@@ -2094,4 +2462,7 @@ each result that long.
|
|
|
2094
2462
|
|
|
2095
2463
|
## License
|
|
2096
2464
|
|
|
2097
|
-
MIT (LICENSE).
|
|
2465
|
+
MIT (LICENSE). The tile sheets of the commentary page
|
|
2466
|
+
(`lib/asterism_games/commentate/assets/tiles_*.bmp`, the hand-drawn tiles
|
|
2467
|
+
of Family mruby's mahjong app) are under the MIT License in this gem too
|
|
2468
|
+
(`TILES_LICENSE.txt`).
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.20.0
|