diff --git a/projects/adventure/adv.py b/projects/adventure/adv.py index 8bc540b5e..43e684cc2 100644 --- a/projects/adventure/adv.py +++ b/projects/adventure/adv.py @@ -1,13 +1,16 @@ from room import Room from player import Player from world import World - +from collections import deque import random from ast import literal_eval # Load world world = World() +#construct a traversal graph +#do dft for finding all the possible room player can move +#do bfs for finding unexplored direction # You may uncomment the smaller graphs for development and testing purposes. # map_file = "maps/test_line.txt" @@ -24,12 +27,82 @@ world.print_rooms() player = Player(world.starting_room) +player.current_room = world.starting_room # Fill this out with directions to walk # traversal_path = ['n', 'n'] traversal_path = [] - +backtrack = { + 'n': 's', + 'e': 'w', + 'w': 'e', + 's': 'n'} + +def bfs(curr_room): + visited = set() + queue = deque() + queue.append((curr_room,[])) + + while len(queue)>0: + (room,path) = queue.popleft() + if room in visited: + continue + visited.add(room) + for direction in visited_room[room]: + if visited_room[room][direction] == '?': + return [path,direction] + else: + newPath = path.copy() + newPath.append(direction) + next_room = visited_room[room][direction] + queue.append((next_room,newPath)) + +def dft(unexplored_diection): + stack = deque() + stack.append(unexplored_diection) + + while len(stack)>0: + curr_exit = stack.pop() + move_direction = curr_exit[-1] + if move_direction not in visited_room[player.current_room.id]: + continue + elif visited_room[player.current_room.id][move_direction] == '?': + prev_room = player.current_room.id + player.travel(move_direction) + traversal_path.append(move_direction) + + visited_room[prev_room][move_direction] = player.current_room.id + opposite_val = backtrack[move_direction] + + if player.current_room.id not in visited_room: + visited_room[player.current_room.id] = {opposite_val:prev_room} + else: + visited_room[player.current_room.id][opposite_val] = prev_room + + for direction in player.current_room.get_exits(): + if direction not in visited_room[player.current_room.id]: + visited_room[player.current_room.id][direction] = '?' + new_dir = [] + new_dir.append(direction) + stack.append(new_dir) + + unexplored_diection = bfs(player.current_room.id) + + if unexplored_diection != None: + for direction in unexplored_diection[0]: + player.travel(direction) + traversal_path.append(direction) + dft([unexplored_diection[1]]) + + +starting_dir = random.choice(player.current_room.get_exits()) + +visited_room = {player.current_room.id:{}} +for direction in player.current_room.get_exits(): + visited_room[player.current_room.id][direction] = '?' + +dft(starting_dir) # TRAVERSAL TEST visited_rooms = set() @@ -46,17 +119,15 @@ print("TESTS FAILED: INCOMPLETE TRAVERSAL") print(f"{len(room_graph) - len(visited_rooms)} unvisited rooms") - - -####### -# 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.") +# ####### +# # 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.") diff --git a/projects/ancestor/ancestor.py b/projects/ancestor/ancestor.py index 3bd003098..e5a62bebd 100644 --- a/projects/ancestor/ancestor.py +++ b/projects/ancestor/ancestor.py @@ -1,3 +1,58 @@ -def earliest_ancestor(ancestors, starting_node): - pass \ No newline at end of file +from collections import deque, defaultdict + +''' +PLAN: +Vertex = People +Edges = parent-child relationships +Weights = not needed +Path = persons family tree + +Build Graph: +build graph based on edges given, each user has a directed edge to its ancestor/parent. + +Traverse Graph: +traverse all paths starting from start node. +Keep track of farthest node with lowest user ID +output that node. +''' + +def earliest_ancestor(ancestors,starting_node): + graph = createGraph(ancestors) + + earliest_ancestor = (starting_node,0) + + stack = deque() + stack.append((starting_node,0)) # tuple with node ID and its distance from starting node + visited = set() + + while len(stack)>0: + curr = stack.pop() + currNode, dist = curr[0],curr[1] + visited.add(curr) + + if currNode not in graph: # this means its a terminal node (node with no parent, it won't be the key in graph) + if dist > earliest_ancestor[1]: + earliest_ancestor = curr + elif ((dist == earliest_ancestor[1]) and (currNode < earliest_ancestor[0])): + earliest_ancestor = curr + else: + for ancestor in graph[currNode]: + if ancestor not in visited: + stack.append((ancestor,dist+1)) + + if earliest_ancestor[0] != starting_node: + return earliest_ancestor[0] + else: + return -1 + +# vertices = {} +# ancestors = [[1,3],[2,3],[3,6],[5,6],[5,7],[4,5],[4,8],[8,9],[11,8],[10,1]] + +def createGraph(paths): # Building out a graph with child as key and parent as value (directional edge from child to parent.) + graph = defaultdict(set) + for edge in paths: + origin,dest = edge[0],edge[1] + graph[dest].add(origin) + return graph + diff --git a/projects/graph/graph.py b/projects/graph/graph.py index 59fecae4b..b54298bd2 100644 --- a/projects/graph/graph.py +++ b/projects/graph/graph.py @@ -3,52 +3,103 @@ """ from util import Stack, Queue # These may come in handy + +from collections import deque class Graph: """Represent a graph as a dictionary of vertices mapping labels to edges.""" def __init__(self): + # vertex ID --> set of neighbors self.vertices = {} + # def __repr__(self): + # return str(self.vertices) + def add_vertex(self, vertex_id): """ Add a vertex to the graph. """ - pass # TODO - - def add_edge(self, v1, v2): + self.vertices[vertex_id] = set() + + # adds directed edge from "from_vertex" to "to_vertex" + def add_edge(self, from_vertex_id, to_vertex_id): """ Add a directed edge to the graph. """ - pass # TODO + if from_vertex_id not in self.vertices or to_vertex_id not in self.vertices: + print ("Attempting to add edge to non existing node") + return + self.vertices[from_vertex_id].add(to_vertex_id) #.add is method for set def get_neighbors(self, vertex_id): """ Get all neighbors (edges) of a vertex. """ - pass # TODO + return self.vertices[vertex_id] + + #remove vertex from graph and any incoming edges to it + def remove_vertex(self,vertex_id): + if vertex_id not in self.vertices: + print("Attempting to remove non existant vertices") + return + + self.vertices.pop(vertex_id) + for remaining_vertex in self.vertices: + self.vertices[remaining_vertex].discard(vertex_id) + + def remove_edge(self,from_vertex_id,to_vertex_id): + if from_vertex_id not in self.vertices or to_vertex_id not in self.vertices: + print("Attempting to remove edges non existant vertex") + return + self.vertices[from_vertex_id].discard(to_vertex_id) + + def bft(self, starting_vertex): """ Print each vertex in breadth-first order beginning from starting_vertex. """ - pass # TODO + queue = deque() + visited = set() + queue.append(starting_vertex) + while len(queue)>0: + currNode = queue.popleft() + if currNode not in visited: + visited.add(currNode) + print (currNode) + for neigbor in self.vertices[currNode]: + queue.append(neigbor) def dft(self, starting_vertex): """ Print each vertex in depth-first order beginning from starting_vertex. """ - pass # TODO + stack = deque() + visited = set() + stack.append(starting_vertex) + while len(stack)>0: + currNode = stack.pop() + if currNode not in visited: + visited.add(currNode) + print (currNode) + for neigbor in self.vertices[currNode]: + stack.append(neigbor) - def dft_recursive(self, starting_vertex): + + def dft_recursive(self, starting_vertex,visited=set()): """ Print each vertex in depth-first order beginning from starting_vertex. This should be done using recursion. """ - pass # TODO + if starting_vertex not in visited: + print(starting_vertex) + visited.add(starting_vertex) + for neighbor in self.vertices[starting_vertex]: + self.dft_recursive(neighbor,visited) def bfs(self, starting_vertex, destination_vertex): """ @@ -56,15 +107,43 @@ def bfs(self, starting_vertex, destination_vertex): starting_vertex to destination_vertex in breath-first order. """ - pass # TODO - + queue = deque() + visited = set() + queue.append([starting_vertex]) + while len(queue)>0: + currPath = queue.popleft() + currNode = currPath[-1] + if currNode == destination_vertex: + return currPath + if currNode not in visited: + visited.add(currNode) + for neighbor in self.vertices[currNode]: + newPath = list(currPath) + newPath.append(neighbor) + queue.append(newPath) + + def dfs(self, starting_vertex, destination_vertex): """ Return a list containing a path from starting_vertex to destination_vertex in depth-first order. """ - pass # TODO + stack = deque() + visited = set() + #push the current path you are onto stack, instwead of single vertex + stack.append([starting_vertex]) + while len(stack)>0: + currPath = stack.pop() + currNode = currPath[-1] # current node you are on is the last node in the path + if currNode == destination_vertex: + return currPath + if currNode not in visited: + visited.add(currNode) + for neigbor in self.vertices[currNode]: + newPath = list(currPath) # make copy of curr path + newPath.append(neigbor) + stack.append(newPath) def dfs_recursive(self, starting_vertex, destination_vertex): """ @@ -74,7 +153,40 @@ def dfs_recursive(self, starting_vertex, destination_vertex): This should be done using recursion. """ - pass # TODO + visited=set() + return self.dfs_recursive_helper([starting_vertex],destination_vertex,visited) + + # return path to goal vertex if it exists , if not it will return empty array. + def dfs_recursive_helper(self,curr_path,destination_vertex,visited): + curr_vertex = curr_path[-1] + #Base-Case + if curr_vertex == destination_vertex: + return curr_path + + visited.add(curr_vertex) + + # find neighbors of current vertex and make a copy of current path. + # If neighbor is not in visited then append it to copy of curr path + # recurse over copy of curr path , until goal vertex is reached. + for neighbor in self.vertices[curr_vertex]: + if neighbor not in visited: + new_path = list(curr_path) + new_path.append(neighbor) + res = self.dfs_recursive_helper(new_path,destination_vertex,visited) + if len(res)>0: + return res + + return [] + + + + + + + + + + if __name__ == '__main__': graph = Graph() # Instantiate your graph @@ -103,6 +215,8 @@ def dfs_recursive(self, starting_vertex, destination_vertex): ''' print(graph.vertices) + # graph.remove_edge(8,1) + ''' Valid BFT paths: 1, 2, 3, 4, 5, 6, 7 @@ -118,7 +232,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 +242,19 @@ 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)) + # Valid BFS path: + # [1, 2, 4, 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)) - ''' - Valid DFS paths: - [1, 2, 4, 6] - [1, 2, 4, 7, 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..874a562cc 100644 --- a/projects/social/social.py +++ b/projects/social/social.py @@ -1,3 +1,5 @@ +import random +import math class User: def __init__(self, name): self.name = name @@ -42,11 +44,25 @@ def populate_graph(self, num_users, avg_friendships): self.last_id = 0 self.users = {} self.friendships = {} - # !!!! IMPLEMENT ME # Add users + for i in range (num_users): + self.add_user("User {i}") + + # create frienships using add_friendship + possible_frienships = [] + for user_id in self.users: + for friend_id in range(user_id+1,self.last_id+1): + possible_frienships.append((user_id,friend_id)) + + # shuffle that list + random.shuffle(possible_frienships) # Create friendships + #generate all possible frienships beween users and put them them in list + for i in range (math.floor(num_users*avg_friendships/2)): + friendship = possible_frienships[i] + self.add_friendship(friendship[0],friendship[1]) def get_all_social_paths(self, user_id): """ @@ -58,7 +74,7 @@ def get_all_social_paths(self, user_id): The key is the friend's ID and the value is the path. """ visited = {} # Note that this is a dictionary, not a set - # !!!! IMPLEMENT ME + return visited diff --git a/projects/wordlist/lecture.py b/projects/wordlist/lecture.py new file mode 100644 index 000000000..0001d9c6c --- /dev/null +++ b/projects/wordlist/lecture.py @@ -0,0 +1,76 @@ +""" +Given two words (begin_word and end_word), and a dictionary's word list, return the shortest transformation sequence from begin_word to end_word, such that: + +Only one letter can be changed at a time. +Each transformed word must exist in the word list. Note that begin_word is not a transformed word. +Note: + +Return None if there is no such transformation sequence. +All words contain only lowercase alphabetic characters. +You may assume no duplicates in the word list. +You may assume begin_word and end_word are non-empty and are not the same. + +Sample: +beginWord = "hit", +endWord = "cog", +wordList = ["hot","dot","dog","lot","log","cog"] +["hit", "hot", "dot", "dog", "cog"] + +beginWord = "hit", +endWord = "cog", +wordList = ["hot","dot","dog","lot","log"] +[] + +PLAN: +Translate in Graph terminology +Build Graph +Traverse graph + +Vertex : Word +Edge: a possible 1 letter transformation from a word to another word +Weights: None +Path: 1 letter Transformations from begin word to end word + +Build Graph: +Creating all possible transformations in an adjecency list will be too expensive +how to find out next vertex by determining if its a valid vertex from word list. +we should visit a vertex if its in the wordlist + +Traverse graph : +Shortest -> BFS +start from begin word and generate word transformations from it +enqueue transformations that are in the word list, ignore rest +once we find endword, return path we took to get there +""" +from collections import deque + +alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + +def findLadders(beginWord,endWord,wordlist): + words = set(wordlist) + visited = set() + queue = deque() + queue.append([beginWord]) + + while len(queue)>0: + currPath = queue.popleft() + currWord = currPath[-1] + if currWord in visited: + continue + + visited.add(currWord) + + if currWord == endWord: + return currPath + + for i in range (len(currWord)): + for letter in alphabet: + transformedWord = currWord[:i]+ letter + currWord[i+1:] + if transformedWord in wordlist: + newPath = list(currPath) + newPath.append(transformedWord) + queue.append(newPath) + + return [] + +print (findLadders("hit","cog",["hot","dot","dog","lot","log","cog"])) \ No newline at end of file