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
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()