public Action choose(WumpusWorld.Hunter hunter, Set<Action> options) {
    System.out.format("%n%n");
    updateInternal(hunter);
    Room here = worldToPrivate.get(hunter.location());

    // check to see if we need to make a new decision
    if (decision != null && !decision.done(options)) {
      return decision.step(options);
    }

    // kill any adjacent, known wumpuses
    for (Room wumpus : here.adjacent) {
      if (wumpusKB.known(wumpus) && wumpusKB.evaluate(wumpus)) {
        System.out.println("DIE!");
        decision = new KillDecision(hunter, wumpus);
        return decision.step(options);
      }
    }

    // find any known wumpuses to kill
    for (Room wumpus : privateToWorld.keySet()) {
      if (wumpusKB.known(wumpus) && wumpusKB.evaluate(wumpus)) {
        Set<Room> safeRooms = safeRooms();
        safeRooms.add(wumpus);

        List<Room> path = path(here, wumpus, safeRooms);
        path.remove(wumpus);
        System.out.println("I've got it!");
        decision = new PathDecision(hunter, path);
        return decision.step(options);
      }
    }

    // find safe rooms to explore
    {
      System.out.println(safeRooms());
      List<Room> path = path(here, safeRooms());
      if (path != null) {
        System.out.println("Walking!");
        decision = new PathDecision(hunter, path);
        return decision.step(options);
      }
    }

    // find bats to exploit
    for (Room bat : privateToWorld.keySet()) {
      if (batKB.known(bat) && batKB.evaluate(bat)) {
        Set<Room> safeRooms = safeRooms();
        safeRooms.add(bat);
        System.out.println("Find a Bat!");
        List<Room> path = path(here, bat, safeRooms);
        return decision.step(options);
      }
    }

    // otherwise, take your 3 chances... they're only arrows!
    decision = new KillDecision(hunter, Groups.any(here.adjacent));
    return decision.step(options);
  }