a star algorithm

This commit is contained in:
Godwinh19 2021-04-11 11:43:33 +02:00
parent 358f2cc604
commit 918d172799
4 changed files with 921 additions and 0 deletions

View File

View File

@ -0,0 +1,319 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Author](https://github.com/). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Challenge Notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement A* algorithm\n",
"\n",
"* [Constraints](#Constraints)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)\n",
"* [Solution Notebook](#Solution-Notebook)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints\n",
"\n",
"* Can we assume we have a Node Class?\n",
" * No\n",
"* Can we assume the inputs are valid?\n",
" * Yes\n",
"* Can we assume this fits memory?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"<table>\n",
"<tr>\n",
"<td>board</td>\n",
"<td>start</td>\n",
"<td>end</td>\n",
"<td>cost</td>\n",
"<td>output</td>\n",
"</tr>\n",
"<tr>\n",
"<td>[]</td>\n",
"<td>[0, 0]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 0]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td><pre>[[0, -1, -1, -1, -1, -1],\n",
"[1, 2, 3, 4, 5, -1],\n",
"[-1, -1, -1, -1, 6, 7],\n",
"[-1, -1, -1, -1, -1, 8],\n",
"[-1, -1, -1, -1, -1, -1]]</pre></td>\n",
"</tr>\n",
" <tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 5]</td>\n",
"<td>[4, 0]</td>\n",
"<td>1</td>\n",
"<td><pre>[[-1, -1, -1, -1, 1, 0],\n",
"[-1, -1, 4, 3, 2, -1],\n",
"[-1, -1, 5, -1, -1, -1],\n",
"[-1, -1, 6, -1, -1, -1],\n",
"[ 9, 8, 7, -1, -1, -1]]</pre></td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 1]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 0]</td>\n",
"<td>[2, 3]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"</table>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"\n",
"Refer to the [Solution Notebook](). If you are stuck and need a hint, the solution notebook's algorithm discussion might be a good place to start."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from typing import List\n",
"class Astar(object):\n",
" def search(self, board, cost: int, start: List[int], end: List[int]) -> List[List[int]]:\n",
" # TODO: Implement me\n",
" pass "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**The following unit test is expected to fail until you solve the challenge.**"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# %load test_astar.py\n",
"\n",
"from unittest import TestCase\n",
"\n",
"\n",
"class TestAstar(TestCase):\n",
" def test_astar_with_empty_board(self):\n",
" board = []\n",
" start = [0, 1]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)\n",
" print('Success: test_astar_with_empty_board')\n",
"\n",
" def test_astar_with_valid_board(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 0]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
" expected = [\n",
" [0, -1, -1, -1, -1, -1],\n",
" [1, 2, 3, 4, 5, -1],\n",
" [-1, -1, -1, -1, 6, 7],\n",
" [-1, -1, -1, -1, -1, 8],\n",
" [-1, -1, -1, -1, -1, -1]\n",
" ]\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_valid_board')\n",
"\n",
" def test_astar_with_another_valid_board(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 5]\n",
" end = [4, 0]\n",
" cost = 1 # cost is 1 per movement\n",
" expected = [\n",
" [-1, -1, -1, -1, 1, 0],\n",
" [-1, -1, 4, 3, 2, -1],\n",
" [-1, -1, 5, -1, -1, -1],\n",
" [-1, -1, 6, -1, -1, -1],\n",
" [9, 8, 7, -1, -1, -1]\n",
" ]\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_another_valid_board')\n",
"\n",
"\n",
" def test_astar_start_from_danger_zone(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 1]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)\n",
" print('Success: test_astar_start_from_danger_zone')\n",
"\n",
"\n",
" def test_astar_with_danger_zone_as_end(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 0]\n",
" end = [2, 3]\n",
" cost = 1\n",
" expected = None\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_danger_zone_as_end')\n",
"\n",
"\n",
"def main():\n",
" test = TestAstar()\n",
" test.test_astar_with_empty_board()\n",
" test.test_astar_with_valid_board()\n",
" test.test_astar_with_another_valid_board()\n",
" test.test_astar_start_from_danger_zone()\n",
" test.test_astar_with_danger_zone_as_end()\n",
"\n",
"\n",
"if __name__ == '__main__':\n",
" main()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Solution Notebook\n",
"\n",
"Review the [Solution Notebook]() for a discussion on algorithms and code solutions."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View File

@ -0,0 +1,494 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook was prepared by [Author](https://github.com/). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solution Notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem: Implement foo(val), which returns val\n",
"\n",
"* [Constraints](#Constraints)\n",
"* [Test Cases](#Test-Cases)\n",
"* [Algorithm](#Algorithm)\n",
"* [Code](#Code)\n",
"* [Unit Test](#Unit-Test)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Constraints\n",
"\n",
"* Can we assume we have a Node Class?\n",
" * No\n",
"* Can we assume the inputs are valid?\n",
" * Yes\n",
"* Can we assume this fits memory?\n",
" * Yes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Test Cases\n",
"\n",
"<table>\n",
"<tr>\n",
"<td>board</td>\n",
"<td>start</td>\n",
"<td>end</td>\n",
"<td>cost</td>\n",
"<td>expected</td>\n",
"</tr>\n",
"<tr>\n",
"<td>[]</td>\n",
"<td>[0, 0]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 0]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td><pre>[[0, -1, -1, -1, -1, -1],\n",
"[1, 2, 3, 4, 5, -1],\n",
"[-1, -1, -1, -1, 6, 7],\n",
"[-1, -1, -1, -1, -1, 8],\n",
"[-1, -1, -1, -1, -1, -1]]</pre></td>\n",
"</tr>\n",
" <tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 5]</td>\n",
"<td>[4, 0]</td>\n",
"<td>1</td>\n",
"<td><pre>[[-1, -1, -1, -1, 1, 0],\n",
"[-1, -1, 4, 3, 2, -1],\n",
"[-1, -1, 5, -1, -1, -1],\n",
"[-1, -1, 6, -1, -1, -1],\n",
"[ 9, 8, 7, -1, -1, -1]]</pre></td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 1]</td>\n",
"<td>[3, 5]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"<tr>\n",
"<td><pre>[[0, 1, 0, 0, 0, 0],\n",
"[0, 0, 0, 0, 0, 1],\n",
"[0, 1, 0, 1, 0, 0],\n",
"[0, 1, 0, 0, 1, 0],\n",
"[0, 0, 0, 0, 1, 0]]</pre></td>\n",
"<td>[0, 0]</td>\n",
"<td>[2, 3]</td>\n",
"<td>1</td>\n",
"<td>None</td>\n",
"</tr>\n",
"</table>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Algorithm\n",
"A* node have some characteristics :\n",
" - parent: the previous node (called parent) of the current node\n",
" - position: the current position of the node in the board\n",
" - g : cost from start to current node\n",
" - h: heuristic (estimated cost) from current node to the goal\n",
" - f : total cost of current node equal to g+h\n",
"To find the path from start to an end:\n",
"* We add the start node to not_visited_nodes list\n",
"* while the not_visited_nodes is not empty:\n",
" * if this node have a lowest f then it become the current_node so we add it to visited_nodes list and remove it from not_visited_nodes list so as we can go further with it children\n",
" * We assume that the max_iteration isn't hit and the node is not the end node\n",
" * if it is the end node, we return the path from start to this node, otherwise\n",
" * we get all children and the current node become the parent of this nodes\n",
" * we will use euclidean distance for the heuristic\n",
" * if the child is already in not_visited_nodes list or have a highest g than others we ignore it and try the next child else we add it to not_visited_nodes list\n",
" * We also ignore the child if its in danger zone\n",
"\n",
" \n",
"Complexity:\n",
"* Time: 2*O(log n)\n",
"* Space: O(b**d) b: average of successors per state, d the depth of the solution"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"from typing import List\n",
"\n",
"import numpy as np\n",
"\n",
"\n",
"class Node:\n",
" \"\"\"\n",
" A Node class for A* algorithm\n",
" the previous node (called parent) of the current node \n",
" position: the current position of the node in the board\n",
" g : cost from start to current node\n",
" h: heuristic (estimated cost) from current node to the goal\n",
" f : total cost of current node equal to g+h\n",
" \"\"\"\n",
"\n",
" def __init__(self, parent=None, position=None):\n",
" self.parent = parent\n",
" self.position = position\n",
"\n",
" self.g = 0\n",
" self.h = 0\n",
" self.f = 0\n",
"\n",
" def __eq__(self, other):\n",
" return self.position == other.position\n",
"\n",
"\n",
"class Astar(object):\n",
"\n",
" @staticmethod\n",
" def get_path(current_node: Node, board):\n",
" path = []\n",
" board_rows, board_columns = np.shape(board)\n",
" output = [[-1 for _ in range(board_columns)] for _ in range(board_rows)]\n",
" while current_node is not None:\n",
" path.append(current_node.position)\n",
" current_node = current_node.parent # node3 - node2 - node1 with node1 the parent of node2 and so on\n",
"\n",
" path = path[::-1] # we reverse to get the logic path: start to end\n",
" for i in range(len(path)):\n",
" output[path[i][0]][path[i][1]] = i\n",
" return output\n",
"\n",
" def search(self, board, cost: int, start: List[int], end: List[int]) -> List[List[int]]:\n",
" \"\"\"\n",
" Return given the start node and the end one, the path for the given board\n",
" :param board:\n",
" :param cost:\n",
" :param start:\n",
" :param end:\n",
" :return:\n",
" \"\"\"\n",
" if len(board) == 0:\n",
" return None\n",
"\n",
" if board[start[0]][start[1]] != 0:\n",
" return None\n",
"\n",
" # INITIALISATION\n",
" start_node, end_node = Node(None, tuple(start)), Node(None, tuple(end))\n",
" start_node.g = start_node.h = start_node.f = 0\n",
" end_node.g = end_node.h = end_node.f = 0\n",
"\n",
" visited_nodes, not_visited_nodes = [], []\n",
" not_visited_nodes.append(start_node) # Here we start\n",
"\n",
" # stop criteria to avoid an infinite search/loop\n",
" _iterations = 0\n",
" max_iteration = (len(board) // 2) ** 12\n",
"\n",
" # take the current node and compare all the f cost and selecting the lowest one. check\n",
" # also if max_iteration is hit to stop searching\n",
"\n",
" while any(not_visited_nodes):\n",
" _iterations += 1\n",
"\n",
" current_node = not_visited_nodes[0]\n",
" current_index = 0\n",
"\n",
" for index, node in enumerate(not_visited_nodes):\n",
" if node.f < current_node.f:\n",
" current_node = node\n",
" current_index = index\n",
"\n",
" if _iterations > max_iteration:\n",
" print(\"Stopping searching...Too much iterations\")\n",
" return self.get_path(current_node, board)\n",
"\n",
" # Then we remove this node from not_visited_nodes, and add it to visited_nodes\n",
" not_visited_nodes.pop(current_index)\n",
" visited_nodes.append(current_node)\n",
"\n",
" if current_node == end_node:\n",
" print(f\"Goal reaches in {_iterations} iterations...\")\n",
" return self.get_path(current_node, board)\n",
"\n",
" # Until here the node is not the goal, so we go further with its children\n",
" children = self.get_children(node=current_node, board=board)\n",
"\n",
" # We have children; let take one by one\n",
" # if it's in visited_nodes we ignore it and try the next one\n",
" # if it's in not_visited_nodes we ignore it, else we move it to that nodes list\n",
"\n",
" for child in children:\n",
" if any([visited_child for visited_child in visited_nodes if visited_child == child]):\n",
" continue\n",
"\n",
" # f,g and h of the child\n",
" child.g = current_node.g + cost\n",
" # we use euclidean distance for the heuristic\n",
" child.h = (((child.position[0] - end_node.position[0]) ** 2) + (\n",
" (child.position[1] - end_node.position[1]) ** 2))\n",
" child.f = child.g + child.h\n",
"\n",
" if any([item for item in not_visited_nodes if item == child and child.g > item.g]):\n",
" continue\n",
"\n",
" not_visited_nodes.append(child)\n",
"\n",
" @staticmethod\n",
" def get_children(node: Node, board) -> List[Node]:\n",
" children = []\n",
" moves = [\n",
" [-1, 0], # UP\n",
" [1, 0], # DOWN\n",
" [0, 1], # RIGHT\n",
" [0, -1] # LEFT\n",
" ]\n",
" board_rows, board_columns = np.shape(board)\n",
" for move in moves:\n",
" node_position = (node.position[0] + move[0],\n",
" node.position[1] + move[1])\n",
"\n",
" # check for board constraints\n",
" if (node_position[0] > (board_rows - 1) or\n",
" node_position[0] < 0 or\n",
" node_position[1] > (board_columns - 1) or\n",
" node_position[1] < 0):\n",
" continue\n",
"\n",
" # can't move to an danger zone\n",
" # Here the value of danger zone in the given board is set to 1\n",
" if board[node_position[0]][node_position[1]] != 0:\n",
" continue\n",
" child = Node(node, node_position)\n",
" children.append(child)\n",
" return children"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Unit Test"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting test_astar.py\n"
]
}
],
"source": [
"%%writefile test_astar.py\n",
"\n",
"from unittest import TestCase\n",
"\n",
"\n",
"class TestAstar(TestCase):\n",
" def test_astar_with_empty_board(self):\n",
" board = []\n",
" start = [0, 1]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)\n",
" print('Success: test_astar_with_empty_board')\n",
"\n",
" def test_astar_with_valid_board(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 0]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
" expected = [\n",
" [0, -1, -1, -1, -1, -1],\n",
" [1, 2, 3, 4, 5, -1],\n",
" [-1, -1, -1, -1, 6, 7],\n",
" [-1, -1, -1, -1, -1, 8],\n",
" [-1, -1, -1, -1, -1, -1]\n",
" ]\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_valid_board')\n",
"\n",
" def test_astar_with_another_valid_board(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 5]\n",
" end = [4, 0]\n",
" cost = 1 # cost is 1 per movement\n",
" expected = [\n",
" [-1, -1, -1, -1, 1, 0],\n",
" [-1, -1, 4, 3, 2, -1],\n",
" [-1, -1, 5, -1, -1, -1],\n",
" [-1, -1, 6, -1, -1, -1],\n",
" [9, 8, 7, -1, -1, -1]\n",
" ]\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_another_valid_board')\n",
"\n",
"\n",
" def test_astar_start_from_danger_zone(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 1]\n",
" end = [3, 5]\n",
" cost = 1 # cost is 1 per movement\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)\n",
" print('Success: test_astar_start_from_danger_zone')\n",
"\n",
"\n",
" def test_astar_with_danger_zone_as_end(self):\n",
" board = [\n",
" [0, 1, 0, 0, 0, 0],\n",
" [0, 0, 0, 0, 0, 1],\n",
" [0, 1, 0, 1, 0, 0],\n",
" [0, 1, 0, 0, 1, 0],\n",
" [0, 0, 0, 0, 1, 0],\n",
" ]\n",
" start = [0, 0]\n",
" end = [2, 3]\n",
" cost = 1\n",
" expected = None\n",
"\n",
" a_start = Astar()\n",
" self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)\n",
" print('Success: test_astar_with_danger_zone_as_end')\n",
"\n",
"\n",
"def main():\n",
" test = TestAstar()\n",
" test.test_astar_with_empty_board()\n",
" test.test_astar_with_valid_board()\n",
" test.test_astar_with_another_valid_board()\n",
" test.test_astar_start_from_danger_zone()\n",
" test.test_astar_with_danger_zone_as_end()\n",
"\n",
"\n",
"if __name__ == '__main__':\n",
" main()\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Success: test_astar_with_empty_board\n",
"Goal reaches in 9 iterations...\n",
"Success: test_astar_with_valid_board\n",
"Goal reaches in 10 iterations...\n",
"Success: test_astar_with_another_valid_board\n",
"Success: test_astar_start_from_danger_zone\n",
"Success: test_astar_with_danger_zone_as_end\n"
]
}
],
"source": [
"%run -i test_astar.py"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.3"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

View File

@ -0,0 +1,108 @@
from unittest import TestCase
class TestAstar(TestCase):
def test_astar_with_empty_board(self):
board = []
start = [0, 1]
end = [3, 5]
cost = 1 # cost is 1 per movement
a_start = Astar()
self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)
print('Success: test_astar_with_empty_board')
def test_astar_with_valid_board(self):
board = [
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0],
]
start = [0, 0]
end = [3, 5]
cost = 1 # cost is 1 per movement
expected = [
[0, -1, -1, -1, -1, -1],
[1, 2, 3, 4, 5, -1],
[-1, -1, -1, -1, 6, 7],
[-1, -1, -1, -1, -1, 8],
[-1, -1, -1, -1, -1, -1]
]
a_start = Astar()
self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)
print('Success: test_astar_with_valid_board')
def test_astar_with_another_valid_board(self):
board = [
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0],
]
start = [0, 5]
end = [4, 0]
cost = 1 # cost is 1 per movement
expected = [
[-1, -1, -1, -1, 1, 0],
[-1, -1, 4, 3, 2, -1],
[-1, -1, 5, -1, -1, -1],
[-1, -1, 6, -1, -1, -1],
[9, 8, 7, -1, -1, -1]
]
a_start = Astar()
self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)
print('Success: test_astar_with_another_valid_board')
def test_astar_start_from_danger_zone(self):
board = [
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0],
]
start = [0, 1]
end = [3, 5]
cost = 1 # cost is 1 per movement
a_start = Astar()
self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), None)
print('Success: test_astar_start_from_danger_zone')
def test_astar_with_danger_zone_as_end(self):
board = [
[0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0],
]
start = [0, 0]
end = [2, 3]
cost = 1
expected = None
a_start = Astar()
self.assertEqual(a_start.search(board=board, cost=cost, start=start, end=end), expected)
print('Success: test_astar_with_danger_zone_as_end')
def main():
test = TestAstar()
test.test_astar_with_empty_board()
test.test_astar_with_valid_board()
test.test_astar_with_another_valid_board()
test.test_astar_start_from_danger_zone()
test.test_astar_with_danger_zone_as_end()
if __name__ == '__main__':
main()