diff --git a/bash.exe.stackdump b/bash.exe.stackdump new file mode 100644 index 000000000..6c70bb288 --- /dev/null +++ b/bash.exe.stackdump @@ -0,0 +1,16 @@ +Stack trace: +Frame Function Args +00800000010 0018006392E (0018027F9A0, 0018026AFD1, 00000000059, 000FFFFB730) +00800000010 0018004973A (00800000010, 00100000000, 00000000000, 00000000001) +00800000010 00180049772 (00000000000, 00000000000, 00000000059, 0018034FAB8) +00800000010 001800706C9 (000FFFFCCE0, 000FFFFC800, 0000000000B, 00000000000) +00800000010 00180070980 (00000000003, 000FFFFC920, 00180045BCF, 000FFFFC920) +00800000010 00180072189 (000FFFFC920, 0018026D795, 001800FDD27, 0000000000D) +00800000010 0018005B0C3 (000FFFFCA70, 00000000000, 00000000000, 008FFFFFFFF) +00800000010 0018005C475 (00000000002, 0018034F400, 00000000000, 000000303E9) +00800000010 0018005C95F (000FFFFCCE0, 00800061E20, 000FFFFCC70, 000FFFFCC70) +000FFFFCBEC 0018005CD26 (000FFFFCE00, 00000000000, 00000000030, 0000000002F) +000FFFFCCE0 00180049EE8 (00000000000, 00000000000, 00000000000, 00000000000) +000FFFFFFF0 00180048846 (00000000000, 00000000000, 00000000000, 00000000000) +000FFFFFFF0 001800488F4 (00000000000, 00000000000, 00000000000, 00000000000) +End of stack trace diff --git a/prob.py b/prob.py new file mode 100644 index 000000000..b803585f7 --- /dev/null +++ b/prob.py @@ -0,0 +1,47 @@ +import math + +probability = 1 +total = 1000 +numerator = 994 +denominator = 998 +count = 1 +greatest_chance = 0 +print() +print("1 Degree Away:") +for i in range(5): + for j in range(4): + probability = probability * (numerator/denominator) + numerator -= 1 + denominator -= 1 + print("Probability of", str(count), "User(s):", str(round(probability * 100, 2))+"%", "\tAverage Users:", math.ceil(count*probability)) + count += 1 +count_2 = 1 +print() +print("2 Degrees Away:") +for i in range(20): + for j in range(4): + probability = probability * (numerator/denominator) + numerator -= 1 + denominator -= 1 + print("Probability of", str(count_2), "User(s):", str(round(probability * 100, 2))+"%", "\tAverage Users:", math.ceil(count_2*probability)) + count_2 += 1 +count_3 = 1 +print() +print("3 Degrees Away:") +for i in range(80): + for j in range(4): + probability = probability * (numerator/denominator) + numerator -= 1 + denominator -= 1 + print("Probability of", str(count_3), "User(s):", str(round(probability * 100, 2))+"%", "\tAverage Users:", math.ceil(count_3*probability)) + count_3 += 1 +# count_4 = 1 +# print() +# print("4 Degrees Away:") +# for i in range(320): +# for j in range(4): +# probability = probability * (numerator/denominator) +# numerator -= 1 +# denominator -= 1 +# print(str(count_4)+": Probability:", probability * 100, "Users:", count_4*probability) +# count_4 += 1 \ No newline at end of file diff --git a/projects/adventure/adv.py b/projects/adventure/adv.py index 8bc540b5e..2914773d6 100644 --- a/projects/adventure/adv.py +++ b/projects/adventure/adv.py @@ -5,16 +5,233 @@ import random from ast import literal_eval +import os +dirpath = os.path.dirname(os.path.abspath(__file__)) +""" +Graph, Stack, and Queue Classes +""" + +class Queue(): + def __init__(self): + self.queue = [] + def enqueue(self, value): + self.queue.append(value) + def dequeue(self): + if self.size() > 0: + return self.queue.pop(0) + else: + return None + def size(self): + return len(self.queue) + +class Stack(): + def __init__(self): + self.stack = [] + def push(self, value): + self.stack.append(value) + def pop(self): + if self.size() > 0: + return self.stack.pop() + else: + return None + def peek(self): + if self.size() > 0: + return self.stack[len(self.stack) - 1] + else: + return None + def size(self): + return len(self.stack) + +class RoomGraph(): + + """Represent a graph as a dictionary of vertices mapping labels to edges.""" + def __init__(self): + self.rooms = {} + + def add_room(self, room_id, exits): #int, list + """ + Add a room to the graph along with its exits. + """ + if room_id not in self.rooms: + self.rooms[room_id] = {} + for r_exit in exits: + self.rooms[room_id][r_exit] = '?' + return True + else: + return False + + + def add_exit_id(self, room1, direction, room2): + """ + Add a directed edge to the graph. + """ + if room1 == room2: + return False + else: + reverse_direction = '' + if direction == 'n': + reverse_direction = 's' + elif direction == 's': + reverse_direction = 'n' + elif direction == 'w': + reverse_direction = 'e' + else: # direction == 'e' + reverse_direction = 'w' + self.rooms[room1][direction] = room2 + self.rooms[room2][reverse_direction] = room1 + return True + + def get_neighbors(self, room_id): + """ + Get all adjacent rooms to a given room. + """ + return self.rooms[room_id] # e.g. {'n': ?, 's': ?} + + def dft(self, starting_room_id, graph, player): + """ + Traverse through all rooms from a given a starting room, depth first. + """ + # picks a random unexplored direction + # picks a random unexplored direction + visited = {} + backward = Stack() + path = [] + visited[player.current_room.id] = player.current_room.get_exits() + while len(visited) < len(graph): + if len(visited) == 11: + print("start") + if player.current_room.id not in visited.keys(): + visited[player.current_room.id] = player.current_room.get_exits() + visited[player.current_room.id].remove(backward.peek()) + if len(visited) == len(graph): + return path + + try: + next_direction = visited[player.current_room.id].pop() + reverse_direction = self.reverse_direction(next_direction) + backward.push(reverse_direction) + player.travel(next_direction) + path.append(next_direction) + except: + next_direction = backward.pop() + player.travel(next_direction) + path.append(next_direction) + # visited = {set()} + # current_room = starting_room_id + # previous_room = None + # next_room = None + # forward = Stack() + # backward = Stack() + # forward_counter = 0 + # reverse_counter = 0 + # path = [] + # all_visited = True + + # # First Call + # exit_direction_list = list(self.get_neighbors(current_room).keys()) + # self.next_direction_1(exit_direction_list, current_room, forward, backward) + # visited.add(current_room) + + # print("path, room, visited") + # while len(visited) < len(graph): + # print(len(path), current_room, len(visited)) + # if len(path) == 344: + # print("here") + # if self.check_neighbors_for_dead_end(exit_direction_list, current_room) == True or next_room in visited: # Backward + # if reverse_counter == forward_counter: + # next_room = graph[current_room][1][forward.peek()] + # if next_room in visited: + # backward.pop() + # forward.pop() + # continue + # next_direction = backward.pop() + # path.append(next_direction) + # reverse_counter += 1 + # next_room = graph[current_room][1][next_direction] + # previous_room = current_room + # current_room = next_room + # exit_direction_list = list(self.get_neighbors(current_room).keys()) + # if self.check_neighbors_for_dead_end(exit_direction_list, current_room) == True: + # next_room = graph[current_room][1][backward.peek()] + # else: + # next_room = graph[current_room][1][forward.peek()] + # if next_room in visited: + # backward.pop() + # forward.pop() + # else: # Forward + # next_direction = forward.pop() + # opposite_direction = self.reverse_direction(next_direction) + # next_room = graph[current_room][1][next_direction] + # try: + # graph[next_room][1][opposite_direction] + # path.append(next_direction) + # forward_counter += 1 + # self.rooms[current_room][next_direction] = next_room + # self.rooms[next_room][opposite_direction] = current_room + # previous_room = current_room + # current_room = next_room + # visited.add(current_room) + # except: + # self.rooms[current_room][next_direction] = next_room + # backward.pop() + # exit_direction_list = list(self.get_neighbors(current_room).keys()) + # self.next_direction_1(exit_direction_list, current_room, forward, backward) + # if self.check_neighbors_for_dead_end(exit_direction_list, current_room) == False: + # next_room = graph[current_room][1][forward.peek()] + # if next_room in visited: + # backward.pop() + # forward.pop() + # self.rooms[current_room][forward.peek()] = next_room + # try: + # self.rooms[next_room][self.reverse_direction(forward.peek())] = current_room + # except: + # pass + # return path + + # def check_neighbors_for_dead_end(self, exit_direction_list, current_room): + # dead_end = True + # for direction in exit_direction_list: + # if self.get_neighbors(current_room)[direction] == '?': + # dead_end = False + # return dead_end + + # def next_direction_2(self, exit_direction_list, current_room, forward, backward, visited): + # for direction in exit_direction_list: + # if self.get_neighbors(current_room)[direction] == '?': + # if current_room in visited: + # break + # else: + # forward.push(direction) + # backward.push(self.reverse_direction(direction)) + + # def next_direction_1(self, exit_direction_list, current_room, forward, backward): + # for direction in exit_direction_list: #n + # if self.get_neighbors(current_room)[direction] == '?': + # forward.push(direction) + # backward.push(self.reverse_direction(direction)) + + def reverse_direction(self, direction): + if direction == 'n': + return 's' + elif direction == 's': + return 'n' #this -> n + elif direction == 'w': + return 'e' + else: # direction == 'e' + return 'w' + +""" +""" # Load world world = World() # You may uncomment the smaller graphs for development and testing purposes. -# map_file = "maps/test_line.txt" -# map_file = "maps/test_cross.txt" -# map_file = "maps/test_loop.txt" -# map_file = "maps/test_loop_fork.txt" -map_file = "maps/main_maze.txt" +# map_file = dirpath + "/maps/test_line.txt" +# map_file = dirpath + "/maps/test_cross.txt" +# map_file = dirpath + "/maps/test_loop.txt" +# map_file = dirpath + "/maps/test_loop_fork.txt" +map_file = dirpath + "/maps/main_maze.txt" # Loads the map into a dictionary room_graph=literal_eval(open(map_file, "r").read()) @@ -27,9 +244,15 @@ # Fill this out with directions to walk # traversal_path = ['n', 'n'] -traversal_path = [] - +# traversal_path = [] +graph = RoomGraph() +for room in room_graph: + graph.add_room(room, list(room_graph[room][1].keys())) +print("Class:", graph.rooms) +print("Neighbors:", graph.get_neighbors(0)) +# print("DFT:", graph.dft(player.current_room.id, room_graph, player)) +traversal_path = graph.dft(player.current_room.id, room_graph, player) # TRAVERSAL TEST visited_rooms = set() @@ -40,6 +263,14 @@ player.travel(move) visited_rooms.add(player.current_room) +print("Room:::", player.current_room.id) +print("Exit:::", player.current_room.get_exits()) +# print("Graph:::", room_graph) +#room_graph[player.current_room.id][1]['n'] in visited +#room_graph[player.current_room.id][1]['s'] in visited +#room_graph[player.current_room.id][1]['e'] in visited +#room_graph[player.current_room.id][1]['w'] in visited +print("Length:::", len(room_graph)) if len(visited_rooms) == len(room_graph): print(f"TESTS PASSED: {len(traversal_path)} moves, {len(visited_rooms)} rooms visited") else: @@ -51,12 +282,12 @@ ####### # UNCOMMENT TO WALK AROUND ####### -player.current_room.print_room_description(player) -while True: - cmds = input("-> ").lower().split(" ") - if cmds[0] in ["n", "s", "e", "w"]: - player.travel(cmds[0], True) - elif cmds[0] == "q": - break - else: - print("I did not understand that command.") +# player.current_room.print_room_description(player) +# while True: +# cmds = input("-> ").lower().split(" ") +# if cmds[0] in ["n", "s", "e", "w"]: +# player.travel(cmds[0], True) +# elif cmds[0] == "q": +# break +# else: +# print("I did not understand that command.") diff --git a/projects/ancestor/ancestor.py b/projects/ancestor/ancestor.py index 3bd003098..3860871b8 100644 --- a/projects/ancestor/ancestor.py +++ b/projects/ancestor/ancestor.py @@ -1,3 +1,203 @@ -def earliest_ancestor(ancestors, starting_node): - pass \ No newline at end of file +def earliest_ancestor(ancestors, starting_node): # ([(parent, child), (parent, child), (parent, child)], 6) + graph = Graph() + # initialize graph, inverting parents and children + for pair in ancestors: + if pair[1] not in graph.vertices.keys(): + graph.add_vertex(pair[1]) + graph.add_edge(pair[1], pair[0]) + else: + graph.add_edge(pair[1], pair[0]) + if pair[0] not in graph.vertices.keys(): + graph.add_vertex(pair[0]) + earliest_ancestor = graph.dft(starting_node) + # last = graph.dfs(starting_node, earliest_ancestor)[-1] + if starting_node != earliest_ancestor: + # return last + return earliest_ancestor + else: + return -1 + +""" +Helper Classes +""" +class Queue(): + def __init__(self): + self.queue = [] + def enqueue(self, value): + self.queue.append(value) + def dequeue(self): + if self.size() > 0: + return self.queue.pop(0) + else: + return None + def size(self): + return len(self.queue) + +class Stack(): + def __init__(self): + self.stack = [] + def push(self, value): + self.stack.append(value) + def pop(self): + if self.size() > 0: + return self.stack.pop() + else: + return None + def size(self): + return len(self.stack) + +class Graph: + + """Represent a graph as a dictionary of vertices mapping labels to edges.""" + def __init__(self): + self.vertices = {} + + def add_vertex(self, vertex_id): + """ + Add a vertex to the graph. + """ + self.vertices[vertex_id] = set() + + def add_edge(self, v1, v2): + """ + Add a directed edge to the graph. + """ + self.vertices[v1].add(v2) + + def get_neighbors(self, vertex_id): + """ + Get all neighbors (edges) of a vertex. + """ + return self.vertices[vertex_id] + + def bft(self, starting_vertex): + """ + Print each vertex in breadth-first order + beginning from starting_vertex. + """ + visited = set() + queue = Queue() + queue.enqueue(starting_vertex) + while queue.size() > 0: + current = queue.dequeue() + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + queue.enqueue(neighbor) + + def dft(self, starting_vertex): + """ + Print each vertex in depth-first order + beginning from starting_vertex. + """ + visited = set() + stack = Stack() + stack.push(starting_vertex) + current = None + while stack.size() > 0: + only_ancestors = True + sorted_neighbors = [] + current = stack.pop() + if current not in visited: + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + sorted_neighbors.append(neighbor) + if len(self.get_neighbors(neighbor)) > 0: + only_ancestors = False + sorted(sorted_neighbors) + for i in range(len(sorted_neighbors)): + if only_ancestors: + neighbor = sorted_neighbors[len(sorted_neighbors)-(i+1)] + else: + neighbor = sorted_neighbors[i] + if neighbor not in visited: + stack.push(neighbor) + return current + + def dft_recursive(self, starting_vertex): + """ + Print each vertex in depth-first order + beginning from starting_vertex. + + This should be done using recursion. + """ + print(starting_vertex) + self.vertices[starting_vertex].add("Visited") + for neighbor in self.get_neighbors(starting_vertex): + if (type(neighbor) is int) and ("Visited" not in self.get_neighbors(neighbor)): + self.dft_recursive(neighbor) + + + def bfs(self, starting_vertex, destination_vertex): + """ + Return a list containing the shortest path from + starting_vertex to destination_vertex in + breath-first order. + """ + visited = set() + queue = Queue() + queue.enqueue([starting_vertex]) + while queue.size() > 0: + current_path = queue.dequeue() + last_vertex = current_path[-1] + if last_vertex not in visited: + if last_vertex == destination_vertex: + return current_path + else: + visited.add(last_vertex) + for neighbor in self.get_neighbors(last_vertex): + path_copy = list(current_path) + path_copy.append(neighbor) + queue.enqueue(path_copy) + + def dfs(self, starting_vertex, destination_vertex): + """ + Return a list containing a path from + starting_vertex to destination_vertex in + depth-first order. + """ + visited = [] + stack = Stack() + stack.push(starting_vertex) + while stack.size() > 0: + current = stack.pop() + if current not in visited: + visited.append(current) + if current == destination_vertex: + return visited + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + stack.push(neighbor) + + def dfs_recursive(self, starting_vertex, destination_vertex, visited=None, path=[]): # Walked through it in the debugger and I get it, but it's dizzyingly complicated. + """ + Return a list containing a path from + starting_vertex to destination_vertex in + depth-first order. + + This should be done using recursion. + """ + if visited is None: + visited = set() + path.append(starting_vertex) + visited.add(starting_vertex) + # current_vertex = path[-1] + if starting_vertex == destination_vertex: + return path + else: + for neighbor in self.get_neighbors(starting_vertex): + if neighbor not in visited: + path_copy = list(path) + path_copy.append(neighbor) + if neighbor == destination_vertex: + return path_copy + # return self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + else: + final_path = self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + final_vertex = final_path[-1] + if final_vertex == destination_vertex: + return final_path + return path \ No newline at end of file diff --git a/projects/ancestor/test_ancestor.py b/projects/ancestor/test_ancestor.py index 4d785f11e..c2e95e81c 100644 --- a/projects/ancestor/test_ancestor.py +++ b/projects/ancestor/test_ancestor.py @@ -15,16 +15,16 @@ class Test(unittest.TestCase): def test_earliest_ancestor(self): test_ancestors = [(1, 3), (2, 3), (3, 6), (5, 6), (5, 7), (4, 5), (4, 8), (8, 9), (11, 8), (10, 1)] self.assertEqual(earliest_ancestor(test_ancestors, 1), 10) - self.assertEqual(earliest_ancestor(test_ancestors, 2), -1) + self.assertEqual(earliest_ancestor(test_ancestors, 2), -1) # no ancestor self.assertEqual(earliest_ancestor(test_ancestors, 3), 10) - self.assertEqual(earliest_ancestor(test_ancestors, 4), -1) + self.assertEqual(earliest_ancestor(test_ancestors, 4), -1) # no ancestor self.assertEqual(earliest_ancestor(test_ancestors, 5), 4) self.assertEqual(earliest_ancestor(test_ancestors, 6), 10) self.assertEqual(earliest_ancestor(test_ancestors, 7), 4) - self.assertEqual(earliest_ancestor(test_ancestors, 8), 4) - self.assertEqual(earliest_ancestor(test_ancestors, 9), 4) - self.assertEqual(earliest_ancestor(test_ancestors, 10), -1) - self.assertEqual(earliest_ancestor(test_ancestors, 11), -1) + self.assertEqual(earliest_ancestor(test_ancestors, 8), 4) # goes to larger ancestor + self.assertEqual(earliest_ancestor(test_ancestors, 9), 4) # goes to larger ancestor + self.assertEqual(earliest_ancestor(test_ancestors, 10), -1) # no ancestor + self.assertEqual(earliest_ancestor(test_ancestors, 11), -1) # no ancestor if __name__ == '__main__': unittest.main() diff --git a/projects/graph/graph.py b/projects/graph/graph.py index 59fecae4b..f565675d0 100644 --- a/projects/graph/graph.py +++ b/projects/graph/graph.py @@ -13,33 +13,63 @@ def add_vertex(self, vertex_id): """ Add a vertex to the graph. """ - pass # TODO + self.vertices[vertex_id] = set() def add_edge(self, v1, v2): """ Add a directed edge to the graph. """ - pass # TODO + self.vertices[v1].add(v2) def get_neighbors(self, vertex_id): """ Get all neighbors (edges) of a vertex. """ - pass # TODO + return self.vertices[vertex_id] def bft(self, starting_vertex): """ Print each vertex in breadth-first order beginning from starting_vertex. """ - pass # TODO + visited = set() + queue = Queue() + queue.enqueue(starting_vertex) + while queue.size() > 0: + current = queue.dequeue() + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + queue.enqueue(neighbor) def dft(self, starting_vertex): """ Print each vertex in depth-first order beginning from starting_vertex. """ - pass # TODO + # visited = set() + # stack = Stack() + # stack.push(starting_vertex) + # while stack.size() > 0: + # current = stack.pop() + # if current not in visited: + # print(current) + # visited.add(current) + # for neighbor in self.get_neighbors(current): + # if neighbor not in visited: + # stack.push(neighbor) + visited = set() + stack = Stack() + stack.push(starting_vertex) + while stack.size() > 0: + current = stack.pop() + if current not in visited: + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + stack.push(neighbor) def dft_recursive(self, starting_vertex): """ @@ -48,7 +78,12 @@ def dft_recursive(self, starting_vertex): This should be done using recursion. """ - pass # TODO + print(starting_vertex) + self.vertices[starting_vertex].add("Visited") + for neighbor in self.get_neighbors(starting_vertex): + if (type(neighbor) is int) and ("Visited" not in self.get_neighbors(neighbor)): + self.dft_recursive(neighbor) + def bfs(self, starting_vertex, destination_vertex): """ @@ -56,7 +91,21 @@ def bfs(self, starting_vertex, destination_vertex): starting_vertex to destination_vertex in breath-first order. """ - pass # TODO + visited = set() + queue = Queue() + queue.enqueue([starting_vertex]) + while queue.size() > 0: + current_path = queue.dequeue() + last_vertex = current_path[-1] + if last_vertex not in visited: + if last_vertex == destination_vertex: + return current_path + else: + visited.add(last_vertex) + for neighbor in self.get_neighbors(last_vertex): + path_copy = list(current_path) + path_copy.append(neighbor) + queue.enqueue(path_copy) def dfs(self, starting_vertex, destination_vertex): """ @@ -64,9 +113,20 @@ def dfs(self, starting_vertex, destination_vertex): starting_vertex to destination_vertex in depth-first order. """ - pass # TODO - - def dfs_recursive(self, starting_vertex, destination_vertex): + visited = [] + stack = Stack() + stack.push(starting_vertex) + while stack.size() > 0: + current = stack.pop() + if current not in visited: + visited.append(current) + if current == destination_vertex: + return visited + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + stack.push(neighbor) + + def dfs_recursive(self, starting_vertex, destination_vertex, visited=None, path=[]): # Walked through it in the debugger and I get it, but it's dizzyingly complicated. """ Return a list containing a path from starting_vertex to destination_vertex in @@ -74,7 +134,28 @@ def dfs_recursive(self, starting_vertex, destination_vertex): This should be done using recursion. """ - pass # TODO + if visited is None: + visited = set() + path.append(starting_vertex) + visited.add(starting_vertex) + # current_vertex = path[-1] + if starting_vertex == destination_vertex: + return path + else: + for neighbor in self.get_neighbors(starting_vertex): + if neighbor not in visited: + path_copy = list(path) + path_copy.append(neighbor) + if neighbor == destination_vertex: + return path_copy + # return self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + else: + final_path = self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + final_vertex = final_path[-1] + if final_vertex == destination_vertex: + return final_path + return path + if __name__ == '__main__': graph = Graph() # Instantiate your graph @@ -118,7 +199,7 @@ def dfs_recursive(self, starting_vertex, destination_vertex): 1, 2, 4, 3, 7, 6, 5 1, 2, 4, 3, 7, 5, 6 ''' - graph.bft(1) + # graph.bft(1) ''' Valid DFT paths: @@ -128,18 +209,18 @@ def dfs_recursive(self, starting_vertex, destination_vertex): 1, 2, 4, 6, 3, 5, 7 ''' graph.dft(1) - graph.dft_recursive(1) + # graph.dft_recursive(1) ''' Valid BFS path: [1, 2, 4, 6] ''' - print(graph.bfs(1, 6)) + # print(graph.bfs(1, 6)) ''' Valid DFS paths: [1, 2, 4, 6] [1, 2, 4, 7, 6] ''' - print(graph.dfs(1, 6)) - print(graph.dfs_recursive(1, 6)) + # print(graph.dfs(1, 6)) + # print(graph.dfs_recursive(1, 6)) diff --git a/projects/social/social.py b/projects/social/social.py index 8609d8800..8eafd9e51 100644 --- a/projects/social/social.py +++ b/projects/social/social.py @@ -1,3 +1,191 @@ +import random +import time + +""" +Helper Classes +""" +class Queue(): + def __init__(self): + self.queue = [] + def enqueue(self, value): + self.queue.append(value) + def dequeue(self): + if self.size() > 0: + return self.queue.pop(0) + else: + return None + def size(self): + return len(self.queue) + +class Stack(): + def __init__(self): + self.stack = [] + def push(self, value): + self.stack.append(value) + def pop(self): + if self.size() > 0: + return self.stack.pop() + else: + return None + def size(self): + return len(self.stack) + +class Graph: + + """Represent a graph as a dictionary of vertices mapping labels to edges.""" + def __init__(self): + self.vertices = {} + + def add_vertex(self, vertex_id): + """ + Add a vertex to the graph. + """ + self.vertices[vertex_id] = set() + + def add_edge(self, v1, v2): + """ + Add a directed edge to the graph. + """ + self.vertices[v1].add(v2) + + def get_neighbors(self, vertex_id): + """ + Get all neighbors (edges) of a vertex. + """ + return self.vertices[vertex_id] + + def bft(self, starting_vertex): + """ + Print each vertex in breadth-first order + beginning from starting_vertex. + """ + visited = set() + queue = Queue() + queue.enqueue(starting_vertex) + while queue.size() > 0: + current = queue.dequeue() + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + queue.enqueue(neighbor) + + def dft(self, starting_vertex): + """ + Print each vertex in depth-first order + beginning from starting_vertex. + """ + visited = set() + stack = Stack() + stack.push(starting_vertex) + current = None + while stack.size() > 0: + only_ancestors = True + sorted_neighbors = [] + current = stack.pop() + if current not in visited: + print(current) + visited.add(current) + for neighbor in self.get_neighbors(current): + sorted_neighbors.append(neighbor) + if len(self.get_neighbors(neighbor)) > 0: + only_ancestors = False + sorted(sorted_neighbors) + for i in range(len(sorted_neighbors)): + if only_ancestors: + neighbor = sorted_neighbors[len(sorted_neighbors)-(i+1)] + else: + neighbor = sorted_neighbors[i] + if neighbor not in visited: + stack.push(neighbor) + return current + + def dft_recursive(self, starting_vertex): + """ + Print each vertex in depth-first order + beginning from starting_vertex. + + This should be done using recursion. + """ + print(starting_vertex) + self.vertices[starting_vertex].add("Visited") + for neighbor in self.get_neighbors(starting_vertex): + if (type(neighbor) is int) and ("Visited" not in self.get_neighbors(neighbor)): + self.dft_recursive(neighbor) + + + def bfs(self, starting_vertex, destination_vertex): + """ + Return a list containing the shortest path from + starting_vertex to destination_vertex in + breath-first order. + """ + visited = set() + queue = Queue() + queue.enqueue([starting_vertex]) + while queue.size() > 0: + current_path = queue.dequeue() + last_vertex = current_path[-1] + if last_vertex not in visited: + if last_vertex == destination_vertex: + return current_path + else: + visited.add(last_vertex) + for neighbor in self.get_neighbors(last_vertex): + path_copy = list(current_path) + path_copy.append(neighbor) + queue.enqueue(path_copy) + + def dfs(self, starting_vertex, destination_vertex): + """ + Return a list containing a path from + starting_vertex to destination_vertex in + depth-first order. + """ + visited = [] + stack = Stack() + stack.push(starting_vertex) + while stack.size() > 0: + current = stack.pop() + if current not in visited: + visited.append(current) + if current == destination_vertex: + return visited + for neighbor in self.get_neighbors(current): + if neighbor not in visited: + stack.push(neighbor) + + def dfs_recursive(self, starting_vertex, destination_vertex, visited=None, path=[]): # Walked through it in the debugger and I get it, but it's dizzyingly complicated. + """ + Return a list containing a path from + starting_vertex to destination_vertex in + depth-first order. + + This should be done using recursion. + """ + if visited is None: + visited = set() + path.append(starting_vertex) + visited.add(starting_vertex) + # current_vertex = path[-1] + if starting_vertex == destination_vertex: + return path + else: + for neighbor in self.get_neighbors(starting_vertex): + if neighbor not in visited: + path_copy = list(path) + path_copy.append(neighbor) + if neighbor == destination_vertex: + return path_copy + # return self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + else: + final_path = self.dfs_recursive(neighbor, destination_vertex, visited, path_copy) + final_vertex = final_path[-1] + if final_vertex == destination_vertex: + return final_path + return path + + class User: def __init__(self, name): self.name = name @@ -43,10 +231,54 @@ def populate_graph(self, num_users, avg_friendships): self.users = {} self.friendships = {} # !!!! IMPLEMENT ME - + if avg_friendships >= num_users: + print("The average number of friendships must be less than the number of users.") + return + elif num_users <= 1: + print("The number of users must be above 1.") + return + else: # Add users + for i in range(num_users): + # name = input("Enter a name: ") + # self.add_user(name) + self.add_user("same_name") # Create friendships + friendships_max = num_users * avg_friendships + friendships_count = 0 + + #randomize the distribution + random_order_users = [] + for i in range(1, num_users+1): + random_order_users.append(i) + random.shuffle(random_order_users) + + for i in random_order_users: + num_friends = None + while (num_friends is None) or (((num_friends * 2) + friendships_count) > friendships_max): + num_friends = random.randint(0, num_users-1) + for j in range(num_friends): + friend_id = 0 + while friend_id == 0 or friend_id == i or friend_id in self.friendships[i]: + friend_id = random.randint(1, num_users) + self.add_friendship(i, friend_id) + friendships_count += num_friends * 2 + + def get_all_social_paths_given(self, user_id): + queue = Queue() + visited = {} + queue.enqueue([user_id]) + while queue.size() > 0: + path = queue.dequeue() + last_vertex = path[-1] + if last_vertex not in visited: + visited[last_vertex] = path + for friend in self.friendships[last_vertex]: + path_copy = path.copy() + path_copy.append(friend) + queue.enqueue(path_copy) + return visited def get_all_social_paths(self, user_id): """ @@ -59,12 +291,49 @@ def get_all_social_paths(self, user_id): """ visited = {} # Note that this is a dictionary, not a set # !!!! IMPLEMENT ME + visited[user_id] = [user_id] + graph = Graph() + for key in self.users.keys(): + graph.add_vertex(key) + for friend in self.friendships[key]: + graph.add_edge(key, friend) + self.get_neighbors_recursively(user_id, user_id, visited, graph) return visited + def get_neighbors_recursively(self, original_vertex, vertex, visited, graph=Graph()): + for neighbor in graph.get_neighbors(vertex): + if neighbor not in visited.keys(): + visited[neighbor] = graph.bfs(original_vertex, neighbor) + self.get_neighbors_recursively(original_vertex, neighbor, visited, graph) + +# if __name__ == '__main__': +# sg = SocialGraph() +# sg.populate_graph(10, 2) +# print(sg.friendships) +# connections = sg.get_all_social_paths(1) +# print(connections) + if __name__ == '__main__': sg = SocialGraph() - sg.populate_graph(10, 2) - print(sg.friendships) + sg.populate_graph(500, 5) + # print(sg.friendships) + start_time = time.time() connections = sg.get_all_social_paths(1) - print(connections) + end_time = time.time() + print("My Function's Runtime:", (end_time - start_time)) + start_time = time.time() + connections = sg.get_all_social_paths_given(1) + end_time = time.time() + print("Given Function's Runtime:", (end_time - start_time)) + # print(connections) + +""" +1. To create 100 users with an average of 10 friends each, I would need to call add_friendship() 500 times because the number of friendships that would be created would be +100 * 10, which is 1000. And because each add_friendship creates 2 friendships (one in each direction), the number of calls would be quotient of 1000 / 2. + +2. +15.8% of other users will be in a particular user's extended social network over 50% of the time. +The average degree of seperation between a user and an extended friend is 2. +Logic found in prob.py. +""" \ No newline at end of file