175 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			175 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""
 | 
						|
Author: Joon Sung Park (joonspk@stanford.edu)
 | 
						|
 | 
						|
File: execute.py
 | 
						|
Description: This defines the "Act" module for generative agents. 
 | 
						|
"""
 | 
						|
import sys
 | 
						|
import random
 | 
						|
sys.path.append('../../')
 | 
						|
 | 
						|
from global_methods import *
 | 
						|
from path_finder import *
 | 
						|
from utils import *
 | 
						|
 | 
						|
def execute(persona, maze, personas, plan): 
 | 
						|
  """
 | 
						|
  Given a plan (action's string address), we execute the plan (actually 
 | 
						|
  outputs the tile coordinate path and the next coordinate for the 
 | 
						|
  persona). 
 | 
						|
 | 
						|
  INPUT:
 | 
						|
    persona: Current <Persona> instance.  
 | 
						|
    maze: An instance of current <Maze>.
 | 
						|
    personas: A dictionary of all personas in the world. 
 | 
						|
    plan: This is a string address of the action we need to execute. 
 | 
						|
       It comes in the form of "{world}:{sector}:{arena}:{game_objects}". 
 | 
						|
       It is important that you access this without doing negative 
 | 
						|
       indexing (e.g., [-1]) because the latter address elements may not be 
 | 
						|
       present in some cases. 
 | 
						|
       e.g., "dolores double studio:double studio:bedroom 1:bed"
 | 
						|
    
 | 
						|
  OUTPUT: 
 | 
						|
    execution
 | 
						|
  """
 | 
						|
  if "<random>" in plan and persona.scratch.planned_path == []: 
 | 
						|
    persona.scratch.act_path_set = False
 | 
						|
 | 
						|
  # <act_path_set> is set to True if the path is set for the current action. 
 | 
						|
  # It is False otherwise, and means we need to construct a new path. 
 | 
						|
  if not persona.scratch.act_path_set: 
 | 
						|
    # <target_tiles> is a list of tile coordinates where the persona may go 
 | 
						|
    # to execute the current action. The goal is to pick one of them.
 | 
						|
    target_tiles = None
 | 
						|
 | 
						|
    print ('aldhfoaf/????')
 | 
						|
    print (plan)
 | 
						|
 | 
						|
    if "<persona>" in plan: 
 | 
						|
      # Executing persona-persona interaction.
 | 
						|
      target_p_tile = (personas[plan.split("<persona>")[-1].strip()]
 | 
						|
                       .scratch.curr_tile)
 | 
						|
      potential_path = path_finder(maze.collision_maze, 
 | 
						|
                                   persona.scratch.curr_tile, 
 | 
						|
                                   target_p_tile, 
 | 
						|
                                   collision_block_id)
 | 
						|
      if len(potential_path) <= 2: 
 | 
						|
        target_tiles = [potential_path[0]]
 | 
						|
      else: 
 | 
						|
        potential_1 = path_finder(maze.collision_maze, 
 | 
						|
                                persona.scratch.curr_tile, 
 | 
						|
                                potential_path[int(len(potential_path)/2)], 
 | 
						|
                                collision_block_id)
 | 
						|
        potential_2 = path_finder(maze.collision_maze, 
 | 
						|
                                persona.scratch.curr_tile, 
 | 
						|
                                potential_path[int(len(potential_path)/2)+1], 
 | 
						|
                                collision_block_id)
 | 
						|
        if len(potential_1) <= len(potential_2): 
 | 
						|
          target_tiles = [potential_path[int(len(potential_path)/2)]]
 | 
						|
        else: 
 | 
						|
          target_tiles = [potential_path[int(len(potential_path)/2+1)]]
 | 
						|
    
 | 
						|
    elif "<waiting>" in plan: 
 | 
						|
      # Executing interaction where the persona has decided to wait before 
 | 
						|
      # executing their action.
 | 
						|
      x = int(plan.split()[1])
 | 
						|
      y = int(plan.split()[2])
 | 
						|
      target_tiles = [[x, y]]
 | 
						|
 | 
						|
    elif "<random>" in plan: 
 | 
						|
      # Executing a random location action.
 | 
						|
      plan = ":".join(plan.split(":")[:-1])
 | 
						|
      target_tiles = maze.address_tiles[plan]
 | 
						|
      target_tiles = random.sample(list(target_tiles), 1)
 | 
						|
 | 
						|
    else: 
 | 
						|
      # This is our default execution. We simply take the persona to the
 | 
						|
      # location where the current action is taking place. 
 | 
						|
      # Retrieve the target addresses. Again, plan is an action address in its
 | 
						|
      # string form. <maze.address_tiles> takes this and returns candidate 
 | 
						|
      # coordinates. 
 | 
						|
      if plan not in maze.address_tiles: 
 | 
						|
        maze.address_tiles["Johnson Park:park:park garden"] #ERRORRRRRRR
 | 
						|
      else: 
 | 
						|
        target_tiles = maze.address_tiles[plan]
 | 
						|
 | 
						|
    # There are sometimes more than one tile returned from this (e.g., a tabe
 | 
						|
    # may stretch many coordinates). So, we sample a few here. And from that 
 | 
						|
    # random sample, we will take the closest ones. 
 | 
						|
    if len(target_tiles) < 4: 
 | 
						|
      target_tiles = random.sample(list(target_tiles), len(target_tiles))
 | 
						|
    else:
 | 
						|
      target_tiles = random.sample(list(target_tiles), 4)
 | 
						|
    # If possible, we want personas to occupy different tiles when they are 
 | 
						|
    # headed to the same location on the maze. It is ok if they end up on the 
 | 
						|
    # same time, but we try to lower that probability. 
 | 
						|
    # We take care of that overlap here.  
 | 
						|
    persona_name_set = set(personas.keys())
 | 
						|
    new_target_tiles = []
 | 
						|
    for i in target_tiles: 
 | 
						|
      curr_event_set = maze.access_tile(i)["events"]
 | 
						|
      pass_curr_tile = False
 | 
						|
      for j in curr_event_set: 
 | 
						|
        if j[0] in persona_name_set: 
 | 
						|
          pass_curr_tile = True
 | 
						|
      if not pass_curr_tile: 
 | 
						|
        new_target_tiles += [i]
 | 
						|
    if len(new_target_tiles) == 0: 
 | 
						|
      new_target_tiles = target_tiles
 | 
						|
    target_tiles = new_target_tiles
 | 
						|
 | 
						|
    # Now that we've identified the target tile, we find the shortest path to
 | 
						|
    # one of the target tiles. 
 | 
						|
    curr_tile = persona.scratch.curr_tile
 | 
						|
    collision_maze = maze.collision_maze
 | 
						|
    closest_target_tile = None
 | 
						|
    path = None
 | 
						|
    for i in target_tiles: 
 | 
						|
      # path_finder takes a collision_mze and the curr_tile coordinate as 
 | 
						|
      # an input, and returns a list of coordinate tuples that becomes the
 | 
						|
      # path. 
 | 
						|
      # e.g., [(0, 1), (1, 1), (1, 2), (1, 3), (1, 4)...]
 | 
						|
      curr_path = path_finder(maze.collision_maze, 
 | 
						|
                              curr_tile, 
 | 
						|
                              i, 
 | 
						|
                              collision_block_id)
 | 
						|
      if not closest_target_tile: 
 | 
						|
        closest_target_tile = i
 | 
						|
        path = curr_path
 | 
						|
      elif len(curr_path) < len(path): 
 | 
						|
        closest_target_tile = i
 | 
						|
        path = curr_path
 | 
						|
 | 
						|
    # Actually setting the <planned_path> and <act_path_set>. We cut the 
 | 
						|
    # first element in the planned_path because it includes the curr_tile. 
 | 
						|
    persona.scratch.planned_path = path[1:]
 | 
						|
    persona.scratch.act_path_set = True
 | 
						|
  
 | 
						|
  # Setting up the next immediate step. We stay at our curr_tile if there is
 | 
						|
  # no <planned_path> left, but otherwise, we go to the next tile in the path.
 | 
						|
  ret = persona.scratch.curr_tile
 | 
						|
  if persona.scratch.planned_path: 
 | 
						|
    ret = persona.scratch.planned_path[0]
 | 
						|
    persona.scratch.planned_path = persona.scratch.planned_path[1:]
 | 
						|
 | 
						|
  description = f"{persona.scratch.act_description}"
 | 
						|
  description += f" @ {persona.scratch.act_address}"
 | 
						|
 | 
						|
  execution = ret, persona.scratch.act_pronunciatio, description
 | 
						|
  return execution
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |