/// <summary> /// The base constructor. /// </summary> /// <param name="domain">The base domain object.</param> /// <param name="problem">The base problem object.</param> /// <param name="path">The path used to save files to disk.</param> public MediationTree(Domain domain, Problem problem, string path) { // Store the domain, problem, and path. this.domain = domain; this.problem = problem; this.path = path; // Store the player's name. player = problem.Player.ToLower(); // Get and store the NPC names. npcs = GetNPCs(); // Establish the turn order. turnOrder = GetTurnOrder(); // Create a new hashtable to store tree edges. tree = new Hashtable(); // Initialize the session counters. nodeCounter = 0; lowestDepth = 0; goalStateCount = 0; deadEndCount = 0; // Create the tree's root node. MediationTreeNode root = CreateNode(domain, problem, null); }
/// <summary> /// Given a mediation tree and a node in the tree, returns the node's world history. /// </summary> /// <param name="node">The node.</param> /// <param name="tree">The tree.</param> /// <returns>The path through the tree from the root to the node.</returns> public static List <MediationTreeNode> GetWorldHistory(MediationTreeNode node, MediationTree tree) { // Create an empty list object to hold the history. List <MediationTreeNode> history = new List <MediationTreeNode>(); // Add the node to the world history. history.Add(node); // Remember the current node's ID. int current = node.ID; // While the current node's parent is not null... while (tree.GetParent(current) != -1) { // Store the current node's parent object. MediationTreeNode parent = tree.GetNode(tree.GetParent(current)); // Remember the parent's ID. current = parent.ID; // Insert the parent node into the world history. history.Insert(0, parent); } // Return the world history. return(history); }
/// <summary> /// Write a node object to disk. /// </summary> /// <param name="node">The node's ID.</param> public void SetNode(MediationTreeNode node) { // Given the path and ID, write the node object to disk. BinarySerializer.SerializeObject <MediationTreeNode>(path + node.ID, node); // Save the mediation tree. SaveTree(); }
/// <summary> /// Given a mediation tree and a node in the tree, returns a event revision domain and problem. /// </summary> /// <param name="node">The mediation tree node.</param> /// <param name="tree">The mediation tree.</param> /// <returns>The event revision problem and domain for the node.</returns> public static Tuple <Domain, Problem> GetEventRevisionPair(MediationTreeNode node, MediationTree tree) { // Create a clone of the node's original domain. Domain revisionDomain = node.Domain.Clone() as Domain; // Create a clone of the node's original problem. Problem revisionProblem = tree.Root.Problem.Clone() as Problem; // Get the series of actions and state literals the player has observed. List <Tuple <Operator, State> > playerKnowledge = GetWorldKnowledge(tree.Player, node, tree); // Rename the problem to the automated slot. revisionProblem.Name = "rob"; // Add the initial state tracking literal to the problem's initial state. revisionProblem.Initial.Add(new Predicate("state-depth", new List <ITerm> { new Term("depth1", true) }, true)); revisionDomain.AddTypePair("depth", "state"); revisionDomain.Predicates.Add(new Predicate("state-depth", new List <ITerm> { new Term("?depth", "", "state") }, true)); // For every operator in the cloned domain... foreach (Operator op in revisionDomain.Operators) { // Add a state tracking precondition that prevents it from being applied until after the event revision operations have been performed. op.Preconditions.Add(new Predicate("state-depth", new List <ITerm> { new Term("depth" + playerKnowledge.Count.ToString(), true) }, true)); } // Loop through the action-state pairs in the player knowledge. for (int i = 1; i < playerKnowledge.Count; i++) { // For each of these, add a new state tracker to the list of objects. revisionProblem.Objects.Add(new Obj("depth" + i.ToString(), "depth")); // If the player did not observed the current action, add a set of unobserved templates to the list of operators. if (playerKnowledge[i].First == null) { revisionDomain.Operators.AddRange(GetUnobservedActionTemplates(i, tree.GetTurnAtIndex(i - 1), node.Domain, playerKnowledge[i - 1].Second)); } // If the player did observe the current action, add the observed template to the list of operators. else { revisionDomain.Operators.Add(GetObservedActionTemplate(i, playerKnowledge[i].First)); } } // Add the final state tracking object to the problem. revisionProblem.Objects.Add(new Obj("depth" + playerKnowledge.Count.ToString(), "depth")); // Return the domain-problem pair. return(new Tuple <Domain, Problem> (revisionDomain, revisionProblem)); }
/// <summary> /// Creates a new node object. /// </summary> /// <param name="domain">The node's domain.</param> /// <param name="problem">The node's problem.</param> /// <param name="incoming">The node's incoming edge.</param> /// <returns></returns> public MediationTreeNode CreateNode(Domain domain, Problem problem, MediationTreeEdge incoming) { // Create a placeholder for the new node object. MediationTreeNode node = null; // If the node is a root, initialize a root node. if (incoming == null) { node = new MediationTreeNode(domain, problem, 0); } // Otherwise, it is a child node... else { // Store the current node's ID in the incoming edge. incoming.Child = ++nodeCounter; // Create the new node object. node = new MediationTreeNode(domain, problem, incoming, GetSuccessorState(incoming), incoming.Child, GetDepth(incoming.Parent) + 1); // Add the edge to the tree hashtable. tree.Add(incoming.Child, incoming.Parent); } // If the node is a goal state, iterate the goal state counter. if (node.IsGoal) { goalStateCount++; } // If the node is at a lower depth than the previous record holder, update the depth counter. if (node.Depth > lowestDepth) { lowestDepth = node.Depth; } // Find and store the node's outgoing edges. node.Outgoing = GetOutgoingEdges(node, GetCurrentTurn(node)); // Iterate through the node's outgoing edges. foreach (GameTreeEdge edge in node.Outgoing) { // And add each of them to the unplayed collection. node.Unplayed.Add(edge); } // Save the current node to disk. SetNode(node); // Return the current node object. return(node); }
/// <summary> /// The base constructor. /// </summary> /// <param name="planner">The planner to use.</param> /// <param name="domain">The base domain object.</param> /// <param name="problem">The base problem object.</param> /// <param name="path">The path used to save files to disk.</param> /// <param name="domainRevision">Whether to use domain revision.</param> /// <param name="eventRevision">Whether to use event revision.</param> /// <param name="superpositionManipulation">Whether to use superposition manipulation.</param> public MediationTree(Domain domain, Problem problem, string path, bool domainRevision, bool eventRevision, bool superpositionManipulation) { // Set the path. this.path = path; // Check each path to see if it exists. If not, create the folder. if (!File.Exists(path)) { Directory.CreateDirectory(path); } // If data already exists, load it from memory. if (File.Exists(path + "mediationtree")) { data = BinarySerializer.DeSerializeObject <MediationTreeData>(path + "mediationtree"); } // Otherwise, initialize a new tree. else { data.eventRevision = eventRevision; data.domainRevision = domainRevision; data.superpositionManipulation = superpositionManipulation; // Store the domain and problem. data.domain = domain; data.problem = problem; // Store the player's name. data.player = problem.Player.ToLower(); // Get and store the NPC names. data.npcs = GetNPCs(); // Establish the turn order. data.turnOrder = GetTurnOrder(); // Create a new hashtable to store tree edges. data.tree = new Hashtable(); // Initialize the session counters. data.nodeCounter = 0; data.lowestDepth = 0; data.goalStateCount = 0; data.deadEndCount = 0; // Create the tree's root node. MediationTreeNode root = CreateNode(domain, problem, null); } }
/// <summary> /// Creates a new node or returns the cached version. /// </summary> /// <param name="domain">The incoming domain.</param> /// <param name="problem">The incoming problem.</param> /// <param name="incoming">The incoming edge.</param> /// <returns>The child mediation tree node.</returns> public MediationTreeNode GetNode(Domain domain, Problem problem, MediationTreeEdge incoming) { // Return the cached child object if it has already been expanded. if (incoming.Child >= 0) { MediationTreeNode node = GetNode(incoming.Child); if (node != null) { return(node); } } // Otherwise, return a new node. return(CreateNode(domain, problem, incoming)); }
/// <summary> /// Returns a list of outgoing edges for a given node. /// </summary> /// <param name="node">The node object.</param> /// <param name="actor">The name of the current actor.</param> /// <returns>A list of outgoing edges.</returns> public List <MediationTreeEdge> GetOutgoingEdges(MediationTreeNode node, string actor) { List <MediationTreeEdge> outgoing = StateSpaceTools.GetAllPossibleActions(actor, node); if (!data.superpositionManipulation) { return(outgoing); } List <MediationTreeEdge> unobservedActions = new List <MediationTreeEdge>(); List <MediationTreeEdge> observedActions = new List <MediationTreeEdge>(); foreach (MediationTreeEdge edge in outgoing) { Superposition super = node.State as Superposition; bool obs = false; foreach (State state in super.States) { if (state.Satisfies(edge.Action.Preconditions)) { if (KnowledgeAnnotator.Observes(Player, edge.Action, state.Predicates)) { observedActions.Add(edge); obs = true; break; } } } if (!obs) { unobservedActions.Add(edge); } } if (unobservedActions.Count > 0) { VirtualMediationTreeEdge super = new VirtualMediationTreeEdge(); foreach (MediationTreeEdge unobserved in unobservedActions) { super.Actions.Add(unobserved.Action as Operator); } super.Parent = node.ID; observedActions.Add(super); } return(observedActions); }
/// <summary> /// Given a planner, a mediation tree node, and a mediation tree, return an event revision plan and state to set for the node. /// </summary> /// <param name="planner">The planner to use.</param> /// <param name="node">The current node in the mediation tree.</param> /// <param name="tree">The mediation tree.</param> /// <returns>A plan state pair for the current node.</returns> public static void EventRevision(Planner planner, MediationTreeNode node, MediationTree tree) { // Compute the event revision problem and domain for the current node. Tuple <Domain, Problem> pair = GetEventRevisionPair(node, tree); // Use the planner to find an event revision plan. Plan plan = PlannerInterface.Plan(planner, pair.First, pair.Second); if (plan.Steps.Count > 0) { Tuple <Plan, State> planState = GetPlanStatePair(plan, node, tree); node.Plan = planState.First; node.State = planState.Second; node.Problem.Initial = node.State.Predicates; } }
/// <summary> /// Given an event revision plan, a mediation tree node, and a mediation tree, returns the plan and state to set in the node. /// </summary> /// <param name="plan">The full event revision plan from the original initial state.</param> /// <param name="node">The current node in the mediation tree.</param> /// <param name="tree">The mediation tree object.</param> /// <returns>The current state to set in the node after event revision and the plan from the state forward.</returns> public static Tuple <Plan, State> GetPlanStatePair(Plan plan, MediationTreeNode node, MediationTree tree) { // Reformat the event revision plan to get a regular plan. Plan formattedPlan = ReformatPlan(plan, tree.Root.Domain, tree.Root.Problem); // Create a list to hold event revision steps. List <IOperator> pastSteps = new List <IOperator>(); // Create another list to hold future plan steps. List <IOperator> futureSteps = new List <IOperator>(); // Loop through the steps in the formatted event revision plan... for (int i = 0; i < formattedPlan.Steps.Count; i++) { // If the step happened before the current state, it is an event revision step. if (i < node.Depth) { pastSteps.Add(formattedPlan.Steps[i]); } // If the step happens at or after the current state, it is a future plan step. else { futureSteps.Add(formattedPlan.Steps[i]); } } // Create a new state object by cloning the initial state of the original planning problem. State current = tree.Root.State.Clone() as State; // Loop through each of the event revision steps... foreach (IOperator step in pastSteps) { // And update the state object by applying the action. current = new State(current.ApplyAction(step as Operator, node.Problem.Objects)); } // Return the future plan along with the computed state. return(new Tuple <Plan, State>(new Plan(node.Domain, node.Problem, futureSteps), current)); }
/// <summary> /// Get the current turn taker. /// </summary> /// <param name="node">The current node.</param> /// <returns>The name of the character who gets to act.</returns> private string GetCurrentTurn(MediationTreeNode node) { // Returns the character who gets to act at this level. return(data.turnOrder[node.Depth % data.turnOrder.Count]); }
/// <summary> /// Allows custom plans to be passed in. /// </summary> /// <param name="domain">The node's domain.</param> /// <param name="problem">The node's problem.</param> /// <param name="incoming">The node's incoming edge.</param> /// <returns>A new tree node.</returns> private MediationTreeNode CreateNode(Domain domain, Problem problem, MediationTreeEdge incoming, Plan plan) { // Create a placeholder for the new node object. MediationTreeNode node = null; // If the node is a root, initialize a root node. if (incoming == null) { if (!data.superpositionManipulation) { node = new MediationTreeNode(domain, problem, 0); } else { node = new VirtualMediationTreeNode(domain, problem, 0); } } // Otherwise, it is a child node... else { // Store the current node's ID in the incoming edge. incoming.Child = ++data.nodeCounter; if (!data.superpositionManipulation) { node = new MediationTreeNode(domain, problem, incoming, GetSuccessorState(incoming), plan, incoming.Child, GetDepth(incoming.Parent) + 1); } else { node = new VirtualMediationTreeNode(domain, problem, incoming, GetSuccessorSuperposition(incoming), plan, incoming.Child, GetDepth(incoming.Parent) + 1); } // Add the edge to the tree hashtable. data.tree[incoming.Child] = incoming.Parent; MediationTreeNode parent = GetNode(incoming.Parent); if (incoming.Action != null) { parent.Outgoing.Find(x => x.Action.Equals(incoming.Action)).Child = node.ID; } else { foreach (MediationTreeEdge edge in parent.Outgoing) { if (edge is VirtualMediationTreeEdge) { if ((edge as VirtualMediationTreeEdge).Equals(incoming as VirtualMediationTreeEdge)) { edge.Child = node.ID; } } } } SetNode(parent); if (data.superpositionManipulation) { Superposition super = node.State as Superposition; super.States = SuperpositionManipulator.Collapse(data.player, node as VirtualMediationTreeNode, SuperpositionChooser.ChooseUtility); node.State = super; } } // If the node is a goal state, iterate the goal state counter. if (node.IsGoal) { data.goalStateCount++; } // If the node is a dead end, iterate the dead end counter. if (node.DeadEnd) { if (data.eventRevision) { EventRevisor.EventRevision(Planner.FastDownward, node, this); } if (node.DeadEnd && data.domainRevision) { DomainRevisor.DomainRevision(Planner.FastDownward, node, this); } if (node.DeadEnd) { data.deadEndCount++; } } // If the node is at a lower depth than the previous record holder, update the depth counter. if (node.Depth > data.lowestDepth) { data.lowestDepth = node.Depth; } // If the node is not a dead end. if (!node.DeadEnd) { // Find and store the node's outgoing edges. node.Outgoing = GetOutgoingEdges(node, GetCurrentTurn(node)); } // Save the current node to disk. SetNode(node); // Return the current node object. return(node); }
public static void DomainRevision(Planner planner, MediationTreeNode node, MediationTree tree) { // Store the incoming action. Operator incoming = node.Incoming.Action as Operator; // Create a new plan object. Plan newPlan = new Plan(); List <Tuple <Operator, State> > knowledge = EventRevisor.GetWorldKnowledge(tree.Player, node, tree); knowledge.RemoveAt(knowledge.Count - 1); List <Operator> observedActions = new List <Operator>(); foreach (Tuple <Operator, State> pair in knowledge) { if (pair.First != null) { if (pair.First.Name.Equals(incoming.Name)) { observedActions.Add(pair.First); } } } // Examine each exceptional effect. foreach (Predicate effect in incoming.ExceptionalEffects) { // Create a new domain object. Domain newDomain = node.Domain.Clone() as Domain; newDomain.Operators = new List <IOperator>(); // If the current effect is conditional... if (incoming.IsConditional(effect)) { bool observed = false; // If the conditional effect has not been observed by the player. foreach (Operator action in observedActions) { if (!observed) { foreach (IAxiom conditional in action.Conditionals) { if (conditional.Effects.Contains(effect) && !observed) { observed = true; } } } } // Remove the conditional effect from the domain. if (!observed) { // Copy the current operator templates into a list. List <IOperator> templates = new List <IOperator>(); foreach (IOperator templ in node.Domain.Operators) { templates.Add(templ.Clone() as IOperator); } // Create a clone of the incoming action's unbound operator template. IOperator temp = incoming.Template() as Operator; // Find the incoming action template in the domain list. Operator template = templates.Find(t => t.Equals(temp)) as Operator; // Remove the incoming action template from the domain list. templates.Remove(template); // Create a clone of the incoming action. Operator clone = incoming.Clone() as Operator; // Create a list of conditional effects to remove from the template. List <IAxiom> remove = new List <IAxiom>(); foreach (IAxiom conditional in clone.Conditionals) { if (conditional.Effects.Contains(effect)) { remove.Add(conditional); } } // Remove each conditional effect from the template. foreach (IAxiom rem in remove) { template.Conditionals.Remove(rem.Template() as IAxiom); } // Add the modified template to the domain list. templates.Add(template); // Push the modified list to the new domain object. newDomain.Operators = templates; // Clone the modified incoming action template. Operator newAction = template.Clone() as Operator; // Bind the cloned action with the incoming action's bindings. newAction.Bindings = incoming.Bindings; MediationTreeEdge newEdge = new MediationTreeEdge(newAction, ActionType.Exceptional, node.Incoming.Parent, node.ID); Problem newProblem = node.Problem.Clone() as Problem; newProblem.Initial = tree.GetSuccessorState(newEdge).Predicates; // Find a new plan. newPlan = PlannerInterface.Plan(planner, newDomain, newProblem); // If the modified domain can accommodate the player's action... if (newPlan.Steps.Count > 0) { node.Plan = newPlan; node.Incoming.Action = newAction; node.State = tree.GetSuccessorState(node.Incoming); node.Domain = newDomain; } } } } }
/// <summary> /// Returns a list of outgoing edges for a given node. /// </summary> /// <param name="node">The node object.</param> /// <param name="actor">The name of the current actor.</param> /// <returns>A list of outgoing edges.</returns> public List <MediationTreeEdge> GetOutgoingEdges(MediationTreeNode node, string actor) { // If it's the player's turn, return their list of actions. return(StateSpaceTools.GetAllPossibleActions(actor, node)); }
/// <summary> /// Given a character, node, and tree, returns the character's knowledge of the world history. /// </summary> /// <param name="character">The character.</param> /// <param name="node">The node that corresponds to the current state.</param> /// <param name="tree">The mediation tree.</param> /// <returns>A list of action-state pairs that represent what the character knows.</returns> public static List <Tuple <Operator, State> > GetWorldKnowledge(string character, MediationTreeNode node, MediationTree tree) { // An object to hold the character's knowledge of the world history. List <Tuple <Operator, State> > knowledge = new List <Tuple <Operator, State> >(); // Get the objective world history. List <MediationTreeNode> history = GetWorldHistory(node, tree); // Loop through the nodes in the world history branch... foreach (MediationTreeNode current in history) { // Create a new action. Operator action = null; // Make sure the current node is not the root. if (current.Incoming != null) { // Check if the character observes the incoming action. if (KnowledgeAnnotator.Observes(character, current.Incoming.Action, current.State.Predicates)) { // If so, remember that the character observed the action. action = current.Incoming.Action.Clone() as Operator; } } // Create a new state consisting only of terms the character observed. State state = new State(KnowledgeAnnotator.FullKnowledgeState(tree.Root.Domain.Predicates, tree.Root.Problem.ObjectsByType, current.State.Predicates, character)); // Store the observed action-state pair as a tuple. knowledge.Add(new Tuple <Operator, State>(action, state)); } // Return the sequence of observed action-state pairs. return(knowledge); }
/// <summary> /// Chooses a set of superposed states based on a simple utility function. /// </summary> /// <param name="observations">The sets of possible literals observed by the player mapped to the sets of states consistent with each observation.</param> /// <param name="node">The current node in the mediation tree.</param> /// <returns>A set of states chosen by the system.</returns> public static HashSet <State> ChooseUtility(Dictionary <List <IPredicate>, HashSet <State> > observations, MediationTreeNode node) { // Create a dictionary of utilities that map to sets of states. Dictionary <float, HashSet <State> > utilities = new Dictionary <float, HashSet <State> >(); // If there is more than one observation the player can make. if (observations.Keys.Count > 1) { // Loop through each set of observed literals. foreach (List <IPredicate> key in observations.Keys) { // Store how many wins and losses the set of states has. float wins = 0; float losses = 0; // Loop through each state in the set of states consistent with the current observation. foreach (State state in observations[key]) { // Store whether we are at a goal state. bool satisfiesGoal = false; // Store whether a goal state is reached by the planner. bool win = true; // Check whether the current state is a goal state. if (state.Satisfies(node.Problem.Goal)) { satisfiesGoal = true; } // Create a new problem object for the current state. Problem problem = new Problem("rob", node.Problem.OriginalName, node.Problem.Domain, node.Problem.Player, node.Problem.Objects, state.Predicates, node.Problem.Intentions, node.Problem.Goal); // Find a plan from the current state if one exists. Plan plan = PlannerInterface.Plan(Planner.FastDownward, node.Domain, problem); // Check if a goal has been reached by the state or the plan. if (plan.Steps.Count == 0 && !satisfiesGoal) { win = false; } // Record the win or loss. if (win) { wins++; } else { losses++; } } // Initialize the utility. float utility = 0; // If none of the states lost, go ahead and return the current state. if (losses == 0) { return(observations[key]); } // Otherwise, record the utility as wins divided by losses. else { utility = wins / losses; } // Map the utility onto the current state set in the dictionary. utilities[utility] = observations[key]; } } else { // Store the single set of observations. List <IPredicate> key = observations.Keys.ToArray()[0]; // Use the observations to return the single set of states. return(observations[key]); } // Return the set of states with the highest utility value. return(utilities[utilities.Keys.Max()]); }
/// <summary> /// Randomly chooses a set of superposed states. /// </summary> /// <param name="observations">The sets of possible literals observed by the player mapped to the sets of states consistent with each observation.</param> /// <param name="node">The current node in the mediation tree.</param> /// <returns>A set of states chosen by the system.</returns> public static HashSet <State> ChooseRandom(Dictionary <List <IPredicate>, HashSet <State> > observations, MediationTreeNode node) { // Create a new Random object. Random rand = new Random(); // Choose a random number to map onto a key in the observation dictionary. int pickNum = rand.Next(0, observations.Keys.Count); // Grab the observation set associated with the chosen integer. List <IPredicate> pickPreds = observations.Keys.ToArray()[pickNum]; // Return the set of states consistent with the observation. return(observations[pickPreds]); }