1301 lines
235 KiB
Plaintext
1301 lines
235 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "6a7ea65e2f19d811f1a48145be4a29dd",
|
|
"grade": false,
|
|
"grade_id": "cell-84617f606b66d110",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"# Artificial Intelligence UE\n",
|
|
"## Exercises 3 - Game Playing\n",
|
|
"\n",
|
|
"In this series of exercises you are looking at game playing - more precisely, at the Minimax algorithm, Alpha-Beta pruning and Q-Learning. \n",
|
|
"\n",
|
|
"The algorithms have been explained in the lecture (VO) and we gave you some additional information in the exercise (UE). Please refer to the lecture slides (VO) for the pseudo algorithms and the exercise slides (UE) for additional hints.\n",
|
|
"\n",
|
|
"<div class=\"alert alert-warning\">\n",
|
|
"\n",
|
|
"<p><strong>Practical hints:</strong></p>\n",
|
|
"<ul>\n",
|
|
"<li>Replace the placeholders <code># YOUR CODE HERE</code>, <code>raise NotImplementedError()</code> with your code.</li>\n",
|
|
"<li>Do not rename any of the already existing variables (this might lead to tests failing / not working).</li>\n",
|
|
"<li>if you want a number smaller than all others, you may use <code>float('-Inf')</code></li>\n",
|
|
"<li>if you want a number larger than all others, you may use <code>float('Inf')</code></li>\n",
|
|
"</ul>\n",
|
|
"</div>\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "8f6aa7b610ced6df3c25d748b625c832",
|
|
"grade": false,
|
|
"grade_id": "cell-9f190755dfdee1fc",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# import stuff\n",
|
|
"from pig_lite.game.base import Game, Node\n",
|
|
"from pig_lite.environment.base import Environment, Outcome\n",
|
|
"from pig_lite.instance_generation.problem_factory import ProblemFactory\n",
|
|
"\n",
|
|
"import math\n",
|
|
"import random\n",
|
|
"import numpy as np"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "bde8a4d40df318658aa6813eced457d2",
|
|
"grade": false,
|
|
"grade_id": "cell-2f0104814be2be96",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"## Small Intro into the World of TicTacToe"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"# you can generate a new game board of TicTacToe as follows; note that the problem_size here describes the depth of the board in a game tree\n",
|
|
"rng = np.random.RandomState(seed=123)\n",
|
|
"game = ProblemFactory().generate_problem('tictactoe', problem_size=3, rng=rng)\n",
|
|
"\n",
|
|
"# or, you can load an existing one from a .json file like so:\n",
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"\n",
|
|
"# if we use Minimax / Alphabeta pruning to derive a move sequence, we can visualise it as follows:\n",
|
|
"move_sequence = [(-1, (0, 2)), (1, (1, 1)), (-1, (1, 2)), (1, (0, 0)), (-1, (2, 0))] # arbitrary move sequence for demonstration purposes\n",
|
|
"game.visualize(move_sequence, show_possible=False, tree_name='Arbitrary Tree 1')\n",
|
|
"# if we set show_possible to True, the function shows all possible moves from a state in the path\n",
|
|
"game.visualize(move_sequence, show_possible=True, tree_name='Arbitrary Tree 2')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "185560b1b20aa70e61fad1611159680a",
|
|
"grade": false,
|
|
"grade_id": "cell-8ca2076d79b78bdb",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"## Minimax\n",
|
|
"\n",
|
|
"Now, let us implement the Minimax algorithm!\n",
|
|
"\n",
|
|
"**NOTE**: If multiple paths lead to the same outcome for these algorithms, choose the first expanded / leftmost path."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": []
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "3b4382686aacd28991f7d0ee2a8d0ad2",
|
|
"grade": false,
|
|
"grade_id": "cell-c5c2a2df427bd111",
|
|
"locked": false,
|
|
"schema_version": 3,
|
|
"solution": true,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Game terminated: True, winner is: 1 (1: Max, -1: Min); nr of expanded nodes: 4024\n",
|
|
"State of the board:\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAAjdJREFUeJzt1LFtAlEURUFjbeDMLVAA5RBRDdVQgVtwBy6AFsjIPgkFrK3FX0eaiV9wg6O3G2OMN4h5nz0A/kK4JAmXJOGSJFyShEuScEkSLknCJWlZfXn+fOEMeDrfVp35uCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJl6Rl7eH+fnnljn91/TjNnrCpn+P37AmbOay883FJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVpmT1ghv39MnvCpr5mD5jAxyVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCTtxhhj9gj4LR+XJOGSJFyShEuScEkSLknCJUm4JAmXpAeDoxLkdriotAAAAABJRU5ErkJggg==",
|
|
"text/plain": [
|
|
"<Figure size 200x200 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Performed moves: 5\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"class Minimax():\n",
|
|
" def play(self, game: Game):\n",
|
|
" \"\"\" Starts game playing, and returns found terminal node according to minimax. \"\"\"\n",
|
|
" start = game.get_start_node()\n",
|
|
" # 'game.get_max_player()' asks the game how it identifies the MAX player internally\n",
|
|
" value, terminal_node = self.minimax(game, start, game.get_max_player())\n",
|
|
" return terminal_node\n",
|
|
"\n",
|
|
" def minimax(self, game, node, max_player):\n",
|
|
" \"\"\" Performs minimax algorithm (recursively). \"\"\"\n",
|
|
" # here we check if the current node 'node' is a terminal node\n",
|
|
" terminal, winner = game.outcome(node)\n",
|
|
"\n",
|
|
" # if it is a terminal node, determine who won, and return\n",
|
|
" # a) the utility value (-1, 0, 1)\n",
|
|
" # and b) the terminal node itself, to be able to determine the path of moves/plies that led to this terminal node\n",
|
|
" if terminal:\n",
|
|
" if winner is None:\n",
|
|
" return 0, node\n",
|
|
" elif winner == max_player:\n",
|
|
" return 1, node\n",
|
|
" else:\n",
|
|
" return -1, node\n",
|
|
"\n",
|
|
" if node.player == max_player:\n",
|
|
" # you have to remember the best value *and* the best node for the MAX player (TODO: initialise appropriately)\n",
|
|
" best_value, best_node = float('-inf'), None\n",
|
|
"\n",
|
|
" for child in game.successors(node):\n",
|
|
" value, terminal_node = self.minimax(game, child, max_player)\n",
|
|
"\n",
|
|
" if value > best_value:\n",
|
|
" best_value = value\n",
|
|
" best_node = terminal_node\n",
|
|
"\n",
|
|
" return best_value, best_node\n",
|
|
" else:\n",
|
|
" # you have to remember the best value *and* the best node for the MIN player (TODO: initialise appropriately)\n",
|
|
" best_value, best_node = float('inf'), None\n",
|
|
"\n",
|
|
" for child in game.successors(node):\n",
|
|
" value, terminal_node = self.minimax(game, child, max_player)\n",
|
|
"\n",
|
|
" if value < best_value:\n",
|
|
" best_value = value\n",
|
|
" best_node = terminal_node\n",
|
|
" \n",
|
|
" return best_value, best_node\n",
|
|
"\n",
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"minimax_nodes = game.get_number_of_expanded_nodes()\n",
|
|
"\n",
|
|
"if outcome is not None:\n",
|
|
" terminated, winner = game.outcome(outcome)\n",
|
|
" print('Game terminated: {}, winner is: {} (1: Max, -1: Min); nr of expanded nodes: {}'.format(terminated, winner, minimax_nodes))\n",
|
|
" outcome.pretty_print()\n",
|
|
" game.visualize(game.get_move_sequence(outcome), False, 'Minimax Tree') "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### Minimax Checks"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "2e346b2a42e7c2d3c69aac9c2a351ade",
|
|
"grade": true,
|
|
"grade_id": "cell-69d1bdefa9930127",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Minimax found correct terminal node and move sequence leading to it for provided problem instance\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# check found path here \n",
|
|
"assert(outcome is not None), 'Minimax returned None, something is wrong with the implementation'\n",
|
|
"# this check tests whether you really chose the left-most path\n",
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"terminated, winner = game.outcome(outcome)\n",
|
|
"assert(terminated == True), 'Minimax did not return a terminal node, so likely wrong node was returned'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'b68a7dd18dfc7694da1b25e0290903b4d2fedba66c8d090c0d11923f5e2c8d22'), 'Minimax did not find correct move sequence, likely due to not taking the first expanded optimal path for MAX player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'fc8ecbd22f45983e14e43ef4cdd120252913898e521f2712cd9d07b8913cade0'), 'Minimax did not find correct move sequence, likely due to not taking the first expanded optimal path for MIN player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'c263685af9af51da439db5aa6714bb39845ad19f2d9f7c4279e9b9c086559e46'), 'Minimax did not find correct move sequence, likely due to not taking the first expanded optimal path for MAX+MIN player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == 'bfb38fb43f84847e4b001d09dcb22cfe573f41efac370e118f3b6630fd0f259a'), 'Minimax did not find correct move sequence to terminal state for the provided problem instance'\n",
|
|
"print('Minimax found correct terminal node and move sequence leading to it for provided problem instance')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "19a815a4bd0fe5cade1bb6080253418a",
|
|
"grade": true,
|
|
"grade_id": "cell-c3fb982c8f3e0663",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Minimax found correct terminal node and move sequence leading to it for private problem instance 1\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe1.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"assert(outcome is not None), 'Minimax returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert(np.all(outcome.state.T == np.array([[1, -1, -1], [-1, 1, 1], [1, -1, -1]]))), 'Minimax did not find correct terminal node for private instance 1, maybe wrong node was returned or minimax has an error'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == '4e49e7612322365aceaccd3a80dca6c4836c0d7196593c785d9e3da7960fe2ea'), 'Minimax did not find correct move sequence to terminal state of private problem instance 1'\n",
|
|
"print('Minimax found correct terminal node and move sequence leading to it for private problem instance 1')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "47365356cc5162c4ab15e9ba3c0e473d",
|
|
"grade": true,
|
|
"grade_id": "cell-d955df46dc4a948b",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Minimax found correct terminal node and move sequence leading to it for private problem instance 2\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe2.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"assert(outcome is not None), 'Minimax returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == 'bba1199f0c4f21f58aac340189660bc99a8b50ded6a227f7f0b2039572b627c4' or\n",
|
|
" np.all(outcome.state.T == np.array([[-1, 0, 1], [0, 0, 1], [0, -1, 1]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MAX player'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == '45940174f9875ce563c45439c8d86b4d1eed0ebaa6947a4587f93bb28bf44476' or\n",
|
|
" np.all(outcome.state.T == np.array([[1, 0, 1], [1, 0, -1], [1, -1, -1]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MIN player'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == '1961082c8650fc498c973b099e4b0c12edba87a04eaf278d5a4e6389364c694e' or\n",
|
|
" np.all(outcome.state.T == np.array([[0, -1, 1], [0, 1, -1], [1, -1, 1]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MAX+MIN player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == '79e76d8da84c876fcd145e25d88601e2997d9557004c0b969b4402ddb9185f78' and\n",
|
|
" np.all(outcome.state.T == np.array([[1, -1, 1], [-1, 1, 0], [1, -1, 0]]))), 'Minimax did not find correct move sequence to terminal state or terminal node for the provided problem instance'\n",
|
|
"print('Minimax found correct terminal node and move sequence leading to it for private problem instance 2')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "c0a98cc7ae08e34d29b193cf19453896",
|
|
"grade": true,
|
|
"grade_id": "cell-7d888625c8c74fc2",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Minimax found correct terminal node and move sequence leading to it for private problem instance 3\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe3.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"assert(outcome is not None), 'Minimax returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == '1b585860ffb92cd318887950c257f7f12d9e0555ae849ffd2b2fad8caf66e8e6' or\n",
|
|
" np.all(outcome.state.T == np.array([[-1, 0, 1],[-1, 1, 1],[1, -1, 0]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MAX player'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == 'b4b1d966698346de24c319905e52877e0afefe1a2b9e60f9b4f314fbdc25d9d5' or\n",
|
|
" np.all(outcome.state.T == np.array([[1, 0, 0], [1, 1, -1], [1, -1, -1]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MIN player'\n",
|
|
"assert not (game.get_move_sequence_hash(outcome) == 'fdb559d7f1b35a9f0a01e2b857c2c592cba1c691292aef491db2a1b89164ed1b' or\n",
|
|
" np.all(outcome.state.T == np.array([[0, 0, -1], [1, 1, 1], [1, -1, -1]]))), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MAX+MIN player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == '20576ffcccc1fa1d6081497f283229761012385ffd5c34f86547801cba3ffead' and\n",
|
|
" np.all(outcome.state.T == np.array([[-1, -1, 1], [1, 1, 0], [1, -1, 0]]))), 'Minimax did not find correct move sequence to terminal state or terminal node for the provided problem instance'\n",
|
|
"print('Minimax found correct terminal node and move sequence leading to it for private problem instance 3')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "b38fd276ca771307aa4bb62c5cb95b74",
|
|
"grade": true,
|
|
"grade_id": "cell-be5f3456e9709954",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Minimax found correct terminal node and move sequence leading to it for private problem instance 4\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe4.json')\n",
|
|
"outcome = Minimax().play(game)\n",
|
|
"assert(outcome is not None), 'Minimax returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert(np.all(outcome.state.T == np.array([[1, -1, 1], [1, -1, 1], [-1, 1, -1]]))), 'Minimax did not find correct terminal node for private instance 4, maybe wrong node was returned or minimax has an error'\n",
|
|
"assert (game.get_move_sequence_hash(outcome) != '1dfb09398dc0726119c331fe7134cf22085cb8b7e9fb32ff8358a5675c2e40a0'), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MAX player'\n",
|
|
"assert (game.get_move_sequence_hash(outcome) != '26c69f8c65d0f1cdac09aafcc00d4ba0ce03fd9336d1977928100481f7570ee7'), 'Minimax did not find correct move sequence or terminal node, likely due to not taking the first expanded optimal path for MIN or MAX+MIN player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == '49e861565ea87b3b2e555c75dc1835b8c030ac0bf55da27c3f4efd2f3585e6ca'), 'Minimax did not find correct move sequence to terminal state or terminal node for the provided problem instance'\n",
|
|
"print('Minimax found correct terminal node and move sequence leading to it for private problem instance 4')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "78a753186db6517a0564a3a4195ecc73",
|
|
"grade": false,
|
|
"grade_id": "cell-592b6c2a2d4b5c05",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
},
|
|
"slideshow": {
|
|
"slide_type": ""
|
|
},
|
|
"tags": []
|
|
},
|
|
"source": [
|
|
"## Alpha-Beta Pruning\n",
|
|
"\n",
|
|
"Here, let us implement Alpha-Beta pruning. \n",
|
|
"\n",
|
|
"**NOTE**: If multiple paths lead to the same outcome for these algorithms, choose the first expanded / leftmost path."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": true,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "1968a996abb1b95f3e682fbbdbb1aaf9",
|
|
"grade": false,
|
|
"grade_id": "cell-d2df9b0e3d90cf00",
|
|
"locked": false,
|
|
"schema_version": 3,
|
|
"solution": true,
|
|
"task": false
|
|
},
|
|
"slideshow": {
|
|
"slide_type": ""
|
|
},
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Game terminated: True, winner is: 1 (1: Max, -1: Min); nr of expanded nodes: 266\n",
|
|
"State of the board:\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAAjdJREFUeJzt1LFtAlEURUFjbeDMLVAA5RBRDdVQgVtwBy6AFsjIPgkFrK3FX0eaiV9wg6O3G2OMN4h5nz0A/kK4JAmXJOGSJFyShEuScEkSLknCJWlZfXn+fOEMeDrfVp35uCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJl6Rl7eH+fnnljn91/TjNnrCpn+P37AmbOay883FJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVpmT1ghv39MnvCpr5mD5jAxyVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCQJlyThkiRckoRLknBJEi5JwiVJuCTtxhhj9gj4LR+XJOGSJFyShEuScEkSLknCJUm4JAmXpAeDoxLkdriotAAAAABJRU5ErkJggg==",
|
|
"text/plain": [
|
|
"<Figure size 200x200 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Performed moves: 5\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAnYAAAHUCAYAAAC3aGWBAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjUsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvWftoOwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAKSNJREFUeJzt3XtUlXWi//HPLkBA5eIl8VKBIYEBOUeHYxrIdCNFU8qCqTNHIzqnic6Zsea0PE2ma41jzJqimcZcscw011SMmcMYaaidRDA0+Y15KUjJUikwlYtJUu7N8/vDNTQ7RbnszaPf/X6ttVfs7/Psh89ercX6+P1+n70dlmVZAgAAwCXvMrsDAAAAwDModgAAAIag2AEAABiCYgcAAGAIih0AAIAhKHYAAACGoNgBAAAYgmIHAABgCIodAACAISh2gI95/vnn5XA4FB8f3+E5DodDCxYs6Nb1U1NTz3ttT1mxYoUcDofbY/DgwUpNTVVxcXG3r7tkyRKtWLHCYzk3b958Vs6OHgDQU352BwDQu15++WVJ0kcffaTt27frX//1X21O1DPLly9XbGysLMtSfX29Fi9erGnTpmnt2rWaNm1al6+3ZMkSDRo0SLNnz/ZIvn/5l39RRUWF21hGRoauueYaPfPMMx75HQDwDxQ7wIdUVlZq165dSk9P19tvv61ly5Zd8sUuPj5e48aNa39+++23Kzw8XK+//nq3ip2nhYSEaPz48W5jffr0UVhY2Fnj/8yyLLW2tiooKMjbEQEYhKVYwIcsW7ZMkpSXl6cJEyaosLBQ33zzzQVf949lz40bN+r+++/XgAED1LdvX02bNk0HDhw452t27Nih5ORkBQcHa+TIkcrLy1NbW1v78dbWVj322GMaM2aMQkNDNWDAAN1www3629/+1qP3GBgYqICAAPn7+7uNf/fdd1q4cKFiY2PVp08fDR48WPfff7+OHj3afk5kZKQ++ugjlZaWti+PRkZGejXvPzgcDj3yyCN68cUXFRcXpz59+uiVV16RJO3fv1/33nuvrrjiCvXp00dxcXF64YUXzrrGiRMn9Ktf/UpRUVEKCAjQ8OHD9ctf/lItLS0eyQjg4seMHeAjTp06pddff10//vGPFR8fr+zsbOXk5OiNN97QrFmzOnWNBx54QLfeeqtee+01HT58WE8++aRSU1O1e/duhYWFtZ9XX1+v++67T4899pjmz5+vv/71r/rf//1fDRs2TP/+7/8uSfr222/V0NCgX/3qVxo+fLi+++47bdq0SXfeeaeWL1/eft6FuFwuOZ1OWZalI0eO6Pe//71aWlp07733tp/T1tam6dOnq6ysTI8//rgmTJiggwcPav78+UpNTVVlZaWCgoL017/+VTNnzlRoaKiWLFki6czsmifznk9RUZHKysr01FNPKSIiQldccYU+/vhjTZgwQVdddZWeffZZRUREqKSkRP/93/+tY8eOaf78+ZKkb775RpMmTVJtba2eeOIJJSYm6qOPPtJTTz2lPXv2aNOmTezjA3yBBcAnrFy50pJkvfjii5ZlWdbXX39t9evXz0pOTj7rXEnW/Pnz258vX77ckmRlZGS4nbd161ZLkrVw4cL2sUmTJlmSrO3bt7udO3r0aCstLa3DfE6n0zp9+rT1wAMPWD/60Y8u+H7+kemHjz59+lhLlixxO/f111+3JFlvvvmm2/iOHTssSW7nX3fdddakSZMu+Pu7mvefXX311VZ6errbmCQrNDTUamhocBtPS0uzRowYYTU3N7uNP/LII1ZgYGD7+U8//bR12WWXWTt27HA7b/Xq1ZYka926dV3KCODSxFIs4COWLVumoKAgZWVlSZL69eunu+++W2VlZdq/f3+nrnHfffe5PZ8wYYKuvvpqvffee27jERERSkpKchtLTEzUwYMH3cbeeOMNTZw4Uf369ZOfn5/8/f21bNkyVVVVtZ/T1tYmp9PZ/nC5XG7XWLlypXbs2KEdO3Zo/fr1mjVrlnJzc7V48eL2c4qLixUWFqZp06a5XWvMmDGKiIjQ5s2bO/X+O5O3J2666SaFh4e3P29tbdW7776rjIwMBQcHu2WfMmWKWltbtW3btvb3GB8frzFjxridl5aWJofD0en3CODSRrEDfEBNTY22bNmi9PR0WZalpqYmNTU1aebMmZK+v1P2QiIiIs45dvz4cbexgQMHnnVenz59dOrUqfbna9as0T333KPhw4frz3/+syoqKrRjxw5lZ2ertbW1/bzs7Gz5+/u3P26++Wa368bFxWncuHEaN26cbr/9dhUUFOi2227T448/rqamJknSkSNH1NTU1L737p8f9fX1Onbs2AXfe2fz9sTQoUPdnh8/flxOp1N/+tOfzso9ZcoUSWrPfuTIEe3evfus8/r37y/Lsjr1HgFc+thjB/iAl19+WZZlafXq1Vq9evVZx1955RUtXLhQl19++XmvU19ff86x6OjoLmf685//rKioKP3lL39x2/v17bffup23YMECPfLII+3P+/fvf8FrJyYmqqSkRPv27VNSUpIGDRqkgQMH6p133jnn+Z25Zmfz9sQP98CFh4fr8ssv189+9jPl5uae8zVRUVGSpEGDBikoKKjDkj5o0CCP5QRw8aLYAYZzuVx65ZVXdM011+ill14663hxcbGeffZZrV+/XlOnTj3vtV599VXddddd7c/ff/99HTx4UDk5OV3O5XA4FBAQ4FZm6uvrz7rLNDIysv3O1M768MMPJUmDBw+WJE2dOlWFhYVyuVwX/HiXH84sdjWvJwUHB+snP/mJdu7cqcTERAUEBHR47tSpU7Vo0SINHDiwvewB8D0UO8Bw69ev15dffqnf/e53Sk1NPet4fHy8Fi9erGXLll2w2FVWVionJ0d33323Dh8+rF//+tcaPny4Hn744S7nmjp1qtasWaOHH35YM2fO1OHDh/Wb3/xGQ4cO7fSeP0nau3evnE6npDNLl2vWrNHGjRuVkZHRXnCysrL06quvasqUKfrFL36hpKQk+fv7q7a2Vu+9956mT5+ujIwMSVJCQoIKCwv1l7/8RSNHjlRgYKASEhI8lrer/vjHP+rGG29UcnKyfv7znysyMlJff/21ampq9NZbb+n//u//JEm//OUv9eabbyolJUVz5sxRYmKi2tradOjQIW3YsEGPPfbYJf+ZhQA6weabNwB42YwZM6yAgADrq6++6vCcrKwsy8/Pz6qvr7csq+O7Yjds2GD97Gc/s8LCwqygoCBrypQp1v79+92uNWnSJOu6664763fMmjXLuvrqq93G8vLyrMjISKtPnz5WXFyctXTpUmv+/PlWZ/40neuu2NDQUGvMmDFWfn6+1dra6nb+6dOnrWeeeca6/vrrrcDAQKtfv35WbGys9Z//+Z9u7+Hzzz+3brvtNqt///6WJLfMPcn7zzq6KzY3N/ec53/22WdWdna2NXz4cMvf398aPHiwNWHCBLe7kS3Lsk6ePGk9+eST1rXXXmsFBARYoaGhVkJCgjVnzpz2/7cAzOawLMuyq1QCuDSsWLFC999/v3bs2OH2LQ8AgIsLd8UCAAAYgmIHAABgCJZiAQAADMGMHQAAgCEo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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 1 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"class AlphaBeta(object):\n",
|
|
" def play(self, game: Game):\n",
|
|
" \"\"\" Starts game playing, and returns found terminal node according to alpha-beta pruning. \"\"\"\n",
|
|
" start = game.get_start_node()\n",
|
|
" alpha = float('-Inf')\n",
|
|
" beta = float('Inf')\n",
|
|
" value, terminal_node = self.alphabeta(game, start, alpha, beta, game.get_max_player())\n",
|
|
" return terminal_node\n",
|
|
"\n",
|
|
" def alphabeta(self, game, node, alpha, beta, max_player):\n",
|
|
" \"\"\" Performs alpha-beta pruning algorithm (recursively). \"\"\"\n",
|
|
" # here we check if the current node 'node' is a terminal node\n",
|
|
" terminal, winner = game.outcome(node)\n",
|
|
" # if it is a terminal node, determine who won, and return\n",
|
|
" if terminal:\n",
|
|
" if winner is None:\n",
|
|
" return 0, node\n",
|
|
" elif winner == max_player:\n",
|
|
" return 1, node\n",
|
|
" else:\n",
|
|
" return -1, node\n",
|
|
"\n",
|
|
" if node.player == max_player:\n",
|
|
" # you have to remember the best value *and* the best node for the MAX player (TODO: initialise appropriately)\n",
|
|
" best_value, best_node = float('-inf'), None\n",
|
|
"\n",
|
|
" for child in game.successors(node):\n",
|
|
" value, terminal_node = self.alphabeta(game, child, alpha, beta, max_player)\n",
|
|
" alpha = max(alpha, value)\n",
|
|
"\n",
|
|
" if value > best_value:\n",
|
|
" best_value = value\n",
|
|
" best_node = terminal_node\n",
|
|
"\n",
|
|
" if alpha >= beta:\n",
|
|
" break;\n",
|
|
"\n",
|
|
" return best_value, best_node\n",
|
|
" else:\n",
|
|
" # you have to remember the best value *and* the best node for the MIN player (TODO: initialise appropriately)\n",
|
|
" best_value, best_node = float('inf'), None\n",
|
|
"\n",
|
|
" for child in game.successors(node):\n",
|
|
" value, terminal_node = self.alphabeta(game, child, alpha, beta, max_player)\n",
|
|
" beta = min(beta, value)\n",
|
|
"\n",
|
|
" if value < best_value:\n",
|
|
" best_value = value\n",
|
|
" best_node = terminal_node\n",
|
|
"\n",
|
|
" if beta <= alpha:\n",
|
|
" break;\n",
|
|
" \n",
|
|
" return best_value, best_node\n",
|
|
"\n",
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"outcome = AlphaBeta().play(game)\n",
|
|
"alphabeta_nodes = game.get_number_of_expanded_nodes()\n",
|
|
"\n",
|
|
"if outcome is not None:\n",
|
|
" terminated, winner = game.outcome(outcome)\n",
|
|
" print('Game terminated: {}, winner is: {} (1: Max, -1: Min); nr of expanded nodes: {}'.format(terminated, winner, alphabeta_nodes))\n",
|
|
" outcome.pretty_print()\n",
|
|
" game.visualize(game.get_move_sequence(outcome), False, 'Alpha-Beta Tree') "
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "458736acf00f1d309bbdb6dd2d60ae36",
|
|
"grade": false,
|
|
"grade_id": "cell-c8aa7d319fc1f6cb",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"### Alpha-Beta Pruning Checks"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "dda53e8b045f8f7f22eb209071c6681f",
|
|
"grade": true,
|
|
"grade_id": "cell-2db5203948b220c7",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Alpha-beta pruning found correct terminal node and move sequence leading to it for provided problem instance\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# check found path here \n",
|
|
"assert(outcome is not None), 'Alpha-beta pruning returned None, something is wrong with the implementation'\n",
|
|
"\n",
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"outcome = AlphaBeta().play(game)\n",
|
|
"terminated, winner = game.outcome(outcome)\n",
|
|
"assert(terminated == True), 'Alpha-beta pruning did not return a terminal node, so likely wrong node was returned'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'f224894cb163f1fd5530927610c3c2667f6c5141c2c026f6fd9e7f6ace1e4bb8'), 'Alpha-beta pruning did not find correct move sequence, likely due to not taking the first expanded optimal path for MAX(+MIN) player'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'fc8ecbd22f45983e14e43ef4cdd120252913898e521f2712cd9d07b8913cade0'), 'Alpha-beta pruning did not find correct move sequence, likely due to not taking the first expanded optimal path for MIN player'\n",
|
|
"assert(np.all(outcome.state.T == np.array([[1, 1, 1], [-1, 1, 0], [-1, -1, 0]]))), 'Alpha-beta did not find correct terminal node, maybe wrong best node was stored'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == 'bfb38fb43f84847e4b001d09dcb22cfe573f41efac370e118f3b6630fd0f259a'), 'Alpha-beta did not find correct move sequence to terminal state'\n",
|
|
"print('Alpha-beta pruning found correct terminal node and move sequence leading to it for provided problem instance')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "8be67fa34c9c3decc3ad39324ebb01a2",
|
|
"grade": true,
|
|
"grade_id": "cell-5e7587f893fa567c",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
},
|
|
"slideshow": {
|
|
"slide_type": ""
|
|
},
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 1\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe1.json')\n",
|
|
"outcome = AlphaBeta().play(game)\n",
|
|
"assert(outcome is not None), 'Alpha-beta pruning returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert(np.all(outcome.state.T == np.array([[1, -1, -1], [-1, 1, 1], [1, -1, -1]]))), 'Alpha-beta did not find correct terminal node for private instance 1, maybe wrong node was returned or minimax has an error'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == '4e49e7612322365aceaccd3a80dca6c4836c0d7196593c785d9e3da7960fe2ea'), 'Alpha-beta did not find correct move sequence to terminal state for private problem instance 1'\n",
|
|
"print('Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 1')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "38c9cd2ff8767c68b1ca36ff59203471",
|
|
"grade": true,
|
|
"grade_id": "cell-127304dc5089e06c",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 2\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe_draw_5x5.json')\n",
|
|
"outcome = AlphaBeta().play(game)\n",
|
|
"assert(outcome is not None), 'Alpha-beta pruning returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) != '7b8a98f36acbd985721d447bd59a9dacc987e31d5d8d2ccafaabe6891cc3de39'), 'Alpha-beta pruning did not find correct move sequence, likely due to not taking the first expanded optimal path for MAX(+MIN) player' \n",
|
|
"assert(game.get_move_sequence_hash(outcome) != 'f728a654955355bc35f3f4ca86a0b409a0febf600899828db999bbe4118a2f65'), 'Alpha-beta pruning did not find correct move sequence, likely due to not taking the first expanded optimal path for MIN player' \n",
|
|
"assert(game.get_move_sequence_hash(outcome) == 'f472a4657161dd8c11256060a52fca0cd9ef075cb0bdd6f809319162cd26bba2'), 'Alpha-beta did not find correct move sequence to terminal state for private problem instance 2'\n",
|
|
"print('Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 2')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "903231436c1055baefa17ae5ac4a15c1",
|
|
"grade": true,
|
|
"grade_id": "cell-20ca8862d1e2a714",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 3\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"game = ProblemFactory().create_problem_from_json(json_path='boards/tictactoe_win_5x5.json')\n",
|
|
"outcome = AlphaBeta().play(game)\n",
|
|
"assert(outcome is not None), 'Alpha-beta pruning returned None, this can have various reasons; e.g., you might have not or incorrectly updated the best value / node, or the update check was wrong (val > best_value for MAX, val < best_value for MIN)'\n",
|
|
"assert(game.get_move_sequence_hash(outcome) == 'da4f6dd7b0fa7fbd53f6191111e7fdbf1082d6c8a41704b981d36bbb016f039d' or\n",
|
|
" game.get_move_sequence_hash(outcome) == '491ace3d81d89d79836ef6919b3d77c9c9a081cdf51de6941403a2ae07ca0dd3'), 'Alpha-beta did not find correct move sequence to terminal state for private problem instance 3'\n",
|
|
"print('Alpha-beta pruning found correct terminal node and move sequence leading to it for private problem instance 3')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "b769d093d7e11e54009a364a4900089f",
|
|
"grade": true,
|
|
"grade_id": "cell-a5135148f16e856c",
|
|
"locked": true,
|
|
"points": 1,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Pruning seems okay\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# check expanded nodes here (whether we actually save something compared to minimax)\n",
|
|
"assert(alphabeta_nodes <= minimax_nodes), 'Alpha-beta pruning took more node expansions than minimax - something must be off here...'\n",
|
|
"game_ab = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"game_mm = ProblemFactory().create_problem_from_json(json_path='boards/game.json')\n",
|
|
"out_ab = AlphaBeta().play(game_ab)\n",
|
|
"out_mm = Minimax().play(game_mm)\n",
|
|
"assert(not(game_ab.get_number_of_expanded_nodes() == game_mm.get_number_of_expanded_nodes() and game_ab.get_move_sequence_hash(out_ab) == game_mm.get_move_sequence_hash(out_mm))), 'Seems like alpha-beta pruning behaves as minimax, nothing was pruned for the provided test instance'\n",
|
|
"assert(not(game_ab.get_number_of_expanded_nodes() == 354 or game_ab.get_number_of_expanded_nodes() == 360 or game_ab.get_number_of_expanded_nodes() == 640 or\n",
|
|
" game_ab.get_number_of_expanded_nodes() == 839 or game_ab.get_number_of_expanded_nodes() == 3079)), 'Did not expand correct number of nodes for alpha-beta pruning, likely either used pruning for MIN/MAX only, or used </> instead of <=/=>'\n",
|
|
"assert(game_ab.get_number_of_expanded_nodes() == 266), 'Did not expand correct number of nodes for alpha-beta pruning, likely due to wrong pruning condition (should be alpha >= beta for MAX, beta <= alpha for MIN)'\n",
|
|
"print('Pruning seems okay')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "7c02e622c196e587436f0154ece2cb7e",
|
|
"grade": false,
|
|
"grade_id": "cell-568544c7786aea22",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"## Small Intro into the Gridworld\n",
|
|
"\n",
|
|
"<div class=\"alert alert-info\">\n",
|
|
"For Q-Learing, we require another new problem type - we here look at a stochastic gridworld.\n",
|
|
"</div>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "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iFSHAUNERESqw4AhIiIi1WHAEBERkeowYIiIiEh1GDBERESkOgwYIiIiUh0GDBEREakOA4aIiIhUhwFDREREqsOAISIiItVhwBAREZHqMGCIiIhIdRgwREREpDoMGCIiIlIdzQZMcXExDAaD0mMQERGRE2g2YLZt24aDBw8qPQYRERE5gWYDZvPmzdi8ebPSYxAREZETaDJgDAYDtmzZwoAhIiLSKE0GzKFDh3D27FkcOnQIZ86cUXocIiIicjBNBkzlR162bNmi4CRERETkDJoPGD6NREREpD0+Sg/gaJcuXUK3bt1QVFSE8PBwJCUloaSkBH5+fkqPRkRERA6iuUdgIiMj8eabbyI8PBwNGjTA0qVLGS9EREQao7mAISIiIu1jwBAREZHqOPw1MOnp6UhPT0dFRYWjd606PXr0UHoExe3atUux87548SKuXbum2AyFhYWKnK+n4Zrzp3N7lio9guKmh7RUegTFlMKz/vkchz8CM2HCBGRlZSEjI8PRuyYiqoZrDpFn4lNIREREpDoMGNIcg8EAIUS1bUREpB0MGNKciooKzJgxA2fOnMHly5cxY8YMvh6FiEhjNPdBdkR6vR5+fn747bffAADR0dEICQlReCoiInIkPgJDmpSammr2/4mISBsYMKRJnTp1gpfXzcO7S5cuCk9DRESOxqeQSJPCwsJw22234cqVK2jYsKHS4xARkYMxYEizunTpgitXrig9BhEROQEDhjQrNTWVAUNEpFEMGNKshg0bIiEhQekxiIjICfgiXtIsnU4Hb29vpccgIiInYMAQERGR6jBgiIiISHUYMERERKQ6DBgiIiJSHQYMERERqQ4DhoiIiFSHAUNERESqw4AhIiIi1WHAEBERkeowYIiIiEh1GDBERESkOgwYIiIiUh0GDBEREakOA4aIiIhUhwFDREREqsOAISIiItVhwBAREZHqMGAc5I8//sD+/fuVHoOIiMgj+Cg9gFoZDAZkZGRg8+bN2Lx5M3788Ud89dVXKCkpUXo0I29vb/j48CYmIiLt0ezfbocOHUJAQIDD93v+/HnMnTsX//znP3Hu3DmT7919990OPz977d69G3feeafSYyhiwIABKCkpwVdffaX0KERE5GCaDZjFixcjIiLC4fuNiYnBwoUL0adPH2zevBlbtmwxhsyLL76ImJgYh5+nrTZv3uyx8QIAI0eOxNWrV5Ueg4iInEAnhBDO2PG1a9cQFhaGq1evIjQ01Bln4RYMBgO+//57bNmyBU2aNMHIkSOVHsmt7Nq1S+kRFFNYWIj+/ftr/j7gLuSak3PmLK9vDzY9pKXSIyimFAasximPWXM0+wiMq3h5eaFjx47o2LGj0qMQERF5DL4LiYiIiFSHAUNERESqw4AhIiIi1WHAEBERkeowYIiIiEh1GDBERESkOgwYIiIiUh0GDBEREakOA4aIiIhUhwFDREREqsOAISIiItVhwBAREZHqMGCIiIhIdRgwREREpDoMGCIiIlIdBgwRERGpDgOGiIiIVIcBQ0RERKrDgCEiIiLVYcAQERGR6jBgiIiISHUYMERERKQ6DBgiIiJSHQYMERERqQ4DhoiIiFSHAUNERESqw4AhIiIi1WHAEBERker4OGvHQggAwLVr15x1FqQChYWFSo+gmBs3bgD4875AziWv54KCAoUnISWVwqD0CIqRl91T1hyHB0x6ejrS09NRWloKALjlllscfRZEqlJQUICwsDClx9CsqmtOq+RmCk9EpCxPWXN0wkmpZjAY0KxZM/zwww/Q6XTOOAu316FDB2RkZCg9hqI8+ToQQqBdu3bIzs6GlxefrXU2rjmefX+TPPk68LQ1x2lPIXl5ecHX19cjKrAm3t7eCA0NVXoMRXn6deDr6+sRC4k74JrD+xvA68CT1hynXsoJEyY4c/duz9MvP8DrwNMvv6t5+vXt6Zcf4HXgSZffaU8hERERETmLZzzORERERJrCgCEiIiLVYcAQERGR6jBgiIiISHUYMERERKQ6DBgiIiJSHQYMERERqQ4DhoiIiFSHAUNERESqw4AhIiIi1WHAEBERkeowYIiIiEh1GDBERESkOgwYIiIiUh0GDBEREakOA4aIiIhUx+6A2b9/P+677z40bNgQfn5+qF+/Prp06YLJkycbT7N3717Mnj0b+fn5Np+PI/ZR1X333YeAgIBa9/nwww9Dr9fj3LlzVu17zZo10Ol0yMnJsW9IK8nzNffnueeec+ks5tR2Oyp1nT366KPQ6/UoLS2t8TR9+/ZFYGAg/vjjD+O2uXPnomXLljAYDBaf16pVqxAfH4/CwkK7ZnY2Nd+vq/r73/8OnU6H22+/3eZ9uONx66z1S6nLU/m81bZ+KXmdVZaRkYHRo0cjMTERfn5+iIyMRL9+/bBnzx6zp7dlDXMku9dDYYctW7YILy8v0atXL7F+/Xqxa9cusX79ejF58mQRHx9vPN3ixYsFAPH777/bfF6O2EdVmzdvFgBEenq62e/n5+eLgIAAMWjQIKv3vXr1aofPa835rl69Wuzbt8/kz8mTJ106izm13Y7nz58X+/btE8XFxS6d6Y033hAAxI8//mj2+1988YUAIObMmWPcdubMGREUFCQ2btxo1XmVlZWJpk2bipkzZ9o1szOp/X5dVZs2bQQAAUB89913Nu3DHY9bZ61fSq1dlc9bbeuXUsdAZS+++KLw8vISvXv3FmvWrBE7duwQa9asEa1btxZeXl5i3bp1Jqe3dQ1zJHvXQ7sC5s477xSNGzcWZWVl1b5XUVFh/H93XejKy8tFXFycaNeundnvv/322wKA2Lx5s9X7VjpgMjIyXHq+lnLFX1jW2r59uwAgPvzww2rfKysrEy1bthRJSUmiqKjIuP35558X8fHxJse5pV599VURFhYmCgsL7ZrbWdR+v64sIyNDABD9+vUTAMTjjz9u037c8bh11vrlDgHD9cs6M2fOFADE4sWLq32vuLhYNG7cWISFhYn8/HzjdnvWMEeyZz20K2Buu+020alTp1pPM2vWLONvP5X/7Ny5UwghxLFjx8To0aNFkyZNREBAgIiLixP9+/c3+W24rn1kZ2eLhx56SERHRwtfX1+RnJwsli5datFlmD59eo2/fXfs2FHExsaK8vJyi2eVqi4Co0aNEomJiTVeP1XZepksXQAsnUd+nZmZKYYNGyZCQ0NFTEyMGDNmjMmdQfrll1/EsGHDRExMjPD19RW33HKLGDFihCguLq7zdjS3cO7Zs0f06tVLBAcHi4CAANGlSxexZcuWGue2dM7KLly4IACIGTNmVPveW2+9JQCITz75xLitpKREREZGiilTppi9/I888ohITEwUvr6+IiYmRvTp00f8+uuvxtOcPXtW6HQ6sWrVqlrnUooW7tfSE088IQCIn376SaSmpoqQkBCzC6Wjj1shLDt27TluhXDO+mXu8nD9qv04UPIY2Ldvn/Dy8hKjRo2q8TQLFy40WcdqW8Pk9VDXOlaX/fv3i6FDh4rExETh7+8voqOjxaBBg8Tx48dNTmfPemjXa2C6dOmC/fv3Y+LEidi/fz/KysqqnWbs2LF4+umnAQCffvop9u3bh3379iElJQUAkJubi8jISCxYsABffPEF0tPT4ePjg06dOuHo0aN17iMrKwsdOnRAZmYmXnvtNWzZsgX9+vXDxIkTMWfOnDovw6OPPgqdTof33nvPZHtWVhYOHDiAUaNGwdvb2+JZHcHeywQAFRUVKC8vN/ljjwceeADNmjXDJ598gmnTpuHDDz/Es88+a3KaI0eOoEOHDvjuu+8wd+5c/Oc//8H8+fNRUlKC0tLSOo+Fqnbv3o1evXrh6tWrWLVqFdavX4+QkBAMGDAAH330kc1zVhUVFYUGDRogMzPTZPuVK1cwe/Zs9O7dG/fff79x+/79+3Hp0iX07NnT5PTHjh1Dhw4dcPXqVbz++uvYtm0blixZgtjYWAQFBRlP16BBAyQnJ+Pzzz+vdS6laOF+DQBFRUVYv349OnTogNtvvx2PPvooCgoKsHHjRpPTOfq4Baw/dm05bgGuX5ZSw/pl6zHw8ssvQ6fTYe7cuTWe5tZbbwUAnD59GkDNaxhg+TpWl8OHD6NNmzZYsmQJvvzyS7zxxhvIzs42WUsBO9dDq5OnkosXL4q0tDRjier1epGamirmz58vCgoKjKez5mG38vJyUVpaKpo2bSqeffbZOvdxzz33iISEBHH16lWT7U899ZTw9/cXly9frvM8u3fvLqKiokRpaalx2+TJkwUAkZ2dbfWsQtj3CIw9l0mer7k/lZ8SsPY3mEWLFpmc7sknnxT+/v7CYDAYt/Xq1UuEh4eL8+fP1zhfbcdC1eusc+fOIiYmxuRYKi8vF7fffrtISEgwOW9r5jTn7rvvFo0bNzbZNnHiROHj4yMyMzNNtsvfZvLy8ky2z5o1SwQGBlr0kOzDDz8s6tevX+fplKCV+/X7778vAIjly5cLIYQoKCgQwcHBolu3bianc/RxK4Tlx669x60Qjl+/7H0ExhPXL6WOgfz8fOHj4yP++te/1niayvOtXLlSCFHzGibnsXQds1RFRYUoKysTK1euFADElStXTL5v63po1yMwkZGR2LNnDzIyMrBgwQIMHDgQ2dnZmD59Olq1aoWLFy/WuY/y8nLMmzcPLVu2hK+vL3x8fODr64tjx47hl19+qfVni4uLsX37dtx3330IDAw0qfW+ffuiuLgY3333XZ0zPPbYY7h48SI2bdpknGndunXo1q0bmjZt6pBZLeWoy/T+++8jIyPD5I+Pj4/Nc917770mX7du3RrFxcU4f/48AODGjRvYvXs3hg4diujoaJvPRyosLMT+/fsxePBgBAcHG7d7e3tjxIgROH36tNnfGuuasyatWrXC77//jhs3bgAAjh49irfffhsTJkzAbbfdZnLa3Nxc6HQ6REVFmWyPiorCjRs3MGbMGHz77beoqKio8fxiYmJw/vx5u3+zdAat3K9XrVqFgIAADBs2DAAQHByMIUOGYM+ePTh27BgAxx+3gG3Hrq3HLcD1yxJqWL9sOQZ++uknlJeXo1WrVrXOs2/fPuM+gZrXMMC6dawmQgj885//RK9evRAXFwcfHx/o9XqMHTsWXl5eCAgIMDm9reuhQz4Hpn379pg6dSo2btyI3NxcPPvss8jJycGiRYvq/NlJkybhpZdewqBBg7B582bs378fGRkZaNOmDYqKimr92UuXLqG8vBxvvfUW9Hq9yZ++ffsCgEWL7eDBgxEWFobVq1cDALZu3Ypz587hsccec9islnLUZWrRogXat29v8scekZGRJl/7+fkBgPFyX7lyBRUVFUhISLDrfKQrV65ACIHY2Nhq34uLiwNw87qyds6atG7dGgaDAVlZWQBu3tbh4eGYPXt2tdMWFRVBr9cbH5qXnnjiCSxYsAAHDhxAWloaYmNj8cwzz+DatWvV9uHv7w8hBIqLi2udS0lqvl8fP34c33zzDfr16wchBPLz85Gfn4/BgwcDgPEpF0cft3Kf1h67th63ANcvS6hh/bLlGJBrS23RVVBQgPXr16NRo0bG67GmNQywbh2rybhx4/DQQw+hZcuWWLZsmfEXojZt2qBJkybGyybZuh7anrQ10Ov1mDVrFt54441qrykwZ926dRg5ciTmzZtnsv3ixYsIDw+v9Wfr1atnLNoJEyaYPU2jRo3qnCEgIAAPPfQQVqxYgbNnz+K9995DSEgIhgwZ4rBZ/f39UVJSUm171Tuzoy5TXSydx1IRERHw9vY2Psdqr3r16sHLywtnz56t9r3c3FwAMPvbg63kbyaZmZm4dOkStm7dihUrVpi9XaOiolBaWorCwkKT54R9fHwwdepUTJ06FadPn8a7776Ll19+Gf7+/li4cKHJPi5fvgw/Pz+T387cmdru1++9957xt8B//vOf1b6/du1avPLKKw4/bgHXH7tcv2qex1JqXb9kcNX2+TOLFi1CQUEB3nrrLeh0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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 4 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"# you can generate a new gridworld as follows\n",
|
|
"rng = np.random.RandomState(seed=123)\n",
|
|
"env = ProblemFactory().generate_problem('gridworld', problem_size=3, rng=rng)\n",
|
|
"\n",
|
|
"# or, you can load an existing one from a .json file like so:\n",
|
|
"env_json = ProblemFactory().create_problem_from_json(json_path='boards/environment.json')\n",
|
|
"\n",
|
|
"# if we use Q-Learning to learn the Q-function, we can visualise its results as follows:\n",
|
|
"rand_policy = np.array([[0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [1, 0, 0, 0], [1, 0, 0, 0]])\n",
|
|
"outcome = Outcome(n_episodes=1, policy=rand_policy, V=np.random.randn(env.get_n_states()), # arbitrary outcome for demonstration purposes\n",
|
|
" Q=np.random.randn(env.get_n_states(), env.get_n_actions())) \n",
|
|
"env.visualize(outcome)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "1135727bbef232f40df175d667718c44",
|
|
"grade": false,
|
|
"grade_id": "cell-233204deb4eb05b0",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"## Q-Learning\n",
|
|
" \n",
|
|
"<strong>Remember: To interact with the (Q-Learning) enviroment, you need</strong>\n",
|
|
"<ul>\n",
|
|
"<li><code>state = env.reset()</code> to reset the environment at the start of an episode</li>\n",
|
|
"<li><code>state, reward, done = env.step(action)</code> to tell the environment that your agent decided to take `action`. The environment then tells you in which state you actually ended up in (<code>state</code>), what the immediate reward was (<code>reward</code>), and whether or not the episode ended (<code>done</code>).</li>\n",
|
|
"<li>If the test takes significantly longer than ~20 seconds you probably have an error in your update step of the state-action function</li>\n",
|
|
" \n",
|
|
"</ul>"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 20,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "f37386df9aa6102c0348b1ffe1f5822c",
|
|
"grade": false,
|
|
"grade_id": "cell-1bbd4598c8d0500f",
|
|
"locked": false,
|
|
"schema_version": 3,
|
|
"solution": true,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "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fPHixQwAO336NGOMsccff5x5eno22YammM1mZjQa2cKFC5mPjw+zWCy108LDw5mjo2O9tlRXVzNvb2/2yCOP1D525513MicnJ5aTk1P7mMlkYl27duX12r/xxhsMAMvJyWFGo5EVFxezVatWMScnJxYWFsaqq6vZN998wwCw3377rd5zP/jgAwaAbdmypV676762ly9fZgDYihUrah8bM2YM8/T0ZHl5eTbbxW2Pf/75Z+1jmZmZTK1Ws7feesvuOpHri/tcJyUl1T42a9asRv2a0Whkfn5+DAA7evRo7eOFhYVMpVKxZ599tlHNJ554ot6ybrnlFgaAffLJJ/Ue79u3L+vfv3/t33z71dTUVAaAPfPMM/Xm+/nnnxkAq33ijh07bL4WJpOJVVRUMBcXF/b555/XPv77779bfW5lZSXz9vZu1HebzWbWp08fNnjwYJvLYux/n6cPPviAGY1GVlNTw44fP86GDRvGgoKC6n3e+fZnW7duZQDY7t27GWOMrVq1irm5ubHHHnuMxcbG1j4vKiqK3X333XZfC4PBwKKiouq9vtzreNNNNzVa5+DgYNa/f/96fV9aWhrTaDQsPDy89jG5+tz2SrZDSImJifD09IROp4PJZKr917dvXwQGBjY647137971UnVdcXFx9f7u1q0bADTaHditWzcUFRU1OoxkzdSpUxstH0C9XaR5eXl49NFHERYWBrVaDY1Gg/DwcABAampqk8toyMfHB3v27EFSUhLef/99TJs2DefOncOCBQvQq1cv3oemfv/9d4wYMQKurq617Vq+fDnvNiUmJiI2NhbBwcH13pubb74ZALBr1y4AwODBg1FSUoK77roL//nPfwQdOtu+fTvGjRsHDw8PqFQqaDQavP766ygsLEReXl69efv27Vu7pwYAHB0dER0dXe+92LFjB8aOHYuAgIDax1QqFe68807ebQKAwMBAaDQaeHl54d5770X//v2xefNmODo6Yvv27XBxccHtt99e7zncIUghu76rqqqwa9cuzJgxA35+fjbni4mJQZ8+fRAfH1/72DfffAOFQoG5c+cKWjdyYygUCkyePLn2b7Vajc6dOyMoKKjeOXfe3t7w9/evtx1zhPRpdZ/Pt1/dsWMHAOCee+6pV2/GjBm8zseoqKjA/Pnz0blzZ6jVaqjVari6uqKyspJXP7Nv3z4UFRVh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",
|
|
"text/plain": [
|
|
"<Figure size 640x480 with 4 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"def eps_greedy(rng, qs, epsilon):\n",
|
|
" \"\"\" Makes an epsilon greedy decision between exploration (trying out a new option)\n",
|
|
" and exploitation (choosing best option so far). \"\"\"\n",
|
|
" if rng.uniform(0, 1) < epsilon:\n",
|
|
" # with probability p == epsilon, an action is chosen uniformly at random\n",
|
|
" # YOUR CODE HERE\n",
|
|
" return rng.randint(len(qs))\n",
|
|
" else:\n",
|
|
" # with probability p == 1 - epsilon, the action having the currently largest q-value estimate is chosen\n",
|
|
" # YOUR CODE HERE\n",
|
|
" return np.argmax(qs)\n",
|
|
"\n",
|
|
"class QLearning():\n",
|
|
" def train(self, env: Environment, n_episodes=10000, alpha=0.2):\n",
|
|
" \"\"\" Performs Q-Learning for given environment. \"\"\"\n",
|
|
" # leave untouched for the sake of reproducibility (tests below rely on these fixed values)\n",
|
|
" self.rng = np.random.RandomState(1234)\n",
|
|
" self.epsilon = 0.3\n",
|
|
" self.gamma = env.get_gamma()\n",
|
|
"\n",
|
|
" # initialize the Q-'table'\n",
|
|
" Q = np.zeros((env.get_n_states(), env.get_n_actions()))\n",
|
|
"\n",
|
|
" for episode in range(1, n_episodes + 1):\n",
|
|
" # implement q-learning update here: generate an episode, interact with environment with env.reset() and env.step(action)\n",
|
|
" # YOUR CODE HERE\n",
|
|
" done = False\n",
|
|
" state = env.reset()\n",
|
|
"\n",
|
|
" while not done:\n",
|
|
" action = eps_greedy(self.rng, Q[state], self.epsilon)\n",
|
|
" next_state, reward, done = env.step(action)\n",
|
|
"\n",
|
|
" best_next_q = np.max(Q[next_state])\n",
|
|
" td_target = reward + self.gamma * best_next_q\n",
|
|
"\n",
|
|
" Q[state, action] += alpha * (td_target - Q[state, action])\n",
|
|
" state = next_state\n",
|
|
"\n",
|
|
" # compute a deterministic policy from the Q value function\n",
|
|
" policy = np.zeros((env.get_n_states(), env.get_n_actions()), dtype=np.int64)\n",
|
|
" policy[np.arange(len(policy)), np.argmax(Q, axis=1)] = 1\n",
|
|
" # finally, compute the state value function V here\n",
|
|
" # it can be computed easily from Q by taking the action that leads to the max future reward\n",
|
|
" V = np.max(Q, axis=1)\n",
|
|
" \n",
|
|
" return Outcome(n_episodes, policy, V=V, Q=Q)\n",
|
|
"\n",
|
|
"environment = ProblemFactory().create_problem_from_json(json_path='boards/environment.json')\n",
|
|
"qlearn = QLearning()\n",
|
|
"outcome = qlearn.train(environment)\n",
|
|
"\n",
|
|
"if outcome is not None:\n",
|
|
" environment.visualize(outcome)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "markdown",
|
|
"checksum": "9b840bfe6c132101d4d814e0dc258945",
|
|
"grade": false,
|
|
"grade_id": "cell-ac953a8ab55a2747",
|
|
"locked": true,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"source": [
|
|
"### Q-Learning Checks"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 21,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "37f495bfb5d0eb45b136f5fd547f0306",
|
|
"grade": true,
|
|
"grade_id": "cell-724a9b7bd5e49b48",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# here we check whether default variables were modified\n",
|
|
"assert(qlearn.epsilon == 0.3), 'Epsilon was changed for Q-Learning'\n",
|
|
"assert(qlearn.gamma == environment.get_gamma()), 'Gamma was changed for Q-Learning'"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "08335d2c6e8f0d1aa5877a81f1c27227",
|
|
"grade": true,
|
|
"grade_id": "cell-321985c0232e9f93",
|
|
"locked": true,
|
|
"points": 1,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"The Q-learning implementation found the same policy as ours for a new test instance!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# here we check a few test instances for their resulting policy (encoded in a hash-value)\n",
|
|
"# first two hashes are obtained by using q-learning update with alpha, second two hashed w/o alpha\n",
|
|
"assert(environment.get_policy_hash(outcome) != 'f35de3a82ae52dc2c67cacc7942b85785a35ba188a20454ccbdf77627b6695c8'), 'Make sure to use both the current and the next state in your Q function update'\n",
|
|
"assert(environment.get_policy_hash(outcome) == 'a138e26bebdd61e38fc045f03a37ee77bc3343dc36cb3f1cf415707a9b5e08ad' or\n",
|
|
" environment.get_policy_hash(outcome) == '6c8ec07e309222af5c0839f8a6fb58597135356f451dc61c624a1ebea86735fe' or\n",
|
|
" environment.get_policy_hash(outcome) == '7e730a7445950f7a9c8125c96d0d45066f995833f08e4ea585b38ce93a6313e0' or\n",
|
|
" environment.get_policy_hash(outcome) == 'af0dc1e412f6985a939691b3f589f89a3fb5d696a3cde67d33438024d14223e9'), 'algorithm did not find the same optimal policy as ours, so there is probably something off'\n",
|
|
"\n",
|
|
"env = ProblemFactory().create_problem_from_json(json_path='boards/gridworld1.json')\n",
|
|
"out = QLearning().train(env)\n",
|
|
"assert(env.get_policy_hash(out) == 'ca2e78d44dacc2645876957f1e10393b77c933a94c562f845025d31b3dc062c4' or\n",
|
|
" env.get_policy_hash(out) == 'c14762db6d5f332ae89cb4209cf460fa9eecffdee197383fa043b2c6ef00b76c' or\n",
|
|
" env.get_policy_hash(out) == '0ceffffae674a851c603e971597718b70d2dce2ed6e38ffc925fbc5a81d8aebf'), 'Q-Learning did not find the same policy as ours; this could be due to incorrect update function (or other aspects, see remaining comments regarding tests)'\n",
|
|
"print('The Q-learning implementation found the same policy as ours for a new test instance!')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "bb39b0cc7f75b224d0a9f9ac75a4ba36",
|
|
"grade": true,
|
|
"grade_id": "cell-096b0e848bc26030",
|
|
"locked": true,
|
|
"points": 2,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Test of eps_greedy sampling, random sampling with epsilon = 0.5 and random state = 12:\n",
|
|
"Seems to work fine!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# here we check whether eps_greedy was implemented (correctly)\n",
|
|
"assert(eps_greedy(qlearn.rng, outcome.Q[environment.reset()], qlearn.epsilon) != -1), 'eps_greedy does not appear to be implemented (correctly) yet'\n",
|
|
"\n",
|
|
"\n",
|
|
"rng = np.random.RandomState(12)\n",
|
|
"print('Test of eps_greedy sampling, random sampling with epsilon = 0.5 and random state = 12:')\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 2), 'first step should be random action, eps_greedy does not work as expected, make sure to use the passed rng to select randomly'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 3), 'second step should be argmax of Q, eps_greedy does not work as expected'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 3), 'third step should be random action, eps_greedy does not work as expected, make sure to use the passed rng to select randomly'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 2), 'fourth step should be random action, eps_greedy does not work as expected, make sure to use the passed rng to select randomly'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 1), 'fifth step should be random action, eps_greedy does not work as expected, make sure to use the passed rng to select randomly'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 2), 'sixth step should be random action, eps_greedy does not work as expected, make sure to use the passed rng to select randomly'\n",
|
|
"assert(eps_greedy(rng, [59.23768459, 59.86590956, 59.15508216, 60.56009166], 0.5) == 3), 'seventh step should be argmax of Q, eps_greedy does not work as expected'\n",
|
|
"print('Seems to work fine!')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "9f46d2c303ee7af69eb42353c81ad2cf",
|
|
"grade": true,
|
|
"grade_id": "cell-3c0c3a8168ade782",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"source": [
|
|
"# here we check whether eps_greedy was called in your QLearning implementation\n",
|
|
"import ast\n",
|
|
"import inspect\n",
|
|
"call_names = [c.func.id for c in ast.walk(ast.parse(inspect.getsource(QLearning.train).lstrip()))\n",
|
|
" if isinstance(c, ast.Call) and not isinstance(c.func, ast.Attribute)] \n",
|
|
"assert('eps_greedy' in call_names), 'eps_greedy function was not used during training to sample next action'"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 25,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "674a5cb9e18b0339310ed3fe125672a5",
|
|
"grade": true,
|
|
"grade_id": "cell-7e0012c0d1af9a1c",
|
|
"locked": true,
|
|
"points": 0.5,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"V seems to be computed correctly!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# here we check whether V was computed (correctly)\n",
|
|
"assert(outcome.V is not None), 'V was not computed (correctly)'\n",
|
|
"assert(np.allclose(outcome.V, [60.56009166229519, 62.0862976417879, 59.69673685231367, 57.2998914308495, 57.16247167797613, 63.72475795338036, 58.681549989922765, 36.160081000906104, -0.7346191191046638, -7.957286438234731, \n",
|
|
" 62.429613717496785, 64.27021333475943, 61.8489623068024, 56.68931070289786, 64.7086260941021, 68.96740198280624, 64.72192080370876, 45.42333812841379, -7.70660099982711, -6.3540660505033255, \n",
|
|
" 63.78874659772162, 66.28022856039745, 64.26691771606903, 0.0, 69.00293688866753, 72.11660041265043, 59.602399420640545, 38.030496836206275, 14.299440372492398, -1.636243819995724, \n",
|
|
" 65.60578892848311, 68.06355849549294, 70.76043474466302, 73.52559642373782, 74.829413699787, 74.7244884571062, 0.0, 0.0, -14.372316463840185, 30.32359198821047, \n",
|
|
" 67.45442443720407, 69.98858830119075, 73.64652924428802, 76.45223284842137, 78.6024777398158, 80.19432277767568, 81.97087573052612, 81.87190792575387, 0.0, 47.6790177531604, \n",
|
|
" 65.25444937464933, 67.06361233729368, 70.09899795480531, 74.8512985464834, 80.99319093176972, 82.70891686090964, 84.1063933238421, 87.10476457339571, 89.55648769695577, 92.02018276648413, \n",
|
|
" 63.98216126752015, 64.78481366263227, 63.37994143735609, 0.0, 83.06026652598523, 84.14552727908097, 86.80838516636535, 89.91640697830174, 92.07232887476961, 94.00906431697199, \n",
|
|
" 61.66891093157388, 63.80128024721642, 59.39816181823294, 45.550034604437634, 84.31066865743784, 86.72480155341584, 89.00776722138832, 91.72422981845112, 94.69912447457426, 97.00665363583768, \n",
|
|
" 17.818059604934152, 53.73008677587408, 38.607997303469254, 0.0, 86.4088656035926, 88.63000118406423, 90.8302457235381, 93.48189534830975, 96.7290118179945, 99.76915525815585, \n",
|
|
" 2.7869296811856383, 1.9107575717432566, 28.481804693589865, 79.17530638335536, 88.17997020406862, 89.31582272957053, 92.06318449933205, 95.62679684531966, 98.34285013960046, 0.0]) or \n",
|
|
" np.allclose(outcome.V, [65.23372476711732, 54.00862916103103, 55.56427187982933, 57.13562816144377, 57.13562816144377, 60.32611790780917, 58.72285672873108, 60.32611790780917, 60.32611790780917, 73.74916255379564, \n",
|
|
" 66.9027522900175, 68.58863867678535, 54.00862916103103, 58.72285672873108, 63.58138751944615, 58.72285672873108, 70.29155421897511, 65.23372476711732, 63.58138751944615, 86.41306958139799, \n",
|
|
" 66.9027522900175, 70.29155421897511, 54.00862916103103, 0.0, 65.23372476711732, 77.27697434322585, 72.01167092825769, 66.9027522900175, 63.58138751944615, 84.54893888558401, \n",
|
|
" 70.29155421897511, 68.58863867678535, 77.27697434322585, 75.50420459979358, 80.87641500176089, 79.06765085174328, 0.0, 0.0, -100.0, 86.41306958139799, \n",
|
|
" 72.01167092825769, 73.74916255379564, 75.50420459979358, 77.27697434322585, 82.70344949672817, 80.87641500176089, 82.70344949672817, 84.54893888558401, 0.0, 86.41306958139799, \n",
|
|
" 73.74916255379564, 73.74916255379564, 73.74916255379564, 75.50420459979358, 84.54893888558401, 86.41306958139799, 80.87641500176089, 90.19800998, 92.119202, 94.0598, \n",
|
|
" 68.58863867678535, 70.29155421897511, 75.50420459979358, 0.0, 80.87641500176089, 86.41306958139799, 82.70344949672817, 88.2960298802, 94.0598, 96.02, \n",
|
|
" 66.9027522900175, 58.72285672873108, 60.32611790780917, 61.94557364425169, 84.54893888558401, 86.41306958139799, 92.119202, 90.19800998, 94.0598, 98.0, \n",
|
|
" 60.32611790780917, 58.72285672873108, 60.32611790780917, 0.0, 82.70344949672817, 88.2960298802, 92.119202, 96.02, 94.0598, 100.0, \n",
|
|
" 57.13562816144377, 58.72285672873108, 58.72285672873108, 58.72285672873108, 92.119202, 82.70344949672817, 96.02, 98.0, 100.0, 0.0])), 'Your computed V does not match our solution'\n",
|
|
"print('V seems to be computed correctly!')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 26,
|
|
"metadata": {
|
|
"deletable": false,
|
|
"editable": false,
|
|
"nbgrader": {
|
|
"cell_type": "code",
|
|
"checksum": "a66094a9e41dab1ccd79ec4ca4ded973",
|
|
"grade": true,
|
|
"grade_id": "cell-aecdeb8567515f69",
|
|
"locked": true,
|
|
"points": 2,
|
|
"schema_version": 3,
|
|
"solution": false,
|
|
"task": false
|
|
},
|
|
"slideshow": {
|
|
"slide_type": ""
|
|
},
|
|
"tags": []
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Q-update looks correct as well\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"# here we check whether the update function is correctly implemented\n",
|
|
"rng = np.random.RandomState(12)\n",
|
|
"env = ProblemFactory().generate_problem('gridworld', problem_size=3, rng=rng)\n",
|
|
"q_approx = QLearning().train(env, 1).Q\n",
|
|
"assert(np.allclose(q_approx, np.array([[-0.36, -0.2, -0.3996, -0.2], [-0.2, 0., 0., 0.], [0., 0., 0., 0.,], [-0.2, -20., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.,], [0., 0., 0., 0.], [0., 0., 0., 0.]])) or\n",
|
|
" np.allclose(q_approx, np.array([[-1., -1., -1.99, -1.], [-1., 0., 0., 0.], [0., 0., 0., 0.,], [-1., -100., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.,], [0., 0., 0., 0.], [0., 0., 0., 0.]]))), 'Q-values are wrong for this instance, probably the update function is wrongly implemented'\n",
|
|
"env = ProblemFactory().generate_problem('gridworld', problem_size=3, rng=rng)\n",
|
|
"q_approx = QLearning().train(env, 2).Q\n",
|
|
"assert(np.allclose(q_approx, np.array([[-0.3996, -0.488, -0.3996, -0.488], [-0.2, -0.2, -0.39168, -0.27128], [-0.2, 0., 0., 0.], [-0.3996, -20., -0.2396, -0.3996], [0., 0., 0., 0.], [0., 0., 0., 0.], [-0.3996, -0.36, -0.2396, -0.2], [-0.2, 20., 0., 0.], [0., 0., 0., 0.]])) or\n",
|
|
" np.allclose(q_approx, np.array([[-1.99, -1.99, -1., -1.], [-1.99, -1., -1.99, -100.], [-1., -1., -1., -1.], [0., 0., -1.99, 0.], [0., 0., 0., 0.], [-100., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]))), 'Q-values are wrong for this instance, probably the update function is wrongly implemented'\n",
|
|
"print('Q-update looks correct as well')"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3 (ipykernel)",
|
|
"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.13.8"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
}
|