diff --git a/README.md b/README.md index 7702ee2..a0843de 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,69 @@ -# q-learning-terrain-navigator +# Q-Learning Terrain Navigator +This repository demonstrates the application of Q-learning algorithm in a jupyter notebook environment. The Q-learning algorithm is used to navigate a terrain map and learn the optimal path. +## Jupyter Notebook + + + +### Description +This notebook show how Q-learning works and demonstrate how it can learn to navigate a terrain map and find the optimal path to travel down. This map made using a pygame program that allows you to interactavly draw out the different parts of the terain. + +### Features +- Create a map for the Q-learning algorithm to try using pygame +- Visualize the map and q-table using a matplotlib +- Implement a multi-threaded version of Q-learning +- Compare different hyper paramaters of Q-learning algorithm + +### Requirements +- Python 3.x +- `jupyter lab` or `jupyter notebook` + +```bash +pip install numpy, matplotlib, threading, tqdm, +``` + + + +## Pygame Map Builder + + + +### Description +This Pygame-based map editor allows users to generate, edit, and save custom maps. Users can define grid sizes, place various terrain types, and automatically apply boundary walls around the grid. The map is saved to a JSON file, and it will load from the file if it exists on startup. + +### Features +- **Adjustable Grid Size**: Use the slider on the right to set the grid size from 5x5 to 15x15. +- **Terrain Types**: Place different terrain types using keyboard shortcuts. +- **Boundary Walls**: Automatically creates a boundary with walls (-1000) around the grid. +- **Save/Load Functionality**: The map is saved to and loaded from `map_data.json`. If you have already created a map it will automatically detect that and load it on start, so you can make simple changes without having to rebuild it again. +### Controls +- **Slider**: Adjust grid size (5x5 to 15x15) using the slider on the right panel. +- **Mouse Click**: Click inside the grid to select a cell and place terrain based on the active key shortcut. + +### Terrain Shortcuts: +- `G`: Place **Goal** (1000) +- `R`: Place **Road** (-1) +- `C`: Place **Cliff** (-100) +- `V`: Place **River** (-10) +- `M`: Place **Mountain** (-50) + +### How to Use +1. **Start the Program**: Run the Python script to start the map editor. +2. **Set Grid Size**: Use the slider on the right panel to adjust the grid size between 5x5 and 15x15. +3. **Edit the Map**: Click on cells and use the keyboard shortcuts to place terrain types. +4. **Save/Load**: On exiting, the current map is saved to `map_data.json`. The map will automatically load the next time the program is started if the file exists. + +### Requirements +- Python 3.x +- `pygame` library +- `numpy` library + +### Installation +Install dependencies using: +```bash +pip install pygame numpy +``` +### Running the Program +```bash +./map_generator +``` diff --git a/docs/map_generator.png b/docs/map_generator.png new file mode 100644 index 0000000..9df654a Binary files /dev/null and b/docs/map_generator.png differ diff --git a/docs/q-table.png b/docs/q-table.png new file mode 100644 index 0000000..29feeb6 Binary files /dev/null and b/docs/q-table.png differ diff --git a/images/plot_20241021_224820.png b/images/plot_20241021_224820.png new file mode 100644 index 0000000..7ca5da6 Binary files /dev/null and b/images/plot_20241021_224820.png differ diff --git a/images/plot_20241021_224858.png b/images/plot_20241021_224858.png new file mode 100644 index 0000000..240c1f9 Binary files /dev/null and b/images/plot_20241021_224858.png differ diff --git a/map_data.json b/map_data.json new file mode 100644 index 0000000..bcfce64 --- /dev/null +++ b/map_data.json @@ -0,0 +1 @@ +[[-1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0], [-1000.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -50.0, -50.0, -1000.0], [-1000.0, -1.0, -100.0, -1.0, -1.0, -1.0, -1.0, -50.0, -50.0, -1.0, -1.0, -1000.0], [-1000.0, -1.0, -100.0, -1.0, -1.0, -1.0, -50.0, -50.0, -100.0, -1.0, -1.0, -1000.0], [-1000.0, -100.0, -100.0, -1.0, -1.0, -50.0, -50.0, -50.0, -100.0, -1.0, -1.0, -1000.0], [-1000.0, -100.0, -1.0, -1.0, -10.0, -10.0, -50.0, -50.0, -1.0, -1.0, -1.0, -1000.0], [-1000.0, -1.0, -1.0, -100.0, -10.0, -10.0, -10.0, -50.0, -1.0, -1.0, -1.0, -1000.0], [-1000.0, -1.0, -100.0, -100.0, -1.0, -10.0, -10.0, -10.0, -50.0, -1.0, -1.0, -1000.0], [-1000.0, -1.0, -100.0, -1.0, -1.0, -1.0, -10.0, -10.0, -10.0, -50.0, -1.0, -1000.0], [-1000.0, -1.0, -100.0, -1.0, -1.0, -1.0, -1.0, -10.0, -10.0, -10.0, -1.0, -1000.0], [-1000.0, 1000.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -10.0, -10.0, -1.0, -1000.0], [-1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0]] \ No newline at end of file diff --git a/map_generator b/map_generator new file mode 100755 index 0000000..21cb746 --- /dev/null +++ b/map_generator @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + + +import pygame +import numpy as np +import json +import os + +# Pygame setup +pygame.init() +width, height = 800, 600 # Adjust window size to make space for the shortcut panel +rows, cols = 10, 10 # Default size of the grid (can be changed) +cell_size = min(width // (cols + 4), height // rows) # Adjust cell size based on window and grid size + +# Colors +colors = { + 'wall': (0, 0, 0), # Black for wall (boundary) + 'cliff': (255, 0, 0), # Red for cliff + 'road': (128, 128, 128), # Grey for road + 'goal': (0, 255, 0), # Green for goal + 'river': (0, 0, 255), # Blue for river + 'mountain': (139, 69, 19), # Brown for mountain + 'empty': (255, 255, 255) # White for default +} + +# Create initial map array with a dynamic size +def create_map(rows, cols): + # Create a new map array and set boundary values to -1000 + new_map = np.full((rows, cols), -1.0) # Default to road (-1) + new_map[0, :] = -1000 # Top boundary + new_map[-1, :] = -1000 # Bottom boundary + new_map[:, 0] = -1000 # Left boundary + new_map[:, -1] = -1000 # Right boundary + return new_map + +# Function to load the map from the JSON file and set the size accordingly +def load_map(): + if os.path.exists("map_data.json"): + with open("map_data.json", "r") as f: + loaded_map = np.array(json.load(f)) + return loaded_map, loaded_map.shape[0], loaded_map.shape[1] # Return map and its dimensions + return create_map(rows, cols), rows, cols # If no file exists, return default map + +# Load the map and set the initial size based on the file +map_array, rows, cols = load_map() +cell_size = min(width // (cols + 4), height // rows) # Adjust cell size based on loaded grid size + +# Create the window +screen = pygame.display.set_mode((width, height)) +pygame.display.set_caption("Map Editor") + +# Slider parameters +slider_x = width - 100 +slider_y = 350 +slider_height = 200 +slider_pos = slider_y + (slider_height // 2) # Initial slider position +min_size = 5 +max_size = 15 +slider_value = rows # Default slider value corresponds to the loaded grid size + +# Function to draw the grid +def draw_grid(): + for row in range(rows): + for col in range(cols): + value = map_array[row, col] + if value == -1000: + color = colors['wall'] # Black for walls + elif value == -100: + color = colors['cliff'] + elif value == -50: + color = colors['mountain'] + elif value == -10: + color = colors['river'] + elif value == -1: + color = colors['road'] + elif value == 1000: + color = colors['goal'] + else: + color = colors['empty'] + pygame.draw.rect(screen, color, (col * cell_size, row * cell_size, cell_size, cell_size)) + pygame.draw.rect(screen, (0, 0, 0), (col * cell_size, row * cell_size, cell_size, cell_size), 1) + +# Function to display shortcut panel and slider +def draw_side_panel(): + font = pygame.font.SysFont(None, 24) + shortcuts = [ + "Shortcuts:", + "G: Goal (1000)", + "R: Road (-1)", + "C: Cliff (-100)", + "V: River (-10)", + "M: Mountain (-50)" + ] + for i, text in enumerate(shortcuts): + img = font.render(text, True, (0, 0, 0)) + screen.blit(img, (cols * cell_size + 10, i * 30 + 10)) + + # Draw the slider + pygame.draw.rect(screen, (150, 150, 150), (slider_x, slider_y, 20, slider_height)) # Slider track + pygame.draw.circle(screen, (0, 0, 0), (slider_x + 10, slider_pos), 10) # Slider knob + label = font.render(f"Size: {slider_value}x{slider_value}", True, (0, 0, 0)) + screen.blit(label, (slider_x - 10, slider_y - 30)) # Display current grid size + +# Main loop +running = True +dragging_slider = False + +while running: + screen.fill((255, 255, 255)) # Fill the background + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + # Save map to file on exit + with open("map_data.json", "w") as f: + json.dump(map_array.tolist(), f) + running = False + + # Handle mouse press on slider + if event.type == pygame.MOUSEBUTTONDOWN: + mouse_x, mouse_y = pygame.mouse.get_pos() + if slider_x <= mouse_x <= slider_x + 20 and slider_y <= mouse_y <= slider_y + slider_height: + dragging_slider = True + + # Handle mouse release for slider + if event.type == pygame.MOUSEBUTTONUP: + dragging_slider = False + + # Handle dragging of the slider + if dragging_slider: + mouse_y = pygame.mouse.get_pos()[1] + slider_pos = max(slider_y, min(slider_y + slider_height, mouse_y)) + # Map the slider position to a value between min_size and max_size + slider_value = min_size + (slider_pos - slider_y) * (max_size - min_size) // slider_height + rows, cols = slider_value, slider_value + map_array = create_map(rows, cols) + cell_size = min(width // (cols + 4), height // rows) + + # Handle mouse clicks for grid drawing + if not dragging_slider and pygame.mouse.get_pressed()[0]: + x, y = pygame.mouse.get_pos() + if x < cols * cell_size: # Only allow clicking inside the grid + col, row = x // cell_size, y // cell_size + + # Change the value based on key press + keys = pygame.key.get_pressed() + if keys[pygame.K_g]: # 'g' for Goal + map_array[row, col] = 1000 + elif keys[pygame.K_r]: # 'r' for Road + map_array[row, col] = -1 + elif keys[pygame.K_c]: # 'c' for Cliff + map_array[row, col] = -100 + elif keys[pygame.K_v]: # 'v' for River + map_array[row, col] = -10 + elif keys[pygame.K_m]: # 'm' for Mountain + map_array[row, col] = -50 + + # Redraw grid and side panel + draw_grid() + draw_side_panel() + pygame.display.update() + +pygame.quit() diff --git a/q-learning-terrain-navigator.ipynb b/q-learning-terrain-navigator.ipynb new file mode 100644 index 0000000..19c48ba --- /dev/null +++ b/q-learning-terrain-navigator.ipynb @@ -0,0 +1,577 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Import Packages" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "# import necessary libraries\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib import colors\n", + "import matplotlib.animation as animation\n", + "import json\n", + "import time\n", + "import threading\n", + "import tqdm\n", + "from tqdm import tqdm\n", + "from tqdm import trange\n", + "import datetime" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Map\n", + "Create a map for the Q-learning algorithm to try. You can choose any grid size, but the larger the grid, the more compute it will take. I would suggest around an 8x8 to 12x12 grid." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pygame 2.1.0 (SDL 2.0.16, Python 3.10.14)\n", + "Hello from the pygame community. https://www.pygame.org/contribute.html\n" + ] + } + ], + "source": [ + "!./map_generator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Importing Map Array and Displaying Map\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Load the saved map\n", + "with open(\"map_data.json\", \"r\") as f:\n", + " rewards = np.array(json.load(f))\n", + "\n", + "#rewards[rewards == 1000] = 500\n", + "\n", + "environment_rows = rewards.shape[0]\n", + "environment_columns = rewards.shape[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaEAAAGxCAYAAADLfglZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAmWUlEQVR4nO3de3RU5b3/8c8QyCRgLiU2CVEIoUWuckuAwz0KRCBgsadViVQQ6kFBJKIUUpAMWBKFysLKbYGCpQjSdRBElyjhIkiBQ0CIHLxQLZAU5QQBEwiYQPL8/nBlfg5JIOhMnlzer7X2H/PsZ+b57pnM/uTZs2ePwxhjBACABfVsFwAAqLsIIQCANYQQAMAaQggAYA0hBACwhhACAFhDCAEArCGEAADWEEIAAGsIoRrmtddek8PhkMPh0AcffFBmvTFGv/zlL+VwOBQfH1+ltWVlZcnhcGjatGkV9vnnP/8ph8OhJ598UpLUvHlzjR49uooqrNjo0aPVvHlzjzZf1nbp0iW5XK5yX8PS1/jEiRM+GdvbTpw4IYfDoddee83dtmfPHrlcLn377bdl+jdv3lxDhw79SWPm5+fr+eefV/fu3RUaGqoGDRooIiJCgwYN0po1a1RYWPiTHv9G4uPjq/z9VVvVt10AfpygoCC9+uqrZd4IO3fu1JdffqmgoKAqr6ljx46KjY3VqlWrNGfOHPn5+ZXps3LlSknS2LFjJUkbNmxQcHBwldZZWb6s7dKlS5o1a5YklXkNExMTtXfvXjVp0sQnY3tbkyZNtHfvXv3iF79wt+3Zs0ezZs3S6NGjFRoa6tXx/vnPf2rQoEHKzc3Vf/3Xf2n69On62c9+pq+//lrvv/++xowZo08//VTPPfecV8eFbxBCNdQDDzyg119/XYsWLfLYUb766qvq0aOH8vPzrdQ1duxYjR8/Xps3by7z325xcbFWrVql2NhYdezYUZLUuXNnG2VWiq3afv7zn+vnP/+5lbF/DKfTqf/4j/+okrGuXr2q4cOH69y5c9q/f7/atGnjsf7+++/XzJkzdejQoSqpBz8dh+NqqBEjRkiS1q5d627Ly8vT+vXrNWbMmHLvM2vWLHXv3l2NGzdWcHCwunTpoldffVXXXsO29HDJhg0b1KFDBwUEBKhFixb6y1/+csO6kpKSFBgY6J7x/NCWLVt06tQpj/quPeRVUlKiP/3pT2rVqpUCAwMVGhqqDh066KWXXnL3Ke/QmSS5XC45HA6PtkWLFqlv374KDw9Xo0aNdOedd2ru3Lm6cuXKDbfl2tri4+Pdh0KvXUoPRZ05c0bjx49X27Ztdcsttyg8PFx33323PvzwQ/fjnDhxwh0ys2bNcj9G6VgVHY5bsWKFOnbsqICAADVu3Fj33XefPv30U48+o0eP1i233KIvvvhCQ4YM0S233KKmTZvq6aefvuEhqilTpigkJETFxcXutokTJ8rhcGjevHnutrNnz6pevXp6+eWX3dvzw+fA5XJpypQpkqSYmJgKDx+/99576tKliwIDA9W6dWutWLHiuvVJ389OP/nkE02fPr1MAJWKjo7W8OHDPdqys7M1cuRIhYeHy+l0qk2bNnrxxRdVUlLi0a+y7xF4DzOhGio4OFi/+c1vtGLFCo0bN07S94FUr149PfDAA1qwYEGZ+5w4cULjxo1Ts2bNJEn79u3TxIkTderUKc2cOdOj7+HDh5WcnCyXy6XIyEi9/vrrmjRpkoqKivTMM89UWFdISIj+8z//U+vWrdOZM2c8/qNfuXKlAgIClJSUVOH9586dK5fLpRkzZqhv3766cuWKPvvss3I/W6iML7/8UklJSYqJiZG/v7+ysrI0Z84cffbZZ5Xa6f3Q4sWLy8wwn332We3YsUOtWrWSJJ07d06SlJqaqsjISF28eFEbNmxQfHy8tm3bpvj4eDVp0kTvvfeeBg0apLFjx+r3v/+9JF139pOenq4//vGPGjFihNLT03X27Fm5XC716NFDmZmZatmypbvvlStXdO+992rs2LF6+umntWvXLj333HMKCQkp8zr/0IABA/TnP/9Z+/fvV48ePSRJW7duVWBgoDIyMtzBsm3bNhljNGDAgHIf5/e//73OnTunl19+WW+++ab7sGLbtm3dfbKysvT0009r2rRpioiI0CuvvKKxY8fql7/8pfr27VthjRkZGZKke++9t8I+1zpz5ox69uypoqIiPffcc2revLneeecdPfPMM/ryyy+1ePFid9+beY/ASwxqlJUrVxpJJjMz0+zYscNIMv/7v/9rjDGma9euZvTo0cYYY9q1a2f69etX4eMUFxebK1eumNmzZ5uwsDBTUlLiXhcdHW0cDoc5fPiwx30GDhxogoODTUFBwXVrLK1r/vz57razZ88ap9NpHnroIY++0dHRZtSoUe7bQ4cONZ06dbru448aNcpER0eXaU9NTTXX+5Mu3eZVq1YZPz8/c+7cues+5rW1XWvevHlGklm2bFmFfa5evWquXLli+vfvb+677z53+5kzZ4wkk5qaWuY+pa/x8ePHjTHGnD9/3gQGBpohQ4Z49MvOzjZOp9MkJSV5bIck8/e//92j75AhQ0yrVq0qrNMYYwoKCoy/v7+ZPXu2McaYf//730aSmTp1qgkMDDTfffedMcaYRx991ERFRbnvd/z4cSPJrFy5ssxzU7oNPxQdHW0CAgLMyZMn3W2XL182jRs3NuPGjbtujYMGDTKS3LWUKikpMVeuXHEvV69eda+bNm2akWT+53/+x+M+jz/+uHE4HObzzz8vd6zrvUf69et33fcXKo/DcTVYv3799Itf/EIrVqzQkSNHlJmZWeGhOEnavn27BgwYoJCQEPn5+alBgwaaOXOmzp49q9zcXI++7dq1c39uUyopKUn5+fn66KOPKlXXDw/Jvf766yosLLxufZLUrVs3ZWVlafz48Xr//fd/8mdbhw4d0r333quwsDD3Nj/88MMqLi7WsWPHfvTjrl27Vn/4wx80Y8YMPfroox7rli5dqi5duiggIED169dXgwYNtG3btjKHzipr7969unz5cpkz9Zo2baq7775b27Zt82h3OBwaNmyYR1uHDh108uTJ647TsGFD9ejRQ1u3bpX0/awjNDRUU6ZMUVFRkXbv3i3p+9lRRbOgyurUqZN7tiFJAQEBuuOOO25YY0VeeuklNWjQwL388G93+/btatu2rbp16+Zxn9GjR8sYo+3bt3v0rex7BN5BCNVgDodDjzzyiFavXq2lS5fqjjvuUJ8+fcrtu3//fiUkJEiSli9frn/84x/KzMzU9OnTJUmXL1/26B8ZGVnmMUrbzp49e8O6xowZoyNHjujAgQOSvj8UFxMTo7vuuuu6901JSdGf//xn7du3T4MHD1ZYWJj69+/vfpybkZ2drT59+ujUqVN66aWX9OGHHyozM1OLFi2SVHabK2vHjh0aPXq0Hn744TJnYM2fP1+PP/64unfvrvXr12vfvn3KzMzUoEGDfvR4pc93eWfLRUVFlXk9GjZsqICAAI82p9Op77777oZjDRgwQPv27VNBQYG2bt2qu+++W2FhYYqNjdXWrVt1/PhxHT9+/CeHUFhYWJk2p9N5w+eoNLiuDaukpCRlZmYqMzNTXbp08Vh39uzZCp+70vXSzb9H4B2EUA03evRoffPNN1q6dKkeeeSRCvu98cYbatCggd555x3df//96tmzp+Li4irsf/r06QrbytuBlFeXn5+fVqxYoaysLB06dEhjxowpc+LAterXr6/Jkyfro48+0rlz57R27Vrl5OTonnvu0aVLlyR9/19zeR+yf/PNNx63N27cqIKCAr355psaOXKkevfurbi4OPn7+9+w/op8/PHHGj58uPr166fly5eXWb969WrFx8dryZIlSkxMVPfu3RUXF6cLFy786DFLn++vv/66zLqvvvpKt956649+7Gv1799fRUVF2rVrl7Zt26aBAwe62zMyMtyfyfTv399rY96M0no2bdrk0R4eHq64uDjFxcWV+XpCWFhYhc+dJPfzd7PvEXgHIVTD3XbbbZoyZYqGDRumUaNGVdjP4XCofv36Ht/duXz5sv72t7+V2//o0aPKysryaFuzZo2CgoLK/KdZnqioKA0aNEhr167VokWLVK9evevWV57Q0FD95je/0YQJE3Tu3Dn32WLNmzdXbm6u/u///s/dt6ioSO+//77H/UsDz+l0utuMMeWGR2VkZ2dr8ODBatGihdavX68GDRqU6eNwODzGk74Prr1793q0lfapzH/XPXr0UGBgoFavXu3R/u9//1vbt2/3aiB069ZNwcHBWrBggU6fPu3e6Q8YMECHDh3S3//+d7Vt29Y9i6jIzWzfzbjvvvvUtm1bpaWl6bPPPqvUffr3769PPvmkzGHkVatWyeFwuGfnN/segXcQQrXA888/r40bN173y42JiYm6ePGikpKSlJGRoTfeeEN9+vQps8MsFRUVpXvvvVcrV67Ue++9p5EjRyojI0MzZsxQw4YNK1XX2LFj9e233+qVV15RQkKCmjZtesP7DBs2TCkpKVq/fr127dqlv/3tb1qwYIGio6PdZ4A98MAD8vPz04MPPqh3331Xb775phISEjxOLZa+/6/Z399fI0aM0ObNm7Vhwwbdc889On/+fKXqv9bgwYP17bffaubMmTp69Kj27dvnXs6cOSNJGjp0qLZs2aLU1FRt375dS5Ys0T333KOYmBiPxwoKClJ0dLTeeustbdmyRQcOHKjwCgmhoaF69tlntWnTJj388MPavHmzVq9erbvuuksBAQFKTU39UdtTHj8/P/Xr109btmxRTEyM+wuovXr1ktPp1LZt2yp1KO7OO++U9P1nNXv37tWBAwd+0mzwh/Vt3LhRoaGh6tatm55++mm9/fbb2r17t9555x09++yzysrK8vju3FNPPaXbbrtNiYmJWr58ubZs2aJJkyZp8eLFevzxx3XHHXdIuvn3CLzE9pkRuDk/PDvueso7O27FihWmVatWxul0mhYtWpj09HTz6quvljmLKTo62iQmJpr//u//Nu3atTP+/v6mefPmHme7VUZRUZGJiIgo92ytH471wzPQXnzxRdOzZ09z6623Gn9/f9OsWTMzduxYc+LECY/7vfvuu6ZTp04mMDDQtGjRwixcuLDcs+Pefvtt07FjRxMQEGBuu+02M2XKFLN582YjyezYscPdrzJnx0mqcCk9M6ywsNA888wz5rbbbjMBAQGmS5cuZuPGjeU+/tatW03nzp2N0+k0ktxjXXt2XKlXXnnFdOjQwfj7+5uQkBDzq1/9yhw9etSjz6hRo0yjRo3KPM83OnPwh1566SUjyTz66KMe7QMHDjSSzKZNmzzayzs7zhhjUlJSTFRUlKlXr57H813693WtmznjLC8vz6SlpZmuXbua4OBgU79+fRMeHm4GDhxoFi1aVOYMzpMnT5qkpCQTFhZmGjRoYFq1amXmzZtniouLPfpV9j3C2XHe4zCGb2HBU/PmzdW+fXu98847tksBUMtxOA4AYA0hBACwhsNxAABrmAkBAKwhhAAA1hBCAABrqt1POZSUlOirr75SUFDQDS/xAgCofowxunDhgqKiolSv3vXnOtUuhL766qtKfbMeAFC95eTk6Pbbb79un2p3OO7aiw8CAGqmyuzPq10IcQgOAGqHyuzPq10IAQDqDkIIAGANIQQAsIYQAgBYQwgBAKwhhAAA1hBCAABrCCEAgDU+C6HFixcrJiZGAQEBio2N1YcffuiroQAANZRPQmjdunVKTk7W9OnTdejQIfXp00eDBw9Wdna2L4YDANRQPvll1e7du6tLly5asmSJu61NmzYaPny40tPTr3vf/Px8hYSEeLskAEAVy8vLU3Bw8HX7eH0mVFRUpIMHDyohIcGjPSEhQXv27CnTv7CwUPn5+R4LAKBu8HoIffPNNyouLlZERIRHe0REhE6fPl2mf3p6ukJCQtwLP+MAAHWHz05MuPbqqcaY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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Define the colormap for the grid values\n", + "cmap = colors.ListedColormap(['black', 'red', (0.5451, 0.2706, 0.0745), 'blue', 'gray', (0,1,0)])\n", + "# Bounds now account for the actual range of values, with small gaps between to handle exact matching\n", + "bounds = [-1000.5, -100.5, -99.5, -49.5, -9, -0.5, 2000.5]\n", + "norm = colors.BoundaryNorm(bounds, cmap.N)\n", + "\n", + "# Create the plot\n", + "plt.imshow(rewards, cmap=cmap, norm=norm)\n", + "\n", + "\n", + "# Display the plot\n", + "plt.title(\"Map Visualization with Goal\")\n", + "plt.show()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualization Functions\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def graph(q_table, save=False, title=\"\"):\n", + " # Define the colormap for the grid values\n", + " #fig, ax = plt.subplots(figsize=(8, 8), dpi=200) # Increased figure size and DPI\n", + "\n", + " cmap = colors.ListedColormap(['black', 'red', (0.5451, 0.2706, 0.0745), 'blue', 'gray', (0,1,0)])\n", + " # Bounds now account for the actual range of values, with small gaps between to handle exact matching\n", + " bounds = [-1000.5, -100.5, -99.5, -49.5, -9, -0.5, 1000.5]\n", + " norm = colors.BoundaryNorm(bounds, cmap.N)\n", + "\n", + " \n", + " # Create the plot for rewards\n", + " plt.imshow(rewards, cmap=cmap, norm=norm)\n", + " \n", + " # Calculate the optimal direction from Q-table\n", + " # Directions: up (0), right (1), down (2), left (3)\n", + " optimal_directions = np.argmax(q_table, axis=2)\n", + " \n", + " # Initialize arrays for arrow direction (dx, dy) at each grid point\n", + " dx = np.zeros_like(optimal_directions, dtype=float)\n", + " dy = np.zeros_like(optimal_directions, dtype=float)\n", + " \n", + " # Define movement deltas for [up, right, down, left]\n", + " move_map = {\n", + " 0: (0, -1), # up\n", + " 1: (1, 0), # right\n", + " 2: (0, 1), # down\n", + " 3: (-1, 0), # left\n", + " }\n", + "\n", + " # Fill in dx, dy based on optimal directions, but only if the sum of Q-values is not zero\n", + " for i in range(optimal_directions.shape[0]):\n", + " for j in range(optimal_directions.shape[1]):\n", + " if np.sum(q_table[i, j]) != 0: # Check if the Q-values are non-zero\n", + " direction = optimal_directions[i, j]\n", + " dx[i, j], dy[i, j] = move_map[direction]\n", + " \n", + " # Create a meshgrid for plotting arrows\n", + " x, y = np.meshgrid(np.arange(optimal_directions.shape[1]), np.arange(optimal_directions.shape[0]))\n", + " \n", + " # Plot arrows using quiver, only for non-zero vectors\n", + " plt.quiver(x, y, dx, dy, angles='xy', scale_units='xy', scale=1, color='black')\n", + " plt.title(title)\n", + "\n", + " if save:\n", + " timestamp = datetime.datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n", + " filename = f\"images/plot_{timestamp}.png\"\n", + " plt.savefig(filename, format='png')\n", + " \n", + " # Display the plot with arrows\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def graph_path(path):\n", + " # Define the colormap for the grid values\n", + " cmap = colors.ListedColormap(['black', 'red', (0.5451, 0.2706, 0.0745), 'blue', 'gray', (0,1,0)])\n", + " bounds = [-1000.5, -100.5, -99.5, -49.5, -9, -0.5, 1000.5]\n", + " norm = colors.BoundaryNorm(bounds, cmap.N)\n", + "\n", + " # Create the plot for rewards\n", + " plt.imshow(rewards, cmap=cmap, norm=norm)\n", + "\n", + " move_map = {\n", + " 0: (0, -1), # up\n", + " 1: (1, 0), # right\n", + " 2: (0, 1), # down\n", + " 3: (-1, 0), # left\n", + " }\n", + "\n", + " # Now plot the path taken by the robot\n", + " path_x = [pos[1] for pos in path]\n", + " path_y = [pos[0] for pos in path]\n", + " \n", + " # Create arrows for the robot's path\n", + " for i in range(len(path) - 1):\n", + " start_x, start_y = path_x[i], path_y[i]\n", + " end_x, end_y = path_x[i + 1], path_y[i + 1]\n", + " plt.arrow(start_x, start_y, end_x - start_x, end_y - start_y, color='yellow', head_width=0.2)\n", + "\n", + " # Display the plot with arrows\n", + " plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Q-Learning helper functions\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# define actions\n", + "# we will use numeric (index) to represent the actions\n", + "# 0 = up, 1 = right, 2 = down, 3 = left\n", + "actions = ['up', 'right', 'down', 'left']" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# because we will end the episode if we reach Goal\n", + "def is_terminal_state(current_row_index, current_column_index):\n", + " if rewards[current_row_index, current_column_index] != np.max(rewards): # it is not terminal if the rewards is -1\n", + " return False\n", + " else:\n", + " return True\n", + "\n", + "# this starting location must not be on the road\n", + "def get_starting_location():\n", + " current_row_index = np.random.randint(environment_rows) # get a random row index\n", + " current_column_index = np.random.randint(environment_columns) # get a random column index\n", + " \n", + " while rewards[current_row_index, current_column_index] != -1: # True if it is terminal\n", + " current_row_index = np.random.randint(environment_rows) # repeat to get another random row index\n", + " current_column_index = np.random.randint(environment_columns) # repeat to get another random row index\n", + " return current_row_index, current_column_index # returns a random starting location that is not terminal\n", + "\n", + "\n", + "# define an epsilon greedy algorithm for deciding the next action\n", + "def get_next_action(current_row_index, current_column_index, epsilon):\n", + " if np.random.random() < epsilon: # choose the action with the highest q_values\n", + " return np.random.randint(4)\n", + " else: # choose a random action\n", + " return np.argmax(q_values[current_row_index, current_column_index])\n", + "\n", + "\n", + "# define a function that will get the next location based on the chosen action\n", + "# refer to how the board is drawn physically, with the rows and columns\n", + "def get_next_location(current_row_index, current_column_index, action_index):\n", + " new_row_index = current_row_index\n", + " new_column_index = current_column_index\n", + " if actions[action_index] == 'up' and current_row_index > 0:\n", + " new_row_index -= 1\n", + " elif actions[action_index] == 'right' and current_column_index < environment_columns - 1:\n", + " new_column_index += 1\n", + " elif actions[action_index] == 'down' and current_row_index < environment_rows - 1:\n", + " new_row_index += 1\n", + " elif actions[action_index] == 'left' and current_column_index > 0:\n", + " new_column_index -= 1\n", + " return new_row_index, new_column_index\n", + "\n", + "\n", + "# Define a function that will get the shortest path that is on the white tiles \n", + "def get_shortest_path(start_row_index, start_column_index):\n", + " i = 0\n", + " if is_terminal_state(start_row_index, start_column_index): # check if it is on Goal or Cliff\n", + " return [] # if yes, there are no available steps\n", + " \n", + " else: #if this is a 'legal' starting location\n", + " current_row_index, current_column_index = start_row_index, start_column_index\n", + " shortest_path = []\n", + " shortest_path.append([current_row_index, current_column_index]) # add the current coordinate to the list\n", + "\n", + " while not is_terminal_state(current_row_index, current_column_index): # repeat until we reach Goal or Cliff\n", + " action_index = get_next_action(current_row_index, current_column_index, 1.) \n", + " # get next coordinate \n", + " \n", + " current_row_index, current_column_index = get_next_location(current_row_index, current_column_index, action_index)\n", + " # update that next coordinate as current coordinate\n", + " \n", + " shortest_path.append([current_row_index, current_column_index]) \n", + " # add the current coordinate to the list\n", + "\n", + " i += 1\n", + " if i > 100:\n", + " return 0;\n", + " return shortest_path" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def q_learn_single(epsilon = 0.9, discount_factor = 0.9, learning_rate = 0.9, epochs = 1000,):\n", + " q_values = np.zeros((environment_rows, environment_columns, 4))\n", + " \n", + " for episode in tqdm(range(epochs), desc=\"Training Progress\", unit=\"epochs\", ncols=100): # Adjust `ncols` to shorten the bar\n", + " row_index, column_index = get_starting_location()\n", + "\n", + " while not is_terminal_state(row_index, column_index):\n", + " # choose which action to take (i.e., where to move next)\n", + " action_index = get_next_action(row_index, column_index, epsilon)\n", + "\n", + " # perform the chosen action, and transition to the next state / next location\n", + " old_row_index, old_column_index = row_index, column_index # store the old row and column indexes\n", + " row_index, column_index = get_next_location(row_index, column_index, action_index)\n", + "\n", + " # receive the reward for moving to the new state, and calculate the temporal difference\n", + " reward = rewards[row_index, column_index]\n", + " old_q_value = q_values[old_row_index, old_column_index, action_index]\n", + " temporal_difference = reward + (discount_factor * np.max(q_values[row_index, column_index])) - old_q_value\n", + "\n", + " # update the Q-value for the previous state and action pair\n", + " new_q_value = old_q_value + (learning_rate * temporal_difference)\n", + " q_values[old_row_index, old_column_index, action_index] = new_q_value\n", + "\n", + " print('Training complete!')\n", + "\n", + " return q_values\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def q_learn_single(epsilon = 0.9, discount_factor = 0.9, learning_rate = 0.9, epochs = 1000):\n", + " # Initialize the Q-table with zeros for each state-action pair\n", + " # The shape is (environment_rows, environment_columns, 4) \n", + " # where 4 represents 4 possible actions (e.g., up, down, left, right)\n", + " q_values = np.zeros((environment_rows, environment_columns, 4))\n", + " \n", + " # Iterate through a number of episodes (i.e., learning cycles)\n", + " for episode in tqdm(range(epochs), desc=\"Training Progress\", unit=\"epochs\", ncols=100):\n", + " # Start each episode by selecting a random starting location in the environment\n", + " row_index, column_index = get_starting_location()\n", + "\n", + " # Continue taking actions until the agent reaches a terminal state\n", + " while not is_terminal_state(row_index, column_index):\n", + " # Choose the next action based on an epsilon-greedy policy\n", + " # This function should balance exploration (random) vs exploitation (best known action)\n", + " action_index = get_next_action(row_index, column_index, epsilon)\n", + "\n", + " # Save the old position before taking the action\n", + " old_row_index, old_column_index = row_index, column_index\n", + " \n", + " # Move to the new state based on the chosen action\n", + " row_index, column_index = get_next_location(row_index, column_index, action_index)\n", + "\n", + " # Get the reward for the new state the agent has moved to\n", + " reward = rewards[row_index, column_index]\n", + " \n", + " # Retrieve the Q-value of the old state-action pair\n", + " old_q_value = q_values[old_row_index, old_column_index, action_index]\n", + "\n", + " # Calculate the temporal difference: \n", + " # TD = Reward + Discount * (Max Q-value for the next state) - Old Q-value\n", + " temporal_difference = reward + (discount_factor * np.max(q_values[row_index, column_index])) - old_q_value\n", + "\n", + " # Update the Q-value for the previous state-action pair using the learning rate\n", + " new_q_value = old_q_value + (learning_rate * temporal_difference)\n", + " q_values[old_row_index, old_column_index, action_index] = new_q_value # Assign updated value\n", + "\n", + " # After all episodes, print a message indicating the training is complete\n", + " print('Training complete!')\n", + "\n", + " # Return the Q-values for further use (e.g., evaluation or exploitation phase)\n", + " return q_values\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# single episode\n", + "\n", + "def eposode(epsilon, discount_factor, learning_rate, epochs):\n", + " for episode in range(epochs):\n", + " row_index, column_index = get_starting_location()\n", + " \n", + " while not is_terminal_state(row_index, column_index):\n", + " # choose which action to take (i.e., where to move next)\n", + " action_index = get_next_action(row_index, column_index, epsilon)\n", + " \n", + " # perform the chosen action, and transition to the next state / next location\n", + " old_row_index, old_column_index = row_index, column_index # store the old row and column indexes\n", + " row_index, column_index = get_next_location(row_index, column_index, action_index)\n", + " \n", + " # receive the reward for moving to the new state, and calculate the temporal difference\n", + " reward = rewards[row_index, column_index]\n", + " old_q_value = q_values[old_row_index, old_column_index, action_index]\n", + " temporal_difference = reward + (discount_factor * np.max(q_values[row_index, column_index])) - old_q_value\n", + " \n", + " # update the Q-value for the previous state and action pair\n", + " new_q_value = old_q_value + (learning_rate * temporal_difference)\n", + " q_values[old_row_index, old_column_index, action_index] = new_q_value\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def q_learn_multi(epsilon=0.9, discount_factor=0.9, learning_rate=0.9, epochs=250, threads = 4):\n", + " \n", + " thread_array = []\n", + "\n", + " \n", + " for num in range(threads):\n", + " thread = threading.Thread(target=eposode, args=(epsilon, discount_factor, learning_rate, epochs))\n", + " thread_array.append(thread)\n", + " thread.start()\n", + "\n", + " for thread in thread_array:\n", + " thread.join()\n", + " print('Training complete!')\n", + "\n", + " return q_values\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Q-Learning Multi-threaded\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training complete!\n" + ] + } + ], + "source": [ + "q_values = np.zeros((environment_rows, environment_columns, 4))\n", + "\n", + "q_values = q_learn_multi(0.7, 0.6, 0.1, 500, 12)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAa0AAAGxCAYAAADRQunXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABxN0lEQVR4nO3deXhU5dn48e9kkkwWkgBZSEKWyTbDLrK8FBABURQQtVrrvtRKacWF8qu11iroW6Xq29YFl2JbxVpcqqKCK5VFNgWRRQFnErKQEAgJ2fdM5vz+GGbMZIEwmTknk9yf65oLzpk5cz9n8pxzP89zNp2iKApCCCGEHwjQugBCCCFEd0nSEkII4TckaQkhhPAbkrSEEEL4DUlaQggh/IYkLSGEEH5DkpYQQgi/IUlLCCGE35CkJYQQwm/0yaR16623YjQa3eY99thjvPfeex0+u2nTJnQ6HZs2bTrj927fvp1ly5ZRWVnZ4T2j0cill17qWYFV9sorr6DT6cjPz9e6KN2Wn5+PTqfjlVdecc3zh/V49tlnGTZsGAaDgbS0NB5++GFaWlrOuNyyZcvQ6XRdvt544w2vlbGz39YZv695/vnn3dbzbDQ3N/PLX/6ShIQE9Ho9Y8eO9WrZAD766COWLVvm9e/1hjfeeIOxY8cSEhJCYmIiixcvpra2ttvLFxQUcNttt5GYmIjBYGDo0KH8+Mc/PvuCKH3QLbfcoqSmprrNCw8PV2655ZYOn62qqlJ27NihVFVVnfF7n3zySQVQ8vLyOryXmpqqzJs3z8MSq+vll1/ucj16q8bGRmXHjh3KiRMnXPN6+3r88Y9/VHQ6nXL//fcrGzduVJ544gklODhYWbBgwRmXLSwsVHbs2NHhNWrUKCU0NFSpqKjwWjnz8vIUQHn55Zc7xO9rRo4cqUyfPt2jZZ966ikFUJ599lll+/btyv79+71bOEVRFi1apPTG3fJrr72mAMrtt9+ubNiwQXnxxReVqKgo5aKLLurW8t9++60SHR2tTJw4Ufn3v/+tbN68WXnjjTeUn/3sZ2ddlsCzT3N9S2RkJD/60Y80LUNDQwMhISF9smXrLQaDQfO/09k4efIkf/zjH1mwYAGPPfYYADNmzKClpYU//OEPLF68mBEjRnS5fFJSEklJSW7z8vPzOXDgADfccAMDBw70ZfE7jd/ffffdd4SGhnLnnXdqXZSz1tDQQGhoqEfLtra2cu+99zJ79mxeeuklAGbOnElERAQ33HADH3/8MXPmzOlyeUVRuOmmm0hOTmbLli0YDAbXe9dcc81Zl0e14UHncMP+/fu5+uqriYqKYvDgwSxZsgSbzYbFYuGSSy4hIiICo9HIE0884bZ8V0NB3Rne0+l01NXVsWrVKtfwyowZM7q9vLP89957LwBpaWmu72m/3CeffMK4ceMIDQ1l2LBh/POf/+x0PT777DNuu+02YmNjCQsLo6mpCYA333yTyZMnEx4ezoABA7j44ovZs2eP23d8/fXXXHvttRiNRkJDQzEajVx33XUUFBR0KPeXX37J1KlTXV36+++/v1vDU2fSnXLeeuutDBgwgAMHDjBr1izCw8OJjY3lzjvvpL6+3u2z//nPf5g0aRJRUVGEhYWRnp7Obbfd5nq/syGsrvzzn//knHPOISQkhMGDB/PjH/+YQ4cOdVq2nJwc5s6dy4ABA0hOTub//b//5/pb9MQnn3xCY2MjP/vZz9zm/+xnP0NRlE6Hqs/kn//8J4qicPvtt3tcruLiYn76058SERFBVFQU11xzDcePH+/wuc6GBzds2MCMGTOIjo4mNDSUlJQUrrrqKre/ZVNTE4888gjDhw8nJCSE6OhoZs6cyfbt212faWxs5P777yctLY3g4GCGDh3KokWLOgy763S6TofKjEYjt956q2vauU1t3LiRX/3qV8TExBAdHc2VV15JcXGx23IHDhxg8+bNru23/WGEruh0Ov7+97/T0NDgWtZZF5977jnOP/984uLiCA8PZ/To0TzxxBOdbmeffPIJs2bNctXz4cOHs3z5csBRJ5977jlXPOfLuc/r7u/mPFTx7rvvcu655xISEsLDDz/crfXszJdffsmxY8c61OWrr76aAQMGsGbNmtMu/8UXX7B3714WL17slrA8pfoxrZ/+9Kecc845vPPOOyxYsIC//vWv/PrXv+aKK65g3rx5rFmzhgsuuID77ruPd9991ysxd+zYQWhoKHPnzmXHjh3s2LGD559//qy+4/bbb+euu+4C4N1333V9z7hx41yf2bdvH//v//0/fv3rX/P+++8zZswYfv7zn/PFF190+L7bbruNoKAg/vWvf/H2228TFBTEY489xnXXXceIESN46623+Ne//kVNTQ3Tpk3j4MGDrmXz8/Mxm8089dRTfPrppzz++OMcO3aMiRMnUlZW5vrcwYMHmTVrFpWVlbzyyiu8+OKL7Nmzhz/+8Y8dyuPc8LuTFLpbToCWlhbmzp3LrFmzeO+997jzzjv529/+5tbC2rFjB9dccw3p6em88cYbfPjhhzz00EPYbLYzlqW95cuX8/Of/5yRI0fy7rvv8vTTT7N//34mT55MdnZ2h7JddtllzJo1i/fff5/bbruNv/71rzz++ONun2ttbcVms53xZbfbXct89913AIwePdrtuxISEoiJiXG93112u51XXnmFzMxMpk+fflbLOjU0NHDhhRfy2WefsXz5cv7zn/8QHx/frdZufn4+8+bNIzg4mH/+85988skn/OlPfyI8PJzm5mYAbDYbc+bM4X//93+59NJLWbNmDa+88gpTpkzhyJEjgKPVfcUVV/B///d/3HTTTXz44YcsWbKEVatWccEFF/SowXD77bcTFBTE6tWreeKJJ9i0aRM33nij6/01a9aQnp7Oueee69p+z7TDddqxYwdz584lNDTUtey8efMAOHz4MNdffz3/+te/WLduHT//+c958sknWbhwodt3/OMf/2Du3LnY7XZefPFF1q5dy913301RUREADz74ID/5yU9c8ZyvhISEs/7dvvnmG+69917uvvtuPvnkE6666iqgZ3V5zJgxbjGCgoIYNmzYGeuyc/8XERHB3LlzCQkJYcCAAVx66aV8//333fr93Zz1gKKHli5dqgDKn//8Z7f5Y8eOVQDl3Xffdc1raWlRYmNjlSuvvNI1r6vjFxs3blQAZePGja55Z3NMq7Plu3KmY1ohISFKQUGBa15DQ4MyePBgZeHChR3W4+abb3Zb/siRI0pgYKBy1113uc2vqalR4uPjlZ/+9Kddlstmsym1tbVKeHi48vTTT7vmX3PNNUpoaKhy/Phxt88OGzasw3qsWrVK0ev1yqpVq077G5xNOW+55RYFcCuToijKo48+qgDK1q1bFUVRlP/7v/9TAKWysrLLuJ0dd2lfJyoqKpTQ0FBl7ty5HcpsMBiU66+/vkPZ3nrrLbfPzp07VzGbzW7zUlNTFeCMr6VLl7qWWbBggWIwGDpdF5PJpMyePbvLde3Mxx9/rADK8uXLz2q5tl544QUFUN5//323+QsWLOjw2zq3V6e3335bAZS9e/d2+f2vvvqqAigvvfRSl5/55JNP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PMFf/vIXVqxYwa5du4iPj+eiiy7qnzcYV/qI//mf/1F++ctfus0bNmyY8rvf/U6jEqnvxIkTCqBs3rxZ66L4XE1NjZKVlaWsX79emT59unLPPfdoXSSfuu+++5TzzjtP62Kobt68ecptt93mNu/KK69UbrzxRo1K5HuAsmbNGte03W5X4uPjlT/96U+ueY2NjUpUVJTy4osvalBCbfWJnlZzczO7d+9m9uzZbvNnz57N9u3bNSqV+qqqqgAYPHiwxiXxvUWLFjFv3jwuvPBCrYuiig8++IAJEyZw9dVXExcXx7nnnstLL72kdbF87rzzzuPzzz/HarUCsG/fPrZu3crcuXM1Lpl68vLyOH78uNv+zWAwMH369H61f3PqdXd590RZWRmtra0MGTLEbf6QIUM4fvy4RqVSl6IoLFmyhPPOO49Ro0ZpXRyfeuONN/jmm2/YtWuX1kVRTW5uLi+88AJLlizh97//PTt37uTuu+/GYDBw8803a108n7nvvvuoqqpi2LBh6PV6WltbefTRR7nuuuu0LppqnPuwzvZvBQUFWhRJU30iaTm1f/6WoigePZPLH915553s37+frVu3al0UnyosLOSee+7hs88+IyQkROviqMZutzNhwgQee+wxAM4991wOHDjACy+80KeT1ptvvslrr73G6tWrGTlyJHv37mXx4sUkJiZyyy23aF08VfXn/VtbfSJpxcTEoNfrO/SqTpw40aF10hfdddddfPDBB3zxxRdeeRZZb7Z7925OnDjB+PHjXfNaW1v54osvWLFiBU1NTej1eg1L6BsJCQmMGDHCbd7w4cN55513NCqROu69915+97vfce211wIwevRoCgoKWL58eb9JWvHx8YCjx5WQkOCa31/2b+31iWNawcHBjB8/nvXr17vNX79+PVOmTNGoVL6nKAp33nkn7777Lhs2bCAtLU3rIvncrFmz+Pbbb9m7d6/rNWHCBG644Qb27t3bJxMWwNSpUztczmC1WklNTdWoROqor6/v8ORbvV7f5055P520tDTi4+Pd9m/Nzc1s3ry5T+/futIneloAS5Ys4aabbmLChAlMnjyZlStXcuTIEX75y19qXTSfWbRoEatXr+b9998nIiLC1dOMiooiNDRU49L5RkRERIdjduHh4URHR/fpY3m//vWvmTJlCo899hg//elP2blzJytXrmTlypVaF82n5s+fz6OPPkpKSgojR45kz549/OUvf+G2227TumheVVtbS05Ojms6Ly+PvXv3MnjwYFJSUli8eDGPPfYYWVlZZGVl8dhjjxEWFsb111+vYak1ou3Ji9713HPPKampqUpwcLAybty4Pn/qN9Dp6+WXX9a6aKrqD6e8K4qirF27Vhk1apRiMBiUYcOGKStXrtS6SD5XXV2t3HPPPUpKSooSEhKipKenKw888IDS1NSkddG8auPGjZ1uy7fccouiKI7T3pcuXarEx8crBoNBOf/885Vvv/1W20JrRJ6nJYQQwm/0iWNaQggh+gdJWkIIIfyGJC0hhBB+Q5KWEEIIvyFJSwghhN+QpCWEEMJvSNISQgjhNyRpCSGE8BuStIQQQvgNSVpCCCH8hiQtIYQQfuP/AwSOumi18DtBAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "graph(q_values, save=True, title=\"multi-thread: epsilon=0.7, discount_factor=0.6\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Q-Learning Single Threaded\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Training Progress: 100%|████████████████████████████████████| 1000/1000 [00:37<00:00, 26.43epochs/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training complete!\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "q_values = np.zeros((environment_rows, environment_columns, 4))\n", + "\n", + "q_values = q_learn_single(0.9, 0.7, 0.1, 1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbAAAAGxCAYAAAADEuOPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB0aUlEQVR4nO3dd3xUZdr4/8/MJJkUkhDSG+kz1IAUWUAELLCsYt191hXb6mN5xMKyxbar6K5i2fWHimXxeb6urgvqqqigqFkQRERBFAXEmYQUEgIppPdM5vz+mOSYSYEwmTmTIdf79ZoXnDbXfSb3Ode579N0iqIoCCGEED5G7+0CCCGEEK6QBCaEEMInSQITQgjhkySBCSGE8EmSwIQQQvgkSWBCCCF8kiQwIYQQPkkSmBBCCJ8kCUwIIYRP8noCu+6660hNTfV4nNTUVK677jq3fudzzz3HP/7xj17jt27dik6n480333RrPE+ZN28e8+bN83YxTsmKFSvQ6XRO44b6epSXl3PdddcRFRVFcHAwM2fOZPPmzQNe/l//+hdnnHEGgYGBREVFceWVV1JcXOz2cvb123pi+/G20tJSVqxYwd69e11a/ptvvmHu3LmEh4ej0+lYtWqVW8sH8Mgjj/DOO++4/XsHq6GhgWXLlpGQkEBgYCCTJ0/mtddeO6XvePfdd5k7dy5hYWGEhIQwfvx41qxZc0rf4fUE9qc//Yn169d7uxgu6S+BCc/77//+b3bu3OntYgxYa2sr5557Lps3b+app57i3XffJTY2lp/+9Kds27btpMs/88wzXHXVVUybNo13332Xxx57jK1btzJnzhyqq6s9Xv7169fzpz/9yeNxtFRaWsqDDz7ocgK7/vrrOXr0KK+99ho7d+7kiiuucG8BGboJ7LLLLuPll1/mgQceYNOmTUyfPp1f/epXrF27dkDLP/roo1x22WVMmDCBN954g/fee49bb72Vtra2UyuIMkykpKQo1157rVu/c/z48crcuXN7jf/kk08UQPn3v//t0vc2NjYOsmSnZu7cuX2uh68Zyuvx7LPPKoDy+eefq+Pa29uVcePGKWeeeeYJl21paVHCw8OVxYsXO43//PPPFUC599573VrWBx54QBkOu4bdu3crgPLSSy+5tLyfn5/yP//zP+4tVA8hISFu32+1tbUp7e3tLi///vvvK4Cydu1ap/Hnn3++kpCQoNhsthMu/9VXXyl6vV557LHHXC5DF4+2wCoqKrjppptITk7GaDQSHR3N7Nmz+c9//qPO01cXok6n47bbbuOf//wnY8eOJTg4mEmTJrFx48ZeMd59912ys7MxGo2kp6fz1FNP9dkF0pe6ujp+97vfkZaWRkBAAImJiSxbtozGxsaTLpuamsqBAwfYtm0bOp0OnU7Xaz3a29u57777SEhIICwsjPPOOw+LxeI0z7x585gwYQKffvops2bNIjg4mOuvv/6Uyvfss89y9tlnExMTQ0hICBMnTuTxxx+nvb3daT5FUXj88cdJSUkhMDCQKVOmsGnTppOu68kMtJxdf9e///3vmEwmjEYj48aN69X10NTUpH5fYGAgo0aNYtq0aaxbt06dZ6B/46qqKm699VYSExMJCAggPT2d++67j9bW1j7LNpA654r169djNpuZOXOmOs7Pz4+rrrqKXbt2ceTIkX6X3b9/P7W1tfzsZz9zGj9z5kxGjRrFW2+95XK53n//fSZPnozRaCQtLY2//vWvfc7XswvRbrfzl7/8BbPZTFBQECNHjiQ7O5unnnrKabkffviBX/3qV8TGxmI0Ghk9ejTXXHON0++/f/9+Lr74YiIiItTuqJdfftnpe/7xj3+g0+koLCx0Gt/VXb9161Z1XNc2tXv3bubMmUNwcDDp6ek8+uij2O12dbnp06cD8Otf/1rdhlesWHHS36yrLDabjeeff15dFhz7vFtvvZVx48YxYsQIYmJiOOecc9i+fXuv72ltbeWhhx5i7NixBAYGEhkZyfz58/n8888BR51sbGzk5ZdfVmN07yIfyO/W9fv885//5Le//S2JiYkYjUby8vJOup79Wb9+PSNGjOAXv/iF0/hf//rXlJaW8uWXX55w+dWrV2M0Grn99ttdLkMXv0F/wwlcffXVfP311zz88MOYTCZqamr4+uuvOX78+EmXff/999m9ezcPPfQQI0aM4PHHH+fSSy/FYrGQnp4OwIcffshll13G2Wefzeuvv47NZuOvf/0rZWVlJ/3+pqYm5s6dS0lJCffeey/Z2dkcOHCA+++/n3379vGf//znhDvI9evX8/Of/5zw8HCee+45AIxGo9M89957L7Nnz+Z///d/qaur46677mLx4sUcPHgQg8Ggznf06FGuuuoq/vCHP/DII4+g1+tPqXyH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2LDhlL5r5MiR/PznP+fIkSMsW7aMwsJCxo0bx9y5c/n3v//Nww8/3O/Bwrnnnsv69espLS11+g1eeeUVgoOD+clPfgKg1s/vvvvO6YKj995775TK2l1/PSKDodPpel1s891337Fz506n7r5Fixaxbt06/vGPf5ywG7G/fcxAf7cT6epCHIjuv/mll17Kiy++yFtvvcUvf/lLdfzLL79MQkKCWr/7c/nll/Pxxx+zadMmrrzySnX8Bx98gF6vV68OHZBTOmN2CmpqapQzzjhDeeKJJ5QNGzYoW7duVZ544gklMDDQ6URdfxdx9HVytOeJ5E2bNil6vV6ZN2+esn79euXNN99UZsyYoaSkpCg6ne6EyzY0NChnnHGGkpSUpPztb39TcnJylI8++kh58cUXlV/84hfKF198cdJ1vPbaaxWj0ai89tpryq5du5TvvvtOUZT+7wPrOmna/aTx3LlzlfHjx/f67oGW7+DBg0pAQIAyb9485YMPPlDefvtt5fzzz1eysrJ6nXz94x//qADKDTfcoHz44YfKiy++qCQmJipxcXEuX8RxKr8joCQnJyvjxo1T1q1bp7z33nvKT3/6UwVQXnvtNXW+M888U3nooYeUd955R9m2bZvywgsvKJGRkcrMmTPVeQZyEUdzc7OSnZ2thIaGKk8++aSSk5OjPPDAA4q/v3+vC2wGWudc1dLSoowfP15JTk5W/vWvfyk5OTnKpZdeqvj5+Slbt251mrev3/7NN99Unn76aSUnJ0fZsGGD8tvf/lbx8/NTbrnlll6xrr322j4veOjp448/VvR6vXLWWWep20/XhVE9f9u+LuK4++67lTfffFPZtm2b8sorryipqalKSkqK0tbWpiiKouzdu1cZMWKEkp6erqxZs0bZsmWLsm7dOuVXv/qVerHMDz/8oISGhiomk0l59dVXlQ8++EBZsmSJAiiPP/64Gs9msylms1kZPXq0snbtWmXTpk3KTTfdpKSlpfV5EUdf21TPfU1jY6MSFBSkzJ49W/nkk0+U3bt3K0eOHDnhb9ZdX3Xm/vvvV3Q6nXL//fcrmzdvVp577jklLi5OycjIcIrd3t6uzJ8/X/H391f+8Ic/KJs2bVLef/995f7771fWrVvntC4xMTHKe++9p+zevVv54YcfTul3G+w9qf05//zzlYiICPXveuONNyqA8uqrrzrN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Oeuss7xdDM1dcMEFyvXXX+807rLLLlOuuuoqL5XI8wBl/fr16rDdblfi4uKURx99VB3X0tKihIeHKy+88IIXSjh0nBYtsLa2Nvbs2cOCBQucxi9YsIDPP//cS6XSXm1tLQCjRo3yckk8b+nSpVxwwQWcd9553i6KJt577z2mTZvGL37xC2JiYjjjjDN48cUXvV0sjzvrrLPYvHkzVqsVgG+//ZbPPvuMn/3sZ14umXYKCgo4duyY0/7NaDQyd+7cYbV/68uQexq9KyorK+no6CA2NtZpfGxsLMeOHfNSqbSlKArLly/nrLPOYsKECd4ujke99tprfP311+zevdvbRdFMfn4+zz//PMuXL+fee+9l165d3HHHHRiNRq655hpvF89j7rrrLmpraxkzZgwGg4GOjg4efvhhfvWrX3m7aJrp2of1tX8rKiryRpGGjNMigXXp+f4wRVFceqeYL7rtttv47rvv+Oyzz7xdFI8qLi7mzjvv5OOPPyYwMNDbxdGM3W5n2rRpPPLIIwCcccYZHDhwgOeff/60TmCvv/46r776KmvXrmX8+PHs3buXZcuWkZCQwLXXXuvt4mlqOO/f+nNaJLCoqCgMBkOv1lZ5eXmvo5bT0e233857773Hp59+6pZ3qQ1le/bsoby8nKlTp6rjOjo6+PTTT1m9ejWtra0YDAYvltAz4uPjGTdunNO4sWPH8tZbb3mpRNr4/e9/z913380VV1wBwMSJEykqKmLlypXDJoHFxcUBjpZYfHy8On647N9O5LQ4BxYQEMDUqVPJyclxGp+Tk8OsWbO8VCrPUxSF2267jbfffpstW7aQlpbm7SJ53Lnnnsu+ffvYu3ev+pk2bRpLlixh7969p2XyApg9e3avWySsVispKSleKpE2mpqaer3R12AwnHaX0Z9IWloacXFxTvu3trY2tm3bdlrv3wbitGiBASxfvpyrr76aadOmMXPmTNasWcPhw4e55ZZbvF00j1m6dClr167l3XffJTQ0VG2BhoeHExQU5OXSeUZoaGivc3whISFERkae1uf+fvOb3zBr1iweeeQR/uu//otdu3axZs0a1qxZ4+2iedTixYt5+OGHGT16NOPHj+ebb77hySef5Prrr/d20dyqoaGBvLw8dbigoIC9e/cyatQoRo8ezbJly3jkkUfIysoiKyuLRx55hODgYK688kovlnoI8O5FkO717LPPKikpKUpAQIAyZcqU0/5ycqDPz0svveTtomlqOFxGryiKsmHDBmXChAmK0WhUxowZo6xZs8bbRfK4uro65c4771RGjx6tBAYGKunp6cp9992ntLa2ertobvXJJ5/0uS1fe+21iqI4LqV/4IEHlLi4OMVoNCpnn322sm/fPu8WegiQ94EJIYTwSafFOTAhhBDDjyQwIYQQPkkSmBBCCJ8kCUwIIYRPkgQmhBDCJ0kCE0II4ZMkgQkhhPBJksCEEEL4JElgQgghfJIkMCGEED5JEpgQQgif9P8DviKmJutpNx4AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "graph(q_values, save=True, title=\"single-thread: epsilon=0.9, discount_factor=0.6\")" + ] + } + ], + "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.10.14" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}