Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[18Uruguay] Adding basic support for loans #10208

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 122 additions & 4 deletions lib/engine/game/g_18_uruguay/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
require_relative 'companies'
require_relative 'trains'
require_relative 'phases'
require_relative 'goods'
require_relative 'loans'
require_relative 'step/route_rptla'
require_relative '../../loan'

module Engine
module Game
Expand All @@ -21,9 +25,11 @@ class Game < Game::Base
include Companies
include Trains
include Phases
include Goods
include InterestOnLoans
include Loans

EBUY_SELL_MORE_THAN_NEEDED = true
GOODS_TRAIN = 'Goods'

register_colors(darkred: '#ff131a',
red: '#d1232a',
Expand All @@ -39,7 +45,6 @@ class Game < Game::Base
SELL_BUY_ORDER = :sell_buy
TILE_RESERVATION_BLOCKS_OTHERS = true
CURRENCY_FORMAT_STR = '$U%d'
GOODS_DESCRIPTION_STR = 'Number of goods: '

MUST_BUY_TRAIN = :always

Expand All @@ -53,6 +58,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 @@ -99,6 +105,8 @@ class Game < Game::Base
'GOODS_CATTLE10' => '/icons/1846/meat.svg',
}.freeze

GOODS_TRAIN = 'Goods'
GOODS_DESCRIPTION_STR = 'Number of goods: '
PORTS = %w[E1 G1 I1 J4 K5 K7 K13].freeze
MARKET = [
%w[70 75 80 90 100p 110 125 150 175 200 225 250 275 300 325 350 375 400 425 450],
Expand Down Expand Up @@ -167,6 +175,7 @@ def cattle_farm

def setup
super
goods_setup
@rptla = @corporations.find { |c| c.id == 'RPTLA' }
@fce = @corporations.find { |c| c.id == 'FCE' }

Expand Down Expand Up @@ -259,13 +268,16 @@ 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,
Engine::Step::Route,
G18Uruguay::Step::Token,
G18Uruguay::Step::Route,
G18Uruguay::Step::RouteRptla,
Engine::Step::Dividend,
Engine::Step::DiscardTrain,
Engine::Step::BuyTrain,
[G18Uruguay::Step::TakeLoanBuyCompany, { blocks: true }],
], round_num: round_num)
end

Expand All @@ -290,6 +302,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
58 changes: 58 additions & 0 deletions lib/engine/game/g_18_uruguay/goods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

module Engine
module Game
module G18Uruguay
module Goods
def goods_setup
@pickup_hex_for_train = {}
end

def train_with_goods?(train)
return unless train

@pickup_hex_for_train.key?(train.id)
end

def attach_good_to_train(train, hex)
train.name += '+' + self.class::GOODS_TRAIN + '(' + hex.id + ')' if hex
@pickup_hex_for_train[train.id] = hex
end

def good_pickup_hex(train)
@pickup_hex_for_train[train.id]
end

def unload_good(train)
train.name = train.name.partition('+')[0] unless train.nil?
@pickup_hex_for_train.delete(train.id) if train_with_goods?(train)
end

def visits_include_port?(visits)
visits.any? { |visit| self.class::PORTS.include?(visit.hex.id) }
end

def route_include_port?(route)
route.hexes.any? { |hex| self.class::PORTS.include?(hex.id) }
end

def check_for_goods_if_run_to_port(route, visits)
true if route.corporation == @rptla
visits_include_port?(visits) || !train_with_goods?(route.train)
end

def check_for_port_if_goods_attached(route, visits)
true if route.corporation == @rptla
!visits_include_port?(visits) || train_with_goods?(route.train)
end

def goods_on_train(train)
m = train.name.match(/.*\+.*(?<count>\d+).*/)
return 0 if m.nil?

m[:count]&.to_i
end
end
end
end
end
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) &&
[email protected]?
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
Loading
Loading