|
From: Matthew Moss on 4 Jul 2008 13:00 Here is the complete, sample client file: require 'statistician' class Defense < Statistician::Reportable rule "[The ]<name> wounds you[ with <attack>] for <amount> point[s] of <kind>[ damage]." rule "You are wounded for <amount> point[s] of <kind> damage." end class Offense < Statistician::Reportable rule "You wound[ the] <name>[ with <attack>] for <amount> point[s] of <kind>[ damage]." rule "You reflect <amount> point[s] of <kind> damage to[ the] <name>." end class Defeat < Statistician::Reportable rule "You succumb to your wounds." end class Victory < Statistician::Reportable rule "Your mighty blow defeated[ the] <name>." end class Healing < Statistician::Reportable rule "You heal <amount> points of your wounds." rule "<player> heals you for <amount> of wound damagepoints." end class Regen < Statistician::Reportable rule "You heal yourself for <amount> Power points." rule "<player> heals you for <amount> Power points." end class Comment < Statistician::Reportable rule "### <comment> ###" end class Ignored < Statistician::Reportable rule "<player> defeated[ the] <name>." rule "<player> has succumbed to his wounds." rule "You have spotted a creature attempting to move stealthily about." rule "You sense that a creature is nearby but hidden from your sight." rule "[The ]<name> incapacitated you." end if __FILE__ == $0 lotro = Statistician::Reporter.new(Defense, Offense, Defeat, Victory, Healing, Regen, Comment, Ignored) lotro.parse(File.read(ARGV[0])) num = Offense.records.size dmg = Offense.records.inject(0) { |sum, off| sum + Integer(off.amount.gsub(',', '_')) } d = Defense.records[3] puts <<-EOT Number of Offense records: #{num} Total damage inflicted: #{dmg} Average damage per Offense: #{(100.0 * dmg / num).round / 100.0} Defense record 3 indicates that a #{d.name} attacked me using #{d.attack}, doing #{d.amount} points of damage. Unmatched rules: #{lotro.unmatched.join("\n")} Comments: #{Comment.records.map { |c| c.comment }.join("\n")} EOT end And here is the output it generates, using the [hunter.txt][1] data file: Number of Offense records: 1300 Total damage inflicted: 127995 Average damage per Offense: 98.46 Defense record 3 indicates that a Tempest Warg attacked me using Melee Double, doing 108 points of damage. Unmatched rules: The Trap wounds Goblin-town Guard for 128 points of Common damage. Nothing to cure. Comments: Chat Log: Combat 04/04 00:34 AM [1]: http://www.splatbang.com/rubyquiz/files/hunter.zip [2]: http://www.rubyquiz.com/quiz67.html
From: Matthew Moss on 4 Jul 2008 13:08 One minor note, about this little bit of code: > Integer(off.amount.gsub(',', '_')) } I found that there was at least one instance of a number over one thousand in the source file, using commas to separate. Neither the Integer initializer nor the to_i methods recognize that, so this gsub makes it safe to convert.
From: Matthias Reitinger on 6 Jul 2008 14:38 Thanks for this quiz, it was actually quite fun! You can find my solution here: http://pastie.org/228592 I didn't expect my code to become this concise when I first read the problem statement, but I guess that's just the way Ruby works :) Matthias. -- Posted via http://www.ruby-forum.com/.
From: Matthew Moss on 6 Jul 2008 15:00 Here's my own solution for this quiz (which I made sure I could do reasonably before posting the quiz!). Very similar in appearance to Matthias' solution above. As a pastie: http://pastie.org/228598 require 'ostruct' module Statistician class Reportable < OpenStruct def Reportable.inherited(klass) # Give each individual Reportable some instance data. # Doing it this way ensures each klass gets it's own rules/ records, rather # than sharing amongst all Reportables. klass.instance_eval %{ @reportable_rules = [] @reportable_records = [] } end # Class methods def self.rule(str) r = Rule.new(str) @reportable_rules << r end def self.match(str) data = nil if @reportable_rules.find { |rule| data = rule.match(str) } return data end end def self.records @reportable_records end # Helpers class Rule def initialize(str) patt = Regexp.escape(str).gsub('\[', '(?:').gsub('\]', ')?').gsub(/<(.+?)>/, '(.+?)') @pattern = Regexp.new("^#{patt}$") @fields = str.scan(/<(.+?)>/).flatten.map { |f| f.to_sym } end def match(str) if md = @pattern.match(str) Hash[*@fields.zip(md.captures).flatten] else nil end end end end # class Reportable class Reporter attr_reader :unmatched def initialize(*reportables) @reportables = reportables @unmatched = [] end def parse(text) text.each do |line| line.strip! data = nil if reportable = @reportables.find { |k| data = k.match(line) } reportable.records << reportable.new(data) else @unmatched << line end end end end # class Reporter end # module Statistician
From: Matthew Moss on 10 Jul 2008 15:51 I wanted to add one more note... > klass.class_eval do > @rules, @records = [], [] > end Considering that this bit of code injects @rules and @records into klass, my preference is that they be named something _less_ straightforward. My own, similar solution used @reportable_rules and @reportable_records. The reason? There is nothing preventing a client from further extending their own subclasses of Reportable. Actually, I will lightly encourage that in part 3. To avoid potential name conflicts with client-side extensions, I'd go with names more complex than the simple @rules and @records.
|
Pages: 1 Prev: [QUIZ] Statistician II (#168) Next: from ruby/RoR to Java (framework unknown) :( |