The Dungeon Crawl
The Dungeon Crawl: a quest is a plan, rooms are tasks, and doors are dependencies. The map is drawn from the orchestrator's own graph BEFORE the run - then the party delves, and the treasure fans in.
Plans & Graphs
Round 5
Matz
exit 0
bundle exec ruby examples/dungeon_crawl.rb
a real captured run
THE MAP (drawn from orchestrator.graph, in delving order) [Entrance Hall] <- you are here [Spider Nest] doors from: Entrance Hall [Flooded Crypt] doors from: Entrance Hall [Treasury] doors from: Spider Nest, Flooded Crypt THE DELVE (seed 4) Entrance Hall: found an ominous note Spider Nest: found someone's boot Flooded Crypt: found a silver coin Treasury: found a chest of coppers (unlocked with someone's boot and a silver coin) (completed in 42ms - the nest and the crypt were delved in parallel; the treasury needed both keys)
source
# frozen_string_literal: true # The Dungeon Crawl: a quest is a plan, rooms are tasks, and doors are # dependencies. The map is drawn from the orchestrator's own graph # BEFORE the run - then the party delves, and the treasure fans in. # # bundle exec ruby examples/dungeon_crawl.rb [seed] # # Runs offline. The dungeon is the dependency graph; there is no # second map to fall out of date. require class="s">"bundler/setup" require class="s">"agentic" seed = (ARGV.first || 4).to_i rng = Random.new(seed) LOOT = { class="s">"Entrance Hall" => [class="s">"a rusty key", class="s">"a torch stub", class="s">"an ominous note"], class="s">"Spider Nest" => [class="s">"silk rope", class="s">"a shed fang", class="s">"someone's boot"], class="s">"Flooded Crypt" => [class="s">"a waterlogged tome", class="s">"a silver coin", class="s">"an eyeless fish"], class="s">"Treasury" => [class="s">"the Amulet of Yendor", class="s">"a chest of coppers", class="s">"a suspicious goose"] }.freeze orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 2) rooms = {} delve = ->(t) { sleep(0.02) # every room takes delving LOOT.fetch(t.description).sample(random: rng) } def room(name) Agentic:class="y">:Task.new(description: name, agent_spec: {class="s">"name" => name, class="s">"instructions" => class="s">"delve"}) end rooms[class="s">"Entrance Hall"] = room(class="s">"Entrance Hall") rooms[class="s">"Spider Nest"] = room(class="s">"Spider Nest") rooms[class="s">"Flooded Crypt"] = room(class="s">"Flooded Crypt") rooms[class="s">"Treasury"] = room(class="s">"Treasury") orchestrator.add_task(rooms[class="s">"Entrance Hall"], agent: delve) orchestrator.add_task(rooms[class="s">"Spider Nest"], [rooms[class="s">"Entrance Hall"]], agent: delve) orchestrator.add_task(rooms[class="s">"Flooded Crypt"], [rooms[class="s">"Entrance Hall"]], agent: delve) orchestrator.add_task(rooms[class="s">"Treasury"], needs: {web: rooms[class="s">"Spider Nest"], depths: rooms[class="s">"Flooded Crypt"]}, agent: ->(t) { class="s">"#{LOOT.fetch(t.description).sample(random: rng)} (unlocked with #{t.needs.web} and #{t.needs.depths})" }) # --- the map, drawn from the plan itself ------------------------------------ graph = orchestrator.graph names = graph[class="y">:tasks].transform_values(&class="y">:description) puts class="s">"THE MAP (drawn from orchestrator.graph, in delving order)" puts graph[class="y">:order].each do |room_id| door_ids = graph[class="y">:dependencies][room_id] if door_ids.empty? puts class="s">" [#{names[room_id]}] <- you are here" else puts class="s">" [#{names[room_id]}] doors from: #{door_ids.map { |d| names[d] }.join(", class="s">")}" end end # --- the delve --------------------------------------------------------------- result = orchestrator.execute_plan puts puts class="s">"THE DELVE (seed #{seed})" rooms.each do |name, task| puts class="s">" #{name}: found #{result.results[task.id].output}" end puts puts class="s">"(#{result.status} in #{(result.execution_time * 1000).round}ms - the nest and" puts class="s">" the crypt were delved in parallel; the treasury needed both keys)"