From ac7bd1d7ed504ac953d35c7d1f8e64fa9f946294 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Mon, 30 Mar 2026 10:16:50 +0200 Subject: [PATCH] Enhance marriage pregnancy management in SQL and UserCharacterWorker: Added a new query to clear stale marriage pregnancy records after 30 days. Updated existing queries to include shared children count for more accurate pregnancy probability calculations. Refactored logic in `maybe_run_hourly_pregnancies` to incorporate the new query and improved documentation for clarity on marriage pregnancy mechanics. --- src/worker/sql.rs | 60 ++++++++++++++++++++++++++++++++---- src/worker/user_character.rs | 9 ++++-- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/worker/sql.rs b/src/worker/sql.rs index aa8116c..a3874db 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -1666,6 +1666,8 @@ pub const QUERY_AUTOBATISM: &str = r#" // Worker würfelt stündlich: P_stunde = 1 - (1 - P_jahr)^(1/8760), damit 24×/Tag zusammen // dieselbe kumulative Jahresaufteilung wie früher 1×/Tag mit P_tag = 1 - (1 - P_jahr)^(1/365). // Grenzen in Tagen: 1 Jahr ≈ 365 Tage (mother_age_days). +// Erstes gemeinsames Kind (0 Zeilen in child_relation für dieses Paar): prob_year mindestens 1.0 +// im Alter 18–~44 (6570–16000 Tage), damit kinderlose Ehen nicht über viele Jahre ohne Nachwuchs bleiben. // WICHTIG: Vater/Mutter und Alter immer über gender ableiten — nicht character1/2 fest als Mutter! // // Ablauf Ehe: (1) Konzeption setzt `marriage_pregnancy_due_at` (+5 Tage), (2) Geburt wenn fällig. @@ -1705,6 +1707,14 @@ pub const QUERY_CLEAR_MARRIAGE_PREGNANCY_DUE: &str = r#" WHERE id = $1::int; "#; +/// Hängengebliebene Ehe-Schwangerschaft (Geburt fehlgeschlagen / inkonsistent): Flag nach 30 Tagen löschen. +pub const QUERY_CLEAR_STALE_MARRIAGE_PREGNANCY_DUE: &str = r#" + UPDATE falukant_data.relationship + SET marriage_pregnancy_due_at = NULL + WHERE marriage_pregnancy_due_at IS NOT NULL + AND marriage_pregnancy_due_at < NOW() - INTERVAL '30 days'; +"#; + /// Stündlicher Konzeptionswurf (Ehe): bei Treffer wird `marriage_pregnancy_due_at` auf +5 Tage gesetzt. pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#" WITH paired AS ( @@ -1717,7 +1727,13 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#" CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id, CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid, CASE WHEN c1.gender = 'female' THEN fu1.id ELSE fu2.id END AS mother_uid, - (CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days + (CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days, + ( + SELECT COUNT(*)::int + FROM falukant_data.child_relation cr + WHERE cr.father_character_id = (CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END) + AND cr.mother_character_id = (CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END) + ) AS shared_children_count FROM falukant_data.relationship r JOIN falukant_type.relationship r2 ON r2.id = r.relationship_type_id AND r2.tr = 'married' @@ -1743,6 +1759,7 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#" p.father_uid, p.mother_uid, p.mother_age_days, + p.shared_children_count, CASE WHEN p.mother_age_days < 4380 THEN 0.005 WHEN p.mother_age_days < 4745 THEN 0.30 @@ -1774,12 +1791,24 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#" WHEN p.mother_age_days < 17520 THEN 0.015 WHEN p.mother_age_days < 18250 THEN 0.005 ELSE 0.001 - END AS prob_year + END AS prob_year_raw FROM paired p ), + mother_age_final AS ( + SELECT + ma.*, + CASE + WHEN ma.shared_children_count = 0 + AND ma.mother_age_days >= 6570 + AND ma.mother_age_days < 16000 + THEN GREATEST(ma.prob_year_raw, 1.0) + ELSE ma.prob_year_raw + END AS prob_year + FROM mother_age ma + ), conceivable AS ( SELECT ma.relationship_id - FROM mother_age ma + FROM mother_age_final ma WHERE ma.mother_age_days >= 4380 AND ma.mother_age_days < 18993 AND random() < (1 - POWER(1 - ma.prob_year, 1.0/8760.0)) @@ -1811,7 +1840,13 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#" CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id, CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid, CASE WHEN c1.gender = 'female' THEN fu1.id ELSE fu2.id END AS mother_uid, - (CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days + (CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days, + ( + SELECT COUNT(*)::int + FROM falukant_data.child_relation cr + WHERE cr.father_character_id = (CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END) + AND cr.mother_character_id = (CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END) + ) AS shared_children_count FROM falukant_data.relationship r JOIN falukant_type.relationship r2 ON r2.id = r.relationship_type_id AND r2.tr = 'married' @@ -1835,6 +1870,7 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#" p.father_uid, p.mother_uid, p.mother_age_days, + p.shared_children_count, CASE WHEN p.mother_age_days < 4380 THEN 0.005 WHEN p.mother_age_days < 4745 THEN 0.30 @@ -1866,8 +1902,20 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#" WHEN p.mother_age_days < 17520 THEN 0.015 WHEN p.mother_age_days < 18250 THEN 0.005 ELSE 0.001 - END AS prob_year + END AS prob_year_raw FROM paired p + ), + mother_age_final AS ( + SELECT + ma.*, + CASE + WHEN ma.shared_children_count = 0 + AND ma.mother_age_days >= 6570 + AND ma.mother_age_days < 16000 + THEN GREATEST(ma.prob_year_raw, 1.0) + ELSE ma.prob_year_raw + END AS prob_year + FROM mother_age ma ) SELECT father_cid, @@ -1879,7 +1927,7 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#" mother_uid, mother_age_days, (1 - POWER(1 - prob_year, 1.0/8760.0)) * 100 AS prob_pct - FROM mother_age + FROM mother_age_final WHERE mother_age_days >= 4380 AND mother_age_days < 18993 AND random() < (1 - POWER(1 - prob_year, 1.0/8760.0)); diff --git a/src/worker/user_character.rs b/src/worker/user_character.rs index 8d09c0b..e0d3bb6 100644 --- a/src/worker/user_character.rs +++ b/src/worker/user_character.rs @@ -33,7 +33,8 @@ use crate::worker::sql::{ QUERY_UPDATE_USER_MONEY, QUERY_GET_FALUKANT_USER_ID, QUERY_AUTOBATISM, - QUERY_CLEAR_MARRIAGE_PREGNANCY_DUE, QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES, + QUERY_CLEAR_MARRIAGE_PREGNANCY_DUE, QUERY_CLEAR_STALE_MARRIAGE_PREGNANCY_DUE, + QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES, QUERY_GET_MARRIAGE_BIRTH_DELIVERIES, QUERY_MARRIAGE_PREGNANCY_COLUMN_READY, QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE, QUERY_INSERT_CHILD, @@ -181,6 +182,7 @@ impl UserCharacterWorker { /// Ehe-Schwangerschaft: höchstens einmal pro Stunde (Daemon-Schleife ~1 s). /// Konzeption setzt `marriage_pregnancy_due_at` (+5 Tage); Geburt über `QUERY_GET_MARRIAGE_BIRTH_DELIVERIES`. /// Stündliche Zerlegung der Jahres-Wahrscheinlichkeit (`1/8760`), siehe `QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE`. + /// Erstes gemeinsames Kind: Jahreswahrscheinlichkeit mindestens 1.0 (18–~44 J.), siehe `sql.rs`. fn maybe_run_hourly_pregnancies(&mut self) { let now = Instant::now(); let should_run = match self.last_pregnancy_run { @@ -569,6 +571,9 @@ impl UserCharacterWorker { self.process_single_marriage_delivery(&mut conn, &row)?; } + conn.prepare("clear_stale_preg", QUERY_CLEAR_STALE_MARRIAGE_PREGNANCY_DUE)?; + conn.execute("clear_stale_preg", &[])?; + conn.prepare("try_conception", QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE)?; conn.execute("try_conception", &[])?; } else { @@ -590,7 +595,7 @@ impl UserCharacterWorker { let relationship_id = parse_i32(row, "relationship_id", -1); let father_cid = parse_i32(row, "father_cid", -1); let mother_cid = parse_i32(row, "mother_cid", -1); - if relationship_id < 0 || father_cid < 0 || mother_cid < 0 { + if father_cid < 0 || mother_cid < 0 { return Ok(()); }