Exemplo n.º 1
0
class Browser:
    def __init__(self):
        self.window = tkinter.Tk()
        self.canvas = tkinter.Canvas(self.window, width=WIDTH, height=HEIGHT)
        self.canvas.pack(expand=True, fill="both")
        self.display_list = []
        self.scroll = 0
        self.window.bind("<Up>", self.scrollup)
        self.window.bind("<Down>", self.scrolldown)
        self.window.bind("<Configure>", self.windowresize)
        self.window.bind("<Button-1>", self.handle_click)
        self.window.bind("<Key>", self.keypress)
        self.window.bind("<Return>", self.pressenter)
        self.width = WIDTH
        self.height = HEIGHT
        self.hstep = HSTEP
        self.vstep = VSTEP
        self.scroll_step = SCROLL_STEP
        self.history = []
        self.focus = None
        self.address_bar = ""
        self.timer = Timer()
        self.cookies = {}
        # self.cookies["username"] = "******" # Test of security

        # http://www.zggdwx.com/

    def scrollup(self, e):
        self.scroll -= self.scroll_step
        self.scroll = min(self.scroll, self.max_y)
        self.scroll = max(0, self.scroll)
        self.render()

    def scrolldown(self, e):
        self.scroll += self.scroll_step
        self.scroll = min(self.scroll, self.max_y)
        self.scroll = max(0, self.scroll)
        self.render()

    def windowresize(self, e):
        if e.width < 10: return
        if e.width == self.width and e.height == self.height: return
        self.width = e.width
        self.height = e.height
        print("Layout called from windowresize")
        self.layout()

    def keypress(self, e):
        if not (len(e.char) == 1 and 0x20 <= ord(e.char) < 0x7f):
            return
        if not self.focus:
            return
        elif self.focus == "address bar":
            self.address_bar += e.char
            self.render()
        else:
            self.focus.node.attributes["value"] += e.char
            self.dispatch_event("change", self.focus.node)
            print("Layout called from keypress")
            self.reflow(self.focus)

    def pressenter(self, e):
        if self.focus == "address bar":
            self.focus = None
            self.load(self.address_bar)
        elif isinstance(self.focus, InputLayout):
            self.submit_form(self.focus.node)

    def handle_click(self, e):
        self.focus = None
        if e.y < 60:  # Browser chrome
            if 10 <= e.x < 35 and 10 <= e.y < 50:
                self.go_back()
            elif 50 <= e.x < 790 and 10 <= e.y < 50:
                self.focus = "address bar"
                self.address_bar = ""
                self.render()
        else:
            x, y = e.x, e.y + self.scroll - 60
            obj = find_layout(x, y, self.document)
            if not obj: return
            elt = obj.node
            if elt and self.dispatch_event("click", elt): return
            while elt:
                if isinstance(elt, TextNode):
                    pass
                elif is_link(elt):
                    url = relative_url(elt.attributes["href"], self.url)
                    self.load(url)
                elif elt.tag == "input":
                    elt.attributes["value"] = ""
                    self.focus = obj
                    print("Layout called from handle_click in input elt")
                    return self.reflow(self.focus)
                elif elt.tag == "button":
                    self.submit_form(elt)
                elt = elt.parent

    def submit_form(self, elt):
        while elt and elt.tag != "form":
            elt = elt.parent
        if not elt: return
        if self.dispatch_event("submit", elt): return
        inputs = find_inputs(elt, [])
        body = ""
        for input in inputs:
            name = input.attributes["name"]
            value = input.attributes.get("value", "")
            body += "&" + name + "=" + value.replace(" ", "%20")
        body = body[1:]

        url = relative_url(elt.attributes["action"], self.url)
        self.load(url, body=body)

    def layout(self, tree=None):
        self.timer.start("Layout Initialization")
        if not tree:
            tree = self.cached_tree
        else:
            self.cached_tree = tree
        self.document = DocumentLayout(tree)
        self.reflow(self.document)

    def reflow(self, obj):
        self.timer.start("Style")
        style(obj.node, obj.parent, self.rules)
        self.timer.start("Layout (phase 1A)")
        obj.size()
        self.timer.start("Layout (phase 1B)")
        while obj.parent:
            obj.parent.compute_height()
            obj = obj.parent
        self.timer.start("Layout (phase 2)")
        self.document.position()
        self.timer.start("Display List")
        self.display_list = []
        self.document.draw(self.display_list)
        self.max_y = self.document.h
        self.render()

    def render(self):
        self.canvas.delete("all")
        self.timer.start("Rendering")
        for cmd in self.display_list:
            if cmd.y1 > self.scroll + self.height - 60:
                continue
            if cmd.y2 < self.scroll:
                continue
            cmd.draw(self.scroll - 60, self.canvas)

        self.timer.start("Chrome")
        self.canvas.create_rectangle(0, 0, 800, 60, width=0, fill='light gray')

        self.canvas.create_rectangle(50, 10, 790, 50)
        font = tkinter.font.Font(family="Courier", size=30)
        self.canvas.create_text(55,
                                15,
                                anchor='nw',
                                text=self.address_bar,
                                font=font)

        self.canvas.create_rectangle(10, 10, 35, 50)
        self.canvas.create_polygon(15, 30, 30, 15, 30, 45, fill='black')
        self.timer.stop()

        if self.focus == "address bar":
            w = font.measure(self.address_bar)
            self.canvas.create_line(55 + w, 15, 55 + w, 45)
        elif isinstance(self.focus, InputLayout):
            text = self.focus.node.attributes.get("value", "")
            x = self.focus.x + self.focus.font.measure(text)
            y = self.focus.y - self.scroll + 60
            self.canvas.create_line(x, y, x, y + self.focus.h)

    def cookie_string(self):
        origin = url_origin(self.history[-1])
        cookies = self.cookies.get(origin, {})
        cookie_string = ""
        for key, value in cookies.items():
            cookie_string += "&" + key + "=" + value
        return cookie_string[1:]

    def load(self, url, body=None):
        self.address_bar = url
        self.url = url
        self.history.append(url)
        self.timer.start("Downloading")
        req_headers = {"Cookie": self.cookie_string()}
        headers, body = request(url, headers=req_headers, payload=body)
        if "set-cookie" in headers:
            kv, *params = headers["set-cookie"].split(";")
            key, value = kv.split("=", 1)
            self.cookies[key] = value
            print(f"Received Cookie key={key}, value={value}")
            origin = url_origin(self.history[-1])
            self.cookies.setdefault(origin, {})[key] = value
        self.timer.start("Parsing HTML")
        self.nodes = parse(lex(body))

        self.timer.start("Parsing CSS")
        with open("browser/src/browser.css") as f:
            browser_style = f.read()
            rules = CSSParser(browser_style).parse()
        for link in find_links(self.nodes, []):
            headers, body = request(relative_url(link, url),
                                    headers=req_headers)
            rules.extend(CSSParser(body).parse())

        # tree_to_string(self.nodes)
        rules.sort(key=lambda selector_body: selector_body[0].priority(),
                   reverse=True)
        self.rules = rules

        self.timer.start("Running JS")
        self.setup_js()
        for script in find_scripts(self.nodes, []):
            header, body = request(relative_url(script, self.history[-1]),
                                   headers=req_headers)
            try:
                # print("Script returned: ", self.js_environment.evaljs(body))
                self.js_environment.evaljs(body)
            except dukpy.JSRuntimeError as e:
                print("Script", script, "crashed", e)

        print("Layout called from load")
        self.layout(self.nodes)

    def go_back(self):
        if len(self.history) > 2:
            self.history.pop()
            back = self.history.pop()
            self.load(back)

    def setup_js(self):
        self.node_to_handle = {}
        self.handle_to_node = {}
        self.js_environment = dukpy.JSInterpreter()
        self.js_environment.export_function("log", print)
        self.js_environment.export_function("querySelectorAll",
                                            self.js_querySelectorAll)
        self.js_environment.export_function("getAttribute",
                                            self.js_getAttribute)
        self.js_environment.export_function("innerHTML", self.js_innerHTML)
        self.js_environment.export_function("cookie", self.js_cookie)
        with open("browser/src/runtime.js") as f:
            self.js_environment.evaljs(f.read())

    def js_cookie(self):
        origin = url_origin(self.history[-1])
        cookies = self.cookies.get(origin, {})

        cookie_string = ""
        for key, value in cookies.items():
            cookie_string += "&" + key + "=" + value
        return cookie_string[1:]

    def js_querySelectorAll(self, sel):
        selector, _ = CSSParser(sel + "{").selector(0)
        elts = find_selected(self.nodes, selector, [])
        return [self.make_handle(elt) for elt in elts]

    def make_handle(self, elt):
        if id(elt) not in self.node_to_handle:
            handle = len(self.node_to_handle)
            self.node_to_handle[id(elt)] = handle
            self.handle_to_node[handle] = elt
        else:
            handle = self.node_to_handle[id(elt)]
        return handle

    def js_getAttribute(self, handle, attr):
        elt = self.handle_to_node[handle]
        return elt.attributes.get(attr, None)

    def js_innerHTML(self, handle, s):
        doc = parse(lex("<html><body>" + s + "</body></html>"))
        new_nodes = doc.children[0].children
        elt = self.handle_to_node[handle]
        elt.children = new_nodes
        for child in elt.children:
            child.parent = elt
        print("Layout called from js_innerHTML")
        self.reflow(layout_for_node(self.document, elt))

    def dispatch_event(self, type, elt):
        handle = self.make_handle(elt)
        code = "__runHandlers({}, \"{}\")".format(handle, type)
        do_default = self.js_environment.evaljs(code)
        return not do_default
Exemplo n.º 2
0
class Game:
    def __init__(self, config=None):
        self.field_width = 10
        self.field_height = 10
        self.field_objects = []
        for j in range(self.field_height):
            self.field_objects.append(
                [Objects.empty_field for i in range(self.field_width)])
        self.checker = Checker(self)
        self.player1 = Player(config.white, 1)
        self.player2 = Player(config.black, 2)
        self.timer1 = Timer(config.timer, config.duration)
        self.timer2 = Timer(config.timer, config.duration)
        self.cur_timer = None
        self.new_game = False
        self.is_first_move = True
        self.is_over = False
        self.cur_figure = None
        self.cur_player = self.player1
        self.cur_move = None
        self.init_field()
        self.was_taken = False
        self.logger = Logger()
        self.dump_logger = Logger()
        self.moves_quantity = 0
        self.game_file = config.game_file
        self.pause = 0.2
        self.load = config.load_game
        self.loaded_moves = queue.Queue()
        self.loaded_moves_amount = 0
        if self.load:
            self.load_moves()
        self.interface = Interface(self)
        # self.interface = ConsoleInterface(self)

    def init_field(self):
        for i in range(self.field_height):
            for j in range(self.field_width):
                color = 'black' if ((i + j) & 1) == 1 else 'white'
                if color == 'black':
                    if i <= 3:
                        self.field_objects[i][j] = Objects.white_draught
                    elif i >= 6:
                        self.field_objects[i][j] = Objects.black_draught

    def read_field(self, file_name):
        text = open(file_name).read().split()
        for i, line in enumerate(text):
            for j, c in enumerate(line):
                cur = int(c)
                if cur == 0:
                    self.field_objects[i][j] = Objects.empty_field
                elif cur == 1:
                    self.field_objects[i][j] = Objects.white_draught
                elif cur == 2:
                    self.field_objects[i][j] = Objects.black_draught
                elif cur == 3:
                    self.field_objects[i][j] = Objects.white_king
                else:
                    self.field_objects[i][j] = Objects.black_king

    def run(self):
        self.dump_logger.add(self.get_dump())
        updating_thread = threading.Thread(name='updating_thread',
                                           target=self.update)
        updating_thread.start()
        self.interface.start()
        return self.new_game

    def load_moves(self):
        try:
            with open(self.game_file, 'r') as f:
                self.pause = 0
                white = eval(f.readline().split()[1])
                black = eval(f.readline().split()[1])
                self.player1.person = white
                self.player2.person = black
                lines = []
                for line in f:
                    lines.append(line)
                for line in lines[:-2]:
                    move = line.split(':')
                    move = eval(move[1])
                    self.loaded_moves.put(move)
                    self.loaded_moves_amount += 1
                enable_timer = eval(lines[-2])
                duration = list(map(int, lines[-1].split()))
                self.timer1 = Timer(enable_timer, duration[0])
                self.timer2 = Timer(enable_timer, duration[1])
        except:
            raise Exception('Something wrong with file')

    def next_move(self, player):
        if self.loaded_moves.empty():
            self.finish_loading()
        if self.load:
            return self.loaded_moves.get()
        if player.person:
            return player.make_person_move(self)
        return player.make_bot_move(self)

    def finish_loading(self):
        self.load = False
        self.pause = 0.2
        self.interface.loading.close_loading()

    def update_elements(self, move):
        x1, y1 = move[0][0], move[0][1]
        x2, y2 = move[1][0], move[1][1]
        self.was_taken = False
        self.field_objects[y2][x2] = self.field_objects[y1][x1]
        self.interface.elements_for_update.append((y2, x2))
        self.field_objects[y1][x1] = Objects.empty_field
        self.interface.elements_for_update.append((y1, x1))
        if abs(x2 - x1) >= 2:
            cur_x = x1
            cur_y = y1
            while cur_x + sgn(x2 - x1) != x2:
                cur_x += sgn(x2 - x1)
                cur_y += sgn(y2 - y1)
                if self.field_objects[cur_y][cur_x] != Objects.empty_field:
                    self.was_taken = True
                    self.interface.elements_for_update.append((cur_y, cur_x))
                self.field_objects[cur_y][cur_x] = Objects.empty_field
        if (y2 == 0 and self.field_objects[y2][x2] == Objects.black_draught
                and not self.checker.can_continue(move, self.cur_player)):
            self.field_objects[y2][x2] = Objects.black_king
        if (y2 == self.field_height - 1
                and self.field_objects[y2][x2] == Objects.white_draught
                and not self.checker.can_continue(move, self.cur_player)):
            self.field_objects[y2][x2] = Objects.white_king

    def complete_move(self, move):
        self.is_first_move = False
        self.update_elements(move)
        self.cur_figure = move[1]
        self.logger.add(Log(self.cur_player, *move))
        if not self.checker.can_continue(self.cur_move, self.cur_player):
            self.is_first_move = True
            self.was_taken = False
            self.moves_quantity += 1
            if self.moves_quantity % 2 == 0:
                self.cur_player = self.player1
            else:
                self.cur_player = self.player2
        self.dump_logger.add(self.get_dump())
        self.interface.update()

    class State:
        def __init__(self):
            self.field_objects = None
            self.is_first_move = None
            self.is_over = None
            self.cur_figure = None
            self.cur_player = None
            self.cur_move = None
            self.was_taken = None
            self.moves_quantity = None
            self.logger = None

    def get_dump(self):
        dump = self.State()
        dump.field_objects = copy.deepcopy(self.field_objects)
        dump.is_first_move = self.is_first_move
        dump.is_over = self.is_over
        dump.cur_figure = copy.deepcopy(self.cur_figure)
        dump.cur_player = self.cur_player
        dump.cur_move = self.cur_move
        dump.was_taken = self.was_taken
        dump.moves_quantity = self.moves_quantity
        dump.logger = copy.deepcopy(self.logger)
        return dump

    def init_by_dump(self, config):
        self.field_objects = copy.deepcopy(config.field_objects)
        self.is_first_move = config.is_first_move
        self.is_over = config.is_over
        self.cur_figure = copy.deepcopy(config.cur_figure)
        self.cur_player = config.cur_player
        self.cur_move = config.cur_move
        self.was_taken = config.was_taken
        self.moves_quantity = config.moves_quantity
        self.logger = copy.deepcopy(config.logger)

    def undo(self):
        if self.player1.person and self.player2.person:
            if self.dump_logger.get_ind() > 0:
                self.dump_logger.undo()
                self.init_by_dump(self.dump_logger.get_cur())
        elif self.cur_player.person:
            dump = self.dump_logger.get_log()
            ind = self.dump_logger.get_ind() - 1
            while ind > 0 and dump[ind].cur_player != self.cur_player:
                ind -= 1
            if ind >= 0 and dump[ind].cur_player == self.cur_player:
                self.dump_logger.undo()
                while self.dump_logger.get_cur().cur_player != self.cur_player:
                    self.dump_logger.undo()
                self.init_by_dump(self.dump_logger.get_cur())
        self.interface.redraw_field()

    def redo(self):
        if self.player1.person and self.player2.person:
            self.dump_logger.redo()
            self.init_by_dump(self.dump_logger.get_cur())
        else:
            dump = self.dump_logger.get_log()
            ind = self.dump_logger.get_ind() + 1
            while ind + 1 < len(
                    dump) and dump[ind].cur_player != self.cur_player:
                ind += 1
            if ind < len(dump) and dump[ind].cur_player == self.cur_player:
                self.dump_logger.redo()
                while self.dump_logger.get_cur().cur_player != self.cur_player:
                    self.dump_logger.redo()
                self.init_by_dump(self.dump_logger.get_cur())
        self.interface.redraw_field()

    def update(self):
        self.moves_quantity = 0
        self.is_first_move = True
        while True:
            if self.load:
                if self.loaded_moves_amount != 0:
                    self.interface.loading.show_loading(
                        int(self.moves_quantity / self.loaded_moves_amount *
                            100))
                else:
                    self.interface.loading.show_loading(100)
            time.sleep(self.pause)
            if self.is_over:
                break
            if not self.cur_player.person:
                self.interface.waiting_bar.show(True)
            else:
                self.interface.waiting_bar.show(False)
            self.checker.make_graph(self.cur_player)
            if self.cur_player.number == 1:
                self.cur_timer = self.timer1
            else:
                self.cur_timer = self.timer2
            self.cur_timer.start()
            self.cur_move = self.cur_player.make_move(self)
            if self.checker.is_move_correct(self.cur_move, self.cur_player):
                self.cur_timer.stop()
                self.complete_move(self.cur_move)
            else:
                if self.load:
                    self.interface.loading.loading_failed(
                    )  # show message, that downloading is incorrect
                    break
                self.interface.incorrect_move()
            self.cur_timer = None
            if self.checker.state(self.cur_player) != '':
                self.timer1.stop()
                self.timer2.stop()
                self.interface.waiting_bar.show(False)
                self.is_over = True
                self.interface.update()
                break

    def save_game(self, file=None):
        if file is None:
            file = self.game_file
        else:
            self.game_file = file
        try:
            f = open(file, 'w')
        except FileNotFoundError:
            return
        f.write('white ' + str(self.player1.person) + '\n')
        f.write('black ' + str(self.player2.person) + '\n')
        for i in self.logger.get_log()[:self.logger.get_ind()]:
            f.write(repr(i) + '\n')
        f.write(str(self.timer1.is_on) + '\n')
        f.write('{} {}\n'.format(str(self.timer1.get()),
                                 str(self.timer2.get())))
        f.close()