require 'coord' require 'plot' class Courier attr_accessor :goods, :destination, :location, :speed, :origin def initialize(speed, location) @speed = speed # units per second @available = true @goods = "" @dist = 0 @location = location @picked_up = false end def available? @available end def picked_up? @picked_up end def deliver(what, origin, dest) @available = false @goods = what @start_pos = @location.clone @origin = origin @destination = dest @dist = @location.distance_to(origin.location) #puts "* courier leaving #{@location} to head for #{origin.name} at #{origin.location}" #print "S" + "-"*@start_pos.distance_to(@origin.location) + "M" + "-"*@start_pos.distance_to(@destination.location) + "F" #puts @last_work = Time.now @last_stop = Time.now end def work delta_t = Time.now - @last_stop eta = @dist / @speed # Update Courier's position temp_dest = @picked_up ? @destination.location : @origin.location temp_start = @picked_up ? @origin.location : @start_pos @location.x = temp_start.x - (temp_start.x - temp_dest.x)/eta * delta_t @location.y = temp_start.y - (temp_start.y - temp_dest.y)/eta * delta_t @location.z = temp_start.z - (temp_start.z - temp_dest.z)/eta * delta_t if delta_t > eta if @picked_up @destination.deliver(@goods) #puts "* courier delivered #{goods} to #{@destination.name}" #@location = @destination.location.clone @available = true @picked_up = false else @origin.pickup(@goods) #puts "* courier picked up #{goods} from #{@origin.name}" @picked_up = true #@location = @origin.location.clone @dist = @location.distance_to(@destination.location) end @last_stop = Time.now end #puts "O: #{@origin.location} D: #{@destination.location} L: #{@location}" @last_work = Time.now end end class City attr_accessor :name def initialize(name) @graph = Plot.new(40,40,2) @name = name @buildings = Array.new @founded = Time.new @delivery_center = DeliveryCenter.new("MPS", Coord.new(0,0,0)) 3.times { @delivery_center.add_courier } end def build(building) @buildings << building end def run while(true) @buildings.each do |cur_b| @graph.add(cur_b.location, cur_b.name[0,1]) @buildings.each do |b| #puts " -- #{cur_b.name} produces #{cur_b.produces} and #{b.name} demands #{b.demand.empty? ? "Nothing" : b.demand}." #puts " #{b.demand.include?(cur_b.produces)} and #{cur_b.cache[cur_b.produces]}" if(b.demand.include?(cur_b.produces) && (cur_b.cache[cur_b.produces] - @delivery_center.num_pickups(cur_b)) > 0) if( @delivery_center.num_deliveries(cur_b.produces, b) + (b.cache[cur_b.produces] || 0) < 8) result = @delivery_center.request(cur_b.produces, cur_b, b) #puts "Delivery of #{cur_b.produces} from #{cur_b.name} to #{b.name} was #{result ? "Accepted" : "Queued"}" end end end end @delivery_center.work @graph.add(@delivery_center.location, @delivery_center.name[0,1]) @delivery_center.each_courier do |c| @graph.add(c.location, c.picked_up? ? 'O' : 'o') end @buildings.each do |b| b.work #print "#{b.name} has " #b.cache.each_pair {|k, v| print "#{v} #{k}, "} #puts "" end @graph.plot @graph.clear gets end end end class Structure attr_accessor :name, :cache, :demand, :produces, :location, :speed def initialize(name, produce, demand, location) @name = name @cache = Hash.new @cache[produce] = 0 @demand = demand @produces = produce @location = location @built = Time.now @last_work = Time.now @speed = 2 # minimum number of seconds between production end def work if (Time.now - @last_work >= @speed) matches = 0 @demand.each { |d| matches += 1 if (@cache.has_key?(d) && @cache[d] > 0) } if (matches == @demand.length && @cache[@produces] < 8) #puts " #{name} did some work" @cache[@produces] = 1 + (@cache[@produces] || 0) @demand.each do |d| if (@cache.has_key?(d) && @cache[d] > 0) @cache[d] -= 1 end end end @last_work = Time.now end end def deliver(resource) if (!@cache.has_key?(resource) || @cache[resource] < 8) @cache[resource] = 1 + (@cache[resource] || 0) return true else return false end end def pickup(resource) @cache[resource] -= 1 if (@cache[resource] >= 1) end end class DeliveryCenter < Structure def initialize(name, location) super(name, "", [], location) @couriers = Array.new @queue = Array.new end def add_courier() @couriers << Courier.new(2.0, @location.clone) end def each_courier @couriers.each { |c| yield c } end def num_pickups(from) count = 0 @couriers.each { |c| count += 1 if (!c.available? && !c.picked_up? && c.origin == from) } @queue.each { |q| count += 1 if (q[1] == from) } return count end def num_deliveries(good, where) count = 0 @couriers.each { |c| count += 1 if (!c.available? && c.destination == where && c.goods == good) } @queue.each { |q| count += 1 if (q[0] == good && q[2] == where) } return count end def available_couriers count = 0 @couriers.each { |c| count += 1 if c.available? } return count end def request(product, origin, dest) req_met = false @couriers.each do |c| if(c.available? && !req_met) c.deliver(product, origin, dest) req_met = true end end @queue << [product, origin, dest] unless req_met return req_met end def work() @couriers.each { |c| c.work unless c.available? } available_couriers.times do q = @queue.shift request(q[0], q[1], q[2]) unless q == nil end #puts "#{name} has #{@queue.length} items waiting to be delivered and #{available_couriers} idle couriers" @queue.each_index do |i| #puts "#{i}\t#{@queue[i][0]}\t#{@queue[i][1].name}\t#{@queue[i][2].name}" end end end if $0 == __FILE__ utopia = City.new("Utopia") utopia.build( Structure.new("Woodcutter1", "Wood", [], Coord.new(5,5,3)) ) utopia.build( Structure.new("Woodcutter2", "Wood", [], Coord.new(2,10,2)) ) utopia.build( Structure.new("Sawmill", "Boards", ["Wood"], Coord.new(10,2,1)) ) utopia.build( Structure.new("Furniture Store", "Chairs", ["Boards"], Coord.new(9,13,2)) ) utopia.run end