diff --git a/lib/engine/game/g_1826/entities.rb b/lib/engine/game/g_1826/entities.rb index 5a1690605d..d419dc272c 100644 --- a/lib/engine/game/g_1826/entities.rb +++ b/lib/engine/game/g_1826/entities.rb @@ -106,7 +106,7 @@ module Entities name: 'Chemin de Fer de Paris', logo: '1826/Paris', simple_logo: '1826/NYC.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], coordinates: 'G9', city: 2, type: 'five_share', @@ -118,7 +118,7 @@ module Entities name: 'Chemin de Fer de Paris-Orléans', logo: '1826/PO', simple_logo: '1826/PO.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], coordinates: 'G9', city: 0, type: 'five_share', @@ -131,7 +131,7 @@ module Entities name: 'Chemin de Fer du Nord', logo: '1826/N', simple_logo: '1826/N.alt', - tokens: [0, 40, 100], + tokens: [0, 20, 20], coordinates: 'G9', city: 3, type: 'five_share', @@ -143,7 +143,7 @@ module Entities name: "Chemin de Fer de l'Est", logo: '1826/Est', simple_logo: '1826/Est.alt', - tokens: [0, 40, 100], + tokens: [0, 20, 20], coordinates: 'G9', city: 4, type: 'five_share', @@ -155,7 +155,7 @@ module Entities name: "Chemin de Fer de l'Ouest", logo: '1846/O', simple_logo: '1826/O.alt', - tokens: [0, 40, 100], + tokens: [0, 20, 20], coordinates: 'G9', city: 1, type: 'five_share', @@ -168,7 +168,7 @@ module Entities name: 'Chemin de Fer du Midi', logo: '1826/Midi', simple_logo: '1826/Midi.alt', - tokens: [0, 40], + tokens: [0, 20], coordinates: 'M3', city: 0, type: 'five_share', @@ -180,7 +180,7 @@ module Entities name: 'Grand Central de France', logo: '1826/GC', simple_logo: '1826/GC.alt', - tokens: [0, 40], + tokens: [0, 20], coordinates: 'L14', type: 'five_share', color: 'lightBlue', @@ -192,7 +192,7 @@ module Entities name: 'Chemin de Fer Paris-Lyon-Méditerranée', logo: '1826/PLM', simple_logo: '1826/PLM.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], coordinates: 'G9', city: 5, type: 'five_share', @@ -205,7 +205,7 @@ module Entities name: "Chemin de Fer d'Alsace", logo: '1826/Al', simple_logo: '1826/Al.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], coordinates: 'G19', type: 'five_share', color: 'blue', @@ -216,7 +216,7 @@ module Entities name: "Chemins de Fer de l'Etat Belge", logo: '1826/B', simple_logo: '1826/B.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], coordinates: 'C13', type: 'ten_share', color: 'green', @@ -235,7 +235,7 @@ module Entities name: "Chemins de Fer de l'Etat", logo: '1826/Etat', simple_logo: '1826/Etat.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], type: 'ten_share', shares: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], color: 'black', @@ -258,7 +258,7 @@ module Entities name: 'Société Nationale des Chemins de Fer Français', logo: '1826/SNCF', simple_logo: '1826/SNCF.alt', - tokens: [0, 40, 100, 100], + tokens: [0, 20, 20, 20], type: 'ten_share', shares: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10], color: 'gray', diff --git a/lib/engine/game/g_1826/game.rb b/lib/engine/game/g_1826/game.rb index e623179b4d..921e903084 100644 --- a/lib/engine/game/g_1826/game.rb +++ b/lib/engine/game/g_1826/game.rb @@ -9,6 +9,8 @@ module Engine module Game module G1826 class Game < Game::Base + attr_reader :recently_floated, :can_buy_trains + include_meta(G1826::Meta) include Entities include Map @@ -24,11 +26,13 @@ class Game < Game::Base brightGreen: '#6ec037', violet: '#601d39', sand: '#c89432') - TRACK_RESTRICTION = :permissive + TRACK_RESTRICTION = :semi_restrictive SELL_BUY_ORDER = :sell_buy + SELL_AFTER = :operate TILE_RESERVATION_BLOCKS_OTHERS = :always HOME_TOKEN_TIMING = :float CURRENCY_FORMAT_STR = 'F%s' + BANKRUPTCY_ENDS_GAME_AFTER = :all_but_one BANK_CASH = 12_000 @@ -60,6 +64,7 @@ class Game < Game::Base train_limit: { five_share: 1, ten_share: 3 }, tiles: %i[yellow green], operating_rounds: 2, + status: ['can_buy_trains'], }, { name: '10H', @@ -67,6 +72,7 @@ class Game < Game::Base train_limit: 2, tiles: %i[yellow green brown], operating_rounds: 3, + status: ['can_buy_trains'], }, { name: 'E', @@ -74,6 +80,7 @@ class Game < Game::Base train_limit: 2, tiles: %i[yellow green brown blue], operating_rounds: 3, + status: ['can_buy_trains'], }, { name: 'TVG', @@ -81,18 +88,25 @@ class Game < Game::Base train_limit: 2, tiles: %i[yellow green brown blue gray], operating_rounds: 3, + status: ['can_buy_trains'], }].freeze TRAINS = [ { name: '2H', distance: 2, price: 100, rusts_on: '6H', num: 8 }, { name: '4H', distance: 4, price: 200, rusts_on: '10H', num: 7 }, - { name: '6H', distance: 6, price: 300, rusts_on: 'E', num: 6 }, + { + name: '6H', + distance: 6, + price: 300, + rusts_on: 'E', + num: 6, + events: [{ 'type' => 'can_buy_trains' }], + }, { name: '10H', distance: 10, price: 600, num: 5, - events: [{ 'type' => 'close_companies' }], }, { name: 'E', @@ -119,8 +133,16 @@ class Game < Game::Base EVENTS_TEXT = Base::EVENTS_TEXT.merge( 'remove_abilities' => ['Mail Token Removed'], + 'can_buy_trains' => ['Corporations can buy trains from other corporations'], ).freeze + # Corps may lay two yellow tiles on their first OR + def tile_lays(entity) + lays = [{ lay: true, upgrade: true }] + lays << { lay: :not_if_upgraded, upgrade: false } if @recently_floated&.include?(entity) + lays + end + def operating_round(round_num) Round::Operating.new(self, [ Engine::Step::Bankrupt, @@ -130,11 +152,11 @@ def operating_round(round_num) Engine::Step::BuyCompany, Engine::Step::HomeToken, Engine::Step::Track, - Engine::Step::Token, + G1826::Step::Token, Engine::Step::Route, - Engine::Step::Dividend, + G1826::Step::Dividend, Engine::Step::DiscardTrain, - Engine::Step::BuyTrain, + G1826::Step::BuyTrain, [Engine::Step::BuyCompany, { blocks: true }], ], round_num: round_num) end @@ -143,11 +165,18 @@ def regie @regie ||= company_by_id('P2') end + # minimum bid increment in the auction + def min_increment + 5 + end + def revenue_for(route, stops) revenue = super revenue += 10 if route.corporation.assigned?(regie.id) && stops.find { |s| s.hex.assigned?(regie.id) } + raise GameError, 'Route visits same hex twice' if route.hexes.size != route.hexes.uniq.size + # Add code here for TGV train bonuses revenue end diff --git a/lib/engine/game/g_1826/step/buy_train.rb b/lib/engine/game/g_1826/step/buy_train.rb new file mode 100644 index 0000000000..30f74c0b3b --- /dev/null +++ b/lib/engine/game/g_1826/step/buy_train.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative '../../../step/base' +require_relative '../../../step/buy_train' + +module Engine + module Game + module G1826 + module Step + class BuyTrain < Engine::Step::BuyTrain + def buyable_trains(entity) + # Can't buy trains from other corporations until phase 6H + return super if @game.phase.status.include?('can_buy_trains') + + super.select(&:from_depot?) + end + end + end + end + end +end diff --git a/lib/engine/game/g_1826/step/dividend.rb b/lib/engine/game/g_1826/step/dividend.rb new file mode 100644 index 0000000000..9290646494 --- /dev/null +++ b/lib/engine/game/g_1826/step/dividend.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative '../../../step/dividend' +require_relative '../../../step/half_pay' + +module Engine + module Game + module G1826 + module Step + class Dividend < Engine::Step::Dividend + DIVIDEND_TYPES = %i[payout half withhold].freeze + include Engine::Step::HalfPay + + def share_price_change(entity, revenue = 0) + price = entity.share_price.price + + if revenue.zero? + { share_direction: :left, share_times: 1 } + elsif revenue < price + {} + elsif revenue >= price + { share_direction: :right, share_times: 1 } + end + end + end + end + end + end +end diff --git a/lib/engine/game/g_1826/step/token.rb b/lib/engine/game/g_1826/step/token.rb new file mode 100644 index 0000000000..f71d38131b --- /dev/null +++ b/lib/engine/game/g_1826/step/token.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative '../../../step/token' + +module Engine + module Game + module G1826 + module Step + class Token < Engine::Step::Token + def process_place_token(action) + entity = action.entity + token = action.token + city = action.city + hex = city.hex + + if @game.loading + token.price = action.cost + else + # Verify there is a route before spending the time to find the shortest path + check_connected(entity, city, hex) + token.price = token_cost(entity, token, hex) + action.cost = token.price + end + + if token.price > entity.cash + raise GameError, "#{entity.name} has #{@game.format_currency(entity.cash)} and cannot spend " \ + "#{@game.format_currency(token.price)} to lay token" + end + + super(action) + end + + def token_cost(entity, token, hex) + min_distance = 999 + + tokened_hexes = entity.tokens.select(&:used).map(&:hex) + hex.tile.nodes.first&.walk(corporation: entity) do |path, visited_paths, _visited| + min_distance = [min_distance, distance(visited_paths) - 1].min if tokened_hexes.include?(path.nodes&.first&.hex) + end + + token.price * min_distance + end + + def distance(path) + path.keys.map(&:hex).uniq.count + end + end + end + end + end +end