Skip to content

Commit

Permalink
Initial commit. Added Node class and BFS algorithm. 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
RodolfoFerro committed Dec 28, 2018
0 parents commit a59cab7
Show file tree
Hide file tree
Showing 4 changed files with 347 additions and 0 deletions.
115 changes: 115 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/
73 changes: 73 additions & 0 deletions flights_bfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from node import Node


def solution_with_bfs(connections, initial_state, solution):
"""
Function that generates new states from the initial state (using the
defined operators) to solve the Linear Puzzle with four elements by
doing a Breath-First Search in a graph.
"""

# We initialize our data structures:
visited, border = [], []
initial_node = Node(initial_state)
border.append(initial_node)

# While we border nodes is not empty and puzzle not solved:
while len(border) > 0:
# Extract a node as FIFO structure and mark it as visited:
node = border.pop(0)
visited.append(node)

# Compare if we already have our solution:
if node.get_data() == solution:
return node
else:
# Visit each connection (child):
node_data = node.get_data()
children = []
for connection in connections[node_data]:
child = Node(connection)
children.append(child)

# Add new children to border list:
if not child.in_list(visited) and not child.in_list(border):
border.append(child)

# Set new children to node:
node.set_children(children)


if __name__ == '__main__':
# Create connections map:
connections = {
"Malaga": ["Salamanca", "Madrid", "Barcelona"],
"Sevilla": ["Santiago", "Madrid"],
"Granada": ["Valencia"],
"Valencia": ["Barcelona"],
"Madrid": ["Salamanca", "Sevilla", "Malaga", "Barcelona", "Santander"],
"Salamanca": ["Malaga", "Madrid"],
"Santiago": ["Sevilla", "Santander", "Barcelona"],
"Santander": ["Santiago", "Madrid"],
"Zaragoza": ["Barcelona"],
"Barcelona": ["Zaragoza", "Santiago", "Madrid", "Malaga", "Valencia"]
}

# Set initial state to the problem:
initial_state = "Malaga"
solution = "Santiago"

# Compute solution:
solution_node = solution_with_bfs(connections, initial_state, solution)

# Build steps (by getting the father nodes of the solution):
resulting_path = []
node = solution_node
while node.get_father() is not None:
resulting_path.append(node.get_data())
node = node.get_father()

# Format solution:
resulting_path.append(initial_state)
resulting_path = resulting_path[::-1]
print(resulting_path)
100 changes: 100 additions & 0 deletions linear_puzzle_bfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from node import Node


def left_operator(node):
"""
Function to swap left values.
"""

data = node.get_data()
operated_data = [data[1], data[0]] + data[2:]
return Node(operated_data)


def center_operator(node):
"""
Function to swap center values.
"""

data = node.get_data()
operated_data = [data[0], data[2], data[1], data[3]]
return Node(operated_data)


def right_operator(node):
"""
Function to swap rigth values.
"""

data = node.get_data()
operated_data = data[:2] + [data[3], data[2]]
return Node(operated_data)


def border_operator(node):
"""
Function to swap border values.
"""

data = node.get_data()
operated_data = [data[3]] + data[1:3] + [data[0]]
return Node(operated_data)


def search_solution_with_bfs(initial_state, solution):
"""
Function that generates new states from the initial state (using the
defined operators) to solve the Linear Puzzle with four elements by
doing a Breath-First Search in a graph.
"""

# We initialize our data structures:
visited, border = [], []
initial_node = Node(initial_state)
border.append(initial_node)
opertators = [left_operator, center_operator,
right_operator, border_operator]

# While we border nodes is not empty and puzzle not solved:
while len(border) > 0:
# Extract a node as FIFO structure and mark it as visited:
node = border.pop(0)
visited.append(node)

# Compare if we already have our solution:
if node.get_data() == solution:
return node
else:
# Generate new children with operators:
children = []
for operator in opertators:
child = operator(node)
children.append(child)

# Add new children to border list:
if not child.in_list(visited) and not child.in_list(border):
border.append(child)

# Set new children to node:
node.set_children(children)


if __name__ == '__main__':
# Set initial state to the problem:
initial_state = [1, 4, 3, 2]
solution = [1, 2, 3, 4]

# Compute solution:
solution_node = search_solution_with_bfs(initial_state, solution)

# Build steps (by getting the father nodes of the solution):
resulting_path = []
node = solution_node
while node.get_father() is not None:
resulting_path.append(node.get_data())
node = node.get_father()

# Format solution:
resulting_path.append(initial_state)
resulting_path = resulting_path[::-1]
print(resulting_path)
59 changes: 59 additions & 0 deletions node.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class Node:
"""
Node class based on the book "Inteligencia Artificial. Fundamentos,
práctica y apliaciones", 2nd. Edition, by Alberto García Serrano.
"""

def __init__(self, data, children=None):
self.data = data
self.children = None
self.father = None
self.cost = None
self.set_children(children)

def set_children(self, children):
self.children = children
if self.children is not None:
for child in self.children:
child.father = self
return

def get_children(self):
return self.children

def set_father(self, father):
self.father = father
return

def get_father(self):
return self.father

def set_data(self, data):
self.data = data
return

def get_data(self):
return self.data

def set_cost(self, cost):
self.cost = cost
return

def get_cost(self):
return self.cost

def equal(self, node):
if self.get_data() == node.get_data():
return True
return False

def in_list(self, node_list):
is_contained = False
for node in node_list:
if self.equal(node):
is_contained = True
break
return is_contained

def __str__(self):
return str(self.get_data())

0 comments on commit a59cab7

Please sign in to comment.