やさしい解説 · ビジュアル多め

なかよしグループを
つくるコンピュータ

matching_demo.py は、みんなの「やりたいこと・あいている時間・いる場所」を見て、 いっしょに遊べそうなグループをつくるプログラムです。

🧒🧒🧒
1. 子どもたちが来る やりたいこと・時間・場所
🚦
2. ルールでチェック ダメな人はおやすみ
3. ふたりずつ点数 なかよし度を測る
🧩
4. グループにする 2〜5人でチーム
📣
5. 結果を見せる 画面にプリント

📖 いちばん大事なおはなし

公園で「いま遊びたい人」を集めて、
同じ遊びができそうな友だち同士をチームにする係です。

flowchart LR
  A["🧒 子どもたち
やりたいこと
あいている時間
いまの場所"] --> B["🧠 コンピュータ"] B --> C["❌ あそべない人"] B --> D["⭐ ふたりの点数"] B --> E["🧩 できたチーム"]

図 — 入ってきた人を分けて、チームをつくる

データベースやネットは使いません。おもちゃ箱ひとつ(このファイルだけ)で動きます。

🧰 ファイルの中は 8 つの引き出し

flowchart TB
  F["matching_demo.py"]
  F --> A["① CONFIG
ルールの数字"] F --> B["② TYPES
ものの形"] F --> C["③ SCENARIOS
お話のセット"] F --> D["④ 時間の道具"] F --> E["⑤ 点数の道具"] F --> F5["⑥ 本番の流れ"] F --> G["⑦ 結果プリント"] F --> H["⑧ main
スタートボタン"]

図 — 上から下へ読むと、そのままプログラムの順番

① CONFIG

「何点以上で合格?」などの数字ルール。

PAIR = 0.60 / GROUP = 0.70

② TYPES

子ども・場所・点数などの「入れもの」。詳しくは ものの形

Candidate / Beacon / Profile

③ SCENARIOS

練習用のお話 A〜E。

build_scenarios()

④ 時間の道具

あいている時間のかさなりを計算。

intersect / clip

⑤ 点数の道具

やりたいこと・場所などで星をつける。

*_match_score

⑥〜⑧ 流れ

門番→ペア→グループ→印刷→起動。

run_matching / main

📦 ものの形(クラス / 構造体)

プログラムの「入れもの」です。
ぜんぶ @dataclass(frozen=True)=いったん作ったら中身を書き換えないカードです。

flowchart TB
  SC["Scenario
お話セット"] --> CA["Candidate
子どもカード"] SC --> BE["Beacon
場所カード"] SC --> PR["Profile
プロフィール"] CA --> TI["TimeInterval
空き時間"] RUN["MatchingResult
結果セット"] --> EX["Exclusion
おやすみ理由"] RUN --> PS["PairScore
ふたりの点数"] RUN --> MG["MatchedGroup
できたチーム"] PS --> FS["FactorScores
7つの星"] MG --> FS

図 — どの入れものが、どの入れものを持つか

TimeInterval ⏱️ 空き時間のひとコマ

「何時から何時まで空いているか」を1本の線で表します。

start始まりの時刻
end終わりの時刻
Candidate 🧒 いま遊びたい人のカード

マッチングに入る「いま」の状態。門番チェックとペア採点の材料になります。

student_idだれか(例: u001)
intentsやりたいこと(study など)
intervals空き時間のリスト
expires_atこのカードが効く期限
current_location_idいまいる場所(なくてもよい)
Beacon 📍 遊ぶ場所のカード

図書館・カフェなどの場所。広さや、同時に何組まで遊べるかを書きます。

id場所の名前(ID)
capacity定員(何人まで)
max_parallel_events同時開催の上限
supports_micro短いイベントOK?
building_idどの建物か
campus_idどのキャンパスか
active_parallel_countいま重なっているイベント数
Profile 🪪 学科と友だち

点数の「学科」「友だち」の材料。Candidate とは別のカードです。

student_idだれのプロフィールか
department_id学科(例: dept_cs)
friend_ids承認済みの友だち一覧
FactorScores ⭐ 7つの星(要因点)

意図・時間・場所など、ひとつずつ 0〜1 の星。
weighted_total() で重みをかけて総合点にします。

intent_matchやりたいことの一致
time_match空き時間の一致
location_match場所の近さ
interest_match興味(デモではいつも0.50)
department_match学科の一致
friend_match友だち関係
capacity_suitability場所の広さの向きやすさ
PairScore 🤝 ふたりの点数カード

子ども2人についての採点結果。総合点が 0.60 以上なら「線」として残ります。

student_a / student_bだれとだれか
matching_score総合点(0〜1)
factors7つの星の内訳
common_minutes準備後に共有できる分数
beacon_id選んだ場所
pair_key並べ替えたIDの組(辞書の鍵)
Exclusion 🚫 おやすみ理由

門番で落ちた人のメモ。点数はつけません。

student_idだれが落ちたか
reason_code理由コード(例: 時間たりない)
detailsおまけ情報(残り分数など)
MatchedGroup 🧩 できたチーム

2〜5人のチーム1つ分。総合点と、選びやすさの点(人数ボーナス込み)を持ちます。

student_idsメンバー一覧
matching_scoreチームの総合点
selection_score選ぶ順番用の点(総合+人数ボーナス)
beacon_id遊ぶ場所
common_minutesみんなの共通空き(分数)
factorsチーム用の7つの星
MatchingResult 📋 結果セット

run_matching が返すおみやげ袋。印刷はこの中身を見ます。

groups採用されたチームたち
exclusionsおやすみになった人
pair_scoresすべてのペア点
eligible_student_ids門番を通った人のID
Scenario 📖 お話セット A〜E

デモ用の「いま」と参加者・場所をひとまとめにした箱。コマンドで名前を選んで動かします。

name / descriptionお話の名前と説明
nowデモの現在時刻
candidates参加する子どもたち
beacons使える場所一覧
profiles学科・友だち辞書
duplicate_student_setsすでに予約済みの人の組(ブロック用)
flowchart LR
  SC["Scenario お話"] --> RUN["run_matching"]
  RUN --> MR["MatchingResult 結果"]
  MR --> PR["print_report 表示"]
      

図 — お話 → 実行 → 結果袋 → プリント

🌊 全体の流れ(いちばんの川)

入口は run_matching。ここで全部がつながります。

flowchart TD
  MAIN["main
シナリオを選ぶ"] --> RUN["run_matching"] RUN --> GATE["filter_eligible
門番チェック"] GATE --> OUT1["除外された人"] GATE --> OK["残った人"] OK --> PAIR["score_pair
ふたりずつ点数"] PAIR --> FORM["form_groups
チームづくり"] FORM --> REP["print_report
3つのお知らせ"] OUT1 --> REP PAIR --> REP

図 — スタートから結果プリントまで

sequenceDiagram
  participant あなた
  participant main
  participant run_matching
  participant 門番 as filter_eligible
  participant 点数 as score_pair
  participant チーム as form_groups
  participant 印刷 as print_report

  あなた->>main: --scenario A
  main->>run_matching: 子どもたちを渡す
  run_matching->>門番: 入れる?
  門番-->>run_matching: OKの人 / ダメな人
  loop ふたりずつ
    run_matching->>点数: AとBはなかよし?
    点数-->>run_matching: 点数カード
  end
  run_matching->>チーム: グループにして!
  チーム-->>run_matching: できたチーム
  main->>印刷: 結果を見せて
  印刷-->>あなた: ターミナルに表示
      

図 — だれがだれに頼むか

🚦 門番さん filter_eligible

遊べない人は、ここで「おやすみ」になります。点数はまだつけません。

flowchart TD
  C["🧒 子ども"] --> Q1{"まだ有効?"}
  Q1 -->|いいえ| X1["❌ inactive"]
  Q1 -->|はい| Q2{"あいている時間ある?"}
  Q2 -->|いいえ| X2["❌ NO_AVAILABILITY"]
  Q2 -->|はい| Q3{"準備15分あとでも
30分以上ある?"} Q3 -->|いいえ| X3["❌ 時間たりない"] Q3 -->|はい| Q4{"あそべる場所ある?"} Q4 -->|いいえ| X4["❌ 場所なし"] Q4 -->|はい| OK["✅ 通過!"]

図 — 門番のチェックリスト

なぜ 15 分? → 「集まる準備の時間」。そのあと最低 30 分あそべないとチームにしません。

⭐ 点数タイム score_pair

ふたりを見て「なかよし度」を 0.00〜1.00 でつけます。
0.60 未満は「まだ友だち未満」で、チームの線になりません。

7つの星の大きさ(重み)

やりたいこと
30%
時間
25%
場所
15%
興味
10%
場所の広さ
10%
学科
5%
友だち
5%
flowchart LR
  A["🧒 A"] --- S["⭐ score_pair"]
  B["🧒 B"] --- S
  S --> F["7つの小さな点"]
  F --> T["合計点"]
  T --> G{"≥ 0.60 ?"}
  G -->|Yes| EDGE["線として残す"]
  G -->|No| DROP["線にしない"]
      

図 — ふたりのカードから一本の線ができるか

やりたいこと

おなじ遊びの重なり。勉強どうしなら高い。

overlap_coefficient

時間

かさなる分数。長いほど高い。

time_match_score

場所

同じ場所◎、同じ建物○、遠い×。

location_match_score

友だち / 学科

小さく加点。シナリオ E で差が見える。

friend / department

🧩 チームづくり form_groups

線でつながった人から、2〜5人のチームを探します。
いちばんよさそうなチームから取って、使った人はもう使いません。

flowchart TD
  P["合格したペアの線"] --> C["2人,3人,4人,5人の候補"]
  C --> S{"グループ点 ≥ 0.70 ?"}
  S -->|No| NG["捨てる"]
  S -->|Yes| R["いい順に並べる"]
  R --> G["貪欲ピック
使った人は再利用しない"] G --> T1["チーム1"] G --> T2["チーム2"] G --> L["あまった人"]

図 — パズルのようにチームを取る(シナリオ C がわかりやすい)

flowchart LR
  subgraph Before["まえ"]
    U1["u001"]
    U2["u002"]
    U3["u003"]
    U4["u004"]
    U5["u005"]
    U6["u006"]
  end
  subgraph After["あと(例: C)"]
    G1["チーム1
study 3人"] G2["チーム2
lunch 2人"] Alone["ひとり
wellness"] end Before --> After

図 — 大勢から複数チーム+あまり、が出る様子

📇 関数いちらん(なにをする係?)

時間の道具

関数やさしい意味
merge_intervalsとなり合う時間をくっつけてひとつにする
intersect_intervalsふたりの時間が重なるところだけ残す
clip_intervals_after「いま+15分」より前を切り落す
common_minutes_after_prep準備後に何分かさなるか数える
remaining_minutes_after_prepひとりの残り時間を数える

点数の道具

関数やさしい意味
overlap_coefficient同じ好きの重なり具合
time_match_score分数 → 星(0 / 0.5 / 0.75 / 1)
location_match_score場所の近さの星
select_beacon_for_membersチームの遊ぶ場所を選ぶ
FactorScores.weighted_total7つの星を重みつきで合計

本番の流れ

関数やさしい意味
filter_eligible門番。ダメな人を外す
score_pairふたりの点数カードを作る
form_groupsチームを貪欲に作る
run_matching上の3つをつなぐキャプテン
print_report除外 / ペア / グループを表示
mainコマンドを読んでスタート

お話セット A〜E

A

うまくいく!チームが1つできる。

B

時間が足りない。門番で止まる。

C

6人。チームが2つ+あまり。

D

すでに予約あり。チームなし。

E

友だち・学科の点の差が見える。

▶️ 動かしてみよう

ターミナルで、お話をひとつ選んで実行します。

# お話の一覧 python matching_demo.py --list # うまくいくお話 python matching_demo.py --scenario A # 門番で止まるお話 python matching_demo.py --scenario B
flowchart LR
  CMD["コマンド"] --> OUT1["[1] Exclusions
おやすみの人"] CMD --> OUT2["[2] Pair scores
ふたりの点数"] CMD --> OUT3["[3] Groups
できたチーム"]

図 — いつも同じ3つのお知らせが出る