General Discussions

Discuss any topics that are not product-specific here.

Advent of Code 2023 Day 22 (BaseA Style)

AlteryxCommunityTeam
Alteryx Community Team
Alteryx Community Team

Discussion thread for day 22 of the Advent of Code - https://adventofcode.com/2023/day/22

9 REPLIES 9
AndrewDMerrill
13 - Pulsar

D22 was a fun one! It was nice to have a pure workflow again (no pen/paper for today). Debugging took me longer than I thought and it seemed like I kept building the same workflow over and over again, thinking it would work until I finished it for the n-th time and remembered why that method wouldn't actually work. I did end up getting things working, and watching my workflow run brought me great satisfaction today for some reason!

 

Algorithm:

Spoiler
Part 1 is fairly easy. I built an iterative macro to handle the bricks falling from the sky and landing on the ground. This macro works by moving every brick down one space on the z-axis, then looking to see if there were any intersections between the new state and previous state (implying a brick was already on top of another one). For those without intersection, make sure the brick didn't cross the ground plane (z > 0), then save the 1 unit drop, and begin the next iteration. Here is a sample for how this approach would work in 2D:
122 
  33
 533
45  


^The bottom row of our table represents the ground plane (So bricks 4 & 5 aren't going anywhere). On the first iteration, the brown bold-ed bricks are able to drop down 1 unit into free space (Notice that even though brick 2 is in the air and could technically move down too, we won't move it yet because it sits on top of brick 3):

 22 
1   
 533
4533

^Now brick 2 is also in impeded from below, so on the second iteration, bricks 1 & 2 will move down, but brick 3 is now on the ground plane and won't move any more.
    
 22 
1533
4533


^After the third iteration, all blocks are now unable to drop any further and the macro would be complete.

We can now solve part one, by adding the count of all blocks with nothing above them (bricks 1 & 2) with the count of all bricks with bricks above them, which all have multiple bricks below them (bricks 3 & 5;
   Example 1)  brick 3 has brick 2 above it, and brick 2 has both bricks 3 & 5 below, so brick 3 is valid. The same argument applies to brick 5.
   Example 2) brick 4 has brick 1 above it, but brick 1 only has brick 4 below, so brick 4 is invalid).


For part 2, we now want to solve essentially the opposite problem, we want to know how many bricks will fall if we disintegrate any given brick. This is trickier than it may seem at first because even though removing a brick may cause nothing to happen, deleting the brick underneath it may cause everything to fall.

The first thing that I did to approach this problem was to create a Connection List, that stores all the bricks connected above a given brick:
44 
 35
112

^The Connection List for the case above would look like this:
ID_BID_T
13
14
25
34
4 
5 
*[ID_B] is the brick ID for the brick that is underneath the brick with ID, [ID_T]

^We can see from our connection list that bricks 4 & 5 have nothing above them (since [ID_T] is null). We can also see that brick 1 is underneath and connected to bricks 3 & 4, but not to brick 5, which is only connected to brick 2.

Using this Connection List and the Validity List (from part 1, which contains only information on direct neighbors), We can do a double join to determine all necessary bricks, B_i that need to be above a brick, A, so that brick C, which is also above brick A will fall when brick A is disintegrated. That is a tremendous mouthful and may be confusing so here is a diagram that should hopefully help clear things up a little. Join 1 looks like:
_Join 1.png
^Looking at the above image (see shaded cells) we now know that in order for brick 4 to fall if brick 1 is disintegrated, then brick 1 needs have brick 3 on top of it, which we know it does, but in order to verify, we can do our second join (using the Join Multiple Tool in order to get a full outer join) between Output Join 1 List and the Connection List. Joining on [R-ID_B] = [ID_T] and [L-ID_B] = [ID_B], respectively. If no match is found (i.e. Null), then the brick being considered, [L-ID_T], will not fall when the corresponding brick underneath it, [L-ID_B], is removed.

The only issue to account for with this method is that if a match is not found, there may still be matches found for every brick above that one, which will cause an error in counting since the bricks above cannot fall unless the brick below does too. This can be handled by propagating nulls up the brick chain with another iterative macro.

With the nulls properly accounted for we can now filter out any bricks, [L-ID_T], that have any nulls associated with them and count the remaining rows to achieve the answer!

Note: Some ambiguities are intentionally left unexplained to provide a challenge to the reader to solve as much as possible on his own.

 Workflow:

Spoiler
Main:
_Main.png

Drop Bricks (Iterative Macro):
_Drop Bricks.png
Build Connection List (Iterative Macro):
_Get Above.png
Null Propagation (Iterative Macro):
_Nullify.png

Happy Solving!!!

 

gawa
15 - Aurora
15 - Aurora

My solution. Part1:10sec, Part2: 2min

phottovy
13 - Pulsar
13 - Pulsar

Fun challenge...

Spoiler

...except I was too brain dead to optimize P2. Fortunately, brute force did work with a batch/iterative macro combo that ran in 18 minutes:

P1 - I think I had a similar logic to @AndrewDMerrill. The hardest part was figuring out how to organize the x,y,z grid for my macro to work correctly. My initial attempt left a lot of floating bricks with the full dataset:
22_P1.png

Settle Macro (every other macro is some variation of this one or uses this one inside a batch macro):
22_P1_Settle_Macro.png

P2 - Brute force all the way. I used a second workflow to pass the results from P1 since I didn't know how long it would take to run:
22_P2.png



 

AkimasaKajitani
17 - Castor
17 - Castor

I learned the viz is very important for debugging again. Because I fell the blocks under the floor or insert a block into another block... At this quiz, the sample data did not work well.

 

Spoiler

The macro to fall the blocks for 3D TETRIS.
AoC_2023_22_02.png

In this macro, I got the position where the blocks is. In other words, these are the coordinates where the blocks touch each other.

And also, I need to consider the pattern below.

 66
445
233
11​


If I disintegrate the block 3, the block 5 will fall down. So I can NOT break the block 3. It took too long time to find this pattern.


The workflow fot part 1:
AoC_2023_22_06.png

Part 2 : 
I can use the result of part 1 macro. I created a list that the block supports another one independently, so I look at the parent-child relationship one block at a time based on this list. In order to view each block one by one, iterative macro is called in a batch macro, and the iterative macro itself counts the number of bricks that fall when one brick is destroyed, and the batch macro runs the entire process.

Batch macro :

AoC_2023_22_08.png

Iterative macro : 

AoC_2023_22_09.png





Final workflow :

AoC_2023_22_11.png



 

Pang_Hee_Choy
12 - Quasar

p2: ~2hours.

 

Tokimatsu
12 - Quasar

Tetris was nostalgic for me because I used to make it with Z80 assembly. Part2 was solved with the relationship between the supporting blocks.

Spoiler
スクリーンショット 2024-01-07 185436.png

Carolyn
8 - Asteroid

@gawa - Thanks for sharing your solution. I've been stuck on Part 2 for WEEKS and it's been killing me. I like how you were using the Append Tool since I was doing all sorts of weirdness and extra tools and stuff whereas a simple Append would have made my life easier. I FINALLY got my 2nd star :) 

gawa
15 - Aurora
15 - Aurora

@Carolyn Happy to hear that my WF helped you somewhat! Count&Append is a nice combo to judge break from loop. 

It seems now you have 4 puzzles to go...good luck!

Carolyn
8 - Asteroid

@gawa Thank you!! Yeah, closing it on it. I'm super stuck on D20, but hopefully when I revisit it, I'll have a flash of insight :) 

Labels