├── 8-puzzle-problem ├── 8-puzzle-problem.py ├── gridworld.py ├── gridworld.pyc └── n-puzzle-problem.py ├── README.md ├── dijkstra ├── README.md └── dijkstra.py ├── images ├── a_star_visual.png ├── bfs_visual.png ├── dfs_visual.png ├── dp_visual.png └── one.png ├── search-algorithms ├── README.md ├── a-star.py ├── breadth-first-search.py ├── depth-first-search.py ├── dynamic-programming.py ├── gridworld.py └── gridworld.pyc └── water-jug-problem ├── README.md └── water-jug-problem-bfs.py /8-puzzle-problem/8-puzzle-problem.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 8 puzzle problem using A Star search- 3 | 4 | initial state is a 3x3 grid: 5 | 2 8 3 6 | 1 6 4 7 | 7 5 8 | 9 | goal state is also a 3x3 grid: 10 | 1 2 3 11 | 8 4 12 | 7 6 5 13 | 14 | There is exactly one blank cell in the grid. 15 | Our aim is to move the cells such that we reach 16 | the goal state in minimum number of steps. 17 | 18 | ''' 19 | 20 | from gridworld import GridWorld 21 | from copy import deepcopy 22 | import time 23 | 24 | screen_size = 500 25 | cell_width = 45 26 | cell_height = 45 27 | cell_margin = 5 28 | 29 | grid = [[0 for col in range(3)] for row in range(3)] 30 | 31 | init = deepcopy(grid) 32 | init[0][0] = 2 33 | init[0][1] = 8 34 | init[0][2] = 3 35 | init[1][0] = 1 36 | init[1][1] = 6 37 | init[1][2] = 4 38 | init[2][0] = 7 39 | init[2][2] = 5 40 | 41 | goal = deepcopy(grid) 42 | goal[0][0] = 1 43 | goal[0][1] = 2 44 | goal[0][2] = 3 45 | goal[1][0] = 8 46 | goal[1][2] = 4 47 | goal[2][0] = 7 48 | goal[2][1] = 6 49 | goal[2][2] = 5 50 | 51 | gridworld = GridWorld(screen_size,cell_width, cell_height, cell_margin,init, goal, init) 52 | 53 | delta = [[-1, 0], # go up 54 | [ 0,-1], # go left 55 | [ 1, 0], # go down 56 | [ 0, 1]] # go right 57 | 58 | def calc_heuristic(grid, goal): 59 | count = 0 60 | for i in range(len(grid)): 61 | for j in range(len(grid[0])): 62 | if grid[i][j] != goal[i][j]: 63 | count += 1 64 | 65 | return count 66 | 67 | def is_valid(cell): 68 | if cell[0] >= 0 and cell[0] < len(grid) and cell[1] >= 0 and cell[1] < len(grid[0]): 69 | return True 70 | else: 71 | return False 72 | 73 | def draw_state(state): 74 | for i in range(len(grid)): 75 | for j in range(len(grid[0])): 76 | gridworld.draw_cell([state[i][j], [i, j]]) # [number_on_cell, [x, y]] 77 | 78 | def run_a_star(init, goal, cost=1): 79 | opened = [] 80 | visited = [] 81 | 82 | f = calc_heuristic(init, goal) # evaluation function 83 | g = 0 # cost so far 84 | 85 | opened.append([f, g, init]) 86 | 87 | next = opened.pop() 88 | visited.append(next) 89 | print "next state is :", next 90 | while next[2] != goal: 91 | 92 | g = next[1] 93 | f = next[0] 94 | # explore the neighborhood! 95 | for i in range(len(next[2])): 96 | for j in range(len(next[2][0])): 97 | # go up, left, down, right 98 | for a in range(len(delta)): 99 | x = i + delta[a][0] 100 | y = j + delta[a][1] 101 | 102 | # check if this cell--> x, y is a valid cell 103 | if is_valid([x, y]): 104 | # now check if this new cell is a zero cell 105 | if next[2][x][y] == 0: 106 | next_copy = [] 107 | next_copy = deepcopy(next[2]) 108 | # swap 109 | next_copy[x][y] = deepcopy(next_copy[i][j]) 110 | next_copy[i][j] = 0 111 | 112 | # check if this new state (entire new_copy grid) is in visited 113 | if next_copy not in visited: 114 | g2 = g + cost 115 | f2 = g2 + calc_heuristic(next_copy, goal) 116 | opened.append([f2, g2, next_copy]) 117 | visited.append(next_copy) 118 | opened.sort() 119 | opened.reverse() 120 | 121 | if len(opened) > 0: 122 | next = opened.pop() 123 | print "next state is :",next[2] 124 | # draw this state on pygame screen 125 | draw_state(next[2]) 126 | gridworld.update() 127 | time.sleep(1) 128 | 129 | 130 | # initial state 131 | draw_state(init) 132 | 133 | gridworld.update() 134 | time.sleep(1) 135 | 136 | run_a_star(init, goal) 137 | 138 | -------------------------------------------------------------------------------- /8-puzzle-problem/gridworld.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import time 3 | from copy import deepcopy 4 | 5 | class GridWorld: 6 | 7 | def __init__(self, screen_size,cell_width, 8 | cell_height, cell_margin,init, goal, grid): 9 | 10 | # define colors 11 | self.BLACK = (0, 0, 0) 12 | self.WHITE = (255, 255, 255) 13 | self.GREEN = (0, 255, 0) 14 | self.RED = (255, 0, 0) 15 | self.BLUE = (0, 0, 255) 16 | self.YELLOW = (255, 255, 0) 17 | 18 | # cell dimensions 19 | self.WIDTH = cell_width 20 | self.HEIGHT = cell_height 21 | self.MARGIN = cell_margin 22 | self.color = self.WHITE 23 | 24 | # adjust the grid in the center of screen 25 | # self.adjust_margin = 75 26 | 27 | 28 | pygame.init() 29 | pygame.font.init() 30 | 31 | # set the width and height of the screen (width , height) 32 | self.size = (screen_size, screen_size) 33 | self.screen = pygame.display.set_mode(self.size) 34 | 35 | self.font = pygame.font.SysFont('arial', 20) 36 | 37 | pygame.display.set_caption("Grid world") 38 | 39 | self.clock = pygame.time.Clock() 40 | 41 | self.init = init 42 | self.goal = goal 43 | self.grid = grid 44 | 45 | 46 | self.screen.fill(self.BLACK) 47 | 48 | for row in range(len(grid)): 49 | for col in range(len(grid[0])): 50 | if self.init[row][col] != 0: 51 | self.color = self.RED 52 | else: 53 | self.color = self.WHITE 54 | 55 | pygame.draw.rect(self.screen, 56 | self.color, 57 | [(self.MARGIN + self.WIDTH )*col+self.MARGIN, 58 | (self.MARGIN + self.HEIGHT )*row+self.MARGIN, 59 | self.WIDTH, 60 | self.HEIGHT]) 61 | 62 | def text_objects(self, text, font): 63 | textSurface = font.render(text, True, self.BLACK) 64 | return textSurface, textSurface.get_rect() 65 | 66 | def draw_cell(self, cell): 67 | 68 | row = cell[1][0] 69 | column = cell[1][1] 70 | number = cell[0] 71 | if number != 0: 72 | rect = pygame.draw.rect(self.screen, 73 | self.RED, 74 | [(self.MARGIN + self.WIDTH)*column+self.MARGIN, 75 | (self.MARGIN + self.HEIGHT)*row+self.MARGIN, 76 | self.WIDTH, 77 | self.HEIGHT]) 78 | TextSurf, TextRect = self.text_objects(str(number), self.font) 79 | TextRect.center = ((self.MARGIN + self.WIDTH)*column + 4*self.MARGIN, 80 | (self.MARGIN + self.HEIGHT)*row + 4*self.MARGIN) 81 | self.screen.blit(TextSurf, TextRect) 82 | else: 83 | rect = pygame.draw.rect(self.screen, 84 | self.WHITE, 85 | [(self.MARGIN + self.WIDTH)*column+self.MARGIN, 86 | (self.MARGIN + self.HEIGHT)*row+self.MARGIN, 87 | self.WIDTH, 88 | self.HEIGHT]) 89 | 90 | def update(self): 91 | pygame.display.update() 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /8-puzzle-problem/gridworld.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/8-puzzle-problem/gridworld.pyc -------------------------------------------------------------------------------- /8-puzzle-problem/n-puzzle-problem.py: -------------------------------------------------------------------------------- 1 | ''' 2 | N puzzle problem using A Star search- 3 | 4 | initial state is a NxN grid. 5 | 6 | goal state is also a NxN grid. 7 | 8 | There is exactly one blank cell in the grid. 9 | Our aim is to move the cells such that we reach 10 | the goal state in minimum number of steps. 11 | 12 | ''' 13 | 14 | from gridworld import GridWorld 15 | from copy import deepcopy 16 | import time 17 | import random 18 | import pygame 19 | 20 | 21 | screen_size = 500 22 | cell_width = 45 23 | cell_height = 45 24 | cell_margin = 5 25 | 26 | grid = [[0 for col in range(4)] for row in range(4)] 27 | 28 | # add numbers from 0 to 99 to a list and then shuffle it 29 | numbers_list = [i for i in range(len(grid)*len(grid[0]))] 30 | 31 | random.shuffle(numbers_list) 32 | 33 | init = deepcopy(grid) 34 | k = 0 35 | for i in range(len(grid)): 36 | for j in range(len(grid[0])): 37 | print k 38 | init[i][j] = numbers_list[k] 39 | k += 1 40 | 41 | # shuffle again to generate goal state 42 | random.shuffle(numbers_list) 43 | 44 | goal = deepcopy(grid) 45 | k = 0 46 | for i in range(len(grid)): 47 | for j in range(len(grid[0])): 48 | goal[i][j] = numbers_list[k] 49 | k += 1 50 | 51 | gridworld = GridWorld(screen_size,cell_width, cell_height, cell_margin,init, goal, init) 52 | 53 | delta = [[-1, 0], # go up 54 | [ 0,-1], # go left 55 | [ 1, 0], # go down 56 | [ 0, 1]] # go right 57 | 58 | def calc_heuristic(grid, goal): 59 | count = 0 60 | for i in range(len(grid)): 61 | for j in range(len(grid[0])): 62 | if grid[i][j] != goal[i][j]: 63 | count += 1 64 | 65 | return count 66 | 67 | def is_valid(cell): 68 | if cell[0] >= 0 and cell[0] < len(grid) and cell[1] >= 0 and cell[1] < len(grid[0]): 69 | return True 70 | else: 71 | return False 72 | 73 | def draw_state(state): 74 | for i in range(len(grid)): 75 | for j in range(len(grid[0])): 76 | gridworld.draw_cell([state[i][j], [i, j]]) # [number_on_cell, [x, y]] 77 | 78 | 79 | def run_a_star(init, goal,cost=1): 80 | opened = [] 81 | visited = [] 82 | 83 | f = calc_heuristic(init, goal) # evaluation function 84 | g = 0 # cost so far 85 | 86 | opened.append([f, g, init]) 87 | 88 | next = opened.pop() 89 | visited.append(next) 90 | print "next state is :", next 91 | while next[2] != goal: 92 | 93 | g = next[1] 94 | f = next[0] 95 | # explore the neighborhood! 96 | for i in range(len(next[2])): 97 | for j in range(len(next[2][0])): 98 | # go up, left, down, right 99 | for a in range(len(delta)): 100 | x = i + delta[a][0] 101 | y = j + delta[a][1] 102 | 103 | # check if this cell--> x, y is a valid cell 104 | if is_valid([x, y]): 105 | # now check if this new cell is a zero cell 106 | if next[2][x][y] == 0: 107 | next_copy = [] 108 | next_copy = deepcopy(next[2]) 109 | # swap 110 | next_copy[x][y] = (next_copy[i][j]) 111 | next_copy[i][j] = 0 112 | 113 | # check if this new state (entire new_copy grid) is in visited 114 | if next_copy not in visited: 115 | g2 = g + cost 116 | f2 = g2 + calc_heuristic(next_copy, goal) 117 | opened.append([f2, g2, next_copy]) 118 | visited.append(next_copy) 119 | opened.sort() 120 | opened.reverse() 121 | 122 | if len(opened) > 0: 123 | next = opened.pop() 124 | print "next state is :",next[2] 125 | # draw this state on pygame screen 126 | draw_state(next[2]) 127 | gridworld.update() 128 | time.sleep(3) 129 | 130 | # initial state 131 | draw_state(init) 132 | 133 | gridworld.update() 134 | 135 | run_a_star(init, goal) 136 | 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cce-ai-2017 2 | CCE-AI Class codes 3 | -------------------------------------------------------------------------------- /dijkstra/README.md: -------------------------------------------------------------------------------- 1 | ## Shortest distance path using Dijkstra's Algorithm 2 | 3 | ![one](../images/one.png) 4 | 5 | ### Algorithm 6 | 7 | * Set the costs of all the nodes to a large number eg. 10000. 8 | * Also initialize a `cost_matrix` with 10000 as the costs. 9 | * Start at the source node.Call it `parent_node`. 10 | * Assign a 0 cost to this node. 11 | * Update `cost_matrix` 12 | * Consider this node as visited. 13 | * Find the child nodes directly connected to the `parent_node` node. 14 | * Find the cost of each child node as follows - 15 | 16 | ```py 17 | if Cost(parent_node) + weight < Cost(child_node): 18 | # update 19 | Cost(child_node) = Cost(parent_node) + weight 20 | else: 21 | Cost(child_node) 22 | ``` 23 | 24 | * Also update the `cost_matrix` with the new cost values of the child nodes. 25 | * Find the child node with the minimum cost and update the `parent_node` as this node. 26 | * Continue like this (steps 6 to 10) till all the nodes are visited. 27 | * *Backtracking* - Start from the last row and column of the `cost_matrix` and add it to the path. 28 | * Then move to the row above, same column.If the cost changes, add the corresponding *visited* node 29 | in the path.If the entry is 10000, change the column to `column -1`. 30 | * Continue till the source node is reached. 31 | 32 | -------------------------------------------------------------------------------- /dijkstra/dijkstra.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Dijkstra's Algorithm for finding the shortest distance. 3 | ''' 4 | import copy 5 | 6 | # backtracking 7 | def findpath(visited,matrix, size, end): 8 | path = [end] 9 | # start from the last row 10 | j = size-1 11 | for i in range(size-1,0, -1): 12 | # compare the cost of the node just above the current node 13 | # if there is a change, append the corresponding visited node 14 | # to the path 15 | if (matrix[i][j] != matrix[i-1][j]): 16 | path.append(visited[i-1]) 17 | # if the cost of the node just above the current node is 10000 (infinity), 18 | # move to the left column for the next iteration 19 | if matrix[i-1][j] == 10000: 20 | j -= 1 21 | return path 22 | 23 | def return_index(unseen_nodes,letter): 24 | i = 0 25 | for key in unseen_nodes: 26 | if key == letter: 27 | return i 28 | i += 1 29 | 30 | def return_index_value(letter): 31 | return ord(letter) - 65 32 | 33 | def return_letter(index): 34 | return chr(int_index+65) 35 | 36 | #graph, start, destination and number of points 37 | def dijkstra(graph, start, end, size): 38 | print "Original graph :",graph 39 | # cost matrix ; set all the costs to a large number 40 | # currently has just one row 41 | matrix = [[10000 for x in range(size)]] 42 | matrix_row_counter = 0 43 | # save start node 44 | start_temp = start 45 | # this dictionary will contain key : cost pair 46 | graph_temp = {} 47 | 48 | # initially all the nodes are unseen 49 | # the node keys will be popped from unseen_nodes 50 | # and added in visited_list 51 | unseen_nodes = graph.keys() 52 | visited = [] 53 | 54 | # all nodes are at really long distances initially 55 | # so the node values are all set to large number 56 | for node in unseen_nodes: 57 | graph_temp.update({node : 10000}) 58 | 59 | # start at 'start' node and assign 0 to it 60 | graph_temp.update({start : 0}) 61 | 62 | # first row first column in the cost matrix set to 0 63 | # eg. [[0, 10000, 10000, 10000]] 64 | matrix[0][0] = 0 65 | # add the start node to the visited list 66 | visited.append(start) 67 | # remove the source node from the unseen_nodes 68 | unseen_nodes.pop(return_index(unseen_nodes,start)) 69 | 70 | while len(unseen_nodes) > 0: 71 | 72 | # a temporary row copies the entries from the previous row of 73 | # the cost matrix 74 | temp_row = copy.copy(matrix[matrix_row_counter]) 75 | 76 | # child_nodes directly connected to the current node 77 | # are temporarily added in keys_temp 78 | keys_temp = [] 79 | for keys, values in zip(graph[start].keys(), graph[start].values()): 80 | # if child_node is not visited, then only consider 81 | if keys not in visited: 82 | # check if source value + path weight < current node value or cost 83 | if graph_temp[start] + values < graph_temp[keys]: 84 | # update 85 | graph_temp.update({keys : graph_temp[start] + values}) 86 | # add the child_nodes 87 | keys_temp.append(keys) 88 | # also update the temp_row with the update values 89 | int_index = return_index_value(keys) 90 | temp_row[int_index] = graph_temp[keys] 91 | 92 | # assume that the first child has smallest cost 93 | min_key = keys_temp[0] 94 | 95 | # go through each child node to see which has smaller cost 96 | # visit the one with minimum cost 97 | for keys in keys_temp: 98 | if graph_temp[keys] < graph_temp[min_key]: 99 | min_key = keys 100 | 101 | visited.append(min_key) 102 | 103 | # add the temp_row to the cost matrix 104 | matrix.append(temp_row) 105 | matrix_row_counter += 1 106 | 107 | # remove the minimum cost node from unseen_nodes 108 | unseen_nodes.pop(return_index(unseen_nodes,min_key)) 109 | 110 | # set start to the minimum cost node 111 | start = min_key 112 | 113 | print "Shortest path is :",findpath(visited,matrix, size, end) 114 | 115 | # example graphs 116 | ''' 117 | graph = { 118 | 'A' : {'B': 5, 'C': 10}, 119 | 'B' : {'A': 5, 'C': 4, 'D': 11}, 120 | 'C' : {'A': 10, 'B': 4, 'D': 5}, 121 | 'D' : {'B': 11, 'C': 5} 122 | } 123 | 124 | ''' 125 | 126 | ''' 127 | graph = { 128 | 'A' : {'B': 4, 'D': 8}, 129 | 'B' : {'A': 4, 'C': 3}, 130 | 'C' : {'B': 3, 'D': 4}, 131 | 'D' : {'C': 4, 'A': 8, 'E': 7}, 132 | 'E' : {'D': 7} 133 | } 134 | ''' 135 | 136 | graph = { 137 | 'A' : {'B': 10, 'C': 5}, 138 | 'B' : {'A': 10, 'C': 8, 'E': 6, 'D': 12}, 139 | 'C' : {'A': 5, 'B': 8, 'E': 12}, 140 | 'D' : {'B': 12, 'E': 5, 'F': 4}, 141 | 'E' : {'C': 12, 'B': 6, 'D': 5, 'F': 6}, 142 | 'F' : {'D': 4, 'E': 6} 143 | } 144 | 145 | dijkstra(graph, 'A', 'F',6) 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /images/a_star_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/images/a_star_visual.png -------------------------------------------------------------------------------- /images/bfs_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/images/bfs_visual.png -------------------------------------------------------------------------------- /images/dfs_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/images/dfs_visual.png -------------------------------------------------------------------------------- /images/dp_visual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/images/dp_visual.png -------------------------------------------------------------------------------- /images/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/images/one.png -------------------------------------------------------------------------------- /search-algorithms/README.md: -------------------------------------------------------------------------------- 1 | # Search algorithms 2 | 3 | ## Breadth First Search Algorithm 4 | 5 | ![bfs](../images/bfs_visual.png) 6 | 7 | ## Depth First Search Algorithm 8 | 9 | ![dfs](../images/dfs_visual.png) 10 | 11 | ## A-star Search Algorithm 12 | 13 | ![a-star](../images/a_star_visual.png) 14 | 15 | ## Dynamic programming search 16 | 17 | ![dp](../images/dp_visual.png) 18 | -------------------------------------------------------------------------------- /search-algorithms/a-star.py: -------------------------------------------------------------------------------- 1 | # A visual demonstration of A* search algorithm 2 | 3 | from gridworld import GridWorld 4 | import time 5 | from math import * 6 | 7 | # build grid structure 8 | grid = [[0 for col in range(10)] for row in range(10)] 9 | grid[0][1] = 1 # obstacle 10 | # grid[1][1] = 1 # obstacle 11 | # grid[2][1] = 1 # obstacle 12 | # grid[3][1] = 1 # obstacle 13 | # grid[4][4] = 1 # obstacle 14 | # grid[5][4] = 1 # obstacle 15 | # grid[5][4] = 1 # obstacle 16 | # grid[6][4] = 1 17 | # grid[7][4] = 1 18 | # grid[8][4] = 1 19 | # grid[9][4] = 1 20 | 21 | init = [0, 0] 22 | goal = [9, 9] 23 | 24 | # build heuristics grid 25 | heuristics = [[0 for col in range(len(grid[0]))] for row in range(len(grid))] 26 | k = len(grid[0]) - 1 27 | for i in range(len(grid)-1, -1, -1): 28 | num = (len(grid[0])-1) - k 29 | for j in range(len(grid[0])-1, -1, -1): 30 | heuristics[i][j] = num 31 | num += 1 32 | k -= 1 33 | 34 | screen_size = 500 35 | cell_width = 45 36 | cell_height = 45 37 | cell_margin = 5 38 | 39 | gridworld = GridWorld(screen_size,cell_width, cell_height, cell_margin,init, goal, grid) 40 | 41 | def check_valid(node, grid): 42 | if node[0] >= 0 and node[0] < len(grid) and node[1] >= 0 and node[1] < len(grid[0]) and (grid[node[0]][node[1]] == 0): 43 | return True 44 | else: 45 | return False 46 | 47 | def heuristic(a, b): 48 | # return (abs(a[0] - b[0]) + abs(a[1] - b[1])) 49 | return sqrt((a[0] - b[0])**2 + (a[1]-b[1])**2) 50 | 51 | def run_a_star(grid, heuristics, init, goal, cost): 52 | delta = [[ 0, -1], # go left 53 | [ 1, 0 ], # go down 54 | [ 0, 1], # go right 55 | [-1, 0 ]] # go up 56 | 57 | delta_name = ['^', '<', 'v', '>'] 58 | action = [[-1 for row in range(len(grid[0]))] for col in range(len(grid))] 59 | policy = [[' ' for row in range(len(grid[0]))] for col in range(len(grid))] 60 | expanded = [[-1 for row in range(len(grid[0]))] for col in range(len(grid))] 61 | 62 | visited = [] 63 | opened = [] 64 | 65 | # [f, g, [x, y]] 66 | # f = g + heuristics[x][y] 67 | # opened.append([0+heuristics[init[0]][init[1]], 0, init[0], init[1]]) 68 | opened.append([heuristic(goal, init), 0, init[0], init[1]]) 69 | 70 | visited.append([init[0], init[1]]) 71 | next = opened.pop() 72 | count = 0 73 | 74 | while [next[2],next[3]] != goal: 75 | 76 | if len(opened) > 0: 77 | opened.sort() 78 | print opened 79 | opened.reverse() 80 | next = opened.pop() 81 | 82 | x = next[2] 83 | y = next[3] 84 | g = next[1] 85 | f = next[0] 86 | gridworld.draw_cell([[f, [x, y]]]) 87 | gridworld.show() 88 | # time.sleep(0.5) 89 | expanded[next[2]][next[3]] = count 90 | count += 1 91 | for a in range(len(delta)): 92 | x2 = x + delta[a][0] 93 | y2 = y + delta[a][1] 94 | 95 | if check_valid([x2, y2], grid): 96 | g2 = g + cost 97 | if [x2, y2] not in visited: 98 | #f = g2 + heuristics[x2][y2] 99 | f = g2 + heuristic(goal, [x2, y2]) 100 | opened.append([f,g2,x2, y2]) 101 | visited.append([x2, y2]) 102 | action[x2][y2] = a 103 | 104 | print expanded 105 | # policy search 106 | x = goal[0] 107 | y = goal[1] 108 | policy[x][y] = '*' 109 | path = [] 110 | path.append([x, y]) 111 | while([x, y] != init): 112 | x1 = x - delta[action[x][y]][0] 113 | y1 = y - delta[action[x][y]][1] 114 | policy[x1][y1] = delta_name[action[x][y]] 115 | x = x1 116 | y = y1 117 | path.append([x, y]) 118 | print policy 119 | path.reverse() 120 | print path 121 | smooth_path = gridworld.smooth_path(path) 122 | gridworld.draw_path(smooth_path) 123 | gridworld.show() 124 | 125 | run_a_star(grid, heuristics, init, goal, cost=1) 126 | 127 | while True: 128 | pass 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /search-algorithms/breadth-first-search.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A visual demonstration of Breadth First Search Algorithm 3 | using Pygame. 4 | ''' 5 | import time 6 | from copy import deepcopy 7 | from gridworld import GridWorld 8 | 9 | grid = [[0 for col in range(10)] for row in range(10)] 10 | init = [0, 0] 11 | goal = [9, 9] 12 | cell_width = 45 13 | cell_height = 45 14 | screen_size = 500 15 | cell_margin = 5 16 | 17 | grid_world = GridWorld(screen_size,cell_width,cell_height, cell_margin, init, goal, grid) 18 | 19 | def check_valid(node): 20 | if node[0] >= 0 and node[0] < len(grid) and node[1] >= 0 and node[1] < len(grid[0]) and (grid[node[0]][node[1]] == 0 or grid[node[0]][node[1]] == 10): 21 | return True 22 | else: 23 | return False 24 | 25 | def run_bfs(init, goal, grid,cost, find_path): 26 | delta = [[-1, 0 ], # go up 27 | [ 0, -1], # go left 28 | [ 0, 1 ], # go down 29 | [ 1, 0]] # go right 30 | delta_name = ['^', '<', 'v', '>'] 31 | action = [[-1 for row in range(len(grid[0]))] for col in range(len(grid))] 32 | policy = [[' ' for row in range(len(grid[0]))] for col in range(len(grid))] 33 | 34 | next = None 35 | visited = [] 36 | opened = [] 37 | opened.append([0, init]) 38 | visited.append(init) 39 | opened.sort() 40 | opened.reverse() 41 | next = opened.pop() 42 | came_from = [] 43 | while next[1]!= goal: 44 | temp = [] 45 | # print "expanding node :",next[1] 46 | # print "neighboring nodes: " 47 | for d in range(len(delta)): 48 | x = next[1][0] + delta[d][0] 49 | y = next[1][1] + delta[d][1] 50 | 51 | if check_valid([x, y]): 52 | if [x, y] not in visited: 53 | opened.append([next[0]+cost, [x, y]]) 54 | visited.append([x, y]) 55 | temp.append([next[0]+cost, [x, y]]) 56 | action[x][y] = d 57 | 58 | # print temp 59 | grid_world.draw_cell(temp) 60 | grid_world.show() 61 | time.sleep(0.1) 62 | if(len(opened)>0): 63 | next = opened.pop(0) 64 | 65 | # policy search 66 | x = goal[0] 67 | y = goal[1] 68 | policy[x][y] = '*' 69 | path = [] 70 | path.append([x, y]) 71 | while([x, y] != init): 72 | x1 = x - delta[action[x][y]][0] 73 | y1 = y - delta[action[x][y]][1] 74 | policy[x1][y1] = delta_name[action[x][y]] 75 | x = x1 76 | y = y1 77 | path.append([x, y]) 78 | print policy 79 | path.reverse() 80 | 81 | if find_path: 82 | # newpath = deepcopy(path) 83 | smooth_path = grid_world.smooth_path(path) 84 | grid_world.draw_path(smooth_path) 85 | grid_world.show() 86 | 87 | 88 | run_bfs(init, goal, grid, cost=1, find_path=True) 89 | 90 | -------------------------------------------------------------------------------- /search-algorithms/depth-first-search.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A visual demonstration of Depth First Search Algorithm 3 | using Pygame. 4 | ''' 5 | import time 6 | from copy import deepcopy 7 | from gridworld import GridWorld 8 | 9 | grid = [[0 for col in range(10)] for row in range(10)] 10 | grid[0][5] = 1 # obstacle 11 | grid[1][5] = 1 # obstacle 12 | grid[2][5] = 1 # obstacle 13 | grid[3][5] = 1 # obstacle 14 | 15 | init = [0, 0] 16 | goal = [4, 9] 17 | cell_width = 45 18 | cell_height = 45 19 | screen_size = 500 20 | cell_margin = 5 21 | 22 | grid_world = GridWorld(screen_size,cell_width,cell_height, cell_margin, init, goal, grid) 23 | 24 | def check_valid(node): 25 | if node[0] >= 0 and node[0] < len(grid) and node[1] >= 0 and node[1] < len(grid[0]) and (grid[node[0]][node[1]] == 0 or grid[node[0]][node[1]] == 10): 26 | return True 27 | else: 28 | return False 29 | 30 | def run_dfs(init, goal, grid,cost, find_path): 31 | delta = [[-1, 0 ], # go up 32 | [ 0, -1], # go left 33 | [ 0, 1 ], # go down 34 | [ 1, 0]] # go right 35 | delta_name = ['^', '<', 'v', '>'] 36 | action = [[-1 for row in range(len(grid[0]))] for col in range(len(grid))] 37 | policy = [[' ' for row in range(len(grid[0]))] for col in range(len(grid))] 38 | 39 | next = None 40 | visited = [] 41 | opened = [] 42 | opened.append([0, init]) 43 | visited.append(init) 44 | opened.sort() 45 | opened.reverse() 46 | next = opened.pop() 47 | came_from = [] 48 | while next[1]!= goal: 49 | grid_world.draw_cell([[next[0], next[1]]]) 50 | grid_world.show() 51 | time.sleep(0.1) 52 | temp = [] 53 | # print "expanding node :",next[1] 54 | # print "neighboring nodes: " 55 | for d in range(len(delta)): 56 | x = next[1][0] + delta[d][0] 57 | y = next[1][1] + delta[d][1] 58 | 59 | if check_valid([x, y]): 60 | if [x, y] not in visited: 61 | opened.append([next[0]+cost, [x, y]]) 62 | visited.append([x, y]) 63 | temp.append([next[0]+cost, [x, y]]) 64 | action[x][y] = d 65 | 66 | # print temp 67 | # grid_world.draw_cell(temp) 68 | # grid_world.show() 69 | # time.sleep(0.1) 70 | if(len(opened)>0): 71 | next = opened.pop() 72 | 73 | # policy search 74 | x = goal[0] 75 | y = goal[1] 76 | policy[x][y] = '*' 77 | path = [] 78 | path.append([x, y]) 79 | while([x, y] != init): 80 | x1 = x - delta[action[x][y]][0] 81 | y1 = y - delta[action[x][y]][1] 82 | policy[x1][y1] = delta_name[action[x][y]] 83 | x = x1 84 | y = y1 85 | path.append([x, y]) 86 | print policy 87 | path.reverse() 88 | 89 | if find_path: 90 | # newpath = deepcopy(path) 91 | smooth_path = grid_world.smooth_path(path) 92 | grid_world.draw_path(smooth_path) 93 | grid_world.show() 94 | 95 | 96 | run_dfs(init, goal, grid, cost=1, find_path=True) 97 | 98 | while True: 99 | pass 100 | -------------------------------------------------------------------------------- /search-algorithms/dynamic-programming.py: -------------------------------------------------------------------------------- 1 | from gridworld import GridWorld 2 | import time 3 | 4 | def compute_value(grid,value,policy, goal,cost): 5 | action = [[-1 for col in range(len(grid[0]))] for row in range(len(grid))] 6 | change = True 7 | while change: 8 | change = False 9 | 10 | for x in range(len(grid)): 11 | for y in range(len(grid[0])): 12 | 13 | if goal[0] == x and goal[1] == y: 14 | if value[x][y] > 0: 15 | value[x][y] = 0 16 | change = True 17 | 18 | elif grid[x][y] == 0: 19 | temp = [] 20 | for a in range(len(delta)): 21 | x2 = x + delta[a][0] 22 | y2 = y + delta[a][1] 23 | 24 | if x2 >= 0 and x2 < len(grid) and y2 >= 0 and y2 < len(grid[0]) and grid[x2][y2]==0: 25 | v2 = value[x2][y2] + cost_step 26 | 27 | if v2 < value[x][y]: 28 | change = True 29 | value[x][y] = v2 30 | temp.append([str(v2)+" "+str(delta_name[a])+"", [x, y]]) 31 | gridworld.draw_cell(temp) 32 | gridworld.show() 33 | time.sleep(0.5) 34 | policy[x][y] = delta_name[a] 35 | 36 | return policy 37 | 38 | grid = [[0, 0, 1, 0, 0, 0], 39 | [0, 0, 1, 0, 0, 0], 40 | [0, 0, 1, 0, 1, 0], 41 | [0, 0, 1, 0, 1, 0], 42 | [0, 0, 0, 0, 1, 0]] 43 | value = [[99 for col in range(len(grid[0]))] for row in range(len(grid))] 44 | policy = [[' ' for col in range(len(grid[0]))] for row in range(len(grid))] 45 | policy[len(grid)-1][len(grid[0]) - 1] = '*' 46 | goal = [len(grid)-1, len(grid[0])-1] 47 | cost = 1 # the cost associated with moving from a cell to an adjacent one 48 | 49 | init = [0, 0] 50 | 51 | delta = [[-1, 0 ], # go up 52 | [ 0, -1], # go left 53 | [ 1, 0 ], # go down 54 | [ 0, 1 ]] # go right 55 | 56 | delta_name = ['^', '<', 'v', '>'] 57 | 58 | cost_step = 1 59 | 60 | screen_size = 500 61 | cell_width = 45 62 | cell_height = 45 63 | cell_margin = 5 64 | 65 | gridworld = GridWorld(screen_size,cell_width, cell_height, cell_margin,init, goal, grid) 66 | 67 | print compute_value(grid,value,policy, goal, cost=1) 68 | while True: 69 | pass 70 | -------------------------------------------------------------------------------- /search-algorithms/gridworld.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import time 3 | from copy import deepcopy 4 | 5 | class GridWorld: 6 | 7 | def __init__(self, screen_size,cell_width, 8 | cell_height, cell_margin,init, goal, grid): 9 | 10 | # define colors 11 | self.BLACK = (0, 0, 0) 12 | self.WHITE = (255, 255, 255) 13 | self.GREEN = (0, 255, 0) 14 | self.RED = (255, 0, 0) 15 | self.BLUE = (0, 0, 255) 16 | self.YELLOW = (255, 255, 0) 17 | 18 | # cell dimensions 19 | self.WIDTH = cell_width 20 | self.HEIGHT = cell_height 21 | self.MARGIN = cell_margin 22 | self.color = self.WHITE 23 | 24 | 25 | pygame.init() 26 | pygame.font.init() 27 | 28 | # set the width and height of the screen (width , height) 29 | self.size = (screen_size, screen_size) 30 | self.screen = pygame.display.set_mode(self.size) 31 | 32 | self.font = pygame.font.SysFont('arial', 20) 33 | 34 | pygame.display.set_caption("Grid world") 35 | 36 | self.clock = pygame.time.Clock() 37 | 38 | self.init = init 39 | self.goal = goal 40 | self.grid = grid 41 | 42 | 43 | self.screen.fill(self.BLACK) 44 | 45 | for row in range(len(grid)): 46 | for col in range(len(grid[0])): 47 | if [row, col] == self.init: 48 | self.color = self.GREEN 49 | elif [row, col] == self.goal: 50 | self.color = self.RED 51 | elif grid[row][col] == 1: 52 | self.color = self.BLACK 53 | else: 54 | self.color = self.WHITE 55 | pygame.draw.rect(self.screen, 56 | self.color, 57 | [(self.MARGIN + self.WIDTH)*col+self.MARGIN, 58 | (self.MARGIN + self.HEIGHT)*row+self.MARGIN, 59 | self.WIDTH, 60 | self.HEIGHT]) 61 | 62 | def text_objects(self, text, font): 63 | textSurface = font.render(text, True, self.BLACK) 64 | return textSurface, textSurface.get_rect() 65 | 66 | def draw_cell(self, nodes): 67 | for node in nodes: 68 | row = node[1][0] 69 | column = node[1][1] 70 | value = node[0] 71 | rect = pygame.draw.rect(self.screen, 72 | self.BLUE, 73 | [(self.MARGIN + self.WIDTH)*column+self.MARGIN, 74 | (self.MARGIN + self.HEIGHT)*row+self.MARGIN, 75 | self.WIDTH, 76 | self.HEIGHT]) 77 | TextSurf, TextRect = self.text_objects(str(value), self.font) 78 | TextRect.center = ((self.MARGIN + self.WIDTH)*column + 4*self.MARGIN, 79 | (self.MARGIN + self.HEIGHT)*row + 4*self.MARGIN) 80 | self.screen.blit(TextSurf, TextRect) 81 | 82 | 83 | def draw_path(self, path): 84 | origin = [0+1*self.MARGIN+22.5,0+1*self.MARGIN+22.5] 85 | col = self.MARGIN + self.WIDTH 86 | row = self.MARGIN + self.HEIGHT 87 | pygame.draw.lines(self.screen, self.GREEN, False, [(origin[0]+col*i[1], origin[1]+row*i[0]) for i in path], 4) 88 | 89 | # smoothen the path 90 | def smooth_path(self,path,weight_data = 0.5, weight_smooth = 0.1, tolerance = 0.000001): 91 | #newpath = return_path 92 | newpath = deepcopy(path) 93 | change = tolerance 94 | while change >= tolerance: 95 | change = 0 96 | for i in range(1, len(path) - 1): 97 | for j in range(len(path[0])): 98 | d1 = weight_data*(path[i][j] - newpath[i][j]) 99 | d2 = weight_smooth*(newpath[i-1][j] + newpath[i+1][j] - 2*newpath[i][j]) 100 | change += abs(d1 + d2) 101 | newpath[i][j] += d1 + d2 102 | 103 | return newpath 104 | 105 | def show(self): 106 | self.clock.tick(500) 107 | pygame.display.flip() 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /search-algorithms/gridworld.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioarun/cce-ai-2017/ec0b4991d9688b99138edd550cf3d8e9fa26145b/search-algorithms/gridworld.pyc -------------------------------------------------------------------------------- /water-jug-problem/README.md: -------------------------------------------------------------------------------- 1 | # Water-Jug problem using BFS 2 | ## Production Rules: 3 | * (x, y) -> (a, y) if x < a 4 | * (x, y) -> (x, b) if y < b 5 | * (x, y) -> (0, y) if x > 0 6 | * (x, y) -> (x, 0) if y > 0 7 | * (x, y) -> (min(x+y, a), max(0, x+y - a)) if y > 0 8 | * (x, y) -> (max(0, x+y - b), min(x+y, b)) if x > 0 9 |
10 | These production rules are used to find the neighbouring states from the current states.
11 | 12 | The Algorithm goes like this:
13 | 14 | 0. Create an empty `path` list. 15 | 1. Add start state to the `front` queue. 16 | 2. Mark it visited by adding it in `visited` list. 17 | 3. While `front` is not empty, follow the steps(4 - 6) below. 18 | 4. Pop out a state from `front` and call it `current`.Add `current` to `path` list. 19 | 5. Expand all it's neighbours following the production rules. 20 | 6. If the neighbours are not in `visited` , then add them to `visited` list and also add them to the `front` queue. 21 | 7. return `path`. 22 | 23 | -------------------------------------------------------------------------------- /water-jug-problem/water-jug-problem-bfs.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Water-Jug solution using Breadth First Search Algorithm 3 | ''' 4 | 5 | print "Solution for water jug problem" 6 | x_capacity = input("Enter Jug 1 capacity:") 7 | y_capacity = input("Enter Jug 2 capacity:") 8 | end = input("Enter target volume:") 9 | 10 | def bfs(start, end, x_capacity, y_capacity): 11 | path = [] 12 | front = [] 13 | front.append(start) 14 | visited = [] 15 | #visited.append(start) 16 | while(not (not front)): 17 | current = front.pop() 18 | x = current[0] 19 | y = current[1] 20 | path.append(current) 21 | if x == end or y == end: 22 | print "Found!" 23 | return path 24 | # rule 1 25 | if current[0] < x_capacity and ([x_capacity, current[1]] not in visited): 26 | front.append([x_capacity, current[1]]) 27 | visited.append([x_capacity, current[1]]) 28 | 29 | # rule 2 30 | if current[1] < y_capacity and ([current[0], y_capacity] not in visited): 31 | front.append([current[0], y_capacity]) 32 | visited.append([current[0], y_capacity]) 33 | 34 | # rule 3 35 | if current[0] > x_capacity and ([0, current[1]] not in visited): 36 | front.append([0, current[1]]) 37 | visited.append([0, current[1]]) 38 | 39 | # rule 4 40 | if current[1] > y_capacity and ([x_capacity, 0] not in visited): 41 | front.append([x_capacity, 0]) 42 | visited.append([x_capacity, 0]) 43 | 44 | # rule 5 45 | #(x, y) -> (min(x + y, x_capacity), max(0, x + y - x_capacity)) if y > 0 46 | if current[1] > 0 and ([min(x + y, x_capacity), max(0, x + y - x_capacity)] not in visited): 47 | front.append([min(x + y, x_capacity), max(0, x + y - x_capacity)]) 48 | visited.append([min(x + y, x_capacity), max(0, x + y - x_capacity)]) 49 | 50 | # rule 6 51 | # (x, y) -> (max(0, x + y - y_capacity), min(x + y, y_capacity)) if x > 0 52 | if current[0] > 0 and ([max(0, x + y - y_capacity), min(x + y, y_capacity)] not in visited): 53 | front.append([max(0, x + y - y_capacity), min(x + y, y_capacity)]) 54 | visited.append([max(0, x + y - y_capacity), min(x + y, y_capacity)]) 55 | 56 | return "Not found" 57 | 58 | def gcd(a, b): 59 | if a == 0: 60 | return b 61 | return gcd(b%a, a) 62 | 63 | # start state: x = 0 , y = 0 64 | start = [0, 0] 65 | #end = 2 66 | #x_capacity = 4 67 | #y_capacity = 3 68 | 69 | # condition for getting a solution: 70 | # the target volume 'end' should be a multiple of gcd(a,b) 71 | 72 | if end % gcd(x_capacity,y_capacity) == 0: 73 | print bfs(start, end, x_capacity, y_capacity) 74 | else: 75 | print "No solution possible for this combination." 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | --------------------------------------------------------------------------------