def test_connect_nodes(self, mock_get_node_by_serial, mock_connect_nodes, mock_utils): # Assemble node_1 = NodeObject(serial=1, location={ 'x': 1, 'y': 1 }, size=1, real=True) node_2 = NodeObject(serial=2, location={ 'x': 2, 'y': 2 }, size=1, real=True) mock_get_node_by_serial.side_effect = [node_1, node_2] mock_connect_nodes.return_value = None mock_utils.graph_config_data = None mock_utils.game_config_data = {'Default': {'log_level': 'ERROR'}} data_handler = GameDataHandler(None, None) # Act data_handler.connect_nodes(node_1, node_1) # Assert mock_connect_nodes.called_once(node_1, node_1, allow_overflow=True)
def run_q_player(self, graph_file_path, log_file_path): Utils.read_game_config_file(CONFIG_FILE_PATH) Utils.read_graph_config_file(GRAPH_CONFIG_PATH) Utils.image_folder = path.join("..", Utils.image_folder) log.setLevel(Utils.game_config_data['Default']['log_level']) session_length = 1000 graph = load_py_graph(graph_file_path) q_matrix = QMatrix(action_space=4, max_steps=int(Utils.game_config_data['Default']['max_turns']), nodes_in_graph=len(graph.node_list)) with open(log_file_path,'w') as f: f.write("episode, score\n") for i in range(session_length): dummy_screen = DummyScreen(graph) game = GraphTabletDisplay(dummy_screen) data_handler = GameDataHandler(GRAPH_CONFIG_PATH, graph.size) data_handler.add_view_to_db(game.get_info_from_screen()) rw = 0 game.press_button(self.auto_first_press + 1) data_handler.add_view_to_db(game.get_info_from_screen()) q_matrix.reinit(known_nodes=len(data_handler.get_real_nodes())) for j in range(1, int(Utils.game_config_data['Default']['max_turns'])): log.debug("doing a step {}/{}".format(j, Utils.game_config_data['Default']['max_turns'])) btn = q_matrix.choose_action_epsilon_greedy() game.press_button(btn + 1) data_handler.add_view_to_db(game.get_info_from_screen()) rw = q_matrix.update_matrix(num_nodes=len(data_handler.get_real_nodes()), current_step=btn) log.info("Q session {}:{} - reword:{}".format(i, session_length, rw)) f.write("{},{}\n".format(i + 1, rw))
def __init__(self, game_screen=None, dim_=None): self.game_screen = game_screen self.original_graph = self.game_screen.graph self.current_data_handler = GameDataHandler( self.game_screen.graph_config, self.original_graph.size) self.max_turns = self.game_screen.max_turns self.button_presses = self.game_screen.button_presses self.layout = GameLayout(self, dim_=dim_) self.send_info_from_screen()
def __init__(self, game_screen=None, **kwargs): super(GraphGameApp, self).__init__(**kwargs) self.game_screen = game_screen self.original_graph = self.game_screen.graph self.current_data_handler = GameDataHandler( self.game_screen.graph_config, self.original_graph.size) self.max_turns = self.game_screen.max_turns self.button_presses = self.game_screen.button_presses self.layout = GameLayout(self) self.send_info_from_screen()
def test_trim_data(self, mock_utils): mock_utils.graph_config_data = None mock_utils.game_config_data = {'Default': {'log_level': 'ERROR'}} Utils.read_game_config_file(path.join("..", CONFIG_FILE_PATH)) Utils.read_graph_config_file(path.join("..", GRAPH_CONFIG_PATH)) data_handler = GameDataHandler(path.join("../", "graph_config.txt"), None) data_handler.graph.add_node(self.node_1_real.x, self.node_1_real.y, serial=self.node_1_real.serial_num) data_handler.graph.add_node(self.node_2_unreal.x, self.node_2_unreal.y, serial=self.node_2_unreal.serial_num) data_handler.graph.add_node(self.node_2_unreal_moved.x, self.node_2_unreal_moved.y, serial=self.node_2_unreal_moved.serial_num) data_handler.graph.add_node(self.node_3_unreal.x, self.node_3_unreal.y, serial=self.node_3_unreal.serial_num) data_handler.graph.add_node(self.node_3_unreal_moved.x, self.node_3_unreal_moved.y, serial=self.node_3_unreal_moved.serial_num) data_handler.graph.add_node(self.node_4_real.x, self.node_4_real.y, serial=self.node_4_real.serial_num) edge_1 = (self.node_1_real, self.node_2_unreal_moved, 1, LineEquation(slope=1, const=0, edge1=self.node_1_real, edge2=self.node_2_unreal_moved)) edge_2 = (self.node_2_unreal, self.node_3_unreal_moved, 1, LineEquation(slope=1, const=0, edge1=self.node_2_unreal, edge2=self.node_3_unreal_moved)) edge_3 = (self.node_3_unreal, self.node_4_real, 1, LineEquation(slope=1, const=0, edge1=self.node_3_unreal, edge2=self.node_4_real)) edge_4 = (self.node_1_real, self.node_4_real, 1, LineEquation(slope=1, const=0, edge1=self.node_1_real, edge2=self.node_4_real)) data_handler.extra_edges = [edge_1, edge_2, edge_3] data_handler.trim_data() self.assertEquals(len(data_handler.edges_to_add), 1) self.assertEquals(data_handler.edges_to_add[0][0].serial_num, edge_4[0].serial_num) self.assertEquals(data_handler.edges_to_add[0][1].serial_num, edge_4[1].serial_num)
def run_buttons_on_graph(graph, buttons): #log = logging.getLogger() #log.setLevel(Utils.game_config_data['Default']['log_level']) dummy_screen = DummyScreen(graph) game = GraphTabletDisplay(dummy_screen) #game.run() data_handler = GameDataHandler(GRAPH_CONFIG_FILE, graph.size) data_handler.add_view_to_db(game.get_info_from_screen()) for i in range(int(Utils.game_config_data['Default']['max_turns'])): #log.debug("doing a step {}/{}".format(i, Utils.game_config_data['Default']['max_turns'])) game.press_button(int(buttons[i])) data_handler.add_view_to_db(game.get_info_from_screen()) # print ("known nodes-"+str(data_handler.get_number_of_known_nodes())+"\n") answer = (data_handler.get_number_of_known_nodes() == len(graph.node_list)) return answer, data_handler.get_number_of_known_nodes()
def test_get_furthest_nodes(self): # Assemble node_1 = NodeObject(serial=1, location={ 'x': 1, 'y': 1 }, size=1, real=True) node_2 = NodeObject(serial=2, location={ 'x': 2, 'y': 2 }, size=1, real=True) node_3 = NodeObject(serial=3, location={ 'x': 3, 'y': 3 }, size=1, real=True) node_4 = NodeObject(serial=4, location={ 'x': 4, 'y': 4 }, size=1, real=True) # Act res = GameDataHandler.get_furthest_nodes(node_1, node_2, node_3, node_4) # Assert self.assertIn( node_1.serial_num, res, "node with serial number {0} was not returned by function." " Got {1}".format(node_1.serial_num, res)) self.assertIn( node_4.serial_num, res, "node with serial number {0} was not returned by function." " Got {1}".format(node_4.serial_num, res))
class GraphGameApp(App): # Used in order to run the game of a single graph, without the questions or the result screen is_playing = True def __init__(self, game_screen=None, **kwargs): super(GraphGameApp, self).__init__(**kwargs) self.game_screen = game_screen self.original_graph = self.game_screen.graph self.current_data_handler = GameDataHandler( self.game_screen.graph_config, self.original_graph.size) self.max_turns = self.game_screen.max_turns self.button_presses = self.game_screen.button_presses self.layout = GameLayout(self) self.send_info_from_screen() def build(self): return self.layout def load(self): pass def send_info_from_screen(self): self.current_data_handler.add_view_to_db(self.get_info_from_screen()) def end_game(self): self.is_playing = False self.game_screen.end_graph() def get_info_from_screen(self): """ Function returns the nodes and edges that are at least partially displayed onscreen :return: returns a dictionary containing two objects: 'nodes': A list containing the nodes that are at least partially displayed onscreen. 'edges': A list representing the edges that are at least partially displayed onscreen. Each edge is represented by a tuple containing the edge's nodes, the edge's original slope and the edge's equation. If one of the nodes is not onscreen, a new NodeObject is created where the x,y coordinates represent the intersection between the edge and the screen and the serial and size are set to None. """ if self.layout.is_zoomed_out: graph_nodes = self.layout.kivy_graph_out.kivy_graph.nodes graph_edges = self.layout.kivy_graph_out.kivy_graph.edges graph_corners = self.layout.kivy_graph_out.kivy_graph.corners else: graph_nodes = self.layout.kivy_graph_in.kivy_graph.nodes graph_edges = self.layout.kivy_graph_in.kivy_graph.edges graph_corners = self.layout.kivy_graph_in.kivy_graph.corners nodes = self.get_onscreen_nodes(graph_nodes, graph_corners) edges = self.get_onscreen_edges(graph_edges, graph_corners) return {'nodes': nodes, 'edges': edges} def get_onscreen_nodes(self, graph_nodes, graph_corners): """ Function goes over the list of nodes in the graph and checks which ones are displayed onscreen :return: A list containing the graph's nodes that are at least partially displayed onscreen. """ bottom_left = graph_corners["bottom_left"] top_right = graph_corners["top_right"] displayed_nodes = [] for node in graph_nodes: if node.serial != -1: real_node = self.original_graph.get_node_by_serial(node.serial) node_x = real_node.x node_y = real_node.y node_r = node.get_radius() + 0.9 if (node_x + node_r) > bottom_left.get_x() and (node_x - node_r) < top_right.get_x() and \ (node_y + node_r) > bottom_left.get_y() and (node_y - node_r) < top_right.get_y(): displayed_nodes.append(real_node) return displayed_nodes def get_onscreen_edges(self, graph_edges, graph_corners): """ Function goes over the list of edges in the graph and checks which ones are displayed onscreen :return: A list representing the edges that are at least partially displayed onscreen. Each edge is represented by a tuple containing the edge's nodes, the edge's original slope and the edge's equation. If one of the nodes is not onscreen, a new NodeObject is created where the x,y coordinates represent the intersection between the edge and the screen, a new serial is created, size is set to 0 and real is set to False. """ # create equations representing the screen's boarders top_left = Point(graph_corners["top_left"].get_x(), graph_corners["top_left"].get_y()) top_right = Point(graph_corners["top_right"].get_x() + 0.001, graph_corners["top_right"].get_y()) bottom_left = Point(graph_corners["bottom_left"].get_x() + 0.001, graph_corners["bottom_left"].get_y()) bottom_right = Point(graph_corners["bottom_right"].get_x(), graph_corners["bottom_right"].get_y()) top = LineEquation.create_equation(top_left, top_right) bottom = LineEquation.create_equation(bottom_left, bottom_right) left = LineEquation.create_equation(bottom_left, top_left) right = LineEquation.create_equation(bottom_right, top_right) displayed_edges = [] for edge in graph_edges: real_node1 = self.original_graph.get_node_by_serial( edge.node1.serial) real_node2 = self.original_graph.get_node_by_serial( edge.node2.serial) point1 = Point(real_node1.x, real_node1.y) point2 = Point(real_node2.x, real_node2.y) edge_equation = LineEquation.create_equation(point1, point2) edge.set_slope(edge_equation) if self.is_node_onscreen(edge.node1, graph_corners): if self.is_node_onscreen(edge.node2, graph_corners): # both of edge's node are onscreen if edge.node1.get_x() < edge.node2.get_x(): curr_edge = (real_node1, real_node2, edge.slope, edge_equation) else: curr_edge = (real_node2, real_node1, edge.slope, edge_equation) else: # only the edge's first node in onscreen curr_edge = self.get_partly_visible_edge( edge, top, bottom, left, right, edge.node1, edge_equation) elif self.is_node_onscreen(edge.node2, graph_corners): # only the edge's second node is onscreen curr_edge = self.get_partly_visible_edge( edge, top, bottom, left, right, edge.node2, edge_equation) else: # neither of the edge's nodes are onscreen curr_edge = self.get_partly_visible_edge( edge, top, bottom, left, right, None, edge_equation) if curr_edge is not None: displayed_edges.append(curr_edge) return displayed_edges def is_node_onscreen(self, node, screen_edges): """ checks if a given node is onscreen :param node: the node to be checked :param screen_edges: boarders of the screen :return: boolean indicating if the node is/isn't onscreen """ real_node = self.original_graph.get_node_by_serial(node.serial) node_x = real_node.x node_y = real_node.y node_r = node.get_radius() * 0.05 return (node_x + node_r) > screen_edges["bottom_left"].get_x() and \ (node_x - node_r) < screen_edges["top_right"].get_x() and \ (node_y + node_r) > screen_edges["bottom_left"].get_y() and \ (node_y - node_r) < screen_edges["top_right"].get_y() def get_partly_visible_edge(self, edge, top, bottom, left, right, node, edge_equation): """ :param edge: an edge that can be seen onscreen but where at least one node is not visible :param top: equation representing the top border of the screen :param bottom: equation representing the bottom border of the screen :param left: equation representing the left border of the screen :param right: equation representing the right border of the screen :param node: the visible node connected to the edge, or None if no node is visible :param edge_equation: the equation of the checked edge :return: A tuple containing two NodeObjects, each representing a one of the edge's nodes, the edge's slope and the edge's equation. If one of the edge's nodes is not onscreen, the x,y coordinates represent the intersection between the edge and the screen, a new serial is created, size is set to 0 and real is set to False. """ first_node = None second_node = None if node: first_node = self.original_graph.get_node_by_serial(node.serial) # check if edge collides with top border if LineEquation.check_collision_point(edge_equation, top): col_point = LineEquation.get_equation_collision_point( edge_equation, top) location = {'x': round(col_point.x, 2), 'y': round(col_point.y, 2)} if first_node is not None: second_node = NodeObject(serial=get_serial(), location=location, size=0) second_node.real = False else: first_node = NodeObject(serial=get_serial(), location=location, size=0) first_node.real = False # check if edge collides with bottom border if LineEquation.check_collision_point(edge_equation, bottom): col_point = LineEquation.get_equation_collision_point( edge_equation, bottom) location = {'x': round(col_point.x, 2), 'y': round(col_point.y, 2)} if first_node is not None: second_node = NodeObject(serial=get_serial(), location=location, size=0) second_node.real = False else: first_node = NodeObject(serial=get_serial(), location=location, size=0) first_node.real = False # check if edge collides with left border if LineEquation.check_collision_point(edge_equation, left): col_point = LineEquation.get_equation_collision_point( edge_equation, left) location = {'x': round(col_point.x, 2), 'y': round(col_point.y, 2)} if first_node is not None: second_node = NodeObject(serial=get_serial(), location=location, size=0) second_node.real = False else: first_node = NodeObject(serial=get_serial(), location=location, size=0) first_node.real = False # check if edge collides with right border if LineEquation.check_collision_point(edge_equation, right): col_point = LineEquation.get_equation_collision_point( edge_equation, right) location = {'x': round(col_point.x, 2), 'y': round(col_point.y, 2)} if first_node is not None: second_node = NodeObject(serial=get_serial(), location=location, size=0) second_node.real = False else: first_node = NodeObject(serial=get_serial(), location=location, size=0) first_node.real = False if second_node is None: if first_node is None: return None else: raise Exception( "Only One viable node for onscreen edge: {}".format( edge.print_by_serial())) min_dist = edge.node1.get_radius() / 2 if first_node.distance(second_node) < min_dist: return None if first_node.x < second_node.x: curr_edge = (first_node, second_node, edge.slope, edge_equation) else: curr_edge = (second_node, first_node, edge.slope, edge_equation) return curr_edge def stop_me(self): self.stop()
def test_data_collection(self): # WIP return # Assemble new_graph = create_rand_graph("../graph_config.txt") gamer = GameDataHandler("../graph_config.txt") nodes_list = [] edges = [] def add_empty_edge(self, graph, nodes_list, edges_list): for node in graph.node_list: if node not in nodes_list: for friend in node.neighbors: if friend in [item.serial_num for item in nodes_list]: tmp_node = graph.get_node_by_serial(friend) fake_node_1 = NodeObject(serial=None, location={ 'x': node.x, 'y': node.y }, size=node.size, real=False) fake_node_2 = NodeObject(serial=None, location={ 'x': tmp_node.x, 'y': tmp_node.y }, size=tmp_node.size, real=False) edges_list.append((fake_node_1, fake_node_2)) return for i in range(3): nodes_list.append(new_graph.node_list[i]) # Add an extra node that is connected to one of the existing ones for node in nodes_list[0].neighbors: if node not in nodes_list: nodes_list.append(new_graph.get_node_by_serial(node)) break for node in nodes_list: for friend in node.neighbors: if friend in [item.serial_num for item in nodes_list]: edges.append((node, new_graph.get_node_by_serial(friend))) else: origin_node = new_graph.get_node_by_serial(friend) fake_node = NodeObject(serial=None, location={ 'x': origin_node.x, 'y': origin_node.y }, size=origin_node.size, real=False) edges.append((node, fake_node)) # clean nodes for node in nodes_list: node.neighbors = set() node.possible_neighbors = set() # Add a few empty edges add_empty_edge(new_graph, nodes_list, edges) add_empty_edge(new_graph, nodes_list, edges) add_empty_edge(new_graph, nodes_list, edges) mock_reader.return_value = {'nodes': nodes_list, 'edges': edges} # act gamer.do_move() time.sleep(1)
def test_connect_edges_advanced(self, mock_get_node_by_serial, mock_clean, mock_connect, mock_utils): def mock_get_node(serial): for node in self.node_list: if node.serial_num == serial: return node return None # Assemble mock_get_node_by_serial.side_effect = mock_get_node mock_utils.graph_config_data = None mock_utils.game_config_data = {'Default': {'log_level': 'ERROR'}} data_handler = GameDataHandler(None, None) edge_two_real_14 = (self.node_1_real, self.node_4_real, self.node_1_real.slope(self.node_4_real), LineEquation(slope=self.node_1_real.slope( self.node_4_real), const=1, edge1=self.node_1_real, edge2=self.node_4_real)) edge_left_real_13 = (self.node_1_real, self.node_3_unreal, self.node_1_real.slope(self.node_3_unreal), LineEquation(slope=self.node_1_real.slope( self.node_3_unreal), const=1, edge1=self.node_1_real, edge2=self.node_3_unreal)) edge_left_real_12 = (self.node_1_real, self.node_2_unreal, self.node_1_real.slope(self.node_2_unreal), LineEquation(slope=self.node_1_real.slope( self.node_2_unreal), const=1, edge1=self.node_1_real, edge2=self.node_2_unreal)) edge_right_real_24 = (self.node_2_unreal, self.node_4_real, self.node_2_unreal.slope(self.node_4_real), LineEquation(slope=self.node_2_unreal.slope( self.node_4_real), const=1, edge1=self.node_2_unreal, edge2=self.node_4_real)) edge_two_unreal_23 = (self.node_2_unreal, self.node_3_unreal, self.node_2_unreal.slope(self.node_3_unreal), LineEquation(slope=self.node_2_unreal.slope( self.node_3_unreal), const=1, edge1=self.node_2_unreal, edge2=self.node_3_unreal)) edge_two_unreal_13 = (self.node_1_unreal, self.node_3_unreal, self.node_1_unreal.slope(self.node_3_unreal), LineEquation(slope=self.node_1_unreal.slope( self.node_3_unreal), const=1, edge1=self.node_1_unreal, edge2=self.node_3_unreal)) # Act + Assert # Case 1 - One edge is connected to two real nodes. Other edge only connects to one real node and the other # to a fake node edge = data_handler.connect_edges(edge_two_real_14, edge_left_real_13) mock_connect.assert_called_with(self.node_1_real, self.node_4_real) self.assertEquals(edge_two_real_14[3].edge1, edge[3].edge1) self.assertEquals(edge_two_real_14[3].edge2, edge[3].edge2) # Case 2 - Both edges each connect to one real node and one fake node. Both real nodes are different edge = data_handler.connect_edges(edge_left_real_13, edge_right_real_24) self.assertEquals(edge_two_real_14[3].edge1, edge[3].edge1) self.assertEquals(edge_two_real_14[3].edge2, edge[3].edge2) # Case 3 - Both edges connect to the same real node. Both edge's other node is different fake one edge = data_handler.connect_edges(edge_left_real_13, edge_left_real_12) mock_connect.assert_called_with(self.node_1_real, self.node_3_unreal) self.assertEquals(edge_left_real_13[3].edge1, edge[3].edge1) self.assertEquals(edge_left_real_13[3].edge2, edge[3].edge2) # Case 4 - One edge is connected to a real node and to a fake node. Other edge connects to two fake nodes. edge = data_handler.connect_edges(edge_left_real_12, edge_two_unreal_23) mock_connect.assert_called_with(self.node_1_real, self.node_3_unreal) self.assertEquals(edge_left_real_13[3].edge1, edge[3].edge1) self.assertEquals(edge_left_real_13[3].edge2, edge[3].edge2) # Case 5 - Both edge have two fake nodes. edge = data_handler.connect_edges(edge_two_unreal_13, edge_two_unreal_23) mock_connect.assert_called_with(self.node_1_unreal, self.node_3_unreal) self.assertEquals(edge_two_unreal_13[3].edge1, edge[3].edge1) self.assertEquals(edge_two_unreal_13[3].edge2, edge[3].edge2)
def test_two_edges_are_one(self, mock_utils): # Assemble node_1 = NodeObject(serial=1, location={ 'x': 100, 'y': 100 }, size=1, real=True) node_2 = NodeObject(serial=2, location={ 'x': 200, 'y': 200 }, size=1, real=True) node_3 = NodeObject(serial=3, location={ 'x': 300, 'y': 300 }, size=1, real=True) node_4 = NodeObject(serial=4, location={ 'x': 400, 'y': 400 }, size=1, real=True) node_5 = NodeObject(serial=4, location={ 'x': 100, 'y': 400 }, size=1, real=True) mock_utils.graph_config_data = None mock_utils.game_config_data = {'Default': {'log_level': 'ERROR'}} data_handler = GameDataHandler(None, None) edge_35 = (node_3, node_5, node_3.slope(node_5), LineEquation(slope=node_3.slope(node_5), const=1, edge1=node_3, edge2=node_5)) edge_24 = (node_2, node_4, node_2.slope(node_4), LineEquation(slope=node_2.slope(node_4), const=1, edge1=node_2, edge2=node_4)) edge_12 = (node_1, node_2, node_1.slope(node_2), LineEquation(slope=node_1.slope(node_2), const=1, edge1=node_1, edge2=node_2)) edge_13 = (node_1, node_3, node_1.slope(node_3), LineEquation(slope=node_1.slope(node_3), const=1, edge1=node_1, edge2=node_3)) edge_34 = (node_3, node_4, node_3.slope(node_4), LineEquation(slope=node_3.slope(node_4), const=1, edge1=node_3, edge2=node_4)) # Act res_miss = data_handler.two_edges_are_one(edge_35, edge_24) res_not_sure = data_handler.two_edges_are_one(edge_12, edge_34) res_hit = data_handler.two_edges_are_one(edge_13, edge_24) res_hit_same_point = data_handler.two_edges_are_one(edge_12, edge_13) res_same_edge = data_handler.two_edges_are_one(edge_12, edge_12) # Assert self.assertFalse(res_not_sure) self.assertFalse(res_miss) self.assertTrue(res_hit) self.assertTrue(res_hit_same_point) self.assertTrue(res_same_edge)