├── README.md ├── .idea ├── misc.xml ├── modules.xml ├── puzzle.iml └── workspace.xml ├── BFS_search.py ├── Astar_search.py ├── RBFS_search.py ├── main.py ├── .gitignore └── puzzle.py /README.md: -------------------------------------------------------------------------------- 1 | # 8-puzzle-search-implementation 2 | this a python BFS , A* and RBFS implementation of 8 puzzle 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/puzzle.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /BFS_search.py: -------------------------------------------------------------------------------- 1 | from queue import Queue 2 | from puzzle import Puzzle 3 | 4 | 5 | def breadth_first_search(initial_state): 6 | start_node = Puzzle(initial_state, None, None, 0) 7 | if start_node.goal_test(): 8 | return start_node.find_solution() 9 | q = Queue() 10 | q.put(start_node) 11 | explored=[] 12 | while not(q.empty()): 13 | node=q.get() 14 | explored.append(node.state) 15 | children=node.generate_child() 16 | for child in children: 17 | if child.state not in explored: 18 | if child.goal_test(): 19 | return child.find_solution() 20 | q.put(child) 21 | return 22 | -------------------------------------------------------------------------------- /Astar_search.py: -------------------------------------------------------------------------------- 1 | from queue import PriorityQueue 2 | from puzzle import Puzzle 3 | 4 | 5 | def Astar_search(initial_state): 6 | count=0 7 | explored=[] 8 | start_node=Puzzle(initial_state,None,None,0,True) 9 | q = PriorityQueue() 10 | q.put((start_node.evaluation_function,count,start_node)) 11 | 12 | while not q.empty(): 13 | node=q.get() 14 | node=node[2] 15 | explored.append(node.state) 16 | if node.goal_test(): 17 | return node.find_solution() 18 | 19 | children=node.generate_child() 20 | for child in children: 21 | if child.state not in explored: 22 | count += 1 23 | q.put((child.evaluation_function,count,child)) 24 | return 25 | 26 | -------------------------------------------------------------------------------- /RBFS_search.py: -------------------------------------------------------------------------------- 1 | from puzzle import Puzzle 2 | from sys import maxsize 3 | 4 | 5 | def recursive_best_first_search(initial_state): 6 | node=RBFS_search(Puzzle(state=initial_state, parent=None, action=None, path_cost=0, needs_hueristic=True), f_limit=maxsize) 7 | node=node[0] 8 | return node.find_solution() 9 | 10 | def RBFS_search(node,f_limit): 11 | successors=[] 12 | 13 | if node.goal_test(): 14 | return node,None 15 | children=node.generate_child() 16 | if not len(children): 17 | return None, maxsize 18 | count=-1 19 | for child in children: 20 | count+=1 21 | successors.append((child.evaluation_function,count,child)) 22 | while len(successors): 23 | successors.sort() 24 | best_node=successors[0][2] 25 | if best_node.evaluation_function > f_limit: 26 | return None, best_node.evaluation_function 27 | alternative=successors[1][0] 28 | result,best_node.evaluation_function=RBFS_search(best_node,min(f_limit,alternative)) 29 | successors[0]=(best_node.evaluation_function,successors[0][1],best_node) 30 | if result!=None: 31 | break 32 | return result,None 33 | 34 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from time import time 2 | from BFS_search import breadth_first_search 3 | from Astar_search import Astar_search 4 | from RBFS_search import recursive_best_first_search 5 | from puzzle import Puzzle 6 | 7 | 8 | state=[[1, 3, 4, 9 | 8, 6, 2, 10 | 7, 0, 5], 11 | 12 | [2, 8, 1, 13 | 0, 4, 3, 14 | 7, 6, 5], 15 | 16 | [2, 8, 1, 17 | 4, 6, 3, 18 | 0, 7, 5]] 19 | 20 | for i in range(0,3): 21 | Puzzle.num_of_instances=0 22 | t0=time() 23 | bfs=breadth_first_search(state[i]) 24 | t1=time()-t0 25 | print('BFS:', bfs) 26 | print('space:',Puzzle.num_of_instances) 27 | print('time:',t1) 28 | print() 29 | 30 | Puzzle.num_of_instances = 0 31 | t0 = time() 32 | astar = Astar_search(state[i]) 33 | t1 = time() - t0 34 | print('A*:',astar) 35 | print('space:', Puzzle.num_of_instances) 36 | print('time:', t1) 37 | print() 38 | 39 | Puzzle.num_of_instances = 0 40 | t0 = time() 41 | RBFS = recursive_best_first_search(state[i]) 42 | t1 = time() - t0 43 | print('RBFS:',RBFS) 44 | print('space:', Puzzle.num_of_instances) 45 | print('time:', t1) 46 | print() 47 | 48 | print('------------------------------------------') -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /puzzle.py: -------------------------------------------------------------------------------- 1 | class Puzzle: 2 | goal_state=[1,2,3,8,0,4,7,6,5] 3 | heuristic=None 4 | evaluation_function=None 5 | needs_hueristic=False 6 | num_of_instances=0 7 | def __init__(self,state,parent,action,path_cost,needs_hueristic=False): 8 | self.parent=parent 9 | self.state=state 10 | self.action=action 11 | if parent: 12 | self.path_cost = parent.path_cost + path_cost 13 | else: 14 | self.path_cost = path_cost 15 | if needs_hueristic: 16 | self.needs_hueristic=True 17 | self.generate_heuristic() 18 | self.evaluation_function=self.heuristic+self.path_cost 19 | Puzzle.num_of_instances+=1 20 | 21 | def __str__(self): 22 | return str(self.state[0:3])+'\n'+str(self.state[3:6])+'\n'+str(self.state[6:9]) 23 | 24 | def generate_heuristic(self): 25 | self.heuristic=0 26 | for num in range(1,9): 27 | distance=abs(self.state.index(num) - self.goal_state.index(num)) 28 | i=int(distance/3) 29 | j=int(distance%3) 30 | self.heuristic=self.heuristic+i+j 31 | 32 | def goal_test(self): 33 | if self.state == self.goal_state: 34 | return True 35 | return False 36 | 37 | @staticmethod 38 | def find_legal_actions(i,j): 39 | legal_action = ['U', 'D', 'L', 'R'] 40 | if i == 0: # up is disable 41 | legal_action.remove('U') 42 | elif i == 2: # down is disable 43 | legal_action.remove('D') 44 | if j == 0: 45 | legal_action.remove('L') 46 | elif j == 2: 47 | legal_action.remove('R') 48 | return legal_action 49 | 50 | def generate_child(self): 51 | children=[] 52 | x = self.state.index(0) 53 | i = int(x / 3) 54 | j = int(x % 3) 55 | legal_actions=self.find_legal_actions(i,j) 56 | 57 | for action in legal_actions: 58 | new_state = self.state.copy() 59 | if action is 'U': 60 | new_state[x], new_state[x-3] = new_state[x-3], new_state[x] 61 | elif action is 'D': 62 | new_state[x], new_state[x+3] = new_state[x+3], new_state[x] 63 | elif action is 'L': 64 | new_state[x], new_state[x-1] = new_state[x-1], new_state[x] 65 | elif action is 'R': 66 | new_state[x], new_state[x+1] = new_state[x+1], new_state[x] 67 | children.append(Puzzle(new_state,self,action,1,self.needs_hueristic)) 68 | return children 69 | 70 | def find_solution(self): 71 | solution = [] 72 | solution.append(self.action) 73 | path = self 74 | while path.parent != None: 75 | path = path.parent 76 | solution.append(path.action) 77 | solution = solution[:-1] 78 | solution.reverse() 79 | return solution 80 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 84 | 85 | 86 | 99 | 100 | 101 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 130 | 131 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 161 | 162 | 178 | 179 | 195 | 196 | 212 | 213 | 229 | 230 | 246 | 247 | 258 | 259 | 277 | 278 | 296 | 297 | 317 | 318 | 339 | 340 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 383 | 384 | 385 | 386 | 1525675718559 387 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | --------------------------------------------------------------------------------