# Coudal School of Government Challenge # Author: Scott Ahten # Fish Class class Fish attr_accessor :name # create a new fish def initialize(aName) @name = aName @objections = Array.new end # add an objection to this fish def addObjection (objection) @objections.push(objection) end # check for objections by this fish def assertObjections (nominations) result = false @objections.each do |objection| begin result = true if objection.assert(nominations) rescue #catch method calls to nil objects in objection expresssions #failed objections are false end end return result end end # of fish class # Office Class class Office attr_accessor :fish attr_accessor :name attr_accessor :rank def initialize(aName, aRank) @fish = nil @name = aName @rank = aRank end # shortcut that retreives a fish's name from the office def fishName return @fish.name if @fish != nil return nil end end # of Office class # Nominations Class - holds canidates, offices and shows valid combinations class Nominations attr_accessor :offices def initialize (offices, nominees) @nominees = nominees @offices = offices end def find for i in 0...offices.size value = offices[i] return value if yield(value) end return nil end def objections conflict = false @offices.each do |office| conflict = true if office.fish.assertObjections(self) end # check for duplicates (This is really ugly. I'm new to Ruby, is there a better way to do this?) @nominees.each do |aFish| count = 0 totalFish = @offices.collect {|office| office.fishName == aFish.name} count += 1 if totalFish[0] count += 1 if totalFish[1] count += 1 if totalFish[2] conflict = true if count > 1 #puts totalFish.inspect end return conflict end def showValid solutions = 0 for president in 0...@nominees.size for vicePresident in 0...@nominees.size for secretary in 0...@nominees.size @offices[0].fish = @nominees[president] @offices[1].fish = @nominees[vicePresident] @offices[2].fish = @nominees[secretary] if ! self.objections puts "President: #{@offices[0].fishName} | Vice-President: #{@offices[1].fishName} | Secretary: #{@offices[2].fishName}" solutions += 1 end end end end puts "#{solutions} found" end end # Nominations class # Objection class class Objection def initialize (&expression) @expression = expression end def assert(nominations) return @expression.call(nominations) end end # Objection class class Challenge def initialize # create canidate fish nominees = [Fish.new("Mr. Grouper"), Fish.new("Dr. Trout"), Fish.new("Mr. Ray"), Fish.new("Mr. Mackerel"), Fish.new("The Snapper"), Fish.new("Mr. Bass")] # assign objections to Fish as Objection objects with Ruby proc blocks # Grouper won't be an officer unless Snapper is President nominees[0].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Grouper" } != nil) && (noms.find {|office| office.name =="President"}.fishName !="The Snapper") }); # Trout won't serve if he outranks Ray nominees[1].addObjection(Objection.new { |noms| noms.find {|office| office.fishName =="Dr. Trout"}.rank > noms.find {|office| office.fishName =="Mr. Ray"}.rank }); # Trout won't serve with Bass under any conditions nominees[1].addObjection(Objection.new { |noms| noms.find {|office| office.fishName =="Dr. Trout"} != nil && noms.find {|office| office.fishName =="Mr. Bass"} != nil }); # Ray won't serve with both Snapper & Bass nominees[2].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Ray" } != nil) && (noms.find {|office| office.fishName =="Mr. Bass" } != nil)}); # Ray won't serve if Bass is President or Trout is secretary nominees[2].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Ray" } !=nil ) && (noms.find {|office| office.name =="President" }.fishName == "Mr. Bass")}); nominees[2].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Ray" } !=nil ) && (noms.find {|office| office.name =="Secretary" }.fishName == "Dr. Trout")}); # Mackerel won't serve with Ray or Snapper unless he outranks him nominees[3].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Mackerel" }.rank) < (noms.find {|office| office.fishName =="Mr. Ray" }.rank)}); nominees[3].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Mackerel" }.rank) < (noms.find {|office| office.fishName =="The Snapper" }.rank)}); # Snapper won't be Vice-President nominees[4].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="The Snapper" }.name == "Vice-President") }); # Snapper won't be secretary if Mackerel is an officer nominees[4].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Mackerel" } != nil) && (noms.find {|office| office.name =="Secretary" }.fishName == "The Snapper")}); # Snapper won't serve with Grouper unless Bass serves too nominees[4].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="The Snapper" } != nil) && (noms.find {|office| office.fishName =="Mr. Grouper" } != nil) && (noms.find {|office| office.fishName =="Mr. Bass" } == nil) }); # Bass won't serve unless either he or Ray is President nominees[5].addObjection(Objection.new { |noms| (noms.find {|office| office.fishName =="Mr. Bass" } != nil) && (noms.find {|office| office.name =="President" }.fishName != "Mr. Bass") &&(noms.find {|office| office.name =="President" }.fishName != "Mr. Ray") }); # create offices offices = [Office.new("President", 3), Office.new("Vice-President", 2), Office.new("Secretary", 1)] @nominations = Nominations.new(offices,nominees) end def solve @nominations.showValid end end # Challenge class # Solve the Challenge Challenge.new.solve