Skip to content

Commit

Permalink
Refactor d12
Browse files Browse the repository at this point in the history
  • Loading branch information
derailed-dash committed Jan 14, 2024
1 parent a61baac commit d47846d
Showing 1 changed file with 60 additions and 33 deletions.
93 changes: 60 additions & 33 deletions src/AoC_2023/Dazbo's_Advent_of_Code_2023.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4770,7 +4770,6 @@
"\n",
"# SETUP LOGGING\n",
"logger.setLevel(logging.INFO)\n",
"# td.setup_file_logging(logger, locations.output_dir)\n",
"\n",
"# Retrieve input and store in local file\n",
"try:\n",
Expand Down Expand Up @@ -4812,6 +4811,27 @@
"\n",
"We need to find all substitutions for `?` that result in valid records. For each `?` we can substitute either `.` or `#`. We need to substitute for every `?` in the record, and the substitutions are only valid if they match the constraints given by the counts of damanaged springs. \n",
"\n",
"For example, this record:\n",
"\n",
"```text\n",
"?###???????? 3,2,1\n",
"```\n",
"\n",
"Has 10 possible arrangements:\n",
"\n",
"```text\n",
".###.##.#...\n",
".###.##..#..\n",
".###.##...#.\n",
".###.##....#\n",
".###..##.#..\n",
".###..##..#.\n",
".###..##...#\n",
".###...##.#.\n",
".###...##..#\n",
".###....##.#\n",
"```\n",
"\n",
"I start by parsing each row. For each row, I create a `SpringRecord` [dataclass](https://aoc.just2good.co.uk/python/classes#dataclass). In this class:\n",
"\n",
"- The `damaged_groups` tuple defines the required lengths of contiguous blocks of `#`.\n",
Expand Down Expand Up @@ -4860,7 +4880,7 @@
" \n",
" @cache # to cache, our SpringRecord must be immutable - hence frozen\n",
" def get_arrangements_count(self, char_idx: int, curr_group_idx: int, curr_group_len: int):\n",
" \"\"\" Determine how many arrangements are possible by recursing.\n",
" \"\"\" Determine how many arrangements are possible by recursion.\n",
" Move through the record one char at a time. \n",
" Each recursive call depth adds one to the char_idx.\n",
" Whenever we reach the end of the record, check if it has completed all the groups.\n",
Expand All @@ -4875,43 +4895,38 @@
" Returns:\n",
" int: count of arrangements\n",
" \"\"\"\n",
" count = 0\n",
" \n",
" # base case - check if we have a complete and valid arrangement\n",
" if char_idx == len(self.record): # if we're at the end; all chars have been processed\n",
" if char_idx == len(self.record): # if we're at the end, so all chars have been processed\n",
" if curr_group_idx == len(self.damaged_groups) and curr_group_len == 0:\n",
" # we're past the last group\n",
" # we've reached the end and we've processed the required number of damanged groups\n",
" return 1 # valid arrangement\n",
" elif curr_group_idx == len(self.damaged_groups) - 1 and self.damaged_groups[curr_group_idx] == curr_group_len:\n",
" # we're on the last char of the last group, and the group is complete\n",
" return 1 # valid arrangement\n",
" else: # we have not completed all groups, or current group length is too long\n",
" return 0 # invalid\n",
"\n",
" # if we're here, we haven't reached the end of the record, so we need to recurse\n",
" # Whether the current char is any of [?.#], we still need to determine if a group has ended or started, and recurse to next char\n",
" count = 0\n",
" if self.record[char_idx] in \".?\": # Recurse with a .\n",
" # We need to be either extending the operational section or adding a . after a damaged group\n",
" if curr_group_len == 0: # we're not in a damaged group, so we must be extending\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx, 0)\n",
" elif (curr_group_idx < len(self.damaged_groups) and \n",
" curr_group_len == self.damaged_groups[curr_group_idx]):\n",
" # we're adding a . after a #, so the group is now complete; move on to next group\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx+1, 0)\n",
" else: # Invalid configuration, which is one of:\n",
" # Either: curr_group_len is too long\n",
" # Or: we're adding . before completing a group\n",
" pass\n",
" \n",
" # Process from left to right\n",
" # If we're not at the end, process the current char in the record by recursion\n",
" # Determine valid states for recursion by trying . and # at the current char idx\n",
" for char in [\".\", \"#\"]:\n",
" # We can subst char for itself (no change), or for ?\n",
" if self.record[char_idx] in (char, \"?\"): \n",
" if char == \".\": \n",
" # we're either extending the operational section or ending the damaged group\n",
" if curr_group_len == 0: # we're not in a group, so we must be extending\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx, 0)\n",
" elif (curr_group_idx < len(self.damaged_groups) and \n",
" curr_group_len == self.damaged_groups[curr_group_idx]):\n",
" # we're adding a . after a #, so the group is now complete; move on to next group\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx+1, 0)\n",
" else:\n",
" # either: curr_group_len is too long\n",
" # or: we're adding . before completing a group\n",
" # Both are invalid \n",
" pass\n",
" else: # we're adding a #; extend the current group (which might be empty at this point)\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx, curr_group_len+1)\n",
" if self.record[char_idx] in \"#?\": # Recurse with a #\n",
" # we're adding a # so extend the current damaged group, or start a new damaged group\n",
" count += self.get_arrangements_count(char_idx+1, curr_group_idx, curr_group_len+1)\n",
" \n",
" return count \n",
" "
" return count \n"
]
},
{
Expand Down Expand Up @@ -4999,7 +5014,19 @@
"source": [
"### Day 12 Part 2\n",
"\n",
"Now we have to replace each record string with a record string that is five copies of the original, separated by `?`. And similarly, we have to replace the damaged springs groups with a new list of groups, that is five copies of the original.\n",
"Now we have to replace each record string with a record string that is five copies of the original, separated by `?`. So this:\n",
"\n",
"```text\n",
".# 1\n",
"```\n",
"\n",
"Becomes\n",
"\n",
"```text\n",
".#?.#?.#?.#?.# 1,1,1,1,1\n",
"```\n",
"\n",
"And similarly, we have to replace the damaged springs groups with a new list of groups, that is five copies of the original.\n",
"\n",
"**My solution:**\n",
"\n",
Expand All @@ -5010,7 +5037,7 @@
"\n",
"- Then, we can run the same code as for Part 1, but with a couple of tweaks.\n",
"\n",
" - I've added @cache to the `get_arrangements_count()` method. This caches any results for any states that we have seen before. And this means that for every recursion, we will have cached all the states that led to this recursion. Because our state is minimal (there are far fewer combinations of [char position, group number, group length] than there are of [record combinations]), this means we can effectively cache each state with each recursed arrangement count.\n",
" - I've added `@cache` to the `get_arrangements_count()` method. This caches any results for any states that we have seen before. And this means that for every recursion, we will have cached all the states that led to this recursion. This approach is called _memoization_. This works because our state is minimal (there are far fewer combinations of [char position, group number, group length] than there are of [record combinations]), and this means we can effectively cache each state with each recursed arrangement count.\n",
" - To make this work, I have to ensure that my _state_ is _hashable_. I.e. this means that all the parameters I pass to the `get_arrangements_count()` method must themselves be hashable. In my original Part 1, my `SpringRecord` class was not hashable, because my class was mutable. I fixed this by 1) changing my `damaged_groups` from a `list` to a `tuple`, and 2) by adding `(frozen=True)` to my `@dataclass` decorator. This is sufficient to make my `SpringRecord` hashable."
]
},
Expand Down Expand Up @@ -10320,9 +10347,9 @@
"toc_visible": true
},
"kernelspec": {
"display_name": "aca_aoc",
"display_name": "ana-aoc",
"language": "python",
"name": "aca_aoc"
"name": "python3"
},
"language_info": {
"codemirror_mode": {
Expand Down

0 comments on commit d47846d

Please sign in to comment.