Reguły symulacji — simulation.c
Dokumentacja wszystkich reguł zachowania mieszkańców zaimplementowanych w src/simulation.c.
1. Architektura — DES (Discrete Event Simulation)
Symulator nie iteruje po sekundach, lecz przeskakuje do momentów gdy „coś się dzieje". Dzięki temu na nowoczesnym sprzęcie jest w stanie symulować aktywność nawet dziesiątek milionów osób.
- Każda osoba ma w schedulerze dokładnie 1 oczekujące zdarzenie w formie: _„O czasie T osoba P zaczyna aktywność A"_.
- Gdy zdarzenie odpala się (
T ≤ sim_time):
1. oblicz czas trwania A → czas_końca = T + duration(A) 2. oblicz następną aktywność B = next_activity(A) 3. zapisz do logu: {T, czas_końca, osoba, A, origin, dest} 4. zaktualizuj stan osoby 5. wstaw do schedulera: "O czas_końca osoba zaczyna B"
- Łańcuch jest samo-zasilający się i nigdy się nie urywa.
1a. Dni tygodnia — weekendy
day_of_week(d) zwraca 0=pon … 5=sob, 6=ndz na podstawie wzoru (d/86400 + 3) % 7.
W soboty i niedziele aktywny jest oddzielny harmonogram weekendowy (patrz sekcja 15). Godziny pobudki i śniadania są takie same jak w dni robocze.
2. Deterministyczna pseudolosowość
prand(person_id, day_epoch, salt)
Zwraca uint32_t z dobrym rozkładem. Algorytm: XOR-Shift Hash z mnożeniem przez stałe Knutha.
- Nie ma globalnego stanu → bezpieczne wątkowo.
- Ta sama kombinacja
(person_id, day_epoch, salt)zawsze daje ten sam wynik → powtarzalność dla tego samego seeda. day_epoch= timestamp początku dnia (00:00:00 UTC), różnicuje tę samą osobę w różnych dniach.saltrozróżnia różne zastosowania tej samej osoby tego samego dnia (np.salt=0: godzina wstania,salt=2: czas śniadania).
variance(person_id, day, salt, range)
Zwraca losowe odchylenie z przedziału [-range, +range] sekund. Używane wszędzie tam, gdzie do czasu bazowego dodawana jest wariacja.
3. Godziny wstania
| Status osoby | Bazowa godzina wstania | Wariacja |
|---|---|---|
Pracujący (STATUS_EMPLOYED) | 6:30 | ±30 min |
Uczeń (STATUS_STUDENT) | 7:00 | ±30 min |
Student wyższej uczelni (STATUS_HIGHER_EDU) | 7:00 | ±30 min |
Dziecko (STATUS_CHILD) | 7:30 | ±30 min |
Bezrobotny (STATUS_UNEMPLOYED) | 8:00 | ±30 min |
Emeryt (STATUS_RETIRED) | 8:00 | ±30 min |
Wariacja używa salt=0. Wynik to sekundy od północy — może przekroczyć 24h, co jest obsługiwane w activity_duration(EVT_SLEEP).
4. Maszyna stanów — harmonogram dnia
4.1 Rytm poranny (wszyscy)
SLEEP → [MORNING_SEX →] WAKEUP → BREAKFAST → (patrz 4.2–4.4)
MORNING_SEX jest opcjonalnym krokiem przed WAKEUP. Warunki wystąpienia:
- Para kohabitująca (partner w tym samym lokalu,
home_unit_ididentyczne) - Wiek mężczyzny: 20–35 lat
- Oboje należą do tej samej grupy godzin wstania (patrz tabela poniżej)
- Prawdopodobieństwo: 15% dziennie
Grupy godzin wstania (same_wakeup_group):
| Grupa | Statusy | Godzina bazowa |
|---|---|---|
| 0 | EMPLOYED | 6:30 |
| 1 | STUDENT, HIGHER_EDU | 7:00 |
| 2 | CHILD | 7:30 |
| 3 | UNEMPLOYED, RETIRED | 8:00 |
Symetria: decyzja opiera się na shared_id = min(A.id, B.id) i salt 77 — oboje obliczają identyczną wartość prand(), więc albo oboje wchodzą w zdarzenie, albo żadne.
Czas trwania: 5–15 minut (M(5) + prand(id, dzien, 78) % (M(10)+1)). Lokalizacja: home_unit_id (własny lokal). Następna aktywność: EVT_WAKEUP.
4.2 Ścieżka pracującego
BREAKFAST → COMMUTE_TO_WORK → WORK_* → WORK_BREAK → WORK_* → COMMUTE_FROM_WORK
→ (80%) HOME_REST / (20%) GROCERY_SHOPPING → HOME_REST → ...
- Po przyjeździe do pracy typ aktywności zależy od typu organizacji pracodawcy:
| Typ budynku pracodawcy | Aktywność |
|---|---|
BTYPE_INDUSTRIAL | EVT_WORK_FACTORY |
BTYPE_SHOP_GROCERY, BTYPE_SHOP_OTHER | EVT_WORK_SHOP |
BTYPE_SERVICE, BTYPE_MEDICAL | EVT_WORK_SERVICE |
| Pozostałe | EVT_WORK_OFFICE |
- Pierwszy blok pracy kończy się przed 12:00 → następuje przerwa (
EVT_WORK_BREAK). - Drugi blok pracy kończy się po 12:00 → następuje powrót do domu (
EVT_COMMUTE_FROM_WORK). - Po powrocie z pracy: 20% szans na zakupy (
EVT_GROCERY_SHOPPING), reszta →EVT_HOME_REST. Decydujeprand(id, dzien, salt=20) % 5 == 0.
4.3 Ścieżka ucznia / studenta
BREAKFAST → COMMUTE_TO_SCHOOL → SCHOOL_CLASS/UNIVERSITY_CLASS
→ SCHOOL_BREAK → SCHOOL_CLASS → COMMUTE_FROM_SCHOOL → HOME_LEARNING → HOME_REST → ...
- Student wyższej uczelni (
STATUS_HIGHER_EDU) →EVT_UNIVERSITY_CLASSzamiastEVT_SCHOOL_CLASS. - Przerwa szkolna odpala się przed 11:00 (dla uczniów) lub przed 12:00 (dla studentów).
- Po powrocie ze szkoły: zawsze
EVT_HOME_LEARNING(odrabianie lekcji) → potemEVT_HOME_REST.
4.4 Ścieżka dzieci, bezrobotnych, emerytów
BREAKFAST → HOME_REST → (wieczorem) DINNER → WATCH_TV (em./bezr.) lub HOME_REST → FALLING_ASLEEP → SLEEP
4.5 Wieczorny rytm (wszyscy)
HOME_REST → DINNER → HOME_REST (lub WATCH_TV) → FALLING_ASLEEP → SLEEP
EVT_WATCH_TVpo kolacji tylko dla emerytów i bezrobotnych; wszyscy inni idą wHOME_REST.EVT_FALLING_ASLEEPtrwa stałe 20 minut, potemEVT_SLEEP.
5. Czasy trwania aktywności
| Aktywność | Czas bazowy | Wariacja | Salt |
|---|---|---|---|
EVT_WAKEUP | 15 min | ±5 min | 1 |
EVT_BREAKFAST | 30 min | ±10 min | 2 |
EVT_WORK_OFFICE/FACTORY/SHOP/SERVICE | 4 h | ±20 min | 4 |
EVT_WORK_BREAK | 45 min | ±10 min | 5 |
EVT_SCHOOL_CLASS | 2 h 30 min | ±15 min | 6 |
EVT_SCHOOL_BREAK | 15 min | — | — |
EVT_UNIVERSITY_CLASS | 2 h | ±15 min | 7 |
EVT_HOME_LEARNING | 2 h | ±30 min | 8 |
EVT_GROCERY_SHOPPING | 30 min | ±10 min | 9 |
EVT_DINNER | 45 min | ±10 min | 10 |
EVT_FALLING_ASLEEP | 20 min | — | — |
Dojazdy
Czas dojazdu oblicza travel_time(miasto, unit_A, unit_B) — na podstawie odległości euklidesowej między lokalami. Minimum: 3 min, maksimum: 60 min.
Sen (EVT_SLEEP)
Trwa od t do następnej godziny wstania:
- Jeśli godzina wstania jest jeszcze dziś →
czas_wstania_dzis - t. - Jeśli godzina wstania już minęła →
czas_wstania_jutro - t.
HOME_REST
Czas do najwcześniejszego z:
- godziny kolacji:
18:00 ± 30 min(salt=11) - godziny snu:
22:30 ± 30 min(salt=12) - jeśli obie minęły: 5 minut
Jeśli osoba ma zaplanowane spotkanie (plans[id].meet_time > 0):
- PLAN_COHABIT / PLAN_HOST: czekamy do
meet_time(skrócenie jeśli krótsze od normalnego). - PLAN_GUEST: wychodzimy wcześniej o czas dojazdu do lokalu partnera.
WATCH_TV
Trwa do godziny snu (22:30 ± 30 min, salt=13). Jeśli poza planem snu → 5 minut. Skracane do meet_time jeśli jest plan spotkania.
6. Seks — czas trwania
Czas trwania zależy od wieku i charakteru relacji.
| Wiek | Z partnerem / kochanką | Z małżonkiem (REL_SPOUSE) |
|---|---|---|
| < 30 lat | 30–45 min | 25–40 min |
| 30–39 lat | 25–35 min | 20–30 min |
| 40–49 lat | 20–30 min | 15–25 min |
| 50–59 lat | 15–25 min | 10–20 min |
| ≥ 60 lat | 10–20 min | 5–15 min |
Reguły kwalifikacji:
EVT_SEX_HOME+rel_type == REL_SPOUSE→ czas z małżonkiem.EVT_SEX_AWAY→ zawsze czas z partnerem / kochanką (niezależnie od rel_type).EVT_SEX_HOME+ inne rel_type → czas z partnerem.
Salty: 30 (SEX_HOME), 31 (SEX_AWAY), 32 (SEX_HOST), 33 (SEX_GUEST). EVT_SEX_HOST i EVT_SEX_GUEST używają tych samych przedziałów co SEX_HOME/AWAY.
7. Planowanie dziennych spotkań (plan_daily_meeting)
Wywoływane przy EVT_WAKEUP. Planuje spotkanie seksualne na dany dzień.
7.1 Wybór partnera
- Romans (
affair_partner_id != 0) ma priorytet przed stałym partnerem. - Fallback: stały partner (
partner_id). - Brak partnera → brak planu.
7.2 Rola: COHABIT / HOST / GUEST / AWAY
- COHABIT: oboje mieszkają pod tym samym adresem (
home_unit_ididentyczne) i brak romansu. - HOST / GUEST (inny dom lub romans):
- Jeśli kobieta jest uczennicą/studentką → mężczyzna zawsze gospodarzem (
PLAN_HOST). - W pozostałych przypadkach: losowo wg
prand(min(A.id, B.id), d, salt=43) % 2. - AWAY (hotel/klub) — nadpisuje HOST/GUEST w dwóch scenariuszach (patrz 7.6).
7.3 Wspólny seed
Oboje partnerzy niezależnie obliczają plan. Używają shared_id = min(osoba->id, partner->id), dzięki czemu wyliczają identyczny meet_time i meet_loc bez żadnej komunikacji.
7.4 Czas spotkania — reguły wg statusu mężczyzny w parze
| Warunek | Przedział czasu |
|---|---|
| Mężczyzna: uczeń/student < 24 lat | 11:00–17:00 |
| Mężczyzna: uczeń/student ≥ 24 lat | 14:00–22:00 |
Romans + mężczyzna żonaty (REL_SPOUSE) | 17:00–22:00 |
| Mężczyzna żonaty (spotkanie z żoną) | 20:00–23:00 |
| Pozostałe | 19:00–22:00 |
Czas dokładny: d + meet_start + prand(shared_id, d, salt=44) % meet_range.
7.5 Lokal spotkania
- COHABIT: własny dom (
home_unit_idinicjatora). - HOST: własny dom gospodarza.
- GUEST: dom gospodarza (
meet_loc = gospodarz->home_unit_id). - AWAY:
meet_loc = 0(placeholder; docelowo id hotelu/klubu).
7.6 Seks w hotelu/klubie (PLAN_AWAY / EVT_SEX_AWAY)
Oboje partnerzy wyjeżdżają do miejsca neutralnego i wracają zaraz po seksie (nigdy nie nocują).
Scenariusz A — romans między małżonkami: romans (is_affair == 1), oboje mają współmałżonka i z nim mieszkają (rel_type == REL_SPOUSE + home_unit_id == partner_home). Dom żadnej ze stron nie jest dostępny → hotel obowiązkowy.
Scenariusz B — casual dating młodych: brak romansu, oboje < 26 lat, rel_type == REL_NONE u obojga. Prawdopodobieństwo hotelu: 25% pn–pt, 50% sob–ndz (prand(shared_id, d, salt=62) % 100). Jeśli nie wylosowano hotelu → standardowy HOST/GUEST.
Łańcuch stanów dla każdej strony:
HOME_REST → EVT_TRAVEL_TO_PARTNER → EVT_SEX_AWAY → EVT_MORNING_RETURN → EVT_HOME_REST
Czas trwania EVT_TRAVEL_TO_PARTNER: travel_time(home, meet_loc=0) = MIN_TRAVEL_SECS = 3 min (placeholder).
8. Synchronizacja COHABIT (force_cohabit_sex)
Gdy osoba A z rolą PLAN_COHABIT wchodzi w EVT_SEX_HOME, partner B może być jeszcze w innej aktywności. Zamiast czekać aż B sam dojdzie do meet_time, wymuszamy B natychmiast.
Mechanizm stale event:
- Inkrementujemy
partner->sched_gen. - Stare zdarzenie B w schedulerze (z poprzednim
gen) zostanie pominięte. - Wstawiamy nowe
EVT_HOME_RESTdla B zczas_koncaidentycznym jak A.
Dzięki temu oboje kończą seks jednocześnie → act_count[EVT_SEX_HOME] jest zawsze parzyste.
Wyjątek: jeśli B jest aktualnie w EVT_SEX_HOST, EVT_SEX_GUEST lub EVT_WAIT_FOR_PARTNER (romansuje) → nie przerywamy.
9. Stale event detection
Każda osoba ma pole sched_gen (licznik generacji). Każde zdarzenie w schedulerze niesie ze sobą gen wstawionego zdarzenia.
W sim_process_event(): jeśli item->gen != osoba->sched_gen → zdarzenie jest stale i jest pomijane.
Używane wyłącznie przez force_cohabit_sex() do unieważnienia starych zdarzeń partnera.
10. Zakończenie seksu jako gość
Po EVT_SEX_GUEST (seks u partnera w jego domu):
- 80%:
EVT_STAY_OVERNIGHT→ nocuje → ranoEVT_MORNING_RETURN→EVT_HOME_REST. - 20%:
EVT_MORNING_RETURNod razu →EVT_HOME_REST.
Decyduje prand(id, dzien, salt=50) % 5 < 4.
11. Ochrona przed romansem partnera (sprawdzenie planu)
W next_activity(EVT_HOME_REST) i next_activity(EVT_WATCH_TV) dla PLAN_COHABIT, przed wejściem w EVT_SEX_HOME sprawdzamy:
- Czy partner jest aktualnie w aktywności seksualnej (
EVT_SEX_HOST,EVT_SEX_GUEST,EVT_WAIT_FOR_PARTNER) → pomiń. - Czy partner ma romans zaplanowany na dziś (
plans[partner.id].role == PLAN_HOSTlubPLAN_GUESTzmeet_time > 0) → pomiń.
Cel: uniknięcie sytuacji gdy A wchodzi w SEX_HOME z B, a B zaraz wejdzie w SEX z kimś trzecim (romans).
12. Czyszczenie planu po spotkaniu
Plan (plans[osoba->id].meet_time) jest zerowany gdy:
EVT_SEX_HOSTodpala się (gospodarz zakończył seks),EVT_SEX_HOMEodpala się (współmieszkańcy zakończyli seks),EVT_MORNING_RETURNodpala się (gość wrócił do domu).
13. Lokalizacja osoby podczas aktywności (LOC_STREET vs LOC_UNIT)
Osoba jest LOC_STREET (w drodze) podczas:
EVT_COMMUTE_TO_WORKEVT_COMMUTE_FROM_WORKEVT_COMMUTE_TO_SCHOOLEVT_COMMUTE_FROM_SCHOOLEVT_TRAVEL_TO_PARTNEREVT_MORNING_RETURN
We wszystkich pozostałych aktywnościach: LOC_UNIT (w konkretnym lokalu).
14. Inicjalizacja (sim_init_all)
Na starcie dla każdej osoby:
- Stan: śpi (
EVT_SLEEP), jest w domu (LOC_UNIT,home_unit_id). - Obliczana jest godzina wstania na bieżący dzień.
- Jeśli godzina wstania już minęła → planowane na jutro (
+86400s). - Do schedulera wstawiane jest
EVT_WAKEUP.
15. Harmonogram weekendowy
15.1 Obiad (sobota i niedziela)
- Dotyczy par współmieszkających (
home_unit_ididentyczne,partner_id > 0). - Czas: losowo 13:00–15:00, synchronizowany przez
shared_id = min(A.id, B.id)→ oboje mają identyczny czas. - Salt=45. Aktywność:
EVT_LUNCH(≈60 min ± 15 min, salt=14). - Dzieci nie dostają własnego zdarzenia obiadu — są w domu podczas
HOME_REST.
15.2 Zakupy sobotnie
- Dotyczy: kobiet (
GENDER_FEMALE) powyżej 18 lat. - Prawdopodobieństwo: 70% (
prand(id, d, 46) % 10 < 7). - Czas: losowo 9:00–13:00 (salt=47). Aktywność:
EVT_GROCERY_SHOPPING.
15.3 Nauka domowa — niedziela
- Dotyczy: dzieci i uczniów (
STATUS_CHILD,STATUS_STUDENT). - Prawdopodobieństwo: 70% (salt=55).
- Czas: losowo 10:00–12:00 (salt=56). Aktywność:
EVT_HOME_LEARNING.
15.4 Seks sobotni (pary COHABIT)
Romanse (affair_partner_id > 0) mają priorytet i działają wg reguł weekday (HOST/GUEST), nie weekendowych.
| Typ pary | Prawdopob. | Slot | Czas |
|---|---|---|---|
Bez dzieci (children_count == 0) | 90% (salt=48) | Rano lub wieczór (50/50, salt=57) | Rano: wakeup + 60–120 min; Wieczór: 16–23 (po kolacji) |
| Z dziećmi | 70% (salt=48) | Tylko wieczór | 16–23 (po kolacji) |
Rano: czas = wakeup_tod + M(60) + prand(58) % M(60), cappowany do shopping_time - M(30) (jeśli kobieta ma zakupy) lub do 12:00.
Wieczór: surowy czas 16:00–23:00 (salt=49) podnoszony do pora_kolacji + M(60), żeby kolacja zawsze była przed seksem. Max 22:30.
Flaga sat_sex_morning = 1 oznacza poranny slot.
15.5 Seks niedzielny (pary COHABIT)
- Dotyczy: osoby ≥ 19 lat, które nie miały seksu w sobotę (
sat_had_sex[id] == 0). - Prawdopodobieństwo: 80% (salt=59).
- Czas: 16:00–23:00 (po kolacji, ta sama logika przesunięcia co sobota).
Flaga sat_had_sex[id]:
- Reset przy
EVT_WAKEUPw sobotę. - Set = 1 przy
EVT_SEX_HOMEw sobotę. - Czytana przy planowaniu niedzielnym.
15.6 Przepływ stanów weekendowych
WAKEUP → BREAKFAST →
[sob, kobieta >18, 70%] → HOME_REST → GROCERY_SHOPPING →
[sob/ndz, para] → HOME_REST → LUNCH →
HOME_REST →
[sob/ndz, seks wieczorny] → SEX_HOME →
HOME_REST → DINNER → FALLING_ASLEEP → SLEEP
[sob, para bez dzieci, seks rano] → HOME_REST → SEX_HOME →
HOME_REST → (powyżej od GROCERY_SHOPPING)
[ndz, dzieci, 70%] → HOME_REST → HOME_LEARNING →
HOME_REST → LUNCH → ...
[sob/ndz, single 18–40, klub] → HOME_REST → CLUB_NIGHT →
[15%] → SEX_AWAY → HOME_REST → FALLING_ASLEEP
[85%] → HOME_REST → FALLING_ASLEEP
15.7 Klub nocny — szukanie przygodnego seksu (EVT_CLUB_NIGHT = 410)
Modeluje sobotnie i niedzielne wyjścia singli do klubów nocnych w poszukiwaniu jednorazowego kontaktu seksualnego.
Podstawa empiryczna (Gorzów Wlkp.): Miasto ma ~5 aktywnych klubów nocnych (Klub Studio, Pub Wesele, Viva Disco, Ósemka, MIXcoolTURA). Klub Studio opisywany jako odwiedzany przez „setki osób co tydzień"; pojemność większych lokali do ~500 os. Łączna frekwencja w sobotni wieczór: szacunkowo 1 500–2 500 os. Z tego ~20–30% aktywnie szuka przygody seksualnej. Źródło: [baza.mgbi.pl](https://baza.mgbi.pl/katalog/baza/baza-firm-gorzow-wielkopolski) + lokalne materiały prasowe (marzec 2026).
Kto uczestniczy:
- Status: dowolny (poza dziećmi — wiek ≥ 18)
- Wiek: 18–40 lat
- Relacja: brak stałego partnera (
partner_id == 0) i brak romansu (affair_partner_id == 0) - Dotyczy soboty i niedzieli
Prawdopodobieństwo wyjścia:
| Dzień | Prawdopob. | Salt |
|---|---|---|
| Sobota | 8% | 64 |
| Niedziela | 3% | 64 |
Godzina wyjścia:
- Sobota: losowo 20:00–23:00 (salt=65)
- Niedziela: losowo 20:00–22:00 (salt=65)
Zapisywane jako plan->meet_time, plan->role = PLAN_AWAY, plan->partner_id = 0, plan->meet_loc = 0. W next_home(): gdy plan->role == PLAN_AWAY && plan->partner_id == 0 → EVT_CLUB_NIGHT (zamiast EVT_TRAVEL_TO_PARTNER).
Czas trwania aktywności EVT_CLUB_NIGHT:
1h ± 30 min (salt=63). Osoba jest „w mieście" bez konkretnego lokalu (activity_unit = 0).
Wynik po wyjściu z klubu:
| Wynik | Prawdopob. | Następna aktywność | Salt |
|---|---|---|---|
| Znaleziono partnera | 15% | EVT_SEX_AWAY | 66 |
| Nic się nie wydarzyło | 85% | EVT_HOME_REST | 66 |
Seks przygodny (EVT_SEX_AWAY po EVT_CLUB_NIGHT):
- Czas trwania: jak standardowy
EVT_SEX_AWAY(tabela czas×wiek, salt=31, brak małżeństwa). - Lokalizacja:
meet_loc = 0(brak konkretnego lokalu). - Po seksie: bezpośrednio
EVT_HOME_REST(boplan->partner_id == 0; brakEVT_MORNING_RETURN). - Flaga
sat_had_sex: nie ustawiana (osoba jest singlem, nie wpływa na plan niedzielny).
Skala dla Gorzowa przy ~12 000 eligible singli:
| Miara | Sobota | Niedziela |
|---|---|---|
| Planuje wyjście | ~960 os. | ~360 os. |
W EVT_CLUB_NIGHT jednocześnie (21–01) | ~500–800 os. | ~180–300 os. |
W EVT_SEX_AWAY (przygodny) | ~140 os. | ~54 os. |
