#!/usr/bin/env/python # Tells Linux systems how to run this file. """ Morningside AI Shell Basic system for interacting with the Morningside worldsim. This file serves as a demo of how to interact with Morningside. You can subclass this or use it for ideas. """ __author__ = "Kris Schnee" __version__ = "2009.10.4" __license__ = "Public Domain" ##### Imported Modules ##### ## Standard import os ## Third-party ## Mine ##### Constants ##### ##### Classes ##### class AIShell: """Basic interface to Morningside worldsim.""" def __init__(self,**options): self.name = options.get("name","AI") ## File names and directories to be used. self.worldsim_path = options.get("worldsim_path") if not self.worldsim_path: ## Define the directory where input/output files will be. f = open("path.txt","r") path = f.read() f.close() self.worldsim_path = path self.input_filename = "to_"+self.name.lower()+".txt" self.output_filename = "from_"+self.name.lower()+".txt" self.input_path = os.path.join(self.worldsim_path,self.input_filename) self.login_path = os.path.join(self.worldsim_path,"login_"+self.name.lower()+".txt") self.output_path = os.path.join(self.worldsim_path,self.output_filename) ## Input system: basic interpretation of the worldsim's output. self.latest_raw_input = None ## String read from a file. self.input_news = [] ## Events self.input_entities = [] ## Objects and characters self.input_system_messages = [] ## Other information """If set to False, The system will keep reading the same file over and over till the file is replaced. The main reason you'd do this is to re-use a file for debugging, rather than copying a file over and over.""" self.delete_input_file = options.get("delete_input_file",True) ## Output system self.command = "" ## The final string of text you want sent. ## Stats; not crucial to my functioning. self.input_search_cycles = 0 ## How many times have I searched? self.input_unpack_cycles = 0 def SearchForInput(self): """Read and destroy input files. Call this function as often as you like. It returns True if it found an input file in the player_io directory of the worldsim. If it did, then it put that data into self.latest_raw_input. You should then call self.UnpackInput to get basic interpretation of it. Suggested speed is 1-2 Hz, since by default Morningside runs at a leisurely 1 Hz.""" self.input_search_cycles += 1 ## FYI print "Searching for input file..." if os.path.exists(self.input_path): f = open(self.input_path,"r") text = f.read() f.close() if self.delete_input_file: os.remove(self.input_path) self.latest_raw_input = text return True return False def UnpackInput(self): """Convert raw input data to a usable form. File format: Each line is one of these, determined by what it starts with: # Comment * News (something that happened) @ Entity $ System (info like "this rule is in play") In each case, character 0 is that marker and 1 is a blank space. """ if not self.latest_raw_input: ## Generally this won't happen, since it's never cleared. return False self.input_unpack_cycles += 1 ## FYI lines = [l for l in self.latest_raw_input.split("\n") if l and l[0] != "#"] result = {"news":[],"entities":[],"system":[]} for line in lines: ## Split into key and value, as with the Python dict format. s = line[2:].split(":") if len(s) > 2: ## This line uses an extra colon somewhere. Messy to deal with. f = line.find(":") k, v = line[2:f], line[f+1:] else: k, v = s v = v.lstrip(" ") ## Store the info based on what type this line is. if line[0] == "@": ## eg. "@ 4,2: 007 coin" coords = k.split(",") coords = [int(coords[0]),int(coords[1])] ## eg. [4,2] v = v.split(" ") e = {"ID":v[0], "coords":coords, "name":v[1], "alive":len(v)>2 and v[2].lower() == "alive" } result["entities"].append(e) elif line[0] == "*": ## eg. "*5: get ok" result["news"].append((k,v)) elif line[0] == "$": ## eg. "you: 5" result["system"].append((k,v)) self.input_news = result["news"] self.input_entities = result["entities"] self.input_system_messages = result["system"] return True def WriteOutput(self): """Create an output file based on my chosen command. Assumes that you've put a string of text in self.command, containing a valid command to Morningside.""" if not self.command: return f = open(self.output_path,"w") f.write(self.command) f.close() def WriteLogin(self): """Put a login file for myself into player_io. When Morningside runs, it creates characters for each file it finds of the form "login_[name].txt" in player_io. This function makes sure that there's a login file there for itself, matching whatever name the AI has been given. It copies the text from a login file in the directory where the AI is running. If there already is a login file in player_io, this function does nothing.""" if os.path.exists(self.login_path): print "Using my existing login file." return else: print "Writing a login file for myself." if not os.path.exists(os.path.join(os.getcwd(),"login_"+self.name.lower()+".txt")): ## There isn't a local one to copy? Write a blank one. f = open("login_"+self.name.lower()+".txt","w") f.close() f = open("login_"+self.name.lower()+".txt","r") text = f.read() f.close() f = open(self.login_path,"w") f.write(text) f.close() def CoreLoop(self): """Demo of a possible main loop. Note: This demo never quits on its own.""" import time self.input_search_speed = 1.0 self.input_next_search_time = time.time() self.input_wait_time = 1.0 ## Make sure the world knows I'm playing. self.WriteLogin() print "Waiting for first input." while True: ## Search for input every so often. while time.time() < self.input_next_search_time: time.sleep(self.input_wait_time) self.input_next_search_time = time.time() + self.input_wait_time ## The AI's action and thought are synchronized to the world's speed. new_input = self.SearchForInput() if new_input: print "\nGot input." self.UnpackInput() print "Unpacked input. Result:" print "News: "+str(self.input_news) print "Entities: "+str(self.input_entities) print "System messages: "+str(self.input_system_messages) ## Insert Brain Here. self.command = "say Hello world!" self.WriteOutput() print "Done." ##### Other Functions ##### ##### Autorun ##### if __name__ == "__main__": ## Runs if this module is the main module (not imported). ai = AIShell() print "Demo AI created as 'ai'. Type 'ai.CoreLoop()' to try it."