{
 "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
}
