Exemplo n.º 1
0
class LevelUp():
    def __init__(self, code):
        self.lines = code.splitlines()
        self.line_num = 0

        # Get name from first line (optional)
        try:
            name_line = re.match("Name\s?:\s?(.+)", self.lines[0], re.I)
            name = name_line.groups(0)[0]
            self.lines = self.lines[1:]

        except IndexError:
            name = "Hero"

        # Get class
        try:
            class_line = re.match("Class\s?:\s?(.+)", self.lines[0], re.I)
            hero_class = class_line.groups(0)[0].title()
            self.lines = self.lines[1:]

        except IndexError:
            exit("Error: Missing or invalid class line")

        self.hero = Hero(name, hero_class)
        self.labels = {}

        self.quest = None

        self.input = None
        self.expected = None
        self.output = None
        self.quest_line_num = None

        self.getch = _Getch()


    def run(self):
        while self.line_num < len(self.lines):
            self.execute_line(self.lines[self.line_num])
            self.line_num += 1


    def execute_line(self, line):
        line = line.strip()

        if not line or line[0] == '#':
            return

        line_types = [["^(\w+):$", self.set_label, []],
                      ["^jmp (\w+)$", self.jump_to_label, [Skills.JMP]],
                      ["^jez (\w+)\s+(\w+)$", self.jump_to_label, [Skills.JEZ]],
                      ["^inc (\w+)$", self.hero.increment, [Skills.INCREMENT]],
                      ["^dec (\w+)$", self.hero.decrement, [Skills.DECREMENT]],
                      ["^pch (\w+)$", self.print_char, [Skills.PRINT_CHAR]],
                      ["^ich (\w+)$", self.input_char, [Skills.INPUT_CHAR]],
                      ["^add (\w+) (\w+) (\w+)$", self.hero.add, [Skills.ADDITION]],
                      ["^Begin quest: '([\w ]+)'$", self.begin_quest, []],
                      ["^Claim reward$", self.claim_reward, []]]

        for regex, func, prereqs in line_types:
            match = re.match(regex, line, re.I)

            if match:
                for skill in prereqs:
                    if skill not in self.hero.skills:
                        exit("Error: need skill '{}' for line '{}'!".format(skill.value, line))

                func(*match.groups())
                return

        exit("Error: Invalid line '{}'".format(line))


    def set_label(self, label):
        self.labels[label] = self.line_num


    def jump_to_label(self, *args):
        label = args[-1]

        if len(args) < 2 or self.hero.get_elem(args[0]) == 0:
            if label not in self.labels:
                try:
                    label_match = re.match("(\w+):", self.lines[self.line_num])

                    while not label_match:
                        self.line_num += 1
                        label_match = re.match("(\w+):", self.lines[self.line_num])

                except IndexError:
                    exit("Error: Invalid label {}".format(label))

            else:
               self.line_num = self.labels[label]


    def input_char(self, name):
        if self.quest is None:
            if sys.stdin.isatty():
                # Console
                char = getch()

                if ord(char) == 3:
                    exit("^C")

            else:
                char = sys.stdin.read(1)

        else:
            if not self.input:
                exit("Error: Read too much input during quest")

            char = self.input[0]
            self.input = self.input[1:]            

        self.hero.input_char(name, char)


    def print_char(self, name):
        if self.quest is None:
            print(self.hero.print_char(name), end="")

        else:
            self.output.append(self.hero.print_char(name))


    def begin_quest(self, quest_name):
        quest_name = quest_name.lower()

        if quest_name in self.hero.completed_quests:
            self.line_num = self.hero.completed_quests[quest_name]
            return

        quest = {q.name.lower(): q for q in [QuestAddition]}[quest_name]

        if self.quest is not None and not isinstance(self.quest, quest):
            exit("Error: Cannot be part of two quests at once")

        self.quest = quest
        print("Initiated quest '{}'!".format(self.quest.name), file=sys.stderr)

        self.input, self.expected = next(self.quest.cases)
        self.output = []
        self.quest_line_num = self.line_num


    def claim_reward(self):
        if self.quest is None:
            exit("Error: Cannot claim reward while not in quest")

        self.output = "".join(self.output)

        if self.output != self.expected:
            exit("Error: Failed quest '{}'.\nExpected: {}\nActual: {}"
                 .format(self.quest.name, self.expected, self.output))

        try:
            self.input, self.expected = next(self.quest.cases)
            self.output = []
            self.line_num = self.quest_line_num

        except StopIteration:
            print("Completed quest '{}'!".format(self.quest.name), file=sys.stderr)
            self.hero.complete_quest(self.quest)

            self.quest = None
            self.input = None
            self.expected = None
            self.output = None
            self.quest_line_num = None