示例#1
0
文件: algo.py 项目: Biuku/KNN_Game
class Algo(Arr):
    def __init__(self, win):
        super().__init__()
        pygame.init()
        self.win = win
        self.printr = Printr(self.win)

        self.draw = Draw(self.win, self.pixels)

        self.max_k = int(len(self.arr))
        self.k = self.init_k()

        ## Flags
        self.moving = False
        self.draw_outer_circle = False
        self.survived = True

        ### Circle ###
        self.pixel_mid = [800, 400]
        self.pixel_radius = 110

        #self.configure_draw()
        self.convert(True)  ## Initialize self.arr_mid

    def configure_draw(self):
        self.draw.configure_scales(self.arr_scale, self.pixel_scale)

    def update(self, moving, draw_outer):
        self.moving = moving
        self.draw_outer_circle = draw_outer
        self.move()
        self.convert(self.moving)
        self.knn()

    def draw_things(self, win_w, win_h):
        self.draw.draw_plot(self.norm_arr)

        radius = 0
        if self.draw_outer_circle:
            radius = self.pixel_radius

        self.draw.draw_circle(self.arr_mid, self.pixel_mid, radius,
                              self.survived)
        self.printr.print_instructions(self.k, self.survived, win_w, win_h)

    """ UPDATES """

    def move(self):
        if self.moving:
            mx, my = pygame.mouse.get_rel()

            ## Move circle if the mouse is anywhere on the graph
            if pygame.mouse.get_pos()[0] < self.pixel_origin[0] + self.pixel_w:
                self.pixel_mid[0] += mx
                self.pixel_mid[1] += my

    def convert(self, moving):
        if moving:
            self.norm_arr_mid = self.convert_pixel_to_norm_arr(self.pixel_mid)
            self.arr_mid = self.convert_norm_arr_to_arr(self.norm_arr_mid)

    def update_k(self, delta_k):
        new_k = self.k + delta_k
        if new_k > 0 and new_k < self.max_k:
            self.k = new_k

    """ KNN ENGINE """

    def knn(self):
        ## Reset the distances and nn
        self.norm_arr[:, 3:] = 0

        ## Calculate the distances and nn -- note, need to vectorize
        """ Need to vectorize this """
        for i, coord in enumerate(self.norm_arr[:, :2]):
            euclid = np.sqrt(np.sum(np.square(self.norm_arr_mid - coord)))
            self.norm_arr[i:, 3] = euclid

        ## partition the array into smallest / largest K
        part = np.argpartition(self.norm_arr[:, 3], self.k)
        self.norm_arr[part[:self.k], 4] = 1

        self.update_radius(self.norm_arr[part[:self.k]])
        self.update_survived(self.norm_arr[part[:self.k]])

    def update_survived(self, part):
        self.survived = True
        if part[:, 2].sum() < (len(part) / 2):
            self.survived = False

    def update_radius(self, part):
        ### Set radius as equal to length of furthest nn

        # Get index of the furthest nn coordinate in norm_arr
        mx = part[:, 3].max()
        index = np.where(self.norm_arr == mx)[0][0]

        # Get the pixel equivalent
        x1, y1 = self.pixels[index, :2]

        ## euclidian distance
        x2, y2 = self.pixel_mid
        self.pixel_radius = m.sqrt((x2 - x1)**2 + (y2 - y1)**2)

    """ Run once """

    def init_k(self):
        ### k should be odd so you can make a clear prediction
        k = int(m.sqrt(self.max_k))
        if k % 2 == 0:
            k += 1

        return k
示例#2
0
class FitLine(Arr):
    def __init__(self, win):
        super().__init__()
        pygame.init()
        self.win = win
        self.printr = Printr(self.win, self.set)
        self.drawline = DrawLine(self.win, self.arr)
        self.show_intercepts = False

        ## Agnostic units
        self.degrees = 30
        self.slope = 1

        ## Pixel units
        self.pixel_mid = [800, 400]
        self.pixel_length = self.pixel_w / 2 + 10
        self.update_end_points()

        ## Array units
        self.arr_start = (1, 1)
        self.arr_mid = (1, 1)
        self.y_intercept = 1
        self.update_RSS()

    def update(self, moving, rotating, show_intercepts):
        self.move(moving)
        self.rotate(rotating)
        self.show_intercepts = show_intercepts

    def update_coords(self):
        ## If I move or rotate, recalculate everything ##
        self.update_end_points()
        self.convert()
        self.update_coefficients()
        self.update_RSS()

    def draw(self):
        ### Draw line ###
        pixel_points = [self.pixel_start, self.pixel_mid, self.pixel_end]
        arr_points = [self.arr_start, self.arr_mid]
        self.drawline.draw_line(pixel_points, arr_points)

        ### Draw intercepts on the line ###
        intercepts = [
            self.arr_y_on_line, self.pixel_y_on_line, self.pixels_of_arr
        ]
        self.drawline.draw_intercepts(self.show_intercepts, intercepts,
                                      self.error)

        self.printr.print_instructions(self.b0, self.b1, self.SSE,
                                       self.y_intercept, self.slope, self.sse)

    """ UPDATES """

    def move(self, moving):
        if moving:
            mx, my = pygame.mouse.get_rel()
            self.pixel_mid[0] += mx
            self.pixel_mid[1] += my

            self.update_coords()

    """ Rotating stuff """

    def rotate(self, rotating):
        def wrap_angle(angle):
            if angle > 360:
                return 1
            if angle < 0:
                return 360
            return angle

        if rotating:
            self.degrees += rotating
            self.degrees = wrap_angle(self.degrees)

            self.update_coords()

    def update_end_points(self):
        """ Update the start and end points if line moves/rotates"""

        midx, midy = self.pixel_mid
        hyp = self.pixel_length
        theta = m.radians(self.degrees)

        ## Do trig
        opp = m.sin(theta) * hyp
        adj = m.cos(theta) * hyp

        ## Get start/end by applying deltas to mid coord
        start_x = int(midx - adj)
        start_y = int(midy + opp)
        end_x = int(midx + adj)
        end_y = int(midy - opp)  # account for pygame negative stupidity

        self.pixel_start = [start_x, start_y]
        self.pixel_end = [end_x, end_y]

    def convert(self):
        self.arr_start = self.convert_to_arr(self.pixel_start)
        self.arr_mid = self.convert_to_arr(self.pixel_mid)

    def update_coefficients(self):
        ## Slope
        x1, y1 = self.arr_mid
        x2, y2 = self.arr_start
        if x2 - x1 != 0:
            self.slope = (y2 - y1) / (x2 - x1)

        ## Y intercept
        self.y_intercept = y2 - self.slope * x2
        b0_mid = y1 - self.slope * x1

    def update_RSS(self):
        self.arr_y_on_line = []
        self.pixel_y_on_line = []
        self.error = []
        self.sse = 0

        for coord in self.arr:
            x, y = coord

            y_on_line = self.y_intercept + x * self.slope  ## line equation

            error = (y - y_on_line)**2
            self.sse += error
            self.error.append(error)

            intercept_coord = [x, y_on_line]
            self.arr_y_on_line.append(intercept_coord)
            self.pixel_y_on_line.append(
                self.convert_to_pixels(intercept_coord))
示例#3
0
class Main(PygameBasics):
    def __init__(self):
        pygame.init()
        super().__init__()

        self.win_w, self.win_h  = 1900, 950
        self.win = pygame.display.set_mode((self.win_w, self.win_h), pygame.RESIZABLE)
        self.printr = Printr(self.win)

        self.nodes = []
        self.queue = []

        self.algo_wait = False

        ## Object flags
        self.hover_node = False
        self.edge_start_node = False
        self.algo_start_node = False
        self.algo_end_node = False


    """ EVENTS """

    def left_click_events(self):
        pygame.mouse.get_rel() ## Reset pygame relative mouse position
        self.start_moving()
        self.make_node()

    def right_click_events(self):
        self.make_edge()


    def mouse_button_up_events(self):
        self.stop_moving()

    def keydown_events(self, event):
        if event.key == pygame.K_SPACE:
            self.make_node()

        if event.key == pygame.K_e:
            self.make_edge()

        if event.key == pygame.K_s:
            self.update_algo_start_node()

        if event.key == pygame.K_x:
            self.update_algo_end_node()

        if event.key == pygame.K_d:
            self.dijkstra()

        ## Tracer
        if event.key == pygame.K_p:
            for nb in self.algo_start_node.neighbours:
                print(nb.ID, nb.total_cost)

        if event.key == pygame.K_q:
            pygame.quit(), quit()

    def keyup_events(self, event):
        pass

    """ Conditional events """

    def make_node(self):
        if not self.hover_node:
            id = self.set.get_node_id()
            mx, my = pygame.mouse.get_pos()

            node = Node(self.win, id, mx, my)
            self.nodes.append(node)

    def start_moving(self):
        for node in self.nodes:
            node.start_moving()

    def stop_moving(self):
        for node in self.nodes:
            node.stop_moving()


    def make_edge(self):
        """ Called by pressing 'e' """

        ## If we haven't yet picked the start of our edge
        if not self.edge_start_node:
            if self.hover_node:
                self.hover_node.set_edge_start_node()
                self.edge_start_node = self.hover_node

        ## If we previously picked a start node, look for an end node
        else:
            if self.hover_node:
                self.edge_start_node.add_neighbour(self.hover_node)
                self.edge_start_node.cancel_edge_start()
                self.edge_start_node = False


    def update_algo_start_node(self):
        if self.hover_node:
            if self.algo_start_node:
                self.algo_start_node.cancel_algo_start()
                self.algo_start_node = False

            else:
                self.hover_node.set_algo_start()
                self.algo_start_node = self.hover_node

    def update_algo_end_node(self):
        if self.hover_node:
            if self.algo_end_node:
                self.algo_end_node.cancel_algo_end()
                self.algo_end_node = False

            else:
                self.hover_node.set_algo_end()
                self.algo_end_node = self.hover_node



    """ ******************** """

    def dijkstra(self):
        if not self.algo_start_node:
            return

        self.queue = [(0, self.algo_start_node)]
        print("Before loop: ", self.queue)

        while self.queue:
            self.curr_node = self.queue.pop(0)[1]
            self.curr_node.set_visiting()

            for self.nb in self.curr_node.get_neighbours():
                pygame.time.delay(1500)

                if self.nb.get_unvisited():
                    self.nb.set_looking_as_neighbour()

                    trial_weight = self.curr_node.get_total_cost() + self.get_euclid(self.curr_node, self.nb)

                    if trial_weight < self.nb.get_total_cost():
                        self.nb.set_total_cost(trial_weight)
                        self.nb.set_parent(self.curr_node)
                        self.queue.append( (trial_weight, self.nb))
                        self.queue.sort() ## Slow, but fine for less than a few thousand items

                self.draw()
                pygame.time.delay(1500)

                self.nb.cancel_looking_as_neighbour()

            self.draw()

            self.curr_node.set_unvisited()
            self.curr_node.cancel_visiting()


    def get_euclid(self, start, end):
        x1, y1 = start.get_coords()
        x2, y2 = end.get_coords()

        return math.sqrt( (x2-x1)**2 + (y2-y1)**2 )



    """ UPDATES """

    def updates(self):
        self.update_hovering()
        self.move()
        self.draw()


    def update_hovering(self):
        self.hover_node = False
        mx, my = pygame.mouse.get_pos()

        for node in self.nodes:
            node.update_hovering(mx, my)
            if node.get_hovering():
                self.hover_node = node

    def move(self):
        for node in self.nodes:
            node.move()

    def draw(self):
        self.win.fill(self.set.background)
        self.draw_page_border()
        self.draw_working_area_border()
        self.printr.print_instructions(self.edge_start_node, self.hover_node, self.queue)
        self.draw_edges()
        self.draw_nodes()

        pygame.display.update()

    def draw_edges(self):
        c = self.set.grey
        for node in self.nodes:
            start_coord = node.get_coords()
            neighbours = node.get_neighbours()

            for end_node in neighbours:
                end_coord = end_node.get_coords()
                pygame.draw.line(self.win, c, start_coord, end_coord, 1)

    def draw_nodes(self):
        for node in self.nodes:
            node.draw()


    """ MAIN """

    def main(self):
        while True:
            self.set.clock.tick(self.set.FPS)
            self.win.fill(self.set.background)
            self.events()
            self.updates()