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 Aczas_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.
  • salt rozróż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 osobyBazowa godzina wstaniaWariacja
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_id identyczne)
  • 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):

GrupaStatusyGodzina bazowa
0EMPLOYED6:30
1STUDENT, HIGHER_EDU7:00
2CHILD7:30
3UNEMPLOYED, RETIRED8: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 pracodawcyAktywność
BTYPE_INDUSTRIALEVT_WORK_FACTORY
BTYPE_SHOP_GROCERY, BTYPE_SHOP_OTHEREVT_WORK_SHOP
BTYPE_SERVICE, BTYPE_MEDICALEVT_WORK_SERVICE
PozostałeEVT_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. Decyduje prand(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_CLASS zamiast EVT_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) → potem EVT_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_TV po kolacji tylko dla emerytów i bezrobotnych; wszyscy inni idą w HOME_REST.
  • EVT_FALLING_ASLEEP trwa stałe 20 minut, potem EVT_SLEEP.

5. Czasy trwania aktywności

AktywnośćCzas bazowyWariacjaSalt
EVT_WAKEUP15 min±5 min1
EVT_BREAKFAST30 min±10 min2
EVT_WORK_OFFICE/FACTORY/SHOP/SERVICE4 h±20 min4
EVT_WORK_BREAK45 min±10 min5
EVT_SCHOOL_CLASS2 h 30 min±15 min6
EVT_SCHOOL_BREAK15 min
EVT_UNIVERSITY_CLASS2 h±15 min7
EVT_HOME_LEARNING2 h±30 min8
EVT_GROCERY_SHOPPING30 min±10 min9
EVT_DINNER45 min±10 min10
EVT_FALLING_ASLEEP20 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.

WiekZ partnerem / kochankąZ małżonkiem (REL_SPOUSE)
< 30 lat30–45 min25–40 min
30–39 lat25–35 min20–30 min
40–49 lat20–30 min15–25 min
50–59 lat15–25 min10–20 min
≥ 60 lat10–20 min5–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

  1. Romans (affair_partner_id != 0) ma priorytet przed stałym partnerem.
  2. Fallback: stały partner (partner_id).
  3. Brak partnera → brak planu.

7.2 Rola: COHABIT / HOST / GUEST / AWAY

  • COHABIT: oboje mieszkają pod tym samym adresem (home_unit_id identyczne) 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

WarunekPrzedział czasu
Mężczyzna: uczeń/student < 24 lat11:00–17:00
Mężczyzna: uczeń/student ≥ 24 lat14:00–22:00
Romans + mężczyzna żonaty (REL_SPOUSE)17:00–22:00
Mężczyzna żonaty (spotkanie z żoną)20:00–23:00
Pozostałe19: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_id inicjatora).
  • 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:

  1. Inkrementujemy partner->sched_gen.
  2. Stare zdarzenie B w schedulerze (z poprzednim gen) zostanie pominięte.
  3. Wstawiamy nowe EVT_HOME_REST dla B z czas_konca identycznym 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 → rano EVT_MORNING_RETURNEVT_HOME_REST.
  • 20%: EVT_MORNING_RETURN od 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:

  1. Czy partner jest aktualnie w aktywności seksualnej (EVT_SEX_HOST, EVT_SEX_GUEST, EVT_WAIT_FOR_PARTNER) → pomiń.
  2. Czy partner ma romans zaplanowany na dziś (plans[partner.id].role == PLAN_HOST lub PLAN_GUEST z meet_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_HOST odpala się (gospodarz zakończył seks),
  • EVT_SEX_HOME odpala się (współmieszkańcy zakończyli seks),
  • EVT_MORNING_RETURN odpala 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_WORK
  • EVT_COMMUTE_FROM_WORK
  • EVT_COMMUTE_TO_SCHOOL
  • EVT_COMMUTE_FROM_SCHOOL
  • EVT_TRAVEL_TO_PARTNER
  • EVT_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_id identyczne, 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 paryPrawdopob.SlotCzas
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ćmi70% (salt=48)Tylko wieczór16–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_WAKEUP w sobotę.
  • Set = 1 przy EVT_SEX_HOME w 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
Sobota8%64
Niedziela3%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 == 0EVT_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:

WynikPrawdopob.Następna aktywnośćSalt
Znaleziono partnera15%EVT_SEX_AWAY66
Nic się nie wydarzyło85%EVT_HOME_REST66

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 (bo plan->partner_id == 0; brak EVT_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:

MiaraSobotaNiedziela
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.