Skip to content

Instantly share code, notes, and snippets.

@sandersch
Created September 24, 2016 21:48
Show Gist options
  • Save sandersch/39fc2d214ec5652ffdbe417621011c43 to your computer and use it in GitHub Desktop.
Save sandersch/39fc2d214ec5652ffdbe417621011c43 to your computer and use it in GitHub Desktop.
require 'ostruct'
class Bounty
class Parser
@@task_assignment_regexs = {
/It appears they have a creature problem they'd like you to solve/ => :cull,
/It appears they need your help in tracking down some kind of lost heirloom/ => :heirloom,
/The local furrier .+ has an order to fill and wants our help/ => :skins,
/The local gem dealer, [^,]+, has an order to fill and wants our help/ => :gem,
# /to provide a protective escort/ => :escort,
/Hmm, I've got a task here from the town of ([^.]+?). The local [^,]+?, [^,]+, has asked for our aid. Head over there and see what you can do. Be sure to ASK about BOUNTIES./ => :herb,
/It appears that a local resident urgently needs our help in some matter/ => :rescue,
/It appears they have a bandit problem they'd like you to solve/ => :bandit,
}
@@task_completed_regexs = {
/^You have succeeded in your task and can return to the Adventurer's Guild/ => :taskmaster,
/^You have located the heirloom and should bring it back/ => :heirloom,
/^You succeeded in your task and should report back to/ => :dangerous,
}
@@task_triggered_regexs = {
/^You have made contact with the child you are to rescue and you must get (?:him|her) back alive to one of the (?:guardsmen) just inside ([^.]+)\.$/ => :rescue,
/^You have been tasked to hunt down and kill a particularly dangerous ([^.]+) that has established a territory [oi]n (?:the\s+)?([^.]+?)(?: near [^.]+)?\. You have provoked (?:his|her|its) attention and now you must(?: return to where you left (?:him|her|it) and)? kill (?:him|her|it)!$/ => :dangerous,
}
@@task_description_regexs = {
/^You have been tasked to(?: help \w+)? suppress (bandit) activity (?:on|in) (?:the\s+)?([^.]+?)(?:\s+near [^.]+)?\. You need to kill (\d+) (?:more\s+)?of them to complete your task\.$/ => :bandit,
/^You have been tasked to(?: help \w+)? suppress ([^.]+) activity (?:on|in) (?:the\s+)?([^.]+?)(?:\s+near [^.]+)?\. You need to kill (\d+) (?:more\s+)?of them to complete your task\.$/ => :cull,
/^You have been tasked to recover (?:an?|some) ([^.]+) that an unfortunate citizen lost after being attacked by an? ([^.]+) [oi]n (?:the\s+)?([^.]+?)(?:\s+near [^.]+)?\. The heirloom can be identified by the initials \w+ engraved upon it\. [^.]*?(LOOT|SEARCH)[^.]+\.$/ => :heirloom,
/^You have been tasked to retrieve (\d+) ([^.]+?)s? of at least ([^.]+) quality for [^.]+ in ([^.]+?)\. You can SKIN them off the corpse of an? ([^.]+) or purchase them from another adventurer\. You can SELL the skins to the furrier as you collect them\."$/ => :skins,
/^The gem dealer in [^,]+, [^,]+, has received orders from multiple customers requesting (?:an?|some) ([^.]+)\. You have been tasked to retrieve (\d+) (?:more\s+)?of them\. You can SELL them to the gem dealer as you find them\.$/ => :gem,
/^(?:The taskmaster told you: ")?I've got a special mission for you\. A certain client has hired us to provide a protective escort on (?:his|her) upcoming journey\. Go to ([^.]+) and WAIT for (?:him|her) to meet you there\. You must guarantee (?:his|her) safety to ([^.]+) as soon as you can, being ready for any dangers that the two of you may face\. Good luck!"?$/ => :escort,
/^The .+? in ([^,]+?), [^,]+?, is working on a concoction that requires (?:an?|some) ([^.]+?) found [oi]n (?:the\s+)?([^.]+?)(?:\s+near [^.]+)?\. These samples must be in pristine condition\. You have been tasked to retrieve (\d+) (?:more\s+)?samples?\.$/ => :herb,
/^You have been tasked to (?: help \w+|hunt down and) kill a (?:particularly )?dangerous ([^.]+) that has established a territory [oi]n (?:the\s+)?([^.]+?)(?: near [^.]+)?\. You can get its attention by killing other creatures of the same type in its territory\.$/ => :dangerous,
/^You have been tasked to rescue the young runaway (?:son|daughter) of a local citizen\. A local divinist has had visions of the child fleeing from an? ([^.]+) [oi]n (?:the\s+)?([^.]+?)(?:\s+near [^.]+)?\. Find the area where the child was last seen and clear out the creatures that have been tormenting (?:him|her) in order to bring (?:him|her) out of hiding\.$/ => :rescue,
}
@@regex_for_task_description_of = @@task_description_regexs.invert
@@regex_for_trigger_description_of = @@task_triggered_regexs.invert
@@regex_for_assignment_of = @@task_assignment_regexs.invert
@@requirement_labels_for_task = {
:cull => [ :creature, :area, :number ],
:heirloom => [ :item, :creature, :area, :action ],
:skins => [ :number, :skin, :quality, :town, :creature ],
:gem => [ :gem, :number ],
:escort => [ :start, :destination ],
:herb => [ :town, :herb, :area, :number ],
:rescue => [ :creature, :area ],
:dangerous => [ :creature, :area ],
:bandit => [ :creature, :area, :number ],
}
@@task_failed_regexs = {
/^You have failed in your task/ => :taskmaster,
/^The child you were tasked to rescue is gone and your task is failed. Report this failure to the Adventurer's Guild./ => :taskmaster,
}
def self.parse(desc)
return nil if desc.nil? or desc.empty?
@description = desc
@requirements = nil #Hash.new
@task = nil
@status = nil
# Figure task type and status from description
if @description =~ /^You are not currently assigned a task/
@task = :taskmaster
elsif check_task_description( desc )
@status = :unfinished
@task = check_task_description( desc )
@requirements = Hash.new
elsif check_task_assignment( desc )
@status = :assigned
@task = check_task_assignment( desc )
if [:herb].include? @task
match_data = @@regex_for_assignment_of[@task].match( @description )
@requirements = { town: match_data[1] }
end
elsif check_task_completed( desc )
@status = :done
@task = check_task_completed( desc )
if :heirloom == @task and defined? @@last_heirloom_item and not @@last_heirloom_item.nil?
@requirements = { :item => @@last_heirloom_item }
end
elsif check_task_failed( desc )
@status = :failed
@task = check_task_failed( desc )
elsif check_task_triggered( desc )
@status = :triggered
@task = check_task_triggered( desc )
@requirements = Hash.new
if :dangerous == @task
if @description =~ @@regex_for_trigger_description_of[@task]
match_data = @@regex_for_trigger_description_of[@task].match( @description ).to_a.flatten
match_data.shift
@@requirement_labels_for_task[@task].each_with_index { |label, index|
@requirements[label] = match_data[index]
}
else
echo "ERROR: couldn't parse triggered dangerous task requirements"
end
elsif :heirloom == @task and defined? @@last_heirloom_item and not @@last_heirloom_item.nil?
@requirements = { :item => @@last_heirloom_item }
end
else
#echo "ERROR: did not recognize bounty description:"
#echo desc
return nil
end
if @task.nil?
# we don't have a bounty, so it doesn't have any requirements
elsif :unfinished == @status and @description =~ @@regex_for_task_description_of[@task]
match_data = @@regex_for_task_description_of[@task].match( @description ).to_a.flatten
match_data.shift
@@requirement_labels_for_task[@task].each_with_index { |label, index|
if :action == label
@requirements[label] = match_data[index].downcase
elsif :number == label
@requirements[label] = match_data[index].to_i
elsif :creature == label
# Clean up creature value if we need to remove extra adjectives (eg beings, centaurs)
critter = match_data[index]
critter = 'being' if critter =~ /^\w+ being$/
@requirements[label] = critter
else
@requirements[label] = match_data[index]
end
}
if :heirloom == @task
@@last_heirloom_item = @requirements[:item]
end
elsif :unfinished == @status
echo "ERROR: couldn't parse requirements from description"
end
return { description: @description, requirements: @requirements, task: @task, status: @status }
end
def self.check_task_description(desc)
return nil if desc.nil? or desc.empty?
for regex in @@task_description_regexs.keys
if desc =~ regex
return :bandit if desc =~ /bandit/
return @@task_description_regexs[regex]
end
end
return nil
end
def self.check_task_assignment(desc)
return nil if desc.nil? or desc.empty?
for regex in @@task_assignment_regexs.keys
return @@task_assignment_regexs[regex] if desc =~ regex
end
return nil
end
def self.check_task_completed(desc)
return nil if desc.nil? or desc.empty?
for regex in @@task_completed_regexs.keys
return @@task_completed_regexs[regex] if desc =~ regex
end
return nil
end
def self.check_task_failed(desc)
return nil if desc.nil? or desc.empty?
for regex in @@task_failed_regexs.keys
return @@task_failed_regexs[regex] if desc =~ regex
end
return nil
end
def self.check_task_triggered(desc)
return nil if desc.nil? or desc.empty?
for regex in @@task_triggered_regexs.keys
return @@task_triggered_regexs[regex] if desc =~ regex
end
return nil
end
end
end
load 'bounty_parser.lic'
class Bounty
describe Parser, "#parse" do
it "can tell when we don't have a task" do
bounty = described_class.parse "You are not currently assigned a task."
bounty[:task].should == :taskmaster
bounty[:requirements].should be_nil
bounty[:status].should be_nil
end
context "when assigned a task" do
it "can tell we were assigned a cull task" do
bounty = described_class.parse "It appears they have a creature problem they'd like you to solve"
bounty[:task].should == :cull
bounty[:status].should == :assigned
end
it "can tell we were assigned an heirloom task" do
bounty = described_class.parse "It appears they need your help in tracking down some kind of lost heirloom"
bounty[:task].should == :heirloom
bounty[:status].should == :assigned
end
it "can tell we were assigned a skins task" do
bounty = described_class.parse "The local furrier Furrier has an order to fill and wants our help"
bounty[:task].should == :skins
bounty[:status].should == :assigned
end
it "can tell we were assigned a gem task" do
bounty = described_class.parse "The local gem dealer, GemTrader, has an order to fill and wants our help"
bounty[:task].should == :gem
bounty[:status].should == :assigned
end
it "can tell we were assigned a herb task" do
bounty = described_class.parse "Hmm, I've got a task here from the town of Ta'Illistim. The local herbalist's assistant, Jhiseth, has asked for our aid. Head over there and see what you can do. Be sure to ASK about BOUNTIES."
bounty[:task].should == :herb
bounty[:status].should == :assigned
bounty[:requirements].should == {
:town => "Ta'Illistim",
}
end
it "can tell we were assigned a rescue task" do
bounty = described_class.parse "It appears that a local resident urgently needs our help in some matter"
bounty[:task].should == :rescue
bounty[:status].should == :assigned
end
it "can tell we were assigned a bandit task" do
bounty = described_class.parse "The taskmaster told you: \"Hmm, I've got a task here from the town of Ta'Illistim. It appears they have a bandit problem they'd like you to solve. Go report to one of the guardsmen just inside the Ta'Illistim City Gate to find out more. Be sure to ASK about BOUNTIES.\""
bounty[:task].should == :bandit
bounty[:status].should == :assigned
end
end
context "completed a task" do
it "can tell we have completed a taskmaster task" do
bounty = described_class.parse "You have succeeded in your task and can return to the Adventurer's Guild"
bounty[:task].should == :taskmaster
bounty[:status].should == :done
end
it "can tell we have completed a heirloom task" do
bounty = described_class.parse "You have located the heirloom and should bring it back"
bounty[:task].should == :heirloom
bounty[:status].should == :done
end
it "knows the heirloom item name for a completed heirloom task" do
bounty = described_class.parse "You have been tasked to recover a dainty pearl string bracelet that an unfortunate citizen lost after being attacked by a festering taint in Old Ta'Faendryl. The heirloom can be identified by the initials VF engraved upon it. Hunt down the creature and LOOT the item from its corpse."
bounty = described_class.parse "You have located the heirloom and should bring it back"
bounty[:task].should == :heirloom
bounty[:status].should == :done
bounty[:requirements][:item].should == "dainty pearl string bracelet"
end
it "can tell we have completed a dangerous task" do
bounty = described_class.parse "You succeeded in your task and should report back to"
bounty[:task].should == :dangerous
bounty[:status].should == :done
end
end
context "triggered a task" do
it "can tell we have triggered a rescue task for a male child" do
bounty = described_class.parse "You have made contact with the child you are to rescue and you must get him back alive to one of the guardsmen just inside the Sapphire Gate."
bounty[:task].should == :rescue
bounty[:status].should == :triggered
end
it "can tell we have triggered a rescue task for a female child" do
bounty = described_class.parse "You have made contact with the child you are to rescue and you must get her back alive to one of the guardsmen just inside the gate."
bounty[:task].should == :rescue
bounty[:status].should == :triggered
end
it "can tell we have triggered a dangerous task (male critter)" do
bounty = described_class.parse "You have been tasked to hunt down and kill a particularly dangerous CRITTER that has established a territory in near A PLACE. You have provoked his attention and now you must kill him!"
bounty[:task].should == :dangerous
bounty[:status].should == :triggered
end
it "can tell we have triggered a dangerous task (female critter)" do
bounty = described_class.parse "You have been tasked to hunt down and kill a particularly dangerous CRITTER that has established a territory in near A PLACE. You have provoked her attention and now you must kill her!"
bounty[:task].should == :dangerous
bounty[:status].should == :triggered
end
it "can tell we have triggered a dangerous task (unsexed critter)" do
bounty = described_class.parse "You have been tasked to hunt down and kill a particularly dangerous CRITTER that has established a territory in near A PLACE. You have provoked its attention and now you must kill it!"
bounty[:task].should == :dangerous
bounty[:status].should == :triggered
end
it "can tell we have triggered a dangerous task (return to area)" do
bounty = described_class.parse "You have been tasked to hunt down and kill a particularly dangerous CRITTER that has established a territory in near A PLACE. You have provoked her attention and now you must return to where you left her and kill her!"
bounty[:task].should == :dangerous
bounty[:status].should == :triggered
end
end
context "have an unfinished task" do
it "can tell we have an unfinished bandit task" do
bounty = described_class.parse "You have been tasked to suppress bandit activity on Sylvarraend Road near Ta'Illistim. You need to kill 20 of them to complete your task."
bounty[:task].should == :bandit
bounty[:status].should == :unfinished
bounty[:requirements].should == { :area => "Sylvarraend Road", :number => 20, :creature => 'bandit' }
end
it "can tell we have an unfinished cull task" do
bounty = described_class.parse "You have been tasked to suppress glacial morph activity in Gossamer Valley near Ta'Illistim. You need to kill 24 of them to complete your task."
bounty[:task].should == :cull
bounty[:status].should == :unfinished
bounty[:requirements].should == { :creature => "glacial morph", :area => "Gossamer Valley", :number => 24 }
end
it "can tell we have an unfinished cull task (ASSIST)" do
bounty = described_class.parse "You have been tasked to help Brikus suppress war griffin activity in Old Ta'Faendryl. You need to kill 14 of them to complete your task."
bounty[:task].should == :cull
bounty[:status].should == :unfinished
bounty[:requirements].should == { :creature => "war griffin", :area => "Old Ta'Faendryl", :number => 14 }
end
context "can tell we have an unfinished heirloom task" do
it "can parse a loot task" do
bounty = described_class.parse "You have been tasked to recover a dainty pearl string bracelet that an unfortunate citizen lost after being attacked by a festering taint in Old Ta'Faendryl. The heirloom can be identified by the initials VF engraved upon it. Hunt down the creature and LOOT the item from its corpse."
bounty[:task].should == :heirloom
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:action => "loot", :area => "Old Ta'Faendryl", :creature => "festering taint",
:item => "dainty pearl string bracelet"
}
end
it "can parse a search task" do
bounty = described_class.parse "You have been tasked to recover an interlaced gold and ora ring that an unfortunate citizen lost after being attacked by a black forest viper in the Blighted Forest near Ta'Illistim. The heirloom can be identified by the initials MS engraved upon it. SEARCH the area until you find it."
bounty[:task].should == :heirloom
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:action => "search", :area => "Blighted Forest", :creature => "black forest viper",
:item => "interlaced gold and ora ring"
}
end
end
context "can tell we have an unfinished skins task" do
it "with a one word town name" do
bounty = described_class.parse "You have been tasked to retrieve 8 madrinol skins of at least fair quality for Gaedrein in Ta'Illistim. You can SKIN them off the corpse of a snow madrinol or purchase them from another adventurer. You can SELL the skins to the furrier as you collect them.\""
bounty[:task].should == :skins
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:creature => "snow madrinol",
:quality => "fair",
:number => 8,
:skin => "madrinol skin",
:town => "Ta'Illistim",
}
end
it "with a multipart town name" do
bounty = described_class.parse "You have been tasked to retrieve 5 thrak tails of at least exceptional quality for the furrier in the Company Store in Kharam-Dzu. You can SKIN them off the corpse of a red-scaled thrak or purchase them from another adventurer. You can SELL the skins to the furrier as you collect them.\""
bounty[:task].should == :skins
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:creature => "red-scaled thrak",
:quality => "exceptional",
:number => 5,
:skin => "thrak tail",
:town => "Kharam-Dzu",
}
end
end
it "can tell we have an unfinished gem task" do
bounty = described_class.parse "The gem dealer in Ta'Illistim, Tanzania, has received orders from multiple customers requesting an azure blazestar. You have been tasked to retrieve 10 of them. You can SELL them to the gem dealer as you find them."
bounty[:task].should == :gem
bounty[:status].should == :unfinished
bounty[:requirements].should == { :gem => "azure blazestar", :number => 10 }
end
it "can tell we have an unfinished escort task" do
bounty = described_class.parse "The taskmaster told you: \"I've got a special mission for you. A certain client has hired us to provide a protective escort on his upcoming journey. Go to the area just inside the Sapphire Gate and WAIT for him to meet you there. You must guarantee his safety to Zul Logoth as soon as you can, being ready for any dangers that the two of you may face. Good luck!\""
bounty[:task].should == :escort
bounty[:status].should == :unfinished
bounty[:requirements].should == { :destination => "Zul Logoth", :start => "the area just inside the Sapphire Gate" }
end
it "can tell we have an unfinished escort task" do
bounty = described_class.parse "I've got a special mission for you. A certain client has hired us to provide a protective escort on her upcoming journey. Go to the south end of North Market and WAIT for her to meet you there. You must guarantee her safety to Zul Logoth as soon as you can, being ready for any dangers that the two of you may face. Good luck!"
bounty[:task].should == :escort
bounty[:status].should == :unfinished
bounty[:requirements].should == { :destination => "Zul Logoth", :start => "the south end of North Market" }
end
context 'for herbs' do
it "can tell we have an unfinished herb task" do
bounty = described_class.parse "The herbalist's assistant in Ta'Illistim, Jhiseth, is working on a concoction that requires a sprig of holly found in Griffin's Keen near Ta'Illistim. These samples must be in pristine condition. You have been tasked to retrieve 6 samples."
bounty[:task].should == :herb
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:herb => "sprig of holly",
:area => "Griffin's Keen",
:number => 6,
:town => "Ta'Illistim",
}
end
it 'can parse an Icemule herb task' do
bounty = described_class.parse "The healer in Icemule Trace, Mirtag, is working on a concoction that requires a withered deathblossom found in the Rift. These samples must be in pristine condition. You have been tasked to retrieve 7 samples."
bounty[:task].should == :herb
bounty[:status].should == :unfinished
bounty[:requirements].should == {
:herb => "withered deathblossom",
:area => "Rift",
:number => 7,
:town => "Icemule Trace",
}
end
end
it "can tell we have an unfinished dangerous task" do
bounty = described_class.parse "You have been tasked to hunt down and kill a particularly dangerous gnarled being that has established a territory in Old Ta'Faendryl. You can get its attention by killing other creatures of the same type in its territory."
bounty[:task].should == :dangerous
bounty[:status].should == :unfinished
bounty[:requirements].should == { :creature => "being", :area => "Old Ta'Faendryl" }
end
it "can tell we have an unfinished rescue task" do
bounty = described_class.parse "You have been tasked to rescue the young runaway son of a local citizen. A local divinist has had visions of the child fleeing from a black forest ogre in the Blighted Forest near Ta'Illistim. Find the area where the child was last seen and clear out the creatures that have been tormenting him in order to bring him out of hiding."
bounty[:task].should == :rescue
bounty[:status].should == :unfinished
bounty[:requirements].should == { :area => "Blighted Forest", :creature => "black forest ogre" }
end
end
it "can recognize a failed bounty" do
bounty = described_class.parse "You have failed in your task. Return to the Adventurer's Guild for further instructions."
bounty[:status].should == :failed
bounty[:task].should == :taskmaster
end
it 'can recognize a failed rescue task' do
bounty = described_class.parse "The child you were tasked to rescue is gone and your task is failed. Report this failure to the Adventurer's Guild."
bounty[:status].should == :failed
bounty[:task].should == :taskmaster
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment