From 8afaea4a5d9ef6d77334c9961f1cd818212a1c5d Mon Sep 17 00:00:00 2001 From: Michael Brandt Date: Mon, 2 Dec 2024 16:37:22 -0700 Subject: [PATCH] [1822 family] fix Mail Contract interactions with permanent L-trains Changed behavior: * 1822: Mail Contract no longer pays with LP-train running to a city and town * 1822CA: Large Mail Contract no longer pays with LP-train running to a city and town * 1822CA: Small Mail Contract now pays with LP-train running to just a city * Fixes #11147 * 1822MX: Mail Contract no longer pays with LP-train running to a city and town Errata references: * https://boardgamegeek.com/thread/2640241/article/40423428#40423428 * https://boardgamegeek.com/thread/2640241/article/43680986#43680986 * https://docs.google.com/document/d/1puHQJV4eLeunOtu_RyqAT-_mBCI93u8dqSBNwWMsAiE/ --- lib/engine/game/g_1822/game.rb | 31 ++++++++++++------ lib/engine/game/g_1822_ca/game.rb | 45 +++++++++++---------------- lib/engine/game/g_1822_mx/entities.rb | 14 ++++----- lib/engine/game/g_1822_mx/game.rb | 2 -- lib/engine/game/g_1822_pnw/game.rb | 6 ++++ 5 files changed, 51 insertions(+), 47 deletions(-) diff --git a/lib/engine/game/g_1822/game.rb b/lib/engine/game/g_1822/game.rb index d88bfb300a..5884b96bbb 100644 --- a/lib/engine/game/g_1822/game.rb +++ b/lib/engine/game/g_1822/game.rb @@ -879,14 +879,17 @@ def train_help(_entity, runnable_trains, _routes) 'the value of its destination station. This only applies to one train per operating turn.' end - if mail_contracts - help << 'Mail contract(s) gives a subsidy equal to one half of the base value of the start and end '\ - 'stations from one of the trains operated. Doubled values (for E trains or destination tokens) '\ - 'do not count.' - end + help << train_help_mail_contracts if mail_contracts + help end + def train_help_mail_contracts + 'Mail contract(s) gives a subsidy equal to one half of the base value of the start and end '\ + 'stations from one of the trains operated. Doubled values (for E trains or destination tokens) '\ + 'do not count. L-trains cannot use mail contracts.' + end + def init_companies(players) game_companies.map do |company| next if players.size < (company[:min_players] || 0) @@ -1744,13 +1747,21 @@ def mail_contract_bonus(entity, routes) mail_bonuses = routes.map do |r| stops = r.visited_stops next if stops.size.zero? - next if stops.size < 2 && !self.class::LOCAL_TRAIN_CAN_CARRY_MAIL - first = stops.first.route_base_revenue(r.phase, r.train) / 2 - last = stops.size < 2 ? 0 : stops.last.route_base_revenue(r.phase, r.train) / 2 - { route: r, subsidy: first + last } + # LP does not work with Mail Contract, even if it stops at a + # town. An exception for 1822PNW is made via + # `LOCAL_TRAIN_CAN_CARRY_MAIL` + # + # https://boardgamegeek.com/thread/2640241/article/40423428#40423428 + # https://boardgamegeek.com/thread/2640241/article/43680986#43680986 + # https://docs.google.com/document/d/1puHQJV4eLeunOtu_RyqAT-_mBCI93u8dqSBNwWMsAiE/ + next if !self.class::LOCAL_TRAIN_CAN_CARRY_MAIL && (r.train.name[0] == 'L' || stops.size < 2) + + first = stops.first.route_base_revenue(r.phase, r.train) + last = stops.size < 2 ? 0 : stops.last.route_base_revenue(r.phase, r.train) + { route: r, subsidy: (first + last) / 2 } end.compact - mail_bonuses.sort_by { |v| v[:subsidy] }.reverse.take(mail_contracts) + mail_bonuses.sort_by { |v| -v[:subsidy] }.take(mail_contracts) end def move_exchange_token(entity) diff --git a/lib/engine/game/g_1822_ca/game.rb b/lib/engine/game/g_1822_ca/game.rb index 282d745888..056116b5da 100644 --- a/lib/engine/game/g_1822_ca/game.rb +++ b/lib/engine/game/g_1822_ca/game.rb @@ -393,53 +393,38 @@ def upgrades_to_correct_label?(from, to) end def mail_contract_bonus(entity, routes) - mail_contracts = entity.companies.count { |c| self.class::PRIVATE_MAIL_CONTRACTS.include?(c.id) } + # "Large Mail Contract" is the same as the standard 1822 family "Mail Contract" + large_bonuses = super + + large_contracts = large_bonuses.size small_contracts = entity.companies.count { |c| self.class::PRIVATE_SMALL_MAIL_CONTRACTS.include?(c.id) } - all_contracts = mail_contracts + small_contracts + all_contracts = large_contracts + small_contracts return [] unless all_contracts.positive? - mail_bonuses = - if mail_contracts.positive? - bonuses = routes.map do |r| - stops = r.visited_stops - next if stops.size < 2 - - first = stops.first.route_base_revenue(r.phase, r.train) - last = stops.last.route_base_revenue(r.phase, r.train) - { route: r, subsidy: (first + last) / 2 } - end - bonuses.compact.sort_by { |v| -v[:subsidy] }.take(mail_contracts) - else - [] - end - small_bonuses = if small_contracts.positive? subsidy = small_mail_subsidy - routes.map do |r| - next if r.visited_stops.size < 2 - { route: r, subsidy: subsidy } end.compact.take(small_contracts) else [] end - if routes.size >= all_contracts || mail_bonuses.empty? || small_bonuses.empty? - mail_bonuses + small_bonuses + if routes.size >= all_contracts || large_bonuses.empty? || small_bonuses.empty? + large_bonuses + small_bonuses else - mail_index = 0 + large_index = 0 small_index = 0 routes.map do - mail = mail_bonuses[mail_index] || { subsidy: 0 } + large = large_bonuses[large_index] || { subsidy: 0 } small = small_bonuses[small_index] || { subsidy: 0 } - if small[:subsidy] > mail[:subsidy] + if small[:subsidy] > large[:subsidy] small_index += 1 small else - mail_index += 1 - mail + large_index += 1 + large end end end @@ -470,6 +455,12 @@ def train_help(entity, runnable_trains, _routes) help end + def train_help_mail_contracts + 'Large mail contract(s) gives a subsidy equal to one half of the base value of the start and end '\ + 'stations from one of the trains operated. Doubled values (for E trains or destination tokens) '\ + 'do not count. L-trains cannot use large mail contracts.' + end + def revenue_for(route, stops) revenue = super diff --git a/lib/engine/game/g_1822_mx/entities.rb b/lib/engine/game/g_1822_mx/entities.rb index 9c4ea5856a..81f27cac3b 100644 --- a/lib/engine/game/g_1822_mx/entities.rb +++ b/lib/engine/game/g_1822_mx/entities.rb @@ -273,12 +273,11 @@ module Entities desc: 'MAJOR, Phase 3. Mail Contract. After running trains, the owning company receives income '\ 'into its treasury equal to one half of the base value of the start and end '\ 'stations from one of the trains operated. Modifications to values (for '\ - 'E-trains, a 3/2-train, or destination tokens) do not apply. An L-train may '\ - 'deliver mail within a single city. The company is not required to maximize '\ - 'the dividend from its run if it wishes to maximize its revenue from the mail '\ + 'E-trains, a 3/2-train, or destination tokens) do not apply. The company is not required to '\ + 'maximize the dividend from its run if it wishes to maximize its revenue from the mail '\ 'contract by stopping at a large city and not running beyond it to include '\ 'towns. A company that owns more than one Mail Contract may not use '\ - 'more than one on any train.', + 'more than one on any train. Cannot be used with an L-train.', abilities: [], }, { @@ -289,12 +288,11 @@ module Entities desc: 'MAJOR, Phase 3. Mail Contract. After running trains, the owning company receives income '\ 'into its treasury equal to one half of the base value of the start and end '\ 'stations from one of the trains operated. Modifications to values (for '\ - 'E-trains, a 3/2-train, or destination tokens) do not apply. An L-train may '\ - 'deliver mail within a single city. The company is not required to maximize '\ - 'the dividend from its run if it wishes to maximize its revenue from the mail '\ + 'E-trains, a 3/2-train, or destination tokens) do not apply. The company is not required to '\ + 'maximize the dividend from its run if it wishes to maximize its revenue from the mail '\ 'contract by stopping at a large city and not running beyond it to include '\ 'towns. A company that owns more than one Mail Contract may not use '\ - 'more than one on any train.', + 'more than one on any train. Cannot be used with an L-train.', abilities: [], }, { diff --git a/lib/engine/game/g_1822_mx/game.rb b/lib/engine/game/g_1822_mx/game.rb index d5fa431083..b988e7192e 100644 --- a/lib/engine/game/g_1822_mx/game.rb +++ b/lib/engine/game/g_1822_mx/game.rb @@ -59,8 +59,6 @@ class Game < G1822::Game PRIVATE_PHASE_REVENUE = %w[].freeze # Stub for 1822 specific code P7_REVENUE = [0, 0, 0, 20, 20, 40, 40, 60].freeze - LOCAL_TRAIN_CAN_CARRY_MAIL = true - # Don't run 1822 specific code for certain private companies COMPANY_LCDR = nil COMPANY_OSTH = nil diff --git a/lib/engine/game/g_1822_pnw/game.rb b/lib/engine/game/g_1822_pnw/game.rb index df112ddb58..1d2797a6f2 100644 --- a/lib/engine/game/g_1822_pnw/game.rb +++ b/lib/engine/game/g_1822_pnw/game.rb @@ -1244,6 +1244,12 @@ def game_end_check [:bank, @round.is_a?(Engine::Round::Operating) ? :full_or : :current_or] end end + + def train_help_mail_contracts + 'Mail contract(s) gives a subsidy equal to one half of the base value of the start and end '\ + 'stations from one of the trains operated. Doubled values (for E trains or destination tokens) '\ + 'do not count. L-trains can use mail contracts, even if they visit one city and no towns.' + end end end end