# Data o programatorech. Speed udava kolik clovekodni
# programator odpracuje za den. Daily_wage je denni mzda.
programmers = [
{:name => "Martin", :speed => 1.9, :daily_wage => 2000},
{:name => "Jarda", :speed => 1.0, :daily_wage => 1300},
{:name => "Lukas", :speed => 0.6, :daily_wage => 900},
{:name => "Pepa", :speed => 1.7, :daily_wage => 2200},
{:name => "Kamil", :speed => 0.4, :daily_wage => 1800},
{:name => "Honza", :speed => 1.3, :daily_wage => 1500},
{:name => "Filip", :speed => 1.1, :daily_wage => 1000}
]
# Data o firmach. Capacity udava kolik programatoru muze firma najmout
# Daily_expenses jsou firemni denni fixni naklady
companies = [
{:name => "Alpha", :capacity => 2, :daily_expenses => 1000, :budget => 90000},
{:name => "Beta", :capacity => 2, :daily_expenses => 1500, :budget => 100000},
{:name => "Gamma", :capacity => 3, :daily_expenses => 3000, :budget => 200000},
{:name => "Delta", :capacity => 5, :daily_expenses => 6000, :budget => 400000},
{:name => "Epsilon", :capacity => 6, :daily_expenses => 8000, :budget => 900000},
{:name => "Theta", :capacity => 7, :daily_expenses => 10000, :budget => 1200000},
{:name => "Omega", :capacity => 7, :daily_expenses => 20000, :budget => 20000000}
]
# Data o projektech. Man days je pocet clovekodni ktere se musi na
# projektu odpracovat. Price je castka kterou firma obdrzi pri dokonceni
projects = [
{:name => "Web", :man_days => 5.0, :price => 20000},
{:name => "Portal", :man_days => 15.0, :price => 60000},
{:name => "Email system", :man_days => 25.0, :price => 90000},
{:name => "Eshop", :man_days => 40.0, :price => 150000},
{:name => "CMS", :man_days => 60.0, :price => 250000},
{:name => "Forum", :man_days => 30.0, :price => 35000},
{:name => "B2B System", :man_days => 120.0, :price => 800000},
{:name => "Multimedia Web", :man_days => 7.0, :price => 50000},
{:name => "TODO List", :man_days => 3.0, :price => 10000},
{:name => "CRM", :man_days => 20.0, :price => 80000}
]
class Programmer
attr_reader :name, :speed, :daily_wage
attr_accessor :project
def initialize(name, speed, daily_wage)
@name = name
@speed = speed
@daily_wage = daily_wage
@project = nil
end
# Uvolnit programatora z projektu
def clear_project
@project = nil
end
# Pracovat na projektu
def write_code
@project.receive_work(@speed)
end
# Vypise nazev prirazeneho projektu, nebo retezec "nil",
# kdyz zadny projekt prirazen neni
def project_name
return project.nil? ? "nil" : project.name
end
# Slouzi pouze pro ladici ucely. Vypise dulezite informace o danem programatorovi.
def print_debug_info
puts "Programmer #{name}:"
puts "\tSpeed: #{speed}"
puts "\tDaily wage: #{daily_wage}"
puts "\tProject: #{project_name}"
end
end
class Project
attr_reader :name, :man_days, :price
attr_reader :man_days_done
attr_accessor :state #moznosti :waiting, :current, :done
def initialize(name, man_days, price)
@name = name
@man_days = man_days
@man_days_done = 0
@price = price
@state = :waiting
end
# Prace, kterou programator provede na projektu
def receive_work(man_days)
@man_days_done += man_days
end
# Slouzi pouze pro ladici ucely. Vypise dulezite informace o danem projektu.
def print_debug_info
puts "Project #{name}:"
puts "\tState: #{state}"
puts "\tMan-days total: #{man_days}"
puts "\tMan-days done: #{man_days_done}"
puts "\tPrice: #{price}"
end
end
class Company
attr_reader :name, :capacity, :daily_expenses, :budget
attr_reader :days, :programmers
attr_reader :projects_waiting, :projects_current, :projects_done
attr_reader :state # moznosti :idle, :running, :finished, :bankrupt
def initialize(name, capacity, daily_expenses, budget)
@name = name
@capacity = capacity
@daily_expenses = daily_expenses
@budget = budget
@state = :idle
@days = 0
@projects_waiting = []
@projects_current = []
@projects_done = []
end
# Nacist vsechny projekty do pole @projects_waiting
def allocate_projects(projects_array)
projects_array.each do |proj|
@projects_waiting << Project.new(proj[:name], proj[:man_days], proj[:price])
end
end
# Najmout tolik programatoru, kolik cini kapacita
def allocate_programmers(programmers_array)
# IMPLEMENTUJTE TUTO METODU
# Z pole programmers_array vyberte prvnich @capacity programatoru
# v poradi podle nejvyhodnejsiho pomeru jejich rychlosti proti jejich cene.
# Nasledne v tomto poradi vytvarejte prislusne instance tridy Programmer
# a vytvorene objekty vkladejte do pole @programmers.
programmers_sorted = []
@programmers = []
programmers_array.each do |prog|
quality = (prog[:daily_wage].to_f / prog[:speed].to_f).to_i
programmers_sorted << [prog[:name], prog[:speed], prog[:daily_wage], quality]
end
capacity = @capacity
(programmers_sorted.sort_by {|q| q[3]}).each do |prog|
capacity -=1
if capacity >= 0
@programmers << Programmer.new(prog[0], prog[1], prog[2])
end
end
end
# Zjistit ktere projekty jsou uz hotove
# a presunout je do @projects_done
def check_projects
# Nasledujici kod si vyzaduje urcitou pozornost a vysvetleni komentarem. Jeho smyslem je z pole @projects_current
# odebrat vsechny projekty, ktere jsou jiz hotove (pocet odpracovanych dni je vetsi nebo roven poctu dni potrebnemu
# k dokonceni projektu) a tyto projekty umistit do pomocneho pole currently_done.
# V podmince bloku metody reject! vyuzivame toho, ze pokud jeste neni odpracovan dostatecny pocet dni, bude hned
# prvni cast podminky vyhodnocena jako false a diky zkracenemu vyhodnocovani vyrazu se druha cast podminky vubec
# neprovede. V pripade, ze uz je odpracovano dostatecne mnozstvi dni, pridani zkoumaneho projektu do currently_done
# se provede a vyslednou hodnotou celeho bloku bude objekt currently_done samotny, coz v podminkovem kontextu
# znamena pravdivou hodnotu, a tedy projekt bude metodou reject! odebran z @projects_current.
# Pokud vam takovato konstrukce pripada ponekud slozita a nepruhledna, nevadi, muzete si predstavit, ze bychom
# misto toho pouzili nasledujici kod -- vysledek by byl stejny:
# currently_done = @projects_current.select { |project| project.man_days_done >= project.man_days }
# @projects_current.reject! { |project| project.man_days_done >= project.man_days }
currently_done = []
@projects_current.reject! { |project| project.man_days_done >= project.man_days && currently_done << project }
currently_done.each do |project|
project.state = :done
@projects_done << project
@budget += project.price
end
end
# Uvolnit programatory, co delaji na projektech,
# ktere uz jsou hotove.
def check_programmers
@projects_done.each do |proj|
@programmers.each do |prog|
if proj == prog.project
prog.clear_project
end
end
end
## protoze check_projects sice presouva hotove projekty do @projects_done, ale nemaze je z @projects_current, je toto provedeno zde
#@projects_done.each do |proj_d|
#@projects_current.each do |proj_c|
#@projects_current.delete(proj_c) if proj_d == proj_c
#end
#end
end
# Nastavit projekty programatorum, kteri jsou volni.
def assign_new_projects
# IMPLEMENTUJTE TUTO METODU
# Pro kazdeho volneho programatora hledejte projekt k prideleni nasledovne:
# - Pokud existuje nejaky projekt v @projects_waiting, vyberte prvni takovy.
# (Nezapomente mu zmenit stav a presunout jej do @projects_current.)
# - Pokud ne, vyberte takovy projekt z @projects_current, na kterem zbyva
# nejvice nedodelane prace.
@programmers.each do |prog|
@projects_waiting.each do |proj|
if prog.project == nil && proj.state == :waiting
proj.state = :current
@projects_current << proj
end
if prog.project == nil && proj.state == :current
end
end
end
end
# Programatori pracuji.
def programmers_work
# IMPLEMENTUJTE TUTO METODU
# Projdete vsechny programatory a predejte jejich denni vykon projektum,
# ktere maji pridelene.
# Zaroven snizte aktualni stav financi firmy o jejich denni mzdu a rovnez
# o denni vydaje firmy.
@programmers.each do |prog|
if not @projects_waiting[0] == nil
prog.write_code
@budget -= prog.daily_wage
end
end
@budget -= @daily_expenses
end
# Zjistit stav spolecnosti.
def check_company_state
# IMPLEMENTUJTE TUTO METODU
# Pokud je aktualni stav financi firmy zaporny, nastavte
# stav spolecnosti na :bankrupt.
# Pokud ne a zaroven pokud jsou jiz vsechny projekty hotovy,
# nastavte stav spolecnosti na :finished.
if @budget < 0
@state = :bankrupt
end
if @projects_waiting[0] == nil
@state = :finished
end
end
# Spusteni simulace. Cyklus se ukonci kdyz je stav firmy
# :bankrupt nebo :finished, nebo pokud simulace bezi vice nez 1000 dni
def run
@state = :running
while @state != :bankrupt and @state != :finished and @days <= 1000
@days += 1
assign_new_projects
programmers_work
check_programmers
check_projects
check_company_state
# odkomentovani nasledujicich radku zpusobi vypsani velmi podrobnych informaci
# o stavu spolecnosti na konci kazdeho dne
# puts
# print_debug_info
end
@state = :idle if @state == :running
end
def output_result
puts
puts "Company name #{name}"
puts "Days running #{@days}"
puts "Final budget #{@budget}"
puts "Final state #{@state}"
puts "Number of projects done #{@projects_done.size}"
end
# Slouzi pouze pro ladici ucely. Vypise dulezite informace o danem projektu,
# vcetne seznamu zamestnanych programatoru a seznamu cekajicich, zpracovavanych a zpracovanych projektu.
# U zpracovavanych a zpracovanych projektu vypise i podrobnosti o techto projektech.
def print_debug_info
puts "Company #{name}, day #{days}:"
puts "\tState: #{state}"
puts "\tCurrent cash flow: #{budget}"
puts "\tDaily expenses: #{daily_expenses}"
puts "\tCapacity: #{capacity}"
puts "\tProgrammers: #{programmers.collect { |programmer| programmer.name + " (" + programmer.project_name + ")" }.join(", ")}"
puts "\tPROJECTS WAITING: #{projects_waiting.collect { |project| project.name }.join(", ")}"
puts "\tPROJECTS CURRENT:"
projects_current.each { |project| project.print_debug_info }
puts "\tPROJECTS DONE:"
projects_done.each { |project| project.print_debug_info }
end
end
company_objects = []
companies.each do |c|
# Vytvorit objekty typu Company a priradit do company_objects
company_objects << Company.new(c[:name],c[:capacity],c[:daily_expenses],c[:budget])
end
# Tento cyklus se opakuje pro kazdou firmu
company_objects.each do |c|
# Prijmout programatory
c.allocate_programmers(programmers)
# Nacist projekty
c.allocate_projects(projects)
# Spustit simulaci
c.run
# Vypsat vysledek
c.output_result
end