# ファミレスの店の形（サービスロボット導入シミュレーション・配膳ロボットの試算用）

このフォルダの店の形が、サイトの試算と Unity の共通の元になる。Unity は `public/sim/layouts/` の JSON を読んで店を建てる。直すときは、ここ（サイト側）を先に直す。

## 何について正か（2026-10-22 エイイチと決めた）

| 何について | 正 | 扱い |
|---|---|---|
| 店の形（卓・壁・部屋・止まる位置） | サイト | このフォルダで作る JSON。Unity で形を作らない |
| 条件（スタッフ・来客・ロボットの台数） | サイト | 試算の図面のある店の値 |
| 店の回し方の決まりと時間、数字の定義（入店待ち・店が回らない） | サイト | 試算（haizen_sim.py）の値と定義 |
| 通路で起きること（実際の速さ・止まる回数と長さ・立ち往生） | Unity が測る | 測った値は、試算を先に直して入れる（`robot_speed`・`pause_rate`・`pause_s`・`stuck_rate`） |
| Unity が建てるための細かい形（通路の区画・椅子・座る位置・並ぶ点など） | このフォルダの規則 | JSON に書き出す。試算は使わない |
| Unity にしかないもの（組の人数・2人席・ドリンクバー・トイレ・椅子を引くなど） | Unity | 「Unity だけの値」として残し、試算とは比べない |

距離の測り方と Unity にしかないものの違いで、Unity と試算の数字は完全には合わない。合っているかは、向きと大きさで見る。

- `layout_v11.json`：記憶の店（昔働いていたファミレスを記憶から起こしたもの）。記憶のまま残し、変えない
- `famires_p0.py`：記憶の店を決まりに合わせた版（②）
- `famires_gen2.py`：③〜⑦の作り方 ／ `famires_v3.py`：決まりの確かめ・ロボットの届き方・図とデータの出力 ／ `test_famires_v3.py`：確かめの仕組みの確かめ

## Unity が店を建てるための細かい形（`public/sim/layouts/unity/{名前}.json`）

`unity_export.py` が、店の形と解析から決まった規則で作る（規則はファイルの先頭に書いた）。`famires_v3.py` を回すと一緒に出る。Unity はこれを読んで店を建て、店の形を自分で作らない。試算（haizen_sim.py）はこれを使わない。

- 卓の数・止まる位置・卓までの距離は analysis と同じ（テストで確かめる）。版の番号（generation）も同じ
- 細い通路：同じ向きのボックス席がすき間なく並んだ列の、隣り合う列のすき間（0.5〜1.5m）。両端の0.8m外に入口の点を置き、受け渡し口の前から近い方を entrance とする。③⑦は3本（③1.2m、⑦0.8m）
- 卓の部品：天板・ベンチ・椅子（2人席の通路側の椅子だけ引ける。引く向きつき）・座る位置（席数ぶん）・席に着くために立つ点（座る側の辺の真ん中から0.3m外。障害物から0.2m＝人の半径以上離れた所だけ）
- 部屋は通れない箱。ただしレジ内は `walkable: true, staff_only: true`（スタッフが中に立つ所。箱にしない）
- 場所の点：受け渡し口の前・下げ膳の返却（受け渡し口と同じ）・入口・待合・レジ（レジ内の反対側）と並ぶ点・レジ内・トイレの入口の前・ドリンクバーの前と立つ点・並ぶ点・ロボットの待機場所（1台ずつ）・スタッフが待つ点
- スタッフが待つ点：パントリー（と接する壁）の客席側の辺にそって壁から0.25m、0.6mおき。外側にロボットのふだんの道（1.0m）が残る所だけ。受け渡し口に近い順に6つ（仮の規則）
- 並ぶ点・立つ点は、障害物から0.25m未満の所と細い通路には置かない
- ロボットの道順（`routes`）：受け渡し口の前→各卓の止まる位置（`from_pass`）と、卓→卓（`between`。"T01|T02" は T01 から T02、逆向きは点を逆にたどる）。ふだん走らせる幅の道で行けるならその道、行けなければ通れる幅の道（試算と同じ規則。`use` が normal か narrow。試算の卓の分け方と一致することをテストで確かめる）。マス目の最短の道を、通れる範囲でまっすぐにつないだ曲がり角の点の並び。Unity のロボットはこの点を順にたどる（Unity で道を決めない）。2026-10-22、Unity で狭い通路の区画を NavMesh に書き込んだら NavMesh が壊れたため、この形にした
- まだないもの：② の4人席・丸テーブル・6人の長テーブルの椅子と座る位置（parts が null）、人がロボットをよける逃げ場（refuges）

## 動かし方

```
pip install numpy==2.5.3 scipy==1.18.1 matplotlib==3.11.2
python3 famires_v3.py              # 6つの店を確かめて描く（出力は public/sim/layouts/）
python3 famires_v3.py Servi        # 機種を替える（出力は public/sim/layouts/Servi/）
python3 test_famires_v3.py         # 確かめの仕組みそのものを確かめる
```

確かめた環境：Python 3.13.16、日本語フォント Noto Sans CJK JP（NotoSansCJK-Regular.ttc）。
フォントが違うと図の文字の収まりは変わるが、数字（`*_analysis.json`・`summary.json`）は変わらない。

## 決まり（2026-10-21 エイイチと決めたこと）

**店の形**
1. 基本の席は4人掛けのボックスの列。ボックスはベンチの背どうしが接して縦（または横）に並ぶ。2列を並べる島では、テーブルの端どうしが真ん中の低い仕切りをはさんで向き合い、座る側（開いた側）は外の通路に向く（日本のファミレスの映像で確かめた形）
2. 狭い所は2人席。家族・団体用に6人ボックスとL字ソファ
3. 効率重視型：仕切りなし・列の間の通路1.2m・2人席多め。ゆったり型：ボックスごとに仕切り・通路1.5m・6人席多め
4. 入口の位置はパターンごとに変える。入口の近くにレジ（2.5m以上）と待合
5. トイレ：客席に面する側は厚さ30cm以上の壁。トイレから1m以内（ちょうど1mも含む）に席を置くなら、席とトイレの入口の間に壁が要る。トイレの入口の前1.2mには何も置かない
6. ドリンクバーあり。ロボットの待機・充電の場所は料理の受け渡し口の横（1台あたり0.8m×0.8m）
7. ⑦は③と同じ卓・同じ席数のまま、列の間の通路だけを80cmに詰めた比較用
8. ②記憶の店は、昔働いていた店の記憶をもとにした店（layout_v11.json）を決まりに合わせた版。トイレとバックヤード（パントリーの出入り口・洗い場・物置）が客席の奥の壁より外へ出っ張り、奥の端はそろう。トイレへはパントリーの壁ぞいの通路から入る

**ロボット**
- 通れる幅は機種の公表値。初期値は BellaBot の80cm（ファミレスで多く使われ、調べた中で一番厳しい）

| 機種 | 公表の最小通過幅 | 出典 |
|---|---|---|
| BellaBot | 80cm | Pudu Academy |
| KettyBot | 55cm | Pudu Academy |
| Keenbot T8 | 55cm以上 | アイリスオーヤマ |
| KEENON T10 | 58.5cm | ミツモア |
| Servi | 60cm | innovatopia（初代の公表値） |
| Servi Q | 45cm | innovatopia |

- ふだん走らせる幅は1.0m（店の運用の設定。日本のファミレスの映像で、ボックスの横の1m前後の通路を走っていたため）
- 卓は3つに分ける：ロボットが届く（1.0m以上の道で行ける）／狭い道なら届く（80cm〜1.0mの道を使う）／人が運ぶ（ロボットから受け取って人が運ぶ。その分の歩く距離も出す）
- 届くかは、座る側の前の通路に止まれるかで判定する。ボックスの背中側・壁側・仕切り越しは数えない

## 決まり（2026-10-22 codex の2回目の検証を受けて、エイイチと決めたこと）

- L字席で止まってよいのは、テーブルのある内側の角の開いた2辺と、そこからソファの端に沿って0.6mまで（端の席の人が受け取れる所）。ソファの横は使わない
- 2人席で止まってよいのは、椅子の側とテーブルの横。ベンチの側・ベンチの横は使わない
- 待機場所は1台分0.8m×0.8m以上、台数は正の整数。1台ずつ、ほかの台が止まったままで受け渡し口の前まで出られること。②の待機場所は2.2×0.7mから2.4×0.8m（3台）に広げた
- ②：パントリーの上の通路への出入り口を2つ開けた（A＝パントリーの壁に1m・客席側とつながる、B＝パントリーの上の端）。どちらも扉はない（扉があると誰かが開けないといけない）。扉は上の通路から洗い場とキッチンへ入る所（位置は仮）。JSON の `layout.doors`（`door` が扉の有無）
- ②：仕切り壁の上の4人席2つを、壁との間0.6mまで寄せた（ロボットが壁側から入らないように）
- トイレから1m以内の席は、席のトイレ側の辺の両端からトイレの入口の両端へ引いた4本の線が、全部壁に当たること。4本の線は近似で、目隠しの壁に切れ目があると、4本は当たっても間から入口が見える置き方を見落とし得る（目隠しは1枚の続いた壁で作る）
- トイレの4辺は、外壁・裏の部屋・建物の外・厚さ30cm以上の壁・入口のどれかで、すきまなく囲む（辺を区間として計算で確かめる）。入口の前と入口そのものは、名前に関係なく何も置かない。入口の前の矩形は、入口の幅いっぱいで奥行き1.2m以上
- 店員の出入り口（`layout.doors`）に壁を重ねない。doors は図と Unity のための情報で、ロボットの道の計算には使わない（パントリーなどの部屋の中は障害物のまま）
- 「ロボットが届く」は、決めた辺の近くの止まる位置まで行けるという意味。客が実際に受け取れるか（天板までの距離・着席の位置）は測っていない
- レジ（2.5m以上）とレジ内（店員の立つ所）が両方あり、接していること
- 仕切りが卓に食い込んでよいのは、ボックス席のベンチの背の辺の上で、深さ8cm以内のときだけ
- 建物の輪郭（`outline`）は縦と横の辺だけでできた形にし、建物の外（`kind: outside`）と同じ所を表すこと（輪郭・建物の外・外形の座標で区切ったマスごとに照合するので、細い切れ込みも見逃さない）。輪郭の外に壁・待機場所・受け渡し口・出発点を置かない

## 計算の約束

- 障害物までの距離は、矩形から正確に出す。マス目は2.5cm
- 幅 w 以上の通路は必ず通れる、w−2.5cm 未満は必ず通れない。その間（2.5cmの幅）だけはマス目の位置で分かれる
- 毎回、マス目2.5cmと1.25cmの2回計算し、分類が分かれた卓は図に ? 印を付け、`resolution_mismatch` に書く。比べているのは分類だけで、距離や止まる位置が同じとは限らない
- 通れる幅の判定には半マス分の許容がある。道の一番狭い所（`narrowest`）が 0.97m のように、1.0m をわずかに下回ることがある
- 斜めの移動は両隣が通れるときだけ（すり抜けない）
- 待機場所は止まっているロボットごと障害物
- 止まる位置から卓の辺までの見通し、トイレの目隠しの線は、線と矩形の交わりを計算で求める（点を並べて確かめると、薄い壁を見逃すため）。止まる位置の候補は打ち切らずに全部見る
- 出発点（受け渡し口の前）が通れない場所なら、計算を止める
- 決まりに違反があれば、重い計算に進まず止まる。出力はいったん作業用のフォルダに出し、6つ全部が通ったときだけ出力先へ入れ替える。違反があれば出力先には何も書かない（前の版がそのまま残り、新旧が混ざらない）
- 入れ替えの途中で1つでも失敗したら、入れ替えた分を元に戻す（全部か、何もなしか）。summary.json は最後に入れ替える
- layout・analysis・summary には同じ版の番号（`generation`：作り方のコード・記憶の店の原本・設定から決まる）を入れる。読む側（サイトの試算・Unity）は、summary と各ファイルの `generation` が同じかを確かめてから使う。公開はサイトの git の commit 単位なので、公開されるのは常に1つの版の一式
- 出力先は `--out フォルダ` で替えられる

## まだ入っていないこと

- 厨房の中の人の動き（扉・作業通路・下げ膳の返却口）
- 人やほかのロボットとのすれ違い、複数台、旋回、積み下ろしの時間（いまは片道の最短距離だけ）
- 引かれた椅子・子ども用の椅子などで通路が一時的に狭くなる分
- 壁や仕切りの高さは仮の値（`furniture_types.walls.height_m_仮`）
- サイトの試算と Unity で共通に使うデータの形（座標の対応は JSON の `meta.coords` に書いた）
