agentic examples

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

source on github

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)"