Skip to content

Commit

Permalink
Day 5, part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
Hamatti committed Dec 6, 2023
1 parent c93e086 commit c1f9aba
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 6 deletions.
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ _If you're on mobile, you can find these in HTML form at [hamatti.org/adventofco
- [Day 2](src/day_2.ipynb): ⭐️⭐️
- [Day 3](src/day_3.ipynb): ⭐️⭐️
- [Day 4](src/day_4.ipynb): ⭐️⭐️
- [Day 5](src/day_5.ipynb): ⭐️
- [Day 5](src/day_5.ipynb): ⭐️⭐️
- [Day 6](src/day_6.ipynb): ⭐️⭐️


Expand Down
177 changes: 172 additions & 5 deletions src/day_5.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 1,
"id": "c4aa8da3",
"metadata": {},
"outputs": [],
Expand All @@ -136,8 +136,10 @@
"\n",
"Range = namedtuple('Range', ['dest_start', 'source_start', 'range'])\n",
"\n",
"prod = '../inputs/day_5.txt'\n",
"test = '../inputs/day_5_example.txt'\n",
"\n",
"with open('../inputs/day_5.txt') as raw_input:\n",
"with open(prod) as raw_input:\n",
" data_sections = raw_input.read().split('\\n\\n')\n",
" \n",
"seeds = [int(seed) for seed in re.findall(r'\\d+', data_sections[0].split(': ')[1])]\n",
Expand Down Expand Up @@ -179,7 +181,7 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 2,
"id": "b642450b",
"metadata": {},
"outputs": [],
Expand All @@ -206,7 +208,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 3,
"id": "05cd43e6",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -236,7 +238,172 @@
"source": [
"## Part 2\n",
"\n",
"I've been under the weather today and the second part turned out to be the first where naive looping didn't work anymore, so I decided to leave it for today and focus on sleeping."
">Everyone will starve if you only plant such a small number of seeds. Re-reading the almanac, it looks like the seeds: line actually describes ranges of seed numbers.\n",
">\n",
">The values on the initial seeds: line come in pairs. Within each pair, the first value is the start of the range and the second value is the length of the range. So, in the first line of the example above:\n",
">```\n",
"seeds: 79 14 55 13\n",
">```\n",
">This line describes two ranges of seed numbers to be planted in the garden. The first range starts with seed number 79 and contains 14 values: 79, 80, ..., 91, 92. The second range starts with seed number 55 and contains 13 values: 55, 56, ..., 66, 67.\n",
">\n",
">Now, rather than considering four seed numbers, you need to consider a total of 27 seed numbers.\n",
">\n",
">In the above example, the lowest location number can be obtained from seed number 82, which corresponds to soil 84, fertilizer 84, water 84, light 77, temperature 45, humidity 46, and location 46. So, the lowest location number is 46.\n",
">\n",
">Consider all of the initial seed numbers listed in the ranges on the first line of the almanac. What is the lowest location number that corresponds to any of the initial seed numbers?"
]
},
{
"cell_type": "markdown",
"id": "92df53f7",
"metadata": {},
"source": [
"Oof, this one was tough. I spent an entire Wednesday evening trying to figure this out. I got tipped by a friend to start from the locations and find a matching seed.\n",
"\n",
"I wrote a `map_to_prev` function that is the inverse of the previous `map_to_value` and finds the corresponding value in the earlier section of input."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "545a327f",
"metadata": {},
"outputs": [],
"source": [
"def map_to_prev(dest, r):\n",
" low = r.dest_start\n",
" high = r.dest_start + r.range - 1\n",
" # Any source numbers that aren't mapped\n",
" # correspond to the same destination number.\n",
" if dest < low or dest > high:\n",
" return -1\n",
" else:\n",
" return dest - r.dest_start + r.source_start"
]
},
{
"cell_type": "markdown",
"id": "732a531f",
"metadata": {},
"source": [
"Then I did a similar thing with `find_seed` which is the inverse of `find_location`. I give it starting location and ranges in reverse order and find a match for each step. If one is not found, keep the value as it is."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "23d005ce",
"metadata": {},
"outputs": [],
"source": [
"def find_seed(location, ranges):\n",
" src = location\n",
" for _range in ranges:\n",
" for individual_range in _range:\n",
" potential = map_to_prev(src, individual_range)\n",
" if potential != -1:\n",
" src = potential\n",
" break\n",
" return src"
]
},
{
"cell_type": "markdown",
"id": "f1d2b570",
"metadata": {},
"source": [
"I needed a way to check if any given number exists as a seed. Since expanding all those ranges would take too much space and looping them processor cycles, I only deal with the lower end and the length.\n",
"\n",
"To check if a seed is in any given seed range, I check if it's within its range."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "b37db800",
"metadata": {},
"outputs": [],
"source": [
"def get_all_seed_ranges(data_sections):\n",
" seed_range_input = [int(seed) for seed in re.findall(r'\\d+', data_sections[0].split(': ')[1])]\n",
" return zip(seed_range_input[::2], seed_range_input[1::2])\n",
"\n",
"def is_valid_seed(seed, seed_ranges):\n",
" for seed_range in seed_ranges:\n",
" if seed_range[0] < seed < seed_range[0] + seed_range[1]:\n",
" return True\n",
" return False"
]
},
{
"cell_type": "markdown",
"id": "1cfcd9d1",
"metadata": {},
"source": [
"To get the locations, I find the highest value in any given range and create a range from 1 to that value."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "67a8b470",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"location_ranges = ranges[-1]\n",
"\n",
"# max() would work here without the lambda\n",
"# because the first value in the Range happens\n",
"# to be what we want.\n",
"#\n",
"# I wanted to make it explicit though\n",
"highest_location_range = max(location_ranges, key=lambda range: range.dest_start)\n",
"max_location = highest_location_range.dest_start + highest_location_range.range\n",
"all_locations = range(1, max_location)\n",
"\n",
"seed_ranges = list(get_all_seed_ranges(data_sections))"
]
},
{
"cell_type": "markdown",
"id": "262b472a",
"metadata": {},
"source": [
"To find the smallest location that maps to a seed, I start from 1 and loop until I find a matching seed and then stop.\n",
"\n",
"This takes roughly 40 seconds to run so there's probably a neater way to solve it in way less time. But it doesn't crash my computer and doesn't take more a minute to run so I'm okay with this."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d27667e5",
"metadata": {},
"outputs": [],
"source": [
"part_2 = None\n",
"\n",
"for location in all_locations:\n",
" # `ranges[-1::-1]` skips the locations range and reverses list\n",
" seed = find_seed(location, ranges[-1::-1])\n",
" if is_valid_seed(seed, seed_ranges):\n",
" print(f'Found {seed=} at {location=}')\n",
" part_2 = location\n",
" break\n",
"print(f'Solution: {part_2}')\n",
"assert part_2 == 2008785"
]
},
{
"cell_type": "markdown",
"id": "bcbefb20",
"metadata": {},
"source": [
"## Two stars!\n",
"\n",
"Finally, managed to wr"
]
}
],
Expand Down

0 comments on commit c1f9aba

Please sign in to comment.