asterism-games 0.19.1 → 0.21.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 +66 -0
- data/README.md +129 -18
- data/VERSION +1 -1
- data/lib/asterism_games/commentate/assets/SOUNDS_LICENSE.txt +63 -0
- data/lib/asterism_games/commentate/assets/commentate.css +9 -4
- data/lib/asterism_games/commentate/assets/commentate.js +298 -7
- data/lib/asterism_games/commentate/assets/index.html +5 -3
- data/lib/asterism_games/commentate/assets/sounds/after_the_rain.mp3 +0 -0
- data/lib/asterism_games/commentate/assets/sounds/chi.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/discard.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/discard_other.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/draw.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/encounter.mp3 +0 -0
- data/lib/asterism_games/commentate/assets/sounds/fanfare1.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/fanfare2.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/fanfare3.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/fanfare4.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/kan.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/kyuushu.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/points.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/pon.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/riichi.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/ron.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/round.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/ryuukyoku.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/sounds.json +335 -0
- data/lib/asterism_games/commentate/assets/sounds/spring_thing.mp3 +0 -0
- data/lib/asterism_games/commentate/assets/sounds/tsumo.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/turn.wav +0 -0
- data/lib/asterism_games/commentate/assets/sounds/yaku.wav +0 -0
- data/lib/asterism_games/commentate/mahjong.rb +11 -4
- data/lib/asterism_games/commentate/mahjong_sounds.rb +118 -0
- data/lib/asterism_games/commentate/server.rb +14 -2
- data/lib/asterism_games/commentate.rb +8 -2
- data/lib/asterism_games/host.rb +1 -1
- data/lib/asterism_games/othello/table.rb +13 -10
- data/lib/asterism_games/play/mahjong.rb +25 -10
- data/lib/asterism_games/play/mahjong_screen.rb +33 -12
- data/lib/asterism_games/play/mahjong_text.rb +47 -6
- metadata +27 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e2b83742bc350a97c81593529368c69fd2143887ac42293d62c9459c28c47b91
|
|
4
|
+
data.tar.gz: d608f964a2f5356ebe84485ed7042558f66c150e530b34aa787a113a431f64fb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d50462655686680b282f50e3e68ceccbc0f761c9b9515013877f4e06e8c6828233ff5795c14c9c008e7bb5fa5b663ff39aea08d2dc96741616fee5f5bc6ca7e6
|
|
7
|
+
data.tar.gz: fbb3bbeee07f77a11c01e7d685b8a7f0a239b0ef17f8f8782b0f8e4adbf466fade43708eb6b5e1daa1389c8de93abccf5f39e2afee5e2a20c338ab411ce14aaa
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,72 @@
|
|
|
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.21.0
|
|
7
|
+
|
|
8
|
+
- **The commentary's sound** (README "The sound"): the page of
|
|
9
|
+
`commentate mahjong` plays Family mruby's mahjong app's BGM and effects,
|
|
10
|
+
the same sounds at the same moments as the app. The BGM follows the
|
|
11
|
+
app's scenes (the lobby, the game, the tense part, none at the end),
|
|
12
|
+
starts low (the app's levels: off, low, mid, high, **B** or the bar's
|
|
13
|
+
button) and goes down to half while the voice speaks. The effects (a
|
|
14
|
+
round's start, the jingles of the calls, riichi and the wins, the
|
|
15
|
+
winner's points, the draws, a discard, the fanfare at the end; as for
|
|
16
|
+
the app's spectator, no draw or turn sound) play beside the voice, sharing the APU's channels as in the
|
|
17
|
+
app; **E** turns them off and on; the page remembers both. Which sound
|
|
18
|
+
when is a pure function of two views (`commentate/mahjong_sounds.rb`);
|
|
19
|
+
each `state` event now carries `bgm` and `sounds`.
|
|
20
|
+
- The files are in the gem (`commentate/assets/sounds/`, 3.6 MB: WAVs for
|
|
21
|
+
the effects, MP3s for the three tunes, `sounds.json`), made with Family
|
|
22
|
+
mruby's own APU emulator by fmruby-core's `tools/mahjong_sounds.rb`.
|
|
23
|
+
The effects are MIT here; the BGM is shiru8bit's under CC BY 3.0,
|
|
24
|
+
unchanged (`SOUNDS_LICENSE.txt`), credited in the page's corner. The
|
|
25
|
+
web server serves `/sounds/` (those files only) and
|
|
26
|
+
`/SOUNDS_LICENSE.txt`.
|
|
27
|
+
|
|
28
|
+
## 0.20.1
|
|
29
|
+
|
|
30
|
+
- `play mahjong`: an owner's command queued with `--commands` (`watch`,
|
|
31
|
+
`start`, `cpu` ...) while someone else holds the owner's role now says
|
|
32
|
+
so (「卓の主ではありません。卓の主の役が回ってくるのを待っています」)
|
|
33
|
+
instead of asking for a seat: since 0.20.0 an owner may be unseated.
|
|
34
|
+
|
|
35
|
+
## 0.20.0
|
|
36
|
+
|
|
37
|
+
- **Watch games** (README "Watch games"): the owner of a mahjong table
|
|
38
|
+
may start a game of CPUs and AI players only, and watch it with the
|
|
39
|
+
others at the table (and a commentary, `commentate mahjong`). The owner
|
|
40
|
+
need not be seated: a member that joins a table where nobody is seated
|
|
41
|
+
and there is no owner is the owner (`join`'s `"owner"`, the new `"mo"`
|
|
42
|
+
of the member's own view); once somebody sits the first seated is the
|
|
43
|
+
owner as before, so a game with people seated goes as it did. The new
|
|
44
|
+
method `watch(token)` starts it when the four seats are CPUs or AI
|
|
45
|
+
players, nobody is seated and at most one seat is Claude's (Jev may take
|
|
46
|
+
all four; `"human"`, `"ai limit"`, `"seats"`, `"not owner"` otherwise).
|
|
47
|
+
The lobby view's `"wc"` says whether it may start (1), is over the AI
|
|
48
|
+
limit (2) or not (0); views during it have `"wm" => 1` and the host's
|
|
49
|
+
`@status` `"watch" => true`. Nobody is seated, so the table's
|
|
50
|
+
abandoning does not apply; instead the game stops (unrecorded) when
|
|
51
|
+
every member has left. One game, then the lobby with the same seats.
|
|
52
|
+
The record is kept with `"watch" => true` (also on `records`' entries)
|
|
53
|
+
and counts in the stats for the CPU and the AI kinds only, never for a
|
|
54
|
+
person. The AI players' daily limits work as before.
|
|
55
|
+
- **The game's AI decisions are each seat's own**: `ASTERISM_GAMES_AI_CALLS_PER_GAME`
|
|
56
|
+
(400) is now how many decisions each AI seat may ask in a game (mahjong
|
|
57
|
+
and othello, every game, watch games or not); a seat past it becomes a
|
|
58
|
+
CPU alone. Before, the seats of a game shared it, so a game with three
|
|
59
|
+
or four AI seats stopped them all part way through a tonpuu. A game's
|
|
60
|
+
most is now 400 times its AI seats; the daily limits are as before.
|
|
61
|
+
- `play mahjong`: `W` (and the command `watch`) starts a watch game, for
|
|
62
|
+
the owner with or without a seat; `A` then a seat's number turns that
|
|
63
|
+
seat into a CPU, Claude, Jev and back (the kinds the lobby offers); the
|
|
64
|
+
owner's commands (`cpu`, `ai`, `watch`, `--commands`) work for an owner
|
|
65
|
+
without a seat; the lobby marks the owner among those watching and says
|
|
66
|
+
the AI limit when the seats are over it; a watch game is headed
|
|
67
|
+
`観戦対局`.
|
|
68
|
+
- Family mruby's mahjong app (fmruby-core) has a `観戦で始める[W]` button
|
|
69
|
+
for the owner when the host says it may start (`"wc"`); with an older
|
|
70
|
+
host it is not shown.
|
|
71
|
+
|
|
6
72
|
## 0.19.1
|
|
7
73
|
|
|
8
74
|
- The commentary tells where to get VOICEVOX when it finds no engine:
|
data/README.md
CHANGED
|
@@ -92,7 +92,7 @@ nothing else (othello, the drawing board), `bin/asterism-games` (with
|
|
|
92
92
|
`play`, the terminal clients of othello and mahjong: a mahjong table needs
|
|
93
93
|
riichi_engine, its client does not; and `commentate`, a mahjong table watched
|
|
94
94
|
in the browser with commentary, with the tile sheets of Family mruby's
|
|
95
|
-
mahjong app). It depends on
|
|
95
|
+
mahjong app and its sound: the effects and the BGM, "The sound"). It depends on
|
|
96
96
|
the asterism gem only (0.7.1 or later in
|
|
97
97
|
0.x; what came after 0.7.1, such as `@status` and `on_app_join`, is used
|
|
98
98
|
when there). Not in it: mahjong (`app/`, it needs the riichi_engine fork
|
|
@@ -476,7 +476,8 @@ asterism/games/games/@status
|
|
|
476
476
|
|
|
477
477
|
`last` is the four in rank order as `[name, rank, score]`, by the names
|
|
478
478
|
the table shows its members anyway (a table app shows them to whoever
|
|
479
|
-
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
|
|
480
481
|
(`GET /status` of the rooms' host) stays counts only.
|
|
481
482
|
|
|
482
483
|
`ais` counts the AI players' seats (they are counted in `cpus` too), and
|
|
@@ -701,6 +702,7 @@ connects again after 3 s; while a client of a router only reconnects
|
|
|
701
702
|
| `stand` | token | `"ok"` or why not (lobby only) |
|
|
702
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) |
|
|
703
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"`) |
|
|
704
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"`) |
|
|
705
707
|
| `view` | token, assist | the member's view (below), or `{"error" => "token"}`; assist: the help its screen shows (Assist, 0-3) |
|
|
706
708
|
| `act` | token, sv, type, tiles | `"ok"`, or why not: `"token"`, `"not seated"`, `"stale"` (sv is not the current one), `"not offered"`, `"not playing"` |
|
|
@@ -712,8 +714,9 @@ connects again after 3 s; while a client of a router only reconnects
|
|
|
712
714
|
|
|
713
715
|
- People join a table and wait in its lobby (`"st" => "wait"`): a member
|
|
714
716
|
sits in a seat or watches. The first member who sat is the owner (when the
|
|
715
|
-
owner stands or leaves, the member seated longest takes over).
|
|
716
|
-
|
|
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
|
|
717
720
|
next (or `over_seconds`) brings the table back to the lobby with the same
|
|
718
721
|
seats, for another game.
|
|
719
722
|
- The node that calls is the caller in the call's key (`from:`, filled
|
|
@@ -973,6 +976,47 @@ going for everyone (the other away seats are played for as before). A
|
|
|
973
976
|
table of CPUs only (`humans: 0`, the permanent spectator tables) is never
|
|
974
977
|
abandoned.
|
|
975
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
|
+
|
|
976
1020
|
### Views
|
|
977
1021
|
|
|
978
1022
|
A Hash (MessagePack); tiles are one byte each in binary strings (bin).
|
|
@@ -986,7 +1030,9 @@ A Hash (MessagePack); tiles are one byte each in binary strings (bin).
|
|
|
986
1030
|
| `sk` | the four seats: 0 empty, 1 a member, 2 a CPU (an AI player too) |
|
|
987
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") |
|
|
988
1032
|
| `aw` | the seats whose member is away, as bits |
|
|
989
|
-
| `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) |
|
|
990
1036
|
| `mb` | the members in the order they joined: `[name, seat (-1 none), flags (1 away, 2 owner)]` |
|
|
991
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)]` |
|
|
992
1038
|
| `sc`, `ri` | the scores; riichi as bits |
|
|
@@ -1171,7 +1217,7 @@ assistant through `asterism mcp`).
|
|
|
1171
1217
|
|
|
1172
1218
|
| Method | Arguments | Returns |
|
|
1173
1219
|
|---|---|---|
|
|
1174
|
-
| `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) |
|
|
1175
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]], ...]`) |
|
|
1176
1222
|
| `record` | id, round | round `round` (from 0) as below, with `id`, `round`, `of` (how many rounds), `part`, `parts` |
|
|
1177
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 |
|
|
@@ -1331,7 +1377,9 @@ pipe or a file (and the tests) they read lines as before, and
|
|
|
1331
1377
|
| `1`..`4` | the lobby: sit there (an empty seat or a CPU's) |
|
|
1332
1378
|
| `S` | the lobby: stand |
|
|
1333
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) |
|
|
1334
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) |
|
|
1335
1383
|
|
|
1336
1384
|
| Othello | |
|
|
1337
1385
|
|---|---|
|
|
@@ -1460,6 +1508,7 @@ Typed (in a pipe or a file each line; on a terminal after `:`):
|
|
|
1460
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") |
|
|
1461
1509
|
| `tonpuu`, `hanchan`, `rule ITEM VALUE` | the owner: the game's length, any local rule ("Local rules") |
|
|
1462
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 |
|
|
1463
1512
|
| `auto` | the client plays for you: wins when offered, passes calls, discards the drawn tile, OKs the results (again to stop) |
|
|
1464
1513
|
| `tables`, `view`, `help`, `quit` | |
|
|
1465
1514
|
|
|
@@ -1519,7 +1568,8 @@ when it is taken), prints the address and opens it in the browser (macOS
|
|
|
1519
1568
|
each, the newest at the bottom. Two seats to a row in a wide window, one
|
|
1520
1569
|
in a narrow one (a phone). Keys: **Space** pauses (what comes meanwhile
|
|
1521
1570
|
is held and shown when it goes on), **M** turns the voice off and on,
|
|
1522
|
-
**
|
|
1571
|
+
**B** steps the BGM (off, low, mid, high), **E** turns the effects off
|
|
1572
|
+
and on, **H** hides the hands on that page (backs instead of the tiles, and the
|
|
1523
1573
|
lines that tell a hand left out). A table that several hosts put is
|
|
1524
1574
|
chosen in the bar.
|
|
1525
1575
|
- **The AI's one-liners.** Claude's short lines at the big moments,
|
|
@@ -1534,6 +1584,9 @@ when it is taken), prints the address and opens it in the browser (macOS
|
|
|
1534
1584
|
Japanese voice when it has one (the bar says which voice, or that it has
|
|
1535
1585
|
none). Browsers play sound only after the page has been clicked once:
|
|
1536
1586
|
the page says so until then. "The voice" below.
|
|
1587
|
+
- **The sound.** The BGM and the effects of Family mruby's mahjong app,
|
|
1588
|
+
at the moments the app plays them, the BGM low by default and lower
|
|
1589
|
+
while the voice speaks. "The sound" below.
|
|
1537
1590
|
- **The commentary.** Made by rules from the difference between one view
|
|
1538
1591
|
and the one before (pure functions, `commentate/mahjong_events.rb` and
|
|
1539
1592
|
`mahjong_phrases.rb`, so the host can take them over later), each event
|
|
@@ -1552,9 +1605,10 @@ when it is taken), prints the address and opens it in the browser (macOS
|
|
|
1552
1605
|
game goes on as before. The delay of the table is the host's
|
|
1553
1606
|
("Spectators"); the page shows it and adds none.
|
|
1554
1607
|
- **The web server.** HTTP by hand on the standard library's `socket`
|
|
1555
|
-
(no gem): the page, its script and style, the two tile sheets
|
|
1556
|
-
`/
|
|
1557
|
-
|
|
1608
|
+
(no gem): the page, its script and style, the two tile sheets, the
|
|
1609
|
+
sounds (`/sounds/`, the files of `assets/sounds/` only) and `/events`,
|
|
1610
|
+
a stream of Server-Sent Events (`status`, `state`, `line`) that pushes
|
|
1611
|
+
the table and the commentary. It listens on `127.0.0.1`
|
|
1558
1612
|
only, answers GET (and HEAD) on those paths only, and refuses a request
|
|
1559
1613
|
whose Host is not `127.0.0.1` or `localhost`. The page loads nothing from
|
|
1560
1614
|
elsewhere.
|
|
@@ -1665,8 +1719,61 @@ your character before you publish anything; they can change):
|
|
|
1665
1719
|
description too** (the page shows it in its corner).
|
|
1666
1720
|
- Whoever you give generated voices to must follow the same terms, so
|
|
1667
1721
|
they cannot be mixed into an MIT package. That is why this gem ships no
|
|
1668
|
-
|
|
1669
|
-
on that machine.
|
|
1722
|
+
voice: each user makes the files with their own VOICEVOX, and they stay
|
|
1723
|
+
on that machine. (The sounds it does ship are the mahjong app's effects
|
|
1724
|
+
and BGM, "The sound".)
|
|
1725
|
+
|
|
1726
|
+
### The sound
|
|
1727
|
+
|
|
1728
|
+
(games 0.21.0) The page plays the sound of Family mruby's mahjong app:
|
|
1729
|
+
its BGM for the scene and its effects and jingles for what happens, the
|
|
1730
|
+
same sounds at the same moments as the app. They are files in the gem
|
|
1731
|
+
(`lib/asterism_games/commentate/assets/sounds/`), made beforehand with
|
|
1732
|
+
Family mruby's own APU emulator from the app's notes and its NSF tunes
|
|
1733
|
+
(not played again by ear), and the page plays them with Web Audio after
|
|
1734
|
+
the first click (the browser's autoplay rule).
|
|
1735
|
+
|
|
1736
|
+
- **The BGM** follows the app's scenes: the lobby (a table waiting, or no
|
|
1737
|
+
table yet) "After The Rain", the game and its results "Spring Thing",
|
|
1738
|
+
the tense part (a riichi on the table, or 10 tiles or fewer left)
|
|
1739
|
+
"Encounter", nothing at the end of a game (the fanfare plays alone). A
|
|
1740
|
+
tune starts from its beginning and repeats its loop without a seam.
|
|
1741
|
+
**B** (or the bar's button) steps it off, low, mid, high: the app's
|
|
1742
|
+
levels (low is 30 % of the tune's full level, mid 55 %, high 100 %, the
|
|
1743
|
+
effects always at theirs), **low by default**.
|
|
1744
|
+
- **Ducking.** While the voice speaks (VOICEVOX or the browser's), the BGM
|
|
1745
|
+
goes down to half its level, and back up a moment after the voice stops
|
|
1746
|
+
(not between two lines). The effects are not lowered.
|
|
1747
|
+
- **The effects** come with each new view of the table, beside the voice
|
|
1748
|
+
(never waiting in its queue): a round's start, the jingles of a call
|
|
1749
|
+
(チー, ポン, カン), of a riichi, of a win (ロン, ツモ) followed by the
|
|
1750
|
+
winner's points, a draw of the round and the abortive draws, a discard
|
|
1751
|
+
(the app's sound for another player's discard, whoever made it), and
|
|
1752
|
+
the fanfare at the end of the game (the app's spectator's: the
|
|
1753
|
+
winner's). As for the app's spectator, a move sounds its discard only:
|
|
1754
|
+
the player's own sounds (a draw, the turn coming) are not played. They
|
|
1755
|
+
share the APU's four channels as in the app: a sound of a higher
|
|
1756
|
+
priority still sounding keeps its channels, otherwise the newest wins.
|
|
1757
|
+
A voice that would start on top of a jingle waits for most of it (at
|
|
1758
|
+
most 0.6 s). **E** turns the effects off and on. The page remembers
|
|
1759
|
+
both switches (the browser's local storage).
|
|
1760
|
+
- **Which sound, when** is a pure function of two views
|
|
1761
|
+
(`commentate/mahjong_sounds.rb`), and each `state` event carries the
|
|
1762
|
+
BGM's scene (`bgm`) and the effects (`sounds`, `[name, delay ms]`).
|
|
1763
|
+
- **The files.** The effects are WAVs (mono, 16-bit, the APU's 15720 Hz,
|
|
1764
|
+
under 40 KB each); the three tunes are MP3s (24 kHz mono, 64 kbit/s,
|
|
1765
|
+
about 1 MB each: the tune from its start through one pass of its loop,
|
|
1766
|
+
115 to 154 s). `sounds.json` lists each with its SHA-256, the loop
|
|
1767
|
+
points, the app's levels and scenes. They are made by fmruby-core's
|
|
1768
|
+
`tools/mahjong_sounds.rb` (`ruby tools/mahjong_sounds.rb <dir>`;
|
|
1769
|
+
`--check <dir>` makes them again elsewhere and says whether any differs).
|
|
1770
|
+
- **Their licenses** (`assets/SOUNDS_LICENSE.txt`): the effects are the
|
|
1771
|
+
app's author's and are under the MIT License in this gem, by his
|
|
1772
|
+
decision (fmruby-core keeps them under GPL-3.0). **The BGM is not MIT**:
|
|
1773
|
+
the tunes are by shiru8bit under CC BY 3.0, unchanged (only played from
|
|
1774
|
+
their NSF files and saved as MP3). The page shows
|
|
1775
|
+
「BGM: shiru8bit (CC BY 3.0)」 in its corner while the BGM is on, beside
|
|
1776
|
+
the voice's credit; put it in a stream's description too.
|
|
1670
1777
|
|
|
1671
1778
|
### The AI's one-liners
|
|
1672
1779
|
|
|
@@ -1825,9 +1932,12 @@ offered), and show the seat by its name (`Claude`, `Jev`, numbered when there ar
|
|
|
1825
1932
|
reports, per day (the host's local date), in `state_dir/ai_usage.json`
|
|
1826
1933
|
(shared by the rooms' hosts and the permanent tables; counts and money
|
|
1827
1934
|
only). When today's total reaches `ASTERISM_GAMES_AI_DAILY_USD` (3.0) no
|
|
1828
|
-
table offers an AI player any more and the seated ones become CPUs;
|
|
1829
|
-
|
|
1830
|
-
then
|
|
1935
|
+
table offers an AI player any more and the seated ones become CPUs; each
|
|
1936
|
+
AI seat asks at most `ASTERISM_GAMES_AI_CALLS_PER_GAME` (400) decisions
|
|
1937
|
+
in a game, then that seat becomes a CPU (games 0.20.0: the count is each
|
|
1938
|
+
seat's own, so a game's most is 400 times its AI seats; before, the
|
|
1939
|
+
seats of a game shared the 400 and every AI seat stopped together). A
|
|
1940
|
+
tonpuu takes about 150-200 decisions a seat, a hanchan about twice that. `bin/asterism-games status` prints the
|
|
1831
1941
|
day's decisions, tokens and money per kind; the host's `@status` has the
|
|
1832
1942
|
totals (`ai_today`).
|
|
1833
1943
|
- **A limit per kind** (games 0.15.0). `ASTERISM_GAMES_AI_DAILY_USD_CLAUDE`
|
|
@@ -1840,7 +1950,7 @@ offered), and show the seat by its name (`Claude`, `Jev`, numbered when there ar
|
|
|
1840
1950
|
0.15.0. A value that is not a number of dollars (0 or more) is ignored,
|
|
1841
1951
|
with a line in the log. The log says the limits at the start
|
|
1842
1952
|
(`games: AI players: claude, jev (at most $10.0 a day for claude, $2.0
|
|
1843
|
-
for jev, 400 decisions a game)`) and once for each kind that stops
|
|
1953
|
+
for jev, 400 decisions a seat in a game)`) and once for each kind that stops
|
|
1844
1954
|
(`today's limit for claude ($10.0) is reached`); a new day lets it play
|
|
1845
1955
|
again. `ai_today` on `@status` then has `kinds` (README "Status").
|
|
1846
1956
|
- **Prices.** USD per million tokens, settings with the published prices
|
|
@@ -1904,8 +2014,9 @@ game between two machines without a host is fmruby-core's
|
|
|
1904
2014
|
"moves" => [{"n", "square", "turns", "text"}]}`; the answer is a move's
|
|
1905
2015
|
`n`. One out of range, one that does not come in `ai_turn_seconds` (30 s)
|
|
1906
2016
|
or a failure is played by the CPU, and the game goes on. When the day's
|
|
1907
|
-
money is spent, or the
|
|
1908
|
-
decisions
|
|
2017
|
+
money is spent, or the seat has asked `ASTERISM_GAMES_AI_CALLS_PER_GAME`
|
|
2018
|
+
decisions in the game (each seat its own count, games 0.20.0), Jev's
|
|
2019
|
+
seat becomes the CPU. The table apps show Jev by its
|
|
1909
2020
|
name with `[AI]` and say when it is thinking; `dl` counts its time down.
|
|
1910
2021
|
- Nothing in othello is hidden, so every member sees the same view: the
|
|
1911
2022
|
table's view (the one the host puts on the state key) and `me`, the
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.21.0
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
The sounds in sounds/
|
|
2
|
+
|
|
3
|
+
These are the sound of Family mruby's mahjong app (fmruby-core,
|
|
4
|
+
flash/app/game/mahjong/mahjong_sound.rb), which the commentary page plays:
|
|
5
|
+
the effects and jingles, and the BGM of its scenes. They were not played
|
|
6
|
+
again by ear: fmruby-core's tools/mahjong_sounds.rb plays the app's notes
|
|
7
|
+
and tunes with Family mruby's own APU emulator (components/apu_emu) and
|
|
8
|
+
writes the files; sounds/sounds.json lists each file with its SHA-256 and
|
|
9
|
+
what it was made from.
|
|
10
|
+
|
|
11
|
+
1. The effects and jingles (sounds/*.wav)
|
|
12
|
+
|
|
13
|
+
discard, discard_other, draw, turn, round, yaku, points, riichi, pon,
|
|
14
|
+
chi, kan, ron, tsumo, kyuushu, ryuukyoku, fanfare1, fanfare2, fanfare3,
|
|
15
|
+
fanfare4
|
|
16
|
+
|
|
17
|
+
Written by Katsuhiko Kageyama, the author of Family mruby and of this
|
|
18
|
+
gem, as notes for the APU. In this gem they are under the MIT License,
|
|
19
|
+
the license of the gem (LICENSE), by the author's decision. In
|
|
20
|
+
fmruby-core they stay under that repository's license (GPL-3.0).
|
|
21
|
+
|
|
22
|
+
MIT License
|
|
23
|
+
|
|
24
|
+
Copyright (c) 2026 Katsuhiko Kageyama
|
|
25
|
+
|
|
26
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
27
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
28
|
+
in the Software without restriction, including without limitation the rights
|
|
29
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
30
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
31
|
+
furnished to do so, subject to the following conditions:
|
|
32
|
+
|
|
33
|
+
The above copyright notice and this permission notice shall be included in all
|
|
34
|
+
copies or substantial portions of the Software.
|
|
35
|
+
|
|
36
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
37
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
38
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
39
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
40
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
41
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
42
|
+
SOFTWARE.
|
|
43
|
+
|
|
44
|
+
2. The BGM (sounds/*.mp3): music by shiru8bit, CC BY 3.0
|
|
45
|
+
|
|
46
|
+
NOT under the MIT License. These three tunes are by shiru8bit and are
|
|
47
|
+
licensed under the Creative Commons Attribution 3.0 Unported license
|
|
48
|
+
(CC BY 3.0, https://creativecommons.org/licenses/by/3.0/), as the NSF
|
|
49
|
+
files Family mruby ships (fmruby-core, flash/usr/share/sounds/nsf/).
|
|
50
|
+
|
|
51
|
+
after_the_rain.mp3 "8-bit Chiptune - After The Rain" by shiru8bit
|
|
52
|
+
https://opengameart.org/content/8-bit-chiptune-after-the-rain
|
|
53
|
+
spring_thing.mp3 "8-bit Chiptune - Spring Thing" by shiru8bit
|
|
54
|
+
https://opengameart.org/content/8-bit-chiptune-spring-thing
|
|
55
|
+
encounter.mp3 "8-bit Chiptune - Encounter" by shiru8bit
|
|
56
|
+
https://opengameart.org/node/100497
|
|
57
|
+
|
|
58
|
+
The music is not changed. Each file is the original NSF played by
|
|
59
|
+
Family mruby's APU emulator and saved as MP3: from the start of the tune
|
|
60
|
+
through one pass of its loop (and a second more); the page repeats the
|
|
61
|
+
loop as the NSF does. Whoever passes these files on, or plays them in a
|
|
62
|
+
stream or a recording, gives the same credit, for example
|
|
63
|
+
"BGM: shiru8bit (CC BY 3.0)" (the page shows it in its corner).
|
|
@@ -80,19 +80,24 @@ body.obs #captions { max-height: none; background: rgba(0, 0, 0, 0.55); }
|
|
|
80
80
|
.cap.ai span { color: var(--ai); font-style: italic; }
|
|
81
81
|
.cap.ai.old span { color: var(--dim); }
|
|
82
82
|
#keys { padding: 0 12px 8px; color: var(--dim); font-size: 12px; }
|
|
83
|
-
/* The
|
|
84
|
-
#
|
|
83
|
+
/* The credits (VOICEVOX:<character>, the BGM's), in the corner, on a stream too. */
|
|
84
|
+
#credits {
|
|
85
85
|
position: fixed;
|
|
86
86
|
right: 8px;
|
|
87
87
|
bottom: 6px;
|
|
88
|
+
display: flex;
|
|
89
|
+
gap: 6px;
|
|
90
|
+
pointer-events: none;
|
|
91
|
+
}
|
|
92
|
+
#credits span {
|
|
88
93
|
padding: 1px 6px;
|
|
89
94
|
font-size: 12px;
|
|
90
95
|
color: var(--dim);
|
|
91
96
|
background: rgba(0, 0, 0, 0.45);
|
|
92
97
|
border-radius: 3px;
|
|
93
|
-
pointer-events: none;
|
|
94
98
|
}
|
|
95
|
-
#
|
|
99
|
+
#credits span[hidden] { display: none; }
|
|
100
|
+
#bar .btn { cursor: pointer; user-select: none; }
|
|
96
101
|
@media (max-width: 600px) {
|
|
97
102
|
#captions { font-size: 16px; }
|
|
98
103
|
.cap.p3 span { font-size: 18px; }
|