Skip to content

Instantly share code, notes, and snippets.

@Hasstrup
Last active April 14, 2023 14:51
Show Gist options
  • Save Hasstrup/726d9940fc0032863a826be4512d585d to your computer and use it in GitHub Desktop.
Save Hasstrup/726d9940fc0032863a826be4512d585d to your computer and use it in GitHub Desktop.
# Implement the sections of code marked TODO above.
# Rules for bidding:
# Agents always bid in sequence, e.g. agent 1 bids first, then agent 2, etc.
# The bidding starts with the first agent deciding on their initial bid (which will be returned by `get_bid_increase`). The amount can also be 0.
# The next agent (e.g. agent 2) must then increase their bid so that their bid is as much as any other agent's bid (which so far is only agent 1), or be forced to withdraw from the bidding completely. (if the agent has withdrawn, they will be skipped from here onwards). They may also decide to bid more, in which case the next agent has to match that higher bid (or withdraw).
# The bidding will come around to agent 1 again if their current bid does not match the highest bid. The agent must increase their bid (by returning the value they want to increase it by from `getBidIncrease`) so that their bid will match the highest bid that any other agent has put in so far, or withdraw from the bidding. They can also have the option to decide to bid more, same as discussed above.
# Keep in mind that the bidding can circle the group of agents more than once, depending on if agents up their bids. Each agent who wishes to proceed is required to have committed a bid that is equal to the highest bid made by any other agent, counted over the duration of the bidding process. NB: The bidding will end immediately once this condition is met - an agent is not allowed to up their bid once everyone else has matched them.
# There can be multiple agents left at the end of bidding process, all with the same bid. That will be handled by a second bidding process that's outside the scope of this exercise.
# Hints:
# - Be sure you understand the above bidding rules clearly.
# - Do not implement a literal solution to the problem, i.e. don't read each line on its own and write code just for that part of the requirements. Rather, think about it holistically so you can come up with a solution that meets all the requirements.
# - No consideration should be paid to performance - clear, readable code is more imporant in this exercise.
class Agent
# This will already be implemented and is not part of this exercise
def initialize; end
# This will return the amount (integer) by which the agent wants to increase its bid
# (i.e. how much they want to add onto their bid as it stands so far)
# This will already be implemented and is not part of this exercise
def get_bid_increase; end
end
class Bidding
INITIAL_BID = 0
POSSIBLE_AGENT_DECISIONS = %i[match top forfeit].freeze
POSSIBLE_BID_INCREASES = [50.0, 100.0, 150.0, 200.0].freeze # assuming that the increases are fixed
Session = Struct.new(*%i[rounds winners participants], keyword_init: true)
RoundParticipant = Struct.new(*%i[agent active last_placed_bid id last_round_decision], keyword_init: true)
def self.run(agent_count: 10)
agents = [*(1..agent_count)].map { Agent.new }
new(agents).run
end
def initialize(agents)
@agents = agents
@session = Session.new(participants: build_participants_from_agents(agents), rounds: 1)
end
def run
while session_ongoing?
run_bidding_session!
session.rounds = session.rounds + 1
end
session.winners = determine_winners_for_session
pp(session) # for debugging purposes
end
private
attr_reader(:agents, :session)
def session_ongoing?
active_participants.any? && bidding_participants.map(&:last_placed_bid).uniq.length != 1
end
def bidding_participants
active_participants.select do |participant|
participant.last_placed_bid > 0
end
end
def determine_winners_for_session
bidding_participants.select do |participant|
participant.last_placed_bid == highest_available_bid
end
end
def active_participants
session.participants.values.select(&:active)
end
def run_bidding_session!
active_participants.each.with_index do |participant, index|
if update_initial_bid?(
participant, index
)
next update_participant_bid!(participant, participant.agent.get_bid_increase || POSSIBLE_BID_INCREASES.sample,
:top)
end
# make decision for agent at random
update_participant_bid_decision(participant, POSSIBLE_AGENT_DECISIONS.sample)
end
end
def update_initial_bid?(participant, index)
index.zero? && participant.active && participant.last_placed_bid.zero?
end
def highest_available_bid
active_participants.map(&:last_placed_bid).max
end
def update_participant_bid_decision(participant, decision)
case decision
when :forfeit
forfeit_round!(participant)
when :top
# increases are selected at random
update_participant_bid!(participant,
highest_available_bid + (participant.agent.get_bid_increase || POSSIBLE_BID_INCREASES.sample), decision)
when :match
update_participant_bid!(participant,
participant.last_placed_bid + (highest_available_bid - participant.last_placed_bid), decision)
end
end
def forfeit_round!(participant)
participant.active = false
participant.last_round_decision = { decision: :forfeit, round: session.rounds,
forfeited_at: highest_available_bid }
session.participants[participant.id] = participant
end
def update_participant_bid!(participant, new_bid, decision)
participant.last_placed_bid = new_bid
participant.last_round_decision = { decision: decision, round: session.rounds }
session.participants[participant.id] = participant
end
def build_participants_from_agents(agents)
agents.each_with_index.with_object({}) do |(agent, index), hash|
participant_id = "participant-#{index + 1}"
hash[participant_id] =
RoundParticipant.new(agent: agent, active: true, last_placed_bid: 0.0, id: participant_id)
end
end
end
# NB: To test the class above, uncomment the method and feel free to modify the no of agents
# by passing the number desired as an arg to the function
Bidding.run(agent_count: 15)
# execute this script in your terminal by running `ruby __filename__``
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment