Ejemplo n.º 1
0
class HAL9000(object):

    _greetings = ["Good ${daytime}! This is HAL.", "Hello.", "Good ${daytime}."]

    _default_responses = [
        "I'm sorry, I can't understand you...",
        "I can't recognize your request. Please, rephrase your question.",
        "Yeah, that's fine... Stop mumbling, please!",
        "Can you be more exact in your requests?",
        "Please, remember that I am an Artificial Intelligence! And sometimes I can be more artificial than "
        "intelligent...",
    ]

    _responses = [
        (r"^(hal|hey you)?[\.\!\s]$", ["Yes?", "HAL9000 is listening.", "HAL on line.", "What?"]),
        (r"(hi|hello|hey)[\,\s\.\!]*", _greetings),
        (r"good (morning|day|evening|night)", _greetings),
        (r"where am i\?*", ["You are in the ${location} now."]),
        (r"(open|please, open|please open)([\w\s]+)", ["${execute} open %2"]),
        (r"(close|please, close|please close)([\w\s]+)", ["${execute} close %2"]),
        (r"(where can i go|where to go)\?*", ["${execute} transitions"]),
        (r"(how can i get to the|how can i get to)([\w\s]+)\?", ["${execute} where %2"]),
        (
            r"(take me to the|take me to|go to the|go to|relocate to the|relocate to|i want to go to the|i want to go to|"
            r"i want to get to the|i want to get to|get to the|get to)([\w\s]+)",
            ["${execute} goto %2"],
        ),
        (r"(ok|o\.k\.|fine|excellent|cool)", ["Good.", "Do you think so?", "Awesome.", "Perfect."]),
        (r"(really|are you serious)\?", ["Of course!", "Sure!", "Yes!", "Absolutely."]),
        (r"([\w\s]+)", ["What do you mean by saying '%1'?"] + _default_responses),
        (r"", _default_responses),
    ]

    def __init__(self, terminal):
        """Constructor for the agent, stores references to systems and initializes internal memory.
        """
        self._terminal = terminal
        self._map = Map()
        self._create_map()
        self._location = self._map.get_room("start location")
        self._chatbot = nltk.chat.Chat(HAL9000._responses, nltk.chat.util.reflections)
        self._commands = {
            "quit": lambda x: vispy.app.quit(),
            "open": self._try_to_open_door,
            "close": self._try_to_close_door,
            "goto": self._try_to_relocate,
            "relocate": self._try_to_relocate,
            "where": self._print_where,
            "transitions": self._print_possible_transitions,
        }

    def _create_map(self):
        self._map.add_rooms(
            (
                "start location",
                "main corridor",
                "kitchen",
                "store",
                "command post",
                "bathroom",
                "engineering module",
                "ventilating trunk",
            )
        )

        self._map.add_door("gate 1", "start location", "main corridor")
        self._map.add_door("engineering hatch", "start location", "engineering module")
        self._map.add_door("vent flap 1", "start location", "ventilating trunk")

        self._map.add_door("gate 2", "main corridor", "command post")
        self._map.add_door("gate 3", "main corridor", "engineering module")
        self._map.add_door("gate 4", "main corridor", "kitchen")
        self._map.add_door("gate 5", "main corridor", "bathroom")

        self._map.add_door("vent flap 2", "ventilating trunk", "command post")
        self._map.add_door("vent flap 3", "ventilating trunk", "engineering module")
        self._map.add_door("vent flap 4", "ventilating trunk", "kitchen")
        self._map.add_door("vent flap 5", "ventilating trunk", "bathroom")
        self._map.add_door("vent flap 6", "ventilating trunk", "store")

        self._map.add_door("small door", "kitchen", "store")

    def on_input(self, evt):
        """Called when user types anything in the terminal, connected via event.
        """
        player_input = evt.text.lower().replace("i'm", "i am")
        output = self._chatbot.respond(player_input)

        if "${execute}" in output:
            output = output[11:]
            self._execute_command(output)
            return

        if "${daytime}" in output:
            output = output.replace("${daytime}", self._get_current_day_time_string())

        if "${location}" in output:
            output = output.replace("${location}", self._location.name())

        self._terminal.log(output, align="right", color="#00805A")

    def on_command(self, evt):
        """Called when user types a command starting with `/` also done via events.
        """
        self._execute_command(evt.text)

    def _execute_command(self, command):
        words = command.split()
        if words and words[0] in self._commands:
            execute_command = self._commands[words[0]]
            attribute = " ".join(words[1:]).lower()
            execute_command(attribute)

        else:
            self._terminal.log("Command '{}' unknown.".format(command), align="left", color="#ff3000")
            self._terminal.log("I'm afraid I can't do that.", align="right", color="#00805A")

    def update(self, _):
        """Main update called once per second via the timer.
        """
        pass

    def _print_where(self, where):
        if not where:
            output = self._chatbot.respond("where am i?").replace("${location}", self._location.name())
            self._terminal.log(output, align="right", color="#00805A")
            return

        if where in self._location.get_doors():
            door = self._map.get_door(where)
            to = []
            for room_name in door.between():
                if room_name != self._location.name():
                    to.append(room_name)
            self._terminal.log(
                "{} is leading to the {}".format(where[:1].upper() + where[1:], ", ".join(to)),
                align="right",
                color="#00805A",
            )
            return

        if where in self._location.possible_transitions():
            doors = self._location.possible_transitions()[where]
            self._terminal.log(
                "You can get to the {} through {}".format(where, ", ".join(doors)), align="right", color="#00805A"
            )
            return

        if where == self._location.name():
            self._print_possible_transitions()
            return

        self._terminal.log(
            "Hm... There is no {} near the {}.".format(where, self._location.name()), align="right", color="#00805A"
        )

    def _print_possible_transitions(self, _):
        self._terminal.log("From {} you can go:".format(self._location.name()), align="right", color="#00805A")
        for room_name, doors in self._location.possible_transitions().items():
            self._terminal.log(
                "- to the {} through {}.".format(room_name, ", ".join(doors)), align="right", color="#00805A"
            )

    def _try_to_relocate(self, location_name):
        if not location_name:
            self._terminal.log("Where do you want to go?", align="right", color="#00805A")
            for room_name in self._location.possible_transitions():
                self._terminal.log("- {}.".format(room_name), align="right", color="#00805A")
            return

        new_location = self._map.get_room(location_name)
        if new_location is None:
            self._terminal.log("Location '{}' unknown.".format(location_name), align="left", color="#ff3000")
            self._terminal.log("There is no {} on the ship.".format(location_name), align="right", color="#00805A")

        elif location_name != self._location.name():
            doors = self._location.get_doors(location_name)
            if doors:
                for door_name in doors:
                    door = self._map.get_door(door_name)
                    if door.state() == DoorState.OPEN:
                        self._location = new_location
                        self._terminal.log("", align="center", color="#404040")
                        self._terminal.log(
                            "\u2014 Now in the {}. \u2014".format(location_name), align="center", color="#404040"
                        )
                        return
                self._terminal.log(
                    "I'm afraid all doors to the {} are closed.".format(location_name), align="right", color="#00805A"
                )
                self._terminal.log(
                    "You have to open one of these doors: {}".format(", ".join(doors)), align="right", color="#00805A"
                )

            else:
                self._terminal.log(
                    "I'm afraid you can't relocate to the {} from your current location.".format(location_name),
                    align="right",
                    color="#00805A",
                )

        else:
            self._terminal.log("You are already in the {}!".format(location_name), align="right", color="#00805A")

    def _try_to_open_door(self, door_name):
        self._try_to_operate_door(door_name, DoorState.OPEN)

    def _try_to_close_door(self, door_name):
        self._try_to_operate_door(door_name, DoorState.CLOSED)

    def _try_to_operate_door(self, door_name, new_state):
        doors = self._location.get_doors()
        if not door_name:
            self._terminal.log("Which door do you want to open?", align="right", color="#00805A")
            for door_name in doors:
                self._terminal.log("- {}.".format(door_name), align="right", color="#00805A")
            return

        if door_name not in doors:
            self._terminal.log(
                "I'm afraid there is no {} in the {}.".format(door_name, self._location.name()),
                align="right",
                color="#00805A",
            )
            return

        state_name = DoorState.to_str(new_state)
        door = self._map.get_door(door_name)
        if door.state() == new_state:
            self._terminal.log("The {} already {}.".format(door_name, state_name), align="right", color="#00805A")
            return

        self._terminal.log("The {} is now {}.".format(door_name, state_name), align="right", color="#00805A")
        door.set_state(new_state)

    @staticmethod
    def _get_current_day_time_string():
        current_hour = datetime.now().hour
        if current_hour < 5 or current_hour > 22:
            return "night"
        if current_hour < 12:
            return "morning"
        if current_hour < 17:
            return "day"
        return "evening"