{ "cells": [ { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "with open('testinput', 'r') as file:\n", " content = file.readlines()\n", "\n", "map = [list(line.strip()) for line in content]\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def get_start_position(map):\n", " for i, row in enumerate(map):\n", " for dir in ['^', '<', '>', 'V']:\n", " if dir in row:\n", " return (i, row.index(dir), dir)\n", " return -1,-1,None\n", "\n", "\n", "start_x,start_y,start_dir = get_start_position(map)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Position: (6, 4) facing ^, sofar visited 0 cells.\n", "....#.....\n", ".........#\n", "..........\n", "..#.......\n", ".......#..\n", "..........\n", ".#..^.....\n", "........#.\n", "#.........\n", "......#...\n" ] } ], "source": [ "def count_visited(map):\n", " return sum(row.count('X') for row in map)\n", "\n", "def printmap(map):\n", " x, y, dir = get_start_position(map)\n", " if dir:\n", " print(f\"Position: ({x}, {y}) facing {dir}, sofar visited {count_visited(map)} cells.\")\n", " else:\n", " print(f\"No guard found! Visited {count_visited(map)} cells.\")\n", " for row in map:\n", " print(''.join(row))\n", "\n", "\n", "printmap(map)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(5, 4)" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def calculate_next_position(x, y, dir):\n", " if dir == '^':\n", " return (x - 1, y)\n", " elif dir == '<':\n", " return (x, y - 1)\n", " elif dir == '>':\n", " return (x, y + 1)\n", " elif dir == 'V':\n", " return (x + 1, y)\n", "\n", "def in_bounds(map, x, y):\n", " return 0 <= x < len(map) and 0 <= y < len(map[0])\n", "\n", "calculate_next_position(6, 4, '^')" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "def move(map,pos=None):\n", " # Get the current position of the robot\n", " if pos is None:\n", " x, y, dir = get_start_position(map)\n", " elif len(pos)==3:\n", " x,y, dir = pos\n", " else:\n", " raise ValueError()\n", " \n", " if in_bounds(map,x,y):\n", " map[x][y] = 'X'\n", "\n", " if dir:\n", " # Determine the next position based on the current direction\n", " new_x, new_y = calculate_next_position(x, y, dir)\n", " # while new position is obstacle turn right and recalculate the next position\n", " while in_bounds(map,new_x,new_y) and map[new_x][new_y] == '#':\n", " if dir == '^':\n", " dir = '>'\n", " elif dir == '<':\n", " dir = '^'\n", " elif dir == '>':\n", " dir = 'V'\n", " elif dir == 'V':\n", " dir = '<'\n", " new_x, new_y = calculate_next_position(x, y, dir)\n", " # Update the map with the robot's new position and direction\n", " if in_bounds(map,new_x,new_y):\n", " map[new_x][new_y] = dir\n", " return True, (new_x,new_y,dir)\n", " \n", " else:\n", " print(f\"No guard found! Visited {count_visited(map)} cells.\")\n", " return False, (-1,-1,dir)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Position: (5, 4) facing ^, sofar visited 1 cells.\n", "....#.....\n", ".........#\n", "..........\n", "..#.......\n", ".......#..\n", "....^.....\n", ".#..X.....\n", "........#.\n", "#.........\n", "......#...\n" ] }, { "data": { "text/plain": [ "(True, (5, 4, '^'))" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "result,pos = move(map)\n", "printmap(map)\n", "result,pos" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "No guard found! Visited 41 cells.\n", "....#.....\n", "....XXXXX#\n", "....X...X.\n", "..#.X...X.\n", "..XXXXX#X.\n", "..X.X.X.X.\n", ".#XXXXXXX.\n", ".XXXXXXX#.\n", "#XXXXXXX..\n", "......#X..\n" ] } ], "source": [ "print(result)\n", "while result:\n", " result,pos = move(map,pos)\n", "\n", "printmap(map)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No guard found! Visited 41 cells.\n", "....#.....\n", "....XXXXX#\n", "....X...X.\n", "..#.X...X.\n", "..XXXXX#X.\n", "..X.X.X.X.\n", ".#XXXXXXX.\n", ".XXXXXXX#.\n", "#XXXXXXX..\n", "......#X..\n", "Position: (6, 4) facing ^, sofar visited 0 cells.\n", "....#.....\n", ".........#\n", "..........\n", "..#.......\n", ".......#..\n", "..........\n", ".#..^.....\n", "........#.\n", "#.........\n", "......#...\n" ] } ], "source": [ "finished_map = map.copy()\n", "map = [list(line.strip()) for line in content]\n", "printmap(finished_map)\n", "printmap(map)\n", "\n" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{(3, 4), (4, 3), (5, 4), (4, 6), (8, 3), (8, 6), (1, 6), (2, 8), (7, 4), (6, 2), (7, 1), (7, 7), (6, 5), (6, 8), (4, 2), (4, 5), (5, 6), (4, 8), (8, 2), (9, 7), (8, 5), (2, 4), (1, 5), (1, 8), (7, 3), (6, 7), (7, 6), (5, 2), (4, 4), (3, 8), (8, 4), (5, 8), (8, 1), (8, 7), (1, 4), (1, 7), (7, 2), (6, 6), (7, 5), (6, 3)}\n" ] } ], "source": [ "#Prepare list of possible positions for a obstiacle to force guard into a loop and check if it causes a loop\n", "def get_visited_positions(map):\n", " visited = set()\n", " for i, row in enumerate(map):\n", " for j, cell in enumerate(row):\n", " if cell == 'X':\n", " if (i,j) != (start_x,start_y): #ignore the starting position\n", " visited.add((i,j))\n", " return visited\n", "\n", "print(get_visited_positions(finished_map))" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# create copy of map with additional obstacle at pos\n", "def add_obstacle(map, pos):\n", " # create a copy of the original map\n", " new_map = [row[:] for row in map]\n", " # add the obstacle to the new map\n", " new_map[pos[0]][pos[1]] = '#'\n", " return new_map\n", "\n", "def check_loop(emptymap,pos):\n", " #make copy of map\n", " map = add_obstacle(emptymap,pos)\n", " # create a set to keep track of visited positions and directions\n", " visited = set()\n", " # Get the current position of the robot\n", " x,y,dir = get_start_position(map)\n", " while dir and (x,y,dir) not in visited:\n", " visited.add((x,y,dir))\n", " # Determine the next position based on the current direction\n", " result,pos = move(map,(x,y,dir))\n", " x,y,dir = pos\n", " \n", " if dir and (x,y,dir) in visited:\n", " print(f\"Found loop with {len(visited)} with obstical at {pos}\")\n", " return True\n", " else:\n", " print(f\"No loop found after {len(visited)} visited positions.\")\n", " return False\n" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Found loop with 24 with obstical at (-1, -1, 'V')\n", "Loop detected at position (3, 4)\n", "Found loop with 29 with obstical at (-1, -1, 'V')\n", "Loop detected at position (4, 3)\n", "Found loop with 7 with obstical at (-1, -1, '>')\n", "Loop detected at position (5, 4)\n", "Found loop with 32 with obstical at (-1, -1, 'V')\n", "Loop detected at position (4, 6)\n", "Found loop with 34 with obstical at (6, 4, '^')\n", "Loop detected at position (8, 3)\n", "Found loop with 37 with obstical at (-1, -1, '<')\n", "Loop detected at position (8, 6)\n", "Found loop with 16 with obstical at (-1, -1, 'V')\n", "Loop detected at position (1, 6)\n", "Found loop with 19 with obstical at (-1, -1, '<')\n", "Loop detected at position (2, 8)\n", "Found loop with 42 with obstical at (-1, -1, 'V')\n", "Loop detected at position (7, 4)\n", "Found loop with 27 with obstical at (-1, -1, '^')\n", "Loop detected at position (6, 2)\n", "Found loop with 45 with obstical at (-1, -1, '>')\n", "Loop detected at position (7, 1)\n", "Found loop with 42 with obstical at (8, 6, 'V')\n", "Loop detected at position (7, 7)\n", "Found loop with 24 with obstical at (-1, -1, '^')\n", "Loop detected at position (6, 5)\n", "Found loop with 23 with obstical at (-1, -1, '<')\n", "Loop detected at position (6, 8)\n", "Found loop with 30 with obstical at (-1, -1, '>')\n", "Loop detected at position (4, 2)\n", "Found loop with 31 with obstical at (-1, -1, 'V')\n", "Loop detected at position (4, 5)\n", "Found loop with 34 with obstical at (-1, -1, '<')\n", "Loop detected at position (5, 6)\n", "Found loop with 21 with obstical at (-1, -1, '^')\n", "Loop detected at position (4, 8)\n", "Found loop with 43 with obstical at (-1, -1, '^')\n", "Loop detected at position (8, 2)\n", "Found loop with 45 with obstical at (8, 5, '<')\n", "Loop detected at position (9, 7)\n", "Found loop with 40 with obstical at (-1, -1, '^')\n", "Loop detected at position (8, 5)\n", "Found loop with 10 with obstical at (-1, -1, '>')\n", "Loop detected at position (2, 4)\n", "Found loop with 15 with obstical at (-1, -1, 'V')\n", "Loop detected at position (1, 5)\n", "Found loop with 19 with obstical at (-1, -1, '^')\n", "Loop detected at position (1, 8)\n", "Found loop with 41 with obstical at (-1, -1, 'V')\n", "Loop detected at position (7, 3)\n", "Found loop with 22 with obstical at (-1, -1, '^')\n", "Loop detected at position (6, 7)\n", "Found loop with 29 with obstical at (6, 5, '<')\n", "Loop detected at position (7, 6)\n", "Found loop with 29 with obstical at (-1, -1, '>')\n", "Loop detected at position (5, 2)\n", "Found loop with 8 with obstical at (-1, -1, '>')\n", "Loop detected at position (4, 4)\n", "Found loop with 20 with obstical at (-1, -1, '<')\n", "Loop detected at position (3, 8)\n", "Found loop with 41 with obstical at (-1, -1, '^')\n", "Loop detected at position (8, 4)\n", "Found loop with 18 with obstical at (-1, -1, '^')\n", "Loop detected at position (5, 8)\n", "Found loop with 37 with obstical at (5, 2, '^')\n", "Loop detected at position (8, 1)\n", "Found loop with 51 with obstical at (-1, -1, '<')\n", "Loop detected at position (8, 7)\n", "Found loop with 11 with obstical at (-1, -1, '>')\n", "Loop detected at position (1, 4)\n", "Found loop with 30 with obstical at (-1, -1, 'V')\n", "Loop detected at position (1, 7)\n", "Found loop with 40 with obstical at (-1, -1, 'V')\n", "Loop detected at position (7, 2)\n", "Found loop with 20 with obstical at (-1, -1, '>')\n", "Loop detected at position (6, 6)\n", "Found loop with 43 with obstical at (-1, -1, 'V')\n", "Loop detected at position (7, 5)\n", "Found loop with 19 with obstical at (5, 4, '^')\n", "Loop detected at position (6, 3)\n", "Possible loops: 40\n" ] } ], "source": [ "\n", "#Check if a loop is formed by placing an obstacle in one of the visited positions\n", "possibles_loops = []\n", "for pos in get_visited_positions(finished_map):\n", " if check_loop(map,pos):\n", " print(f\"Loop detected at position {pos}\")\n", " possibles_loops.append(pos)\n", "\n", "print(f\"Possible loops: {len(possibles_loops)}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "printmap(map)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" } }, "nbformat": 4, "nbformat_minor": 2 }