アイテム収支レポートの読み方
数値は stats/balance.yaml にあります。
tests/diagnostics/balanceStatsReport.test.ts が、定義(src/assets/world-codex/*.yaml)だけから
計算した「時間あたりの収支」です。定義の数値を変えたら再生成します。
npm run stats:balance
同じ表はコーデックスビューアの「収支」ページでも見られます(アイコンつき)。絵つきで読めるのは そちら、数値を変えたときの差分を読めるのはこちらです。
この文書は手書きで、再生成しても書き換わりません。 持つのは読み方——何を測ったか、どこに線を 引いたか、何を数えていないか——だけです。測った数値そのものは1行も書きません(書けば、再生成した YAMLとずれます)。
YAMLの節
1つのレコードが違う量を並べて持つので、単位はキーの名前へ入れています(per_unit_minutes・
capacity_ml・day_percent など)。時間はすべて労働時間で、待ち時間は含みません。
null は「決まらない」ではなく、節ごとに意味が決まっています——device_count と days は
その経路に当てはまらないこと、build_minutes はその土地の値段が出ないこと、
object_costs.total_minutes は総コストを出せないこと、devices の lifetime_property と
lifetime_days は朽ちないこと、supply の driver は外から押されない工程であることを表します。
「値段が付かない」と「入手経路が無い」は別のことです。 総コストが出ないのは、島のどこにも
入手経路が無いからとは限らず、手に入るが値段を付けられない(出どころを辿ると朽ちない設備の
待ち生産に行き着き、按分できない。「待って得る生産の数え方」)こともあります。どちらかを答えるのは
object_costs の obtainable_without_cost と prerequisites の object で、
total_minutes: null・minutes: null は値段が付かないことしか言っていません。
土地ごとの節(devices)はどちらも答えません——build_minutes: null の理由は分かれる(他の土地で
なら作れる・島のどこでも値段が付かない・入手経路が無い)が、どれも島全体の事実なので、
土地ごとの行には持たせず object_costs のその型の行で読みます。
| 節 | 中身 |
|---|---|
meta |
1日ぶんの必要量を取ったキャラクタと、tickと日の刻み |
excluded_locations |
表が数えなかった土地(海区と、海区にしか湧かない土地)と、外した根拠のタグ |
daily_needs |
値ごとの1日ぶんの必要量。何で埋まるか・尽きると死ぬか |
daily_minimum |
土地ごとの、1日を賄う最小労働(貪欲解) |
daily_minimum_menu |
その献立の内訳 |
chain_routes |
土地×値ごとの、素材から摂取までの経路と単位あたりの時間 |
chain_untimed_routes |
時間を数えられない経路(労働0で値が返るもの) |
chain_gaps |
島全体で入手経路が無いものと、それが塞いでいる経路(空でもよい——穴が無いのが望ましい) |
object_costs |
オブジェクト1つを手に入れるまでの総労働 |
devices |
待ち生産(設備が時間をかけて返す分)の周期とレート |
rain_water |
空けたまま置いた容器が1日に受ける水と失う水 |
consumption |
キャラクタが1日に動かす自分のプロパティの量 |
supply |
何かを生むか値を動かす工程(海でしか手に入らないものを除く)の、所要時間と産出 |
この表とYAMLが食い違うと npm test が赤くなります。 表に挙げた節がYAMLに在って空でないことと、
表に無い節がYAMLに無いことの両方を、生成元のテストが見ます。
計測方法
pickの分岐はweightから期待値を取る。入れ子のpickは確率の積まで畳んである。- 代入(
set)を含む分岐では、その分岐でそれより前に足した量を数えない。 代入はそのものが 行き先なので、物を食べて満腹が500増えても、吐く枝が満腹を0にすればその枝の取り分は0 (DigestionSystem.md6.3節)。逆に、重みが0の枝は期待値を 下げません——吐く重みは傷んでいない食べ物では0なので、raw_meat.eatのsatietyは宣言どおり 500です。代入した先がいくつかは数えない——動く量は今の値によるので、宣言だけからは言えない。 値域の端で走るクランプ(on_maxが自分をmaxへ戻す既定、GameElementDefinition.md6.3節)はこれに当たらない。端で止まった ことを言うだけで、足した量が起きなかったとは言っていないため。 - 候補が自分の重みの値を自分で減らしているなら、その値は「仕込んだ在庫」として読む。 塩田に張った海水も畑に撒いた株も、宣言された初期値は空——そこで重みを読むと全候補が0になり、 「何も仕込んでいない回」だけが確率1になって、設備が何を返すかを一度も数えないことになる。 1回ぶん仕込んであるとして数えるので、在庫を使う候補どうしは互角に並ぶ。 減らさない重み(罠の掛かりやすさ・器に残った水・食べ物の傷み)は宣言値のまま。
- 1つの工程が複数の値を返す場合、所要時間は按分せず全額を各値に計上する。 按分には
水と満腹の交換レートが要るが、そのレートこそこの表が見つけようとしているもの。
代わりに
deltas(同時に返す値)を添えた——それらを縦に足すと二重計上になる。 - 道具(消費されない入力)の入手時間は単位あたりの時間に含めない。 繰り返し使えるものを
1個あたりへ按分するには「何回使うか」の仮定が要り、その仮定が数字を支配するため。
代わりに
prerequisites(前提)へ、1度だけ払う入手時間として別に並べる。 ただし、道具を産物と同じ型でしか満たせない工程は、その型の出どころに数えない(囲いの鶏が 鶏を増やす繁殖)。繰り返せば増えても、1つ目はその工程では手に入らないので、chain_routes・chain_untimed_routes・object_costsのどれにもその型の作り方としては出ない。タグ指定の道具は、 そのタグを持つ型のうち島のどこかで手に入るものが他にも在れば、その型の値段にかかわらず作り方に 数える。ここでの「手に入る」はobject_costsの可否とは別に、値段を積む前に決める——道具も 含めて入力がすべて揃う工程が島のどこかに在る型で、値段を積む途中と確定した後とで答えを変えないため。 - 中身を持ち出される器は道具ではない。 水入りの甕は飲んでも消えない(
transferでfillが 減るだけ)が、中身は1杯ごとに減るので、消えないことを道具の印にすると中身がただで湧く。 外へ移した量(transfer)だけを消費として読み、1回あたりの個数は「持ち出す量 ÷ 器1つが 抱える量」——甕(4000mL)から1杯250mLなら16分の1、ヤシの殻の器(250mL)なら1杯で1個。 空になって手元に残る器は差し引くので、払うのは中身を用意した時間だけ。器そのものはprerequisitesには並ばない——中身ごと消費される入力なので、器を作る工程は経路の上流 (route)にそのまま現れ、1つぶんの値段はobject_costsに在る。 使って傷むだけの値(道具の刃こぼれ)は物が外へ出ていないので、ここには入らない——そちらが 何回使えるかはDurationStats.mdが出す。 - 1つの終端へ、経路が2本並ぶことがある。 上流を最も安く辿ると労働0の工程(雨を受けて溜める)を
通るが、時間を払えば通る上流(湧き水から汲む)も在る、という場合。最安だけを挙げると
数えられない経路が、数えられる経路を隠すので、両方を挙げる——前者は
chain_untimed_routes、 後者はchain_routesへ出て、daily_minimumが見るのは後者だけになる。時間を払う側の値段は 最安ではない(労働0の工程を外して解き直した値段)。ただし前提(道具)が手に入るかは、解き直す 前の側が答える——道具の時間は経路へ按分しないので、要るのは「島のどこかで手に入るか」だけで、 労働0の工程を外したことと関わりがない。解き直した側で答えさせると、労働0でしか手に入らない道具を 使う経路が、時間を払える上流を持っていても表から消える。 - 連鎖の起点は探索。土地ごとに得られる物が違うので、
chain_routesはplaceで分かれる。 ただし資源は土地をまたいで分かれている(木は砂浜、石は岩場)ので、渡り歩ける前提の 島全体を先頭に置く——各資源を最も得やすい土地で得て、移動時間は数えない場合。
待って得る生産の数え方
罠のように、仕掛けてから時間が経つと産物が返るものは、待っている間に他のことができる。 そこで工程の時間を2本に分けて数える。
- 労働時間: プレイヤーが払う分。他の行動と直接競合するのはこれだけで、
*_minutesはすべてこちら。 - 周期: 経過するだけの分。単位あたりの時間には足さない。
では待ち時間が無コストかというと、そうではない。設備は待っている間も朽ちるので、
1周期で使い切る設備の割合(周期 ÷ 寿命)が、そのまま製作労働の按分になる——罠1回の判定は
「罠を作る労働の、周期÷寿命ぶん」を払っている。chain_routes の数字はこの按分を含む。
この数え方が成り立つのは並列度に上限があるときだけ。いくらでも並べられて朽ちもしない
設備は、待つだけで無限に得られることになるので按分できず、chain_routes から外して devices へ回す。
隣の物に押されて起こる作り替え
焼くのも失血死も、自分では動かない値を隣の物が動かす。炉は火にかけた物の
cooking_progress を進め(add: {child: ...})、刺さった傷は持ち主の blood を奪う
(add: {parent: ...})。値が range の端へ届いた瞬間に、その型自身の on_max/
on_min が生肉を焼けた肉へ、獲物を死体へ置き換える。
どちらも「1回で終わる待ち生産」なので、労働0・経過時間ありの工程として chain_routes に載せ、
押し手(炉・傷)は要る道具として prerequisites に出す。誰が誰の隣に立てるかは、枠の
accept だけで判断する——炉の火の枠が roastable を受けるから、そこへ入る物は焼ける。
同じ値を動かす宣言が1つの型に複数あるときは、どの組み合わせが同時に成立しうるかで読みます
(自分のプロパティの側と同じ問い、GameElementDefinition.md 8.2節)。同時に効く分は足し合わせて
1つの押し方にし、同時には効かない押し方どうしが1つの押し手の取りうる速さとして並びます——炉の
火力は heat の段で入れ替わるので並び、常時効く宣言と条件つきの宣言が並べば「常時分」と
「常時分+条件つき分」の和になります。ただし効いている間の違う押し方は、同時に効かなくても
束ねません——止まるまでと効き始めは、押し手が1つの答えを持たなければならないからです。
押されている間、押される側自身の条件つきの増減は数えません——その条件が成立する場面と押されて
いる場面が同時に来るかは、定義からは決まらないからです。炉の外に居る間だけ冷める石(祖先の火力を
not で見て heat_soak を引く分)を足し合わせると、押し手の向き——熱を溜める——を打ち消して、
周期そのものが消えます。
押している間ずっと成立していると言い切れるものだけは、押し手の速さへ重ねます。 言い切れるのは、
縛りが外側(親・祖先)の段を名指した条件だけで、押し手がその段に居ると分かっているとき——刻んだ
タロイモが自分で足す加熱(prepped の、親の heat が coals 以上のあいだの +2)は、火にかけている
炉がまさにその段に居るからこそ押されています。数えても「どの組み合わせが同時に成立しうるか」の枠から
出ません。見るのは押し方ごとです——炉の火力は段で入れ替わるので押し方が分かれ、どの押し方でも
成立することだけが、押されている間ずっと成立すると言えます。内側から押す押し手(刺さった傷)は、
外側の段を名指した条件を保証しません——熱いのは刺さっている物のほうで、置かれた場所のことは何も
言っていないからです。
押し手が止まるまでの長さも数える。罠の傷が奪える血は、傷の bleeding が1を割れば
止まる——ネズミ(6mL)は血の下端まで押し切られるが、ヤケイ(80mL)には届かない。
効き始めてから止まるまでの間に端へ届かない組み合わせはその工程を立てない。答えているのは
押し手1つが1つの値を端まで運べるかだけで、その物が別の道で同じ端へ行き着くかは、この数え方の
外です。
止まるのは、自分の値に課された比較の外へ出たときと、段を抜けたときです。どちらも上へも下へも 抜けます。
比較は、外れる向きとしきい値で数えます。 {bleeding, gte: 1} は bleeding が1を割った時点で、
{fullness, lt: 30} は fullness が30へ上がった時点で外れます。端ちょうどの扱いは比較のとおりで、lte・gte は端ちょうどではまだ成立して
います。外へ出られない端では止まりません——range の上限まで許す比較(上限100の値を lte: 100 で
見る)は、上がり続けても外れません。点で決まる比較(neq・in・not_in)も止まりません——値が
その点ちょうどに止まるかは速さからは読めません。生まれた時点で既に比較の外に在る値も止まりません
——段と同じく、その条件が成り立つのはこれからです。
止まる時刻を決めるのは、成立していなければ効かない比較だけ——論理和・否定の下の比較は、成立して
いなくても増減が効くので数えません。別のプロパティを相手にした比較も、しきい値が定義だけからは決まら
ないので数えません。段の宣言の下に置かれた増減(GameElementDefinition.md 6.4節)を持つ値も数えません
——読めた増減だけで外へ出ると見ても、読み落とした増減が内側へ連れ戻すかもしれないからです。
上へ抜けて止まるのは、ちょうどその段に居る間だけ効く増減(in_stage、
GameElementDefinition.md 14.1節)だけ——膿んだ傷が宿主へ菌を押し出す分は、膿み具合が
festering から septic へ上がればそこで止まり、先は septic の分が引き継ぎます。
「その段以上」(in_stage_or_above)は上の段へ移っても成立したままなので、こちらの止まり方は
しません。抜ける先は、値の並びの上でその段のすぐ上に来る段。抜ける先が読めないものは止まりません
——上に段が無い、名指した段が値の並びの上に位置を持たない(完全一致で決まる段、
GameElementDefinition.md 6.4節)、上に在る段の下端がどれも range の上限より上にある
(値の取れない位置なので、抜けて行き着く先になりません)。その値が上がっていかない場合——炉の
火力のように、段の下に置かれた増減で育つ値(同 6.4節)——も、抜ける時が読めないので止まりません。
生まれた時点で上端より上に在る値も止まりません——下へ抜ける場合と同じで、その段へ入るのは
これからです。上端ちょうどは、もう抜けています(段は下端を含む半開区間、同 6.4節)。
下へ抜けて止まるのは、in_stage も in_stage_or_above も同じ——どちらも名指した段の下端を
割れば外れます。減っていく値を段で見ている増減は、値が尽きるより先にここで止まります。
下端ちょうどは、まだ割っていません(段は下端を含む半開区間、同 6.4節)。
割る先が読めないものは、上へ抜ける場合と同じく止まりません。 下端が range の下限以下の段
——下端を書いていない受け皿と、下限そのものから始まる段——は、そこより下へ値が行けないので割る先を
持ちません。その値が下がっていかない場合も、割る時が読めないので止まりません。生まれた時点で
下端より下に在る値も止まりません——その段へ入るのはこれからで、いつ入るかは下の「押し手が効き
始めるまでの時間」が答えます。
名指した段に居る間に効く、段の宣言の下の増減(GameElementDefinition.md 6.4節)を持つ値も、上へも
下へも止まりません——読めた増減だけで抜けると見ても、その段が自分で値を保つなら抜けないかも
しれません。効くのは、名指した段(in_stage_or_above なら、その段以上のどれか)が宣言したものと、
別のプロパティの段が宣言したもの(そちらの段に居るかは決まらない)です。同じ値の他の段が宣言した
増減は、名指した段に居る間は効かないので、数えたまま止めます——比較の外へ出る場合と違うのはここです。
ただし名指した段の外に生まれた値では止めません——その段へ入るまでの道中は外の段に居るので、そこが
宣言した増減が効きます(外の段の間は自分で熱を保つ石は、その段へ下りてきません)。
小数の速さでも、端ちょうどへ着くtickは宣言の値のとおりに数えます。 0.1 のような速さは2進で 割り切れないので、そのまま割ると端ちょうどへ着く組が1 tickずれます。
段を名指した条件が見ている値は、比較の側では数えません。 段の条件が外れるのは段を出たときなので、 混ぜると受け皿の段に居ることを求める増減まで、値が下がった時点で止まるものとして数えられます。
比較の外へ出て止まる長さは、外へ向かう最も速い動きで数えます——押し手が同時に見ている比較の
うち最も早く外れるものを採ります。生成時に1回引く初期値(GameElementDefinition.md 6.2節、
value: {min, max})は、段を抜ける場合と同じく両端の個体のうち窓が狭いほう(下の段落)で数えます。比較は
効き始めを遅らせないので、下がって外れる比較なら軽く出たほう——40〜100 で引かれる罠の傷の bleeding
は 40——、上がって外れる比較なら重く出たほうです。それ以外の個体でしか成立しない組み合わせは、この表に
出ません。
段を抜けて止まる場合に最も狭く見積もるのは、抜けるまでの長さではなく「効き始めから抜けるまでの 窓」です。 速さは、抜ける向きへ動かす増減のうち最も速いものを採ります。初期値は、軽く出た個体と 重く出た個体それぞれで窓を数え、狭いほうを採ります。窓は同じ1つの個体についての長さなので、 効き始めと止まるまでで別の初期値は採れません。狭いのがどちらの端かは、段へ入るか・どちらの端から 抜けるかで入れ替わるので、片方に決め打つとその向きでだけ広いほうを数えます。比べるのは端の個体 どうしだけで、ロールが段や比較の境目を跨ぐとき、境目のすぐ内側に生まれた個体がもっと狭い窓を 持つことまでは数えません。窓が潰れて押し手の起こらない個体は比べる相手から外し、どちらの端の個体でも 起こらないときだけ、その押し手を表から落とします。
押し手が効き始めるまでの時間も周期に入ります。膿んだ傷が持ち主の全身の菌を押し上げる分は
膿み具合が festering へ入ってから効くので、傷を負った時点からその段へ届くまでの時間が先に要ります。
こちらは最も長く見積もります——届くまでを最も遅い増減で数えます。
値が端で戻って繰り返す周期には乗せません(効くのは初回だけで、次の発火までの間隔は変わりません)。
入るのも、抜けるのと同じく上下の両方です。 上がっていって段の下端へ届くか、下がっていって段の
上端を割るか——痩せていく body_fat が gaunt へ落ちるように、値が減っていって上から落ちて入る
段があり、そこに縛られた増減は落ちるまで効きません。下から開く場合しか数えないと、届く時が
来ないぶん「届くまでが読めない段」の側へ倒れて、生まれた瞬間から効くものとして数えられます。上端ちょうどに
着いた時点では、まだ入っていません(段は下端を含む半開区間、GameElementDefinition.md 6.4節)。
「その段以上」(in_stage_or_above)へ上から入ることが起こらないのは、下の押し手の側と同じです。
段へ着くまでの道中に効く、段の宣言の下の増減を持つ値は、いつ入るかも読めない側へ倒します——抜ける
場合の「名指した段の外に生まれた値」と同じ理由です。
段の向こう側に生まれて、そこから遠ざかる向きにしか動かない値は、その段に縛られた増減を 起こしません——上端より上に生まれて上がっていく値も、下端より下に生まれて下がっていく値も、 入る時が来ません。この場合だけは「届くまでが読めない段」の側へ倒さず、その押し手を表から落とします。 倒してしまうと、その値が段を決して跨がないのに、最初のtickから止まらずに効く押し手として 数えられます。
上の端と下の端は同じ分かれ目で分かれます。 決して入らないと言うには、段から遠ざける動きを 名指せることと、その値を動かす宣言を1つも読み落としていないことの両方が要ります。読み落とすのは 2通りです。
- 段の下に置かれた増減(
GameElementDefinition.md6.4節)を持つ値——炉の火力がこれ。外した分が 段の向こうへ連れていくことがあるので、上端より上に在っても下端より下に在っても読めない側のままです。 - 著者が書いた端のイベント(同 6.3節の
on_max/on_min)を持つ値。下がる一方の値でも、下端へ 届いて入れ直すなら段を何度でも跨ぎます。自分の値を戻すかどうかを問わず読み落としとして数えます ——控えめに見ておけば、取りこぼしで押し手が落ちることがありません(rangeだけを書いたときの 既定のクランプは端に留めるだけなので、ここには入りません)。
動きが1つも読めない値も同じで、読めるものが何も無いのは、決して動かないことではありません ——炉の火力は薪が焚べられて上がるので、そこを「決して入らない」と読むと焼く押し手が丸ごと落ちます。
「決して入らない」かだけは、初期値を向きで裏返します——段にいちばん近い側に出た個体で見るので、
上がっていく値なら軽く出たほう、下がっていく値なら重く出たほうです。言い切る相手はその型の
すべての個体なので、いちばん近い個体が段の向こうに居なければ言い切れません。下端より下に生まれる
個体と段の中に生まれる個体が混じる値(value: {min, max})で軽いほうに固定すると、段の中に生まれる
個体が押す分ごと押し手が落ちます。いつ入るか・いつ抜けるかを数えるほうは、次のとおり向きでは
裏返しません——問いが「どの個体も入らないか」ではなく「1つの個体がどれだけの長さを持つか」だからです。
いつ入るかの初期値は、いつ抜けるかと同じ個体——上の「両端の個体のうち窓が狭いほう」——で、入り方の向きでは 裏返しません。自分の増減で動く値は、同じ値に上向きの組と下向きの組が並ぶので、向きで裏返すと、段へ 落ちて入る個体と段を割って出る個体が別々になります(押し手の側が裏返せる理由は下の「初期値も押して いる向きで裏返ります」)。
押した先で開く段が動かす、相手自身の別のプロパティも押し手が動かします。 膿んだ傷が動かすのは
宿主の全身の菌(pathogen)だけで、血には触れません——血を削るのは、菌が septicemic へ届いて初めて
開く、宿主自身の段の下の増減(GameElementDefinition.md 6.4節)です。押し手が直に動かす分だけを数えると、
罠に掛けて傷を負わせ、放っておけば死体になる道が丸ごと落ちます。効き始めるまでの時間には、押し手が
その段まで押し込むのに要る時間が積み上がります。
段は下からも上からも開きます。 押し上げる押し手は段の下端へ届いた時点で入り、押し下げる押し手は
段の上端を割った時点で入ります——値を減らす押し手が相手を低い段へ落とし、そこに置かれた増減が動き出す
道は、下から入る場合しか見なければ丸ごと落ちます。上端ちょうどに着いた時点では、まだ入っていません
(段は下端を含む半開区間、GameElementDefinition.md 6.4節)。「その段以上」
(in_stage_or_above)へ上から入ることは起こりません——上に在る値は既にその条件を成立させているので、
開けたのは押し手ではありません。同じ値に段の要求が複数並ぶなら、入り口は段ごとに見ます——どれか
1つでも生まれた時点で入り口を越えていれば、押す向きが上でも下でも、開けたのは押し手ではありません。
初期値も押している向きで裏返ります。 段から遠いほうを採るので、押し上げるなら軽く出たほう、 押し下げるなら重く出たほうです。ここで裏返せるのは押している向きが1つに決まっているから ——自分の増減で入る側(上の「入るのも、抜けるのと同じく上下の両方です」)が裏返さないのと、そこが 違います。段を割って抜けるまでも同じ初期値で数えます(窓は同じ1つの個体についての長さなので、 効き始めと別の初期値は採れません)。
開けた押し手が、そのまま段の外へ押し抜けさせます。 熱で余計に渇く分(feverish)は、同じ押し手が
菌を septicemic まで押し上げた時点で止まり、先は septicemic の分が引き継ぎます。押し下げる押し手なら
名指した段の下端を割った時点で止まります——上の「段を抜けても止まります」を、押しているのが自分の増減では
なく押し手である場合へそのまま当てたものなので、上へ抜けて落ちるのは in_stage だけ・下へ割って
落ちるのは in_stage_or_above も同じという分かれ方もそのままです。
辿るのは1段だけです。 段が開き続けていることは、1段目なら押し手が傍に在り続けること(上の 「押し手が止まるまでの長さ」)で言えますが、その先を決めるのは押し手ではなく相手自身の値で、 そこには段で入れ替わる増減——段ごとに速さが変わるものは1つの周期で言い表せないので、この表が周期として 数えないもの——が混じります。辿るほど、成立しない経路が立ちます。段のほかにも縛りのある増減も 辿りません——その条件が押されている間に成立するかは、定義からは決まらないからです。 外側の段が重なっているだけなら別で、そこは上の「押している間ずっと成立していると言い切れるもの」と 同じ見方をします。
一撃で端まで押す効果も同じ引き金を引く。仕留めの一撃(set: {self: {blood: 0}})は
血を空にするだけで、死体を生むのは blood の on_min ——工程の結果にこれを
畳まないと、突いて仕留める道が何も生まない工程になる。
確率でしか消えない入力は、その確率ぶんだけ消費されるものとして数える。
この表が数えていないもの
- 土地の間の移動時間。 道ごとに違い、地形生成が個体へ書き込むため定義からは決まらない。 設備を見回る時間もこれに含まれるので、必要設備数が多い経路ほど実際は不利になる。
- 餌の効果。 餌は
modify(実効値への可逆な寄与)で重みを押し上げるが、静的に読めるのは 宣言値だけなので、罠のレートは餌なしの値。 - 雨で溜まる水を汲む労働。 量を増やすのは
rain_filled_liquidのtick毎の持続効果で、 工程ではない。そのため水を汲む経路は労働0分になり、chain_untimed_routesへ分けてある。 溜まる量そのものはrain_waterに出す(労働ではなく、季節ごとの mL/日)。 - 採取ポイントの枯渇。 同じ木から何度でも採れる前提で計算している。
- 炉の薪。 焼くには火を保たなければならないが、そのぶんの薪は数えていない。炉は
prerequisites(道具)としてだけ出る。 - 何を使ったか。 使う物(役
instrument、GameElementDefinition.md11.5節)の値を見る宣言は、 その型を決めないと解けない——一撃の当たり所の配分は武器が宣言する({subject: instrument}の 重み)。分岐の重みもdurationも同じ手立てで解く。ここではその値を最も高く宣言している型を 使ったとして読むため、配分は「分岐ごとに最も良い武器を選べる場合」のものになる——1本の武器 では出ない配分で、仕留めの確率は実際より低く出る。 - 実行時にしか決まらない条件。 起こりえない工程は立てないが、偽と判定できるのは型が定まって
いる起点——
subject: self、型の定まった相手(subject: instrument)、そして土地ごとの行では その土地(subject: ancestor)——に課された条件だけ。祖先が土地だと言えるのは、そのプロパティを 土地しか宣言していないときに限る——祖先は「そのプロパティを宣言している最初の祖先」(同8.6節) なので、持ち物も器も世界も宣言していなければ遡り着く先はその土地1つに決まるが、他に宣言している型が 在れば定義からは決まらない(天候がこれ。宣言しているのはworld)。 そのうえで読むのは、その型の定義だけで決まること——プロパティがその型の取りうる範囲 (range。端に達した瞬間にその型でなくなるなら、その端を除く)から外れることと、その型が そのプロパティを宣言していないこと(実行時は解決先が無く、どの比較も偽になる)と、matches(同4.1節)がその型に当てはまること——に限る。これに当たらない葉は真偽を決めずに素通しするので、 それだけで弾かれる操作は工程として残る。 - 条件つきの二択の、満たさなかった回。
on_max/on_minにconditionsを書くと、満たさない回は 既定のクランプ(端へ戻す)へ倒れるが、どちらへ倒れるかの割合は宣言のどこにも無いので、量を 数えるのは著者が書いた枝だけ(EffectReader.tsのConditionalBranch.authored)。消える物・変わる物だけは 倒れる先からも拾う。今の定義では著者の枝が自分の値を動かさないため数値は変わらないが、動かす宣言を 入れると、その枝が毎回走るものとして出て、倒れる先で起きることは出ない。
何を「島の土地」と数えるか
土地ごとの表が答えるのは「島の1日が賄えるか」なので、数えるのは探索できる土地のうち海でしか
手に入らないものではないものだけです。海区(voyage.yaml)は探索でき、
寝られ、雨も貯まるので、集め方の条件(locationタグ+exploration_progress)をそのまま満たしますが、
島の土地ではないので分母に入れません。外した場所は excluded_locations に出ます。
線は「海でしか手に入らない」で、seaタグだけでは引けません。 小島(offshore_islet)は海区に
湧く設置物でありながら上陸して探索できる土地で、seaタグは持ちません(付けると筏のdisembarkが
通らなくなる)。湧き元を辿って、海区からしか生まれない型をまとめて外します——excluded_locations
の tag は、そうして辿り着いた根拠のタグです。
工程の目録(supply)と総コスト(object_costs)からも、海でしか手に入らないものを外して
います。 海区の見張りだけでなく、漁り場の漁も海鳥の卵も落ちます——生肉を30分で返す漁り場を残すと、
生肉の代表経路が海に決まり、入手経路が無い経路として落とされて、島で最も安い肉の経路
(イノシシ)が chain_routes から丸ごと消えます。筏と帆は島で作る物なので残ります。
何を「1日に要る量」と数えるか
daily_needs の出どころは consumption。輸送で減る値は需要にしない——
carbohydrate/protein/lipid はtick毎に体脂肪へ流れるが、あの速さは在庫がある間の流量で
あって、要る量ではない。体が実際に燃やすのは受け皿側の body_fat の減りだけで、三大栄養素は
そこへ注ぐ原資(DigestionSystem.md 3節)。流量を要求量として数えると、必要な何倍も食べさせる
ことになる。
例外は、段を持つ在庫。 在庫へ段を置くのは尽きたときの弊害を持たせるためなので(同7節)、
lipid は切らすこと自体に罰が立ち、流量がそのまま「1日に運び入れなければならない量」になる。
引くのは段の有無で、次にどの在庫へ段が生えてもその日から数えられる。
段で減る速さが変わる値は、初期値が入る段の速さを採る。 体脂肪は段ごとに減り方が違うので、 全部を足すとどの段にも当てはまらない量になる。
増える側が悪い値は、減っても需要にしない。 pathogen は免疫の段が -0.2/tick で引くが
(DigestionSystem.md 6.2節)、それは体が菌を片付けているので
あって、外から1日19.2集めてくる量ではない。減る=補うが成り立つのは、尽きると困る値だけ。
satiety は胃のかさであってエネルギーではない(同2節)。尽きても死なず、食べれば同時に
埋まるので、献立では他の値を賄うついでに満たされることが多い。
1日を賄う最小労働(daily_minimum)
その土地に留まって1日ぶんの需要を全部埋めるのに要る労働(貪欲解)と、それが1日に占める割合。
選んだ経路の内訳は daily_minimum_menu。day_percent が100を超える土地は、そこだけでは
1日が回らない——渡り歩く前提の place: 島全体 と読み比べる。
超える土地が1つも無いことは tests/diagnostics/dailyMinimumFitsDay.test.ts が見張る。
見張るのはこの表が出す値そのもので、そこから「回せる/回せない」を導いてはいない——献立は貪欲解
なので真の最小とは限らず、unmet の立つ土地では値段の付かなかった需要が入っていない。
unmet は「この表が値段を付けられる経路では賄えない」であって、「その値を返す物がこの土地に
無い」ではない。 献立に載せられるのは chain_routes の経路だけなので、次のどれも unmet に立つ。
どちらなのかを答えるのは unmet ではなく、その土地の devices と chain_untimed_routes。
- その値を返す物がこの土地に無い。 どちらの節にもその値へ届く行が無ければこちら。定義の側の 話で、埋めるなら土地の宣言を足すことになる。
- 在るが、この表が数えられない。 朽ちない設備の待ち生産(
devicesのlabor_minutes_per_unitがnullの行。上の「待って得る生産の数え方」)と、労働0の経路(chain_untimed_routes)は 献立に載らない。畑は作れるのにvitaminが賄えないと出る土地がこれで、空心菜は穫れるが、 畑が朽ちないので値段が付かない。
連鎖表(chain_routes)
daily_minutes は1日ぶんを賄うのに要る労働、day_percent はそれが1日(meta.minutes_per_day)に
占める割合。device_count は、待ち生産の経路で1日ぶんを賄うのに同時に要る設備の数。
device_count は devices の条件が成立し続けた場合の数です。 devices は経路が待つ設備を
上流から下流の順に並べたもので、condition は周期が進む条件(「待ち生産表」の同名の列と同じもの。
周期とレートはそちらが持つ)。条件が満たせなければ設備を何個並べても産物は0なので、数だけでは
「置けば回る」としか読めません——罠は items枠にある かつ catch枠にquarryが無い で、地面に置いて
掛かっていない間だけ進みます。経路は設備を何個でも通りうるので列は並びで、待ち生産を含まない
経路では空(device_count も null)。
土地ごとの行は可否を判定しない。 答えるのは「この土地を起点にすると単位あたり何分か」
だけで、ここでの可否は入手の可否——手に入るかどうかは place: 島全体 でだけ判定し、島のどこにも
経路が無いものは chain_gaps へまとめる。
ただし、その土地では押せない操作は載らない。 祖先(=立っている土地、GameElementDefinition.md
8.6節)に課された条件が成立しない工程は、その土地の行から消える——支点(hanging_anchor)を
宣言していない土地に、ハンモックを吊って寝る経路は出ない。どこまで判定するかは
「この表が数えていないもの」の実行時にしか決まらない条件。
imported: true は、他の土地で用意した材料・道具が要る経路。 AとBの土地で集めた物を合わせて
作るのは普通の遊び方なので可否は分けないが、土地の間の移動時間を数えていない以上、この経路は
実際にはこの表より不利になる。
「要る」は、その土地では揃わないという意味で、他所のほうが安いという意味ではない。 材料が
その土地でも揃うなら、島のどこかにもっと安い産地が在っても、その土地の産地で数える——数えて
いない移動の向こうに在る1分は、そもそも安さではない。安いほうで決めると、その土地だけで
完結する経路が丸ごと持ち込み扱いになり、rootedHere が偽になって土地の表から落ちる
(罠で獣が獲れる土地に、その獣の経路が1本も載らない。issue #2285)。その土地の産地のほうが高い
ぶんは per_unit_minutes に出る——それがその土地に留まる値段だから。
道具の imported も、材料と同じく入手連鎖を伝う。 砂浜では石が採れないので、そこで石を打って
作る尖った石も imported: true——その土地で作れても、材料が持ち込みなら持ち込みが要る。
ただし、道具の持ち込みでは経路は土地の表から落ちない。 道具は1度作れば持ち歩けるので、他の
土地の産物が要っても、その土地で作るか持ってくれば済む(経路の imported は真になる)。罠の繊維が
採れない山腹でも、くくり罠の経路は山腹の表に載る(issue #2285)。
表から落ちるのは、その土地の探索を含まない経路のうち、消費する材料に持ち込みが要るか、
その土地に置けない前提が要るもの。置けない前提は、その土地で用意できず、持ち運べもしない
(item でない)型——他の土地にしか無いヤシの木や泉がこれに当たる。その土地の探索を含む経路は、
それだけで起点がその土地なので落ちない——岸壁の探索で得たヤシの実を割り、泉で水を汲む経路は、
泉が岸壁に無くても岸壁の表に載る(imported: true)。
ただし imported が意味を持つのは、値段の付く材料・道具についてだけです。 値段の付かない道具
(minutes: null。朽ちない設備の待ち生産でしか得られないもの)では常に false になります——
値段の付く型には「その土地の値段表」と「島全体の値段表」の2つが在るので分けられますが、
値段の付かない型の集合は島全体に1つしかない(入手の可否を place: 島全体 でだけ判定するという、
「土地ごとの行は可否を判定しない」の帰結)ため、土地の文脈も島全体も同じ答えを返すからです。同梱の定義に値段の付かない
道具は無いので今は出ませんが、現れれば「他の土地でしか用意できない道具が要る経路」が
imported: false で載り、それが持ち運べない型なら、その土地を起点にする経路として載ります
(その土地に置けないと言えないので rootedHere が真になる)。そのとき表は嘘をつくので、値段の
付かない道具を足すなら、ここの決め方から見直すことになります。
時間を数えられない経路(労働0で値が返るもの)は chain_untimed_routes へ分けた。
時間を数えられていないだけで、本当にタダなわけではない(雨で水が溜まるのはtick毎の持続効果で、
工程ではない)。同じ並びに0分として混ぜると、最安の手段に見えてしまう。
prerequisites の label は要求の書き方(タグ指定なら タグ → 実際に使う型)、object は
実際に使う型。 chain_routes の object は必ず埋まっている——「島のどこにも入手経路が無い」道具が
要る経路はこの表に載せず、その道具が chain_gaps に挙がって経路ごとそちらへ回るため。
minutes: null は値段が付かないことしか言わない——朽ちない設備の待ち生産で
しか得られない道具はここへ来るが、穴ではないので経路は成立する(道具の時間は単位あたりへ
按分しないので、時間も変わらない)。
chain_gaps は、島のどこを探しても作れも見つかりもしないもの。定義の穴で、これが
blocked_route の経路を塞いでいる。空であることが望ましい状態なので、この節だけは空でもよい。
オブジェクトの総コスト(object_costs)
1つ手に入れるまでの労働を、素材の採集から数えたもの。組み立ての時間だけではない——筏は組む だけなら短いが、丸太と縄を揃えるところから数えると桁が変わる。
days は、生存に要る労働を引いた残り(1日の余剰時間)で割った日数。目標までに何日かかるかが
これで出る。道具(prerequisites)の時間は総コストに含めない(#550のまま)。
total_minutes: nullは総コストを出せないもの。その理由を分けるのがobtainable_without_costです。obtainable_without_cost: falseは、島のどこにも作り方も見つけ方も無いもの。missing(足りない入力)がそのまま埋めるべき穴になり、missingが空なら作る工程そのものが無い。obtainable_without_cost: trueは、手に入るのに値段を付けられないもの。出どころを辿ると 朽ちない設備の待ち生産に行き着き、按分の分母(寿命)が無いので工程として数えられません (「待って得る生産の数え方」)。その産物を材料に取る物も同じ側——値段の付かなさは連鎖を 下流へ伝わるので、devicesに行が在るのは設備が直に返す物だけです(塩には在り、塩から 作る物には在りません)。工程の入力(設備・材料・道具)のどれかが揃わないなら立ちません ——値段が付かない理由が按分ではなく入手経路のほうにあるので、上の行として出ます。blocked_by_tool: trueは、材料は揃うが要る道具に入手経路が無いもの。総コストは出るが、 実際には作れない——道具の時間を総コストへ按分しない決まり(#550)の裏返し。無い道具はprerequisitesのobject: nullの行で、minutes: nullの行ではありません(値段が付かない だけの道具は作れなさの理由になりません)。
土地・キャラクタ・単独で存在できない物(怪我・道)・製作中オブジェクト・軸の値の型
(液体の種類。世界に現れるのは中身入りの容器という変種のほうで、water_liquid そのものの
インスタンスは作られない)は、手に入れるという言い方が成り立たないので対象外。
待ち生産表(devices)
仕掛けてから時間が経つと産物が返るもの。period_minutes は単位あたりの労働時間には足していない
(「待って得る生産の数え方」参照)ので、この節が代わりに周期とレートを出す。
行が土地ごとに並ぶのは掛かる相手の重みが土地で変わるからで、「その土地に置けば働く」という意味では
ありません。 周期を進めるのはtick毎の増減(GameElementDefinition.md 8.4節)で、それがゲート
(同8.2節)で縛られていれば、条件が成立しない限り1 tickも進みません。condition がそれです。
condition: 周期が進む条件。常時は置くだけで進むもので、それ以外は周期を進める増減の ゲートの中身をそのまま書き出したものです(識別子は定義のまま)。進め方が複数あるならその数だけ 並びます——丸ごと揃って初めて進む増減はかつで、どれが成立しても進む進め方どうしはまたはでつなぎます。遅くするだけの増減はここに出ません——最も遅い場合を作っていても、進むこと自体は それに依らないので、period_minutesの出どころとは別です。その行の数字は、 条件が成立し続けた場合のレート——罠の判定はitems枠にある かつ catch枠にquarryが無い、囲いの判定はcatch枠にquarryが無い、ヤケイの繁殖はcatch枠にある かつ 親のfodder ≥ 1で、 囲い(build_minutes)と飼葉が要ることが行から読めます。否定は葉まで押し下げて 書きます(not: {slot: catch, matches: {tag: quarry}}は「catch枠にquarryが無い」)。per_day(個/日): 1日は24時間まるごと回る。眠っている間も進むのが待ち生産の取り柄。lifetime_property/lifetime_days: 設備を終わらせるプロパティと、それが尽きるまでの日数。 条件つきの減りは最小の1つだけが効くとして数えている。罠の寿命は地面にある間の減りだけで 割った値で、獲物が掛かっている間の減りは含まない——どの条件がいつ重なるかは定義からは決まらない ため。実際の寿命はこれより短く、下の行はその分だけ設備に有利に出る。nullは朽ちないこと。over_lifetime(個): 設備1つが朽ちるまでに返す総数。これが並列度の上限を決める。lifetime_propertyがconditionと別の条件で減るなら、この積は同時には成立しない仮定の 掛け算になります——ヤケイのstay_remainingは地面に立っていて深手も負っていない間しか減らないので、 囲いの中のヤケイは立ち去らず、地面に立っているヤケイは増えません。build_minutes: 設備1つを作るまでの労働。その土地を起点にした値段で、chain_routesが 周期へ按分しているのと同じ値です(labor_minutes_per_unitがその1個あたり)。nullの理由はこの節では言いません——他の土地でなら作れる(罠の掛かる動物は土地が決めるので、 ヤケイの湧かない土地ではヤケイの行がこれになりますが、島全体では値段が付きます)・島のどこでも 値段が付かない・入手経路が無い、のどれもありえて、どれも島全体の事実だからです。この節は 可否を判定しません(連鎖表と同じ、上の「土地ごとの行は可否を判定しない」)。理由はobject_costsのその設備の行が答えます。labor_minutes_per_unit: 製作労働 ÷ 寿命の間に返す数。chain_routesに載るのはこの値。
雨で溜まる水(rain_water)
空けたまま置いた容器が、1日に受ける水と失う水(LiquidContainerSystem.md 6・7節)。
降雨も蒸発も気候の実測値から出している(ClimateSystemStats.md)。
単一の平均は出さない。 雨季とそれ以外では降る時間が1桁違い、平均するとどの季節にも
存在しない中間の状態を測ることになる。読みたいのは net_ml_per_day の符号——雨だけで水を
賄えるのは雨季だけで、それ以外の季節は置いておくだけでは減る。
- 降雨も蒸発も、同じ1つの数え方で出している。 どちらも
fillをtick毎に動かす持続効果で、 違うのは符号だけ。器の居る場所の天候と明るさを時刻ごとに置き、そのとき条件が成立する増減を 出現時間で加重して均している(天候と時刻は独立とみなす近似)。 - 明るさは開けた土地のもの。 蒸発の上乗せは
ancestor.ambient_brightnessを見るので (LiquidContainerSystem.md6節)、樹冠も反射もある土地では変わる。この節はambient_brightnessのvalueが0の土地に置いた容器の量。 - 雨よけの無い場所に置いた容器の量。 水が増える宣言は、雨が降っていることに加えて
「雨よけの下でないこと」(
{subject: ancestor, prop: sheltered})も課しており、この節は 場所のshelteredを0として判定する。屋根の下に置いた容器はこの節の雨を受けない。天候と 明るさと屋根以外の祖先の条件は、真偽を決めずに素通しする(「この表が数えていないもの」)。 - 段(
stage)で縛られた増減は数に入らない。 その段だった時間は天候の出現時間からは 決まらないため。今の定義に該当は無いが、書き方をそちらへ変えるとその増分はこの節から 黙って消える。 - 蒸発は中身がある間しか効かない。 空になった容器は素の型へ戻って蒸発も止まるので、
evaporation_ml_per_dayは満杯を保った場合の上限。実際の減りはこれより小さい。 - 容量を超えた分は捨てられる。 雨季のヤシの器は
capacity_mlに対して桁違いの雨を受けるので、 汲み替えなければそのほとんどが失われる。net_ml_per_dayはその損失を含まない。
消費表(consumption)
キャラクタが自分のプロパティをtick毎にどれだけ動かすか(passives の add と transfer)。
per_day は1日ぶん(per_tick × meta.ticks_per_day)。個体差は character の行に出る。
daily_needs の出どころ。
condition は条件の正準な鍵で、日本語の文ではありません(src/domain/conditionKey.ts)。
同じプロパティの増減は、この鍵が一致する分だけが1行へ足し合わさります。文書がこの節のセルを
名指すときの鍵(引用印の condition= セレクタ)も同じ値です。鍵に出るのは定義が書いている識別子
だけなので、表に見せる言い回し(コーデックスビューアの「条件」の列)を直してもここは動きません。
定義のほうで条件を書き換えたときだけ動きます。
供給表(supply)
何かを生むか、値を動かす工程すべて(海区が宣言するものを除く、「何を「島の土地」と数えるか」)。
spawns は1回の実行あたりの期待個数。各オブジェクトのページにも同じ宣言があるので、ここは横断して
見比べるための一覧。
unresolved_references: true は、所要時間か分岐の重みが定義だけでは決まらない工程(相手の
持ち物を見る {subject: instrument, prop: ...} 参照など)。解けない重みは0として扱うので、その行の
期待値は残った候補へ寄っている——例えば strike の当たり方は武器が決めるため、ここでは出せない。
kind: periodic は時間で回る工程(罠の判定)。労働は0で、elapsed_minutes だけが経過する。
agent_deltas/self_deltas の増減は宣言された上限で、実際に動く量は在庫と空きで目減りする。
driver は、その周期を外から押している型(火にかけた物を焼く炉、血を奪う刺さった傷)。押されずに
回る工程(罠の判定)と、プレイヤーが起こす工程では null。
周期は押し手ごとに決まるので、同じ工程でも押し手が違えば別の行になる——獲物が失血で死体になる
までの時間は、負った傷の種類で変わる。どの押し手のことなのかが読めなければ行を突き合わせられない
(issue #1579)。同じ押し手で複数の行が並ぶこともある——直に押す分と、押した先で開く段が動かす分
(上の「押した先で開く段が動かす、相手自身の別のプロパティも押し手が動かします」)は別の周期で、
driver はどちらも押し手を指す。