Skip to content

Commit

Permalink
Merge pull request #10246 from patrikolesen/18_uruguay_pre_alpha_loan…
Browse files Browse the repository at this point in the history
…s_rebased

[18Uruguay] Adding basic support for loans
  • Loading branch information
michaeljb authored Feb 4, 2024
2 parents bffaf7d + b3a5d61 commit 53db65d
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 1 deletion.
115 changes: 114 additions & 1 deletion lib/engine/game/g_18_uruguay/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
require_relative 'companies'
require_relative 'trains'
require_relative 'phases'
require_relative 'loans'
require_relative '../../loan'

module Engine
module Game
Expand All @@ -21,6 +23,8 @@ class Game < Game::Base
include Companies
include Trains
include Phases
include InterestOnLoans
include Loans

EBUY_SELL_MORE_THAN_NEEDED = true
GOODS_TRAIN = 'Goods'
Expand Down Expand Up @@ -53,6 +57,7 @@ class Game < Game::Base
RPTLA_STARTING_PRICE = 50
RPTLA_STOCK_ROW = 11
NUMBER_OF_LOANS = 99
LOAN_VALUE = 100

GAME_END_CHECK = { custom: :one_more_full_or_set }.freeze

Expand Down Expand Up @@ -259,13 +264,15 @@ def operating_round(round_num)
G18Uruguay::Step::CattleFarm,
Engine::Step::SpecialTrack,
Engine::Step::SpecialToken,
G18Uruguay::Step::TakeLoanBuyCompany,
Engine::Step::HomeToken,
Engine::Step::Track,
Engine::Step::Token,
G18Uruguay::Step::Token,
Engine::Step::Route,
Engine::Step::Dividend,
Engine::Step::DiscardTrain,
Engine::Step::BuyTrain,
[G18Uruguay::Step::TakeLoanBuyCompany, { blocks: true }],
], round_num: round_num)
end

Expand All @@ -290,6 +297,112 @@ def abilities_ignore_owner(entity, type = nil, time: nil, on_phase: nil, passive

active_abilities
end

# Nationalized
def nationalized?
@nationalized
end

def operating_order
super.sort.partition { |c| c.type != :bank }.flatten
end

# Loans
def float_corporation(corporation)
return if corporation == @rptla
return unless @loans

amount = corporation.par_price.price * 5
@bank.spend(amount, corporation)
@log << "#{corporation.name} receives #{format_currency(corporation.cash)}"
take_loan(corporation, @loans[0]) if @loans.size.positive? && !nationalized?
end

def perform_ebuy_loans(entity, remaining)
ebuy = true
while remaining.positive? && entity.share_price.price != 0
# if at max loans, company goes directly into receiverhsip
if @loans.empty?
@log << "There are no more loans available to force buy a train, #{entity.name} goes into receivership"
break
end
loan = @loans.first
take_loan(entity, loan, ebuy: ebuy)
remaining -= loan.amount
end
end

# Goods
def number_of_goods_at_harbor
ability = @rptla.abilities.find { |a| a.type == 'Goods' }
ability.description[/\d+/].to_i
end

def add_good_to_rptla
ability = @rptla.abilities.find { |a| a.type == 'Goods' }
count = number_of_goods_at_harbor + 1
ability.description = GOODS_DESCRIPTION_STR + count.to_s
end

def remove_goods_from_rptla(goods_count)
return if number_of_goods_at_harbor < goods_count

ability = @rptla.abilities.find { |a| a.type == 'Goods' }
count = number_of_goods_at_harbor - goods_count
ability.description = GOODS_DESCRIPTION_STR + count.to_s
end

def check_distance(route, visits, train = nil)
@round.current_routes[route.train] = route
if route.corporation != @rptla && !nationalized?
raise RouteTooLong, 'Need to have goods to run to port' unless check_for_port_if_goods_attached(route,
visits)
raise RouteTooLong, 'Goods needs to be shipped to port' unless check_for_goods_if_run_to_port(route,
visits)
end
raise RouteTooLong, '4D trains cannot deliver goods' if route.train.name == '4D' && visits_include_port?(visits)

super
end

# Revenue
def revenue_str(route)
return super unless route&.corporation == @rptla

'Ship'
end

def routes_revenue(route, corporation)
revenue = super
return revenue if @rptla != corporation

revenue += (corporation.loans.size.to_f / 2).floor * 10
revenue
end

def routes_subsidy(route, corporation)
return super if @rptla != corporation

(corporation.loans.size.to_f / 2).ceil * 10
end

def revenue_for(route, stops)
revenue = super
revenue *= 2 if route.train.name == '4D'
revenue *= 2 if final_operating_round?
return revenue unless route&.corporation == @rptla

train = route.train
revenue * goods_on_train(train)
end

def or_round_finished
corps_pay_interest unless nationalized?
end

def final_operating_round?
@final_turn == @turn
end
end
end
end
Expand Down
91 changes: 91 additions & 0 deletions lib/engine/game/g_18_uruguay/loans.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# frozen_string_literal: true

module Engine
module Game
module G18Uruguay
module Loans
def init_loans
Array.new(self.class::NUMBER_OF_LOANS) { |id| Loan.new(id, self.class::LOAN_VALUE) }
end

def maximum_loans(entity)
entity == @rptla ? self.class::NUMBER_OF_LOANS : entity.num_player_shares
end

def loans_due_interest(entity)
entity.loans.size
end

def interest_owed(entity)
return 0 if entity == @rptla

10 * loans_due_interest(entity)
end

def interest_owed_for_loans(count)
10 * count
end

def can_take_loan?(entity, ebuy: nil)
# return false if nationalized?
return false if entity == @rlpta
return true if ebuy

entity.corporation? &&
entity.loans.size < maximum_loans(entity) &&
!@loans.empty?
end

def take_loan(entity, loan, ebuy: nil)
raise GameError, "Cannot take more than #{maximum_loans(entity)} loans" unless can_take_loan?(entity, ebuy: ebuy)

# raise GameError, "Not allowed to take loans after nationalization" if @game.nationalized?

@bank.spend(loan.amount, entity)
entity.loans << loan
@rptla.loans << loan.dup
@loans.delete(loan)
@log << "#{entity.name} takes a loan and receives #{format_currency(loan.amount)}"
end

def payoff_loan(entity, number_of_loans, spender)
total_amount = 0
number_of_loans.times do |_i|
paid_loan = entity.loans.pop
amount = paid_loan.amount
total_amount += amount
spender.spend(amount, @bank)
end
@log << "#{spender.name} payoff #{number_of_loans} loan(s) for #{entity.name} and pays #{total_amount}"
end

def adjust_stock_market_loan_penalty(entity)
delta = entity.loans.size - maximum_loans(entity)
return unless delta.positive?

delta.times do |_i|
@stock_market.move_left(entity)
end
end

def take_loan_if_needed_for_interest!(entity)
owed = interest_owed(entity)
return if owed.zero?

remaining = owed - entity.cash
perform_ebuy_loans(entity, remaining + 10) if remaining.positive?
end

def corps_pay_interest
corps = @round.entities.select { |entity| entity.loans.size.positive? && entity != @rptla }
corps.each do |corp|
next if corp.closed?

take_loan_if_needed_for_interest!(corp)
pay_interest!(corp)
end
end
end
end
end
end
61 changes: 61 additions & 0 deletions lib/engine/game/g_18_uruguay/step/take_loan_buy_company.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require_relative '../../../step/base'

module Engine
module Game
module G18Uruguay
module Step
class TakeLoanBuyCompany < Engine::Step::BuyCompany
def actions(entity)
return [] if !entity.corporation? || entity != current_entity
return [] if entity == @game.rptla
return [] unless can_buy_company?(entity)

actions = []
actions << 'take_loan' if @game.can_take_loan?(entity) && !@round.loan_taken && !@game.nationalized?
actions << 'buy_company' if can_buy_company?(entity)
actions << 'pass' if can_buy_company?(entity) || (@game.can_take_loan?(entity) && !@round.loan_taken)

actions
end

def can_buy_company?(entity)
companies = @game.purchasable_companies(entity)

entity == current_entity &&
@game.phase.status.include?('can_buy_companies') &&
companies.any? &&
companies.map(&:min_price).min <= buying_power(entity)
end

def description
'Take Loans or Buy Company'
end

def blocks?
true
end

def process_take_loan(action)
entity = action.entity
@game.take_loan(entity, action.loan)
@round.loan_taken = true
end

def round_state
super.merge({
# has player taken a loan this or already
loan_taken: false,
})
end

def setup
# you can only take one loan per OR turn
@round.loan_taken = false
end
end
end
end
end
end
31 changes: 31 additions & 0 deletions lib/engine/game/g_18_uruguay/step/token.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require_relative '../../../step/token'

module Engine
module Game
module G18Uruguay
module Step
class Token < Engine::Step::Token
def actions(entity)
return [] if entity == @game.rptla
return [] if @game.final_operating_round?

@round.loan_taken |= false
actions = super.map(&:clone)
if !actions.empty? && @game.can_take_loan?(entity) && !@round.loan_taken && !@game.nationalized?
actions << 'take_loan'
end
actions
end

def process_take_loan(action)
entity = action.entity
@game.take_loan(entity, action.loan) unless @round.loan_taken
@round.loan_taken = true
end
end
end
end
end
end

0 comments on commit 53db65d

Please sign in to comment.