Skip to content

Commit

Permalink
Merge pull request #10199 from michaeljb/18RoyalGorge-auction
Browse files Browse the repository at this point in the history
[18RoyalGorge] 1861-style auction, priority deal
  • Loading branch information
michaeljb authored Jan 28, 2024
2 parents 0b2d4be + 48cc12e commit 87c1a48
Show file tree
Hide file tree
Showing 2 changed files with 216 additions and 0 deletions.
37 changes: 37 additions & 0 deletions lib/engine/game/g_18_royal_gorge/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ def status_array(corporation)
end
end

def new_auction_round
Engine::Round::Auction.new(self, [
G18RoyalGorge::Step::SingleItemAuction,
])
end

def stock_round
Round::Stock.new(self, [
Engine::Step::DiscardTrain,
Expand Down Expand Up @@ -143,6 +149,37 @@ def par_prices
@stock_market.share_prices_with_types(@available_par_groups)
end

def next_round!
@round =
case @round
when Round::Stock
@operating_rounds = @phase.operating_rounds
reorder_players
new_operating_round
when Round::Operating
if @round.round_num < @operating_rounds
or_round_finished
new_operating_round(@round.round_num + 1)
else
@turn += 1
or_round_finished
or_set_finished
new_stock_round
end
when Round::Auction
# reorder as normal first so that if there is a tie for most cash,
# the player who would be first with :after_last_to_act turn order
# gets the tiebreaker
reorder_players

# most cash goes first, but keep same relative order; don't
# reorder by descending cash
@players.rotate!(@players.index(@players.max_by(&:cash)))

new_stock_round
end
end

def corporation_opts
@players.size == 2 ? { max_ownership_percent: 70 } : {}
end
Expand Down
179 changes: 179 additions & 0 deletions lib/engine/game/g_18_royal_gorge/step/single_item_auction.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# frozen_string_literal: true

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

module Engine
module Game
module G18RoyalGorge
module Step
class SingleItemAuction < Engine::Step::Base
# Auction which puts up the first item
# if nobody buys switches to a dutch auction until a player purchases
include Engine::Step::PassableAuction
ACTIONS = %w[bid pass].freeze

def actions(entity)
return [] if @companies.empty?

entity == current_entity ? ACTIONS : []
end

attr_reader :companies

def description
'Bid on Companies'
end

def available
@companies
end

def active_auction
company = @auctioning
bids = @bids[company]
yield company, bids
end

def initial_auction_entities
entities.rotate(@round.entity_index)
end

def active_entities
winning_bid = highest_bid(@auctioning)
if winning_bid
[@active_bidders[(@active_bidders.index(winning_bid.entity) + 1) % @active_bidders.size]]
else
[@active_bidders.reject { |e| @declined_bids.include?(e) }&.first]
end
end

def process_pass(action)
entity = action.entity

winning_bid = highest_bid(@auctioning)
if winning_bid || @dutch_mode
pass_auction(entity)
else
@game.log << "#{entity.name} declined to bid on #{@auctioning.name}"
@declined_bids << entity
if @declined_bids.size == @active_bidders.size
enter_dutch(@auctioning)
@declined_bids = []
end
end
resolve_bids
end

def process_bid(action)
@declined_bids = []
add_bid(action)
end

def min_increment
@game.class::MIN_BID_INCREMENT
end

def setup
setup_auction
@companies = @game.companies.dup

auction_entity_log(@companies.first) unless @companies.empty?
end

def starting_bid(company)
company.min_bid
end

def auction_entity_log(entity)
@dutch_mode = false
@declined_bids = []
@game.log << "#{entity.name} is up for auction, minimum bid is #{@game.format_currency(min_bid(entity))}"
auction_entity(entity)
end

def min_bid(company)
return unless company

return company.min_bid if @dutch_mode

return starting_bid(company) unless @bids[company].any?

high_bid = highest_bid(company)
(high_bid.price || company.min_bid) + min_increment
end

def may_purchase?(_company)
@dutch_mode
end

def max_bid(player, _company)
player.cash
end

private

def add_bid(bid)
entity = bid.entity
price = bid.price

if @dutch_mode
@active_bidders.select! { |e| e == bid.entity }
else
@log << "#{entity.name} bids #{@game.format_currency(price)} for #{bid.company.name}"
end
super
resolve_bids
end

def enter_dutch(company)
# Switch to dutch auction
tense = @dutch_mode ? 'bought' : 'bid on'
company.discount += 5
@log << "Nobody #{tense} #{company.name}, reducing price to #{@game.format_currency(company.min_bid)}"
@dutch_mode = true
end

def win_bid(winner, company)
return enter_dutch(company) unless winner

player = winner.entity
company = winner.company
price = winner.price
company.owner = player
player.companies << company

player.spend(price, @game.bank) if price.positive?
@companies.delete(company)
@log << "#{player.name} wins the auction for #{company.name} "\
"with a bid of #{@game.format_currency(price)}"
@game.after_buy_company(player, company, price)
end

def post_win_bid(winner, company)
# Avoid startup infinite recursion
return if @game.players.empty?

if winner
@round.goto_entity!(winner.entity)
@round.next_entity_index!
if @companies.empty?
pass!
else
auction_entity_log(@companies.first)
end
else
auction_entity(company)
if company.min_bid.zero?
# Fake the first player making a bid
bid = Engine::Action::Bid.new(@active_bidders.first, company: company, price: company.min_bid)
add_bid(bid)
end
resolve_bids
end
end
end
end
end
end
end

0 comments on commit 87c1a48

Please sign in to comment.