pattern matching, Sets, seven segment display - Advent of Code 2021 - Day 8 with Ruby

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up welcome back this is day eight of the advent of code this one is is uh has been the most challenging for me uh i've never worked with one of these seven segment search or the seven segment displays before and so i wanted to kind of just go through that i want to show um the solution that i came up with and then i after i did this i also want to talk a little bit about deliberate practice because i think when you're watching youtube videos when you're learning how to code and when you're trying to just get inspired it can be really tricky to know how to get better and so yeah i wanted to just cover a little bit about deliberate practice meaning like you go out and you intentionally try to get better and what brought that to mind was that when i went through and solved this i didn't want to look at anyone else's solutions i wanted to like really figure it out for myself and then after i after i solved it i went and i was like you know what that was actually pretty tricky i've never done this before are there better ways to do this and so i went and looked at some other solutions in rust and in elixir and there are some really cool ways to do this with pattern matching so what i want to do is kind of take this one a little bit more slowly show you how i'm thinking about it and then also talk about how and then try to solve it with ruby's pattern matching which is this new feature newer feature in ruby but i think it's a really clever way to do it and so let's jump in and talk about these seven segment displays so a seven segment display is one of these things where kind of you have each of these seven segments that allows you to make up a number and so for instance on the far left here if we wanted to create the number zero right we might um if you kind of like squint your eyes a little bit you can see a zero there and then the next one would be a one and so for a zero everything is filled in except that middle segment right and so a zero in particular has one two three four five six different segments and so the way that we are going to receive the input for this project or this problem is going to be sort of like letters so maybe this one is like f g a c b um d uh e or something like this right and so we're gonna have to like decode the incoming letters and figure out like okay that's always gonna be b then like the middle one will always be b uh but the the order in which the letters come in is going to be different every time all that we're going to get is the um 10 different input values that are sort of randomly ordered and we have to figure out what the numbers are that represent or that those that those uh or like the segments that those letters are related to so we have zero one two um two looks like this okay so here is all of our numbers zero one two three four five six seven eight nine and what uh what i wanted to do is like let's switch over to a different color here and start talking about this so there's a couple of unique things that happen as part of this first or as part one part one wants us to just return the number of ones fours sevens and eights and the reason is that these each of these numbers have a unique number of segments so there's no other number that has only two segments so one has just these two segments and a seven has three segments no other number has three segments four has four segments no other number has four segments and eight of course has all seven segments so the first part of the project is to just return the number of ones fours sevens and eights we're gonna add day eight day eight and we're gonna have uh segment or display or something dot rb and then we'll have class display and i guess like uh day eight spec that rb and we're gonna require relative um day eight display dot rb and we're gonna r spec dot describe display and this is going to say something like it returns the correct number of ones four sevens and eights and the input again is going to be this like random strings or like these these numbers here so the input is going to be something like display.parse this thing and this is uh so this is kind of like what it what it's coming in as so the first part here you can notice that there is a pipe there's the pipe operator that separates the sort of encoded side from the the answer side so we have two parts here we have the um we have all of the numbers and their representation based on whatever was mixed up and then on the right side we have the value that we're supposed to output and so what this should give us back is a new display and that displays numbers are going to correlate with this thing and then like we need to figure out how many of the numbers so expect display dot um simple numbers do equal and then some number of numbers so these are all um none of these are one uh one four seven or eight because they're all five digit there are they're all like made up of five different segments so each of the five segment pieces if we look back here at our numbers one two three four five so a two uh one two three four five threes fives so a two three and a five are all five segment pieces so none of those answers are going to work for our test so what we can do instead is we can add in a couple that are like a b a b and then a e a f b that should be a four and then we want like d a b that should be a seven and then a b c d e f or let's see what is the the eight here looks like this so we're gonna add in an eight and so we'll expect that there is one two three four five simple numbers and none of this is going to work yet because we have to go implement parts of the display self.parse this is going to be our input and we're going to say num numbers i guess like this is i don't know what how do we wanna um i guess like what is the yeah so this the patterns i guess patterns and then the numbers are input dot split on pipe and then after we split on pipe we want to map or yeah we want to map those and split again um on space and then we want to map those two integers to i and i think that should work fine we want to return a new display of patterns and numbers i guess like we want to test out that that this parse thing works too so it parses as expected uh and we'll just say display is display.parse um actually we'll just do the the same exact thing here and we'll expect that display dot um patterns oh actually we can't map these to numbers these are not numbers oh gosh they're uh they're strings um two equal whatever we'll just put them we'll put them in here so okay so this is gonna look like this um okay so like basically all of the different patterns are going to be the pattern portion of this next we need to do the same thing for our numbers all right so this should work as expected and wrong number of arguments uh oh right because we didn't actually want to map to i here uh still wrong number of arguments got the input oh right okay so we need to put an initialize method here patterns and numbers and patterns is patterns and numbers is numbers and then we'll add some getter methods for those okay and let's see wrong number of arguments given two expected one on day spec given two expected one oh um this is uh expect the patterns to be that and then expect display dot numbers to equal this okay all right now we're crashing and burning because that doesn't have a closing paren i misspelled numbers somewhere okay so that's passing parse works as expected we expect that simple numbers is going to equal 5. let's come back over here and we'll say simple numbers is numbers.count where the length is in like one or two three four or seven okay that's passing and i think this is actually the first part of the test so focus on the easy digits consider this large input how many digits are one four seven or 8 and so we go through here and we have to count up which ones are 1 4 7 or 8. so we're going to get our puzzle input this is our puzzle input it's pretty massive so we're going to add that here input and um okay so then we want to come back over to our display and add our little file thing at the bottom that says if we're running this same file then we want to read all of the lines and those are going to create different displays so file dot read lines of argv first dot map chomp map um into display.new for the things that we're mapping into and we'll have displays and displays dot inject so we want to add up all of the um the uh simple numbers so d or like a and d a plus d dot simple numbers and we want to just print that out so it puts that okay so day eight display and day eight input crashed initialize got the wrong number of arguments um oh this should not be new this is parse okay undefined method plus for huh um oh gosh we just want to start at zero yeah okay all right 369 was that our answer our answer was 369 okay so we've got it working for the simple case but that case didn't really get us very far so so it says through a little deduction you should now be able to determine the remaining digits consider this first example above after some careful analysis the mapping between the signal wires and segments only makes sense in the following configuration so that's like based on this input it only makes sense in this configuration where a b c are over like in these positions and then this gives you out the numbers so like in this case you take all of these as input you and then you have to figure out where the segments are based on these inputs because these are like randomly ordered right and ultimately you figure out this is the mapping and you see that in the answer here anywhere that you see fc abd fc ad right i guess or yeah cd feb or cd fbe so these are also not ordered the same but then you end up getting out these values for the numbers so then like the answer for this value entry is five three five three so this is five three five three based on all the different random inputs so um yeah so that is kind of like the the problem for part two is we need to deduce and figure out what the um the values are and then what it asks us to do is add all of the output values in this example and it should spit out some number so here is our larger example here it looks like this and we want to go through like determine what the actual value is for that segment or that display and then return that and add them all up okay so this is where it got super tricky in my opinion because um yeah so going back to the drawing board here so back on the drawing board we can there's a couple different approaches that i took the first was like okay what if i just count up the frequency of like each segment so here right like if i if we're just talking about this top segment so we've got one zero right so that doesn't count one two three four five six seven eight so there's eight in that top position so then i like kind of would write like an eight at the top and then um if we're going to the next section let's let's uh let's just go on the left so we have one two three four five six so i put a six over here and then if we're looking at this next segment one two three four five six seven eight so there's another eight on that side go to the next segment maybe the middle so this is our first occurrence one two three four five six seven so there's seven in the middle and then we go to the left here one two three four so there's four over here and then if we go to the bottom we have one two three four five six seven seven on the bottom and then ultimately this far right sides we have one two three four five six seven eight nine okay so this is nine so if if the frequency of the segment or like if the frequency of the letter in the original patterns is nine then we know that that is the bot for the bottom right hand side segment we know if it's four it's for the bottom left-hand side segment right and then the others we have eight and eight so that we can't really deduce it that way we have seven and seven we can't deduce it that way but if we have a six there's only one six and that is for this upper left segment so we can fig we can sort of figure out like upper left bottom left and bottom right based on the frequency in which they appear inside of the inside of the original pattern so that's one approach it's just like looking at the frequencies right i'm going to remove all of these the other way that we can do it is by looking at the intersection of different numbers okay so let me show you what i mean so if you look at we know for a fact that we can find one we know we can find four uh seven and eight right and so what we can start to do is say like okay if i take one and i intersect it with 9 or if i take 4 and i intersect it with 9 then i know that i'm going to result with like a length of 2 that are different so 4 intersect with 9 is going to give me back like two right or like not the intersection but like the the the set difference between four and nine is going to be um two segments and if i look at the set difference between like 4 and 5 or something that gives us something interesting and if we look at the set difference between 1 and let's see 1 and 0 that gives us these four segments 1 2 3 4 right and so that gives us 4. and i think using pattern matching we might be able to solve this by looking at the set differences between the numbers that we know for sure so like ones ones and uh one four sevens eights and nines so let's start here and we're gonna try this out so i've never actually solved it this way this is how we're gonna get into it um okay so um what i think we want to do is we want to figure out um for each pattern we want to know whether or not it is a a one okay so we want to go through the patterns and we need to match them and i guess we're let's like figure out what the output is again so yeah we want to figure out what the like some mapping where we have like a pattern and that pattern maps to some value so like um let's see so the other thing that we can do too is i think we can sort the patterns because each pattern is unique um and that will help us with our like or the other thing too i'm wondering is if we want to make each pattern a set instead of an array or instead of just like a string of numbers if we map it into a set is that going to give us anything valuable so ruby set also ruby pattern matching is pretty new and so i was excited to try that out um so yeah set we could just convert it to a set i don't know let's let's let's uh let's just give this a let's let's give this a whirl so what i think we want to do is say something like def mappings is something okay so actually yeah i guess we got to write a spec for this so um let's see so mappings mappings returns a mapping of pattern to number okay so in this case let's actually take this example here um and use this as our as our test okay and then what we want to do is say like display we expect that display dot mappings to equal um some dictionary or hash where the keys are like this and the values are like this so we want to get back basically this um and whether that's a string or a symbol or whatever i don't let's make it let's make them strings just because um all right so this is what we expect our mappings to be right now we get back nothing and so we need to have like mapping and this is going to be some something interesting so i the other thing i think might be helpful is to have some methods for each of like one two oh okay so let's just let's just do it as variables in line here so one is going to be um patterns dot find uh where the length is equal to two and then seven is going to be the same thing but for three four is going to be the same thing but for four and eight is going to be the same thing but for seven okay and then what we want to do is we want to like go uh well okay we need a couple of things we need the length of actually yeah okay so in like the way that the pattern matching works in ruby is you say case and then you have some input thing and then you say in and then you have your matcher and you can have else and you can have end so in this case what i think we want to do is start with length so um so pattern.each do p our pattern whatever patterns.h2 pattern okay and if the pattern pattern.length is our first attribute then for if it's 2 then we want to return then we return the value 1. if it's 3 then we return the value 7. if it's for we return the value four if it's eight we return the value eight okay and then i don't think we need this anymore um and i don't know how we're going to collect it up into an actual thing but the other thing that we need now is to know like the after we do the intersection of two patterns what does that actually look like so okay so we actually we do want these ones we do want one one four seven eight because these tell us these tell us something valuable so for each pattern if the pattern let's see so if the pattern intersection with 1 what does that give us the other thing is that like yeah okay so we need to figure out two in something gives us two uh in something gives us three what i'm thinking here is that let's say we have something like um a is the set uh or like yeah set or i guess we have to say require well set dot new oh wow okay we before you had to like require set now it's like just included there okay so a is set dot new of like one two three right and then b is set dot nu of 2 3 4. and let's say like a intersect b what does that give us back it gives us 2 and 3. so what about minus okay that gives us one which is the leftover thing and then if we do like dot length um ada intersection b is it the same yeah okay so all right so to get zero we want uh the length the pattern length should be what how many segments are in a zero and that's a six and then i guess the other part of this is yeah intersection so let's let's map all of the patterns when they come in let's make them uh sets of the characters so we're gonna map the pattern into set.new of pattern.chars and that way our patterns are all sets is that what we want i think so um and s is set dot new one two three s dot length okay yeah that gives us what we expect okay so then if we have these if we have all these sets what i'm wondering is if we can do something like um one inters or like one diff or something and say like pattern minus one and this will give us like a length and then we can pass this as the second pattern match thing so one diff here and then all of these need to have a second argument now um and then also like i guess for diff and pattern minus four dot length um four diff uh okay so then so a two the length of a two is five the length of a three is five the length of a five is one two three four five five a six is six and a nine is six right six patterns okay and or like six length um okay so for an eight we don't care about those four three okay so the difference between a three and a one should be um one two three so we should get back three here i think um the difference between a six i don't know um i don't know four doesn't matter we don't need okay yeah five or four three wait we already did three what were we doing down here this is okay so three i think i don't know i don't know what i'm doing let's just see uh let's just see what this does so patterns dot each do pattern um so i guess we're just gonna run it and see what happens okay uh no matching pattern error interesting no matching pattern error um that's cool uh i guess that's okay so like yeah um all right so ah what do we want to do here i guess um one diff and four diff does that give us enough so if we're trying to figure out zero zero the difference between zero and one is going to be one two three four so that should be a four and we should get back zero let's actually just try this um yeah let's let's add a buy bug also i think this only works with ruby 2 7 and above because pattern matching is pretty new alright so um the pattern is the set a c e d f g okay and then um one is the set of a b so the difference between this this thing that we got in which is one two three four five six seven so the first the first set here is an eight the value is an eight um and the difference between eight and one is five and i think that's fine but we don't actually care about that four diff is uh three because if you have a four the only ones that are missing is the top the bottom left and the bottom so that's right um and then if we did this thing exception what do we get no matching seven five three okay so then i think to get that first one working uh yes so this should be seven and then nothing nothing all right let's try this again okay we did get an eight back so then mapping has that pattern as the set and then the value is eight so that worked as expected so then one diff for or i guess what is our pattern this time so the pattern this time is five different letters and um the one diff is four and the four diff is two so and the length of the pattern is five so we've got like five four two is what we're matching on right here and i don't actually know what this letter is c d f b e c d f p e so yeah i don't i don't know if this is gonna work um it's definitely not a two but whatever um and then if we get let's see pattern again another five so that's going to give us another two mapping yeah so we're incorrectly marking these as twos because we don't actually know more about the the difference there so the pattern here is again five pattern is three so this one we should get back a seven and we did okay cool um the pattern here is six so this could be a zero a six or a nine the diff one diff is four um so it should be a zero this should be a zero so we should get the length should be six and the one diff should be zero so i think we should actually get a zero here and we do nice okay so i think we've got zero figured out let's just keep track here also so we have one figured out four figured out zero figured out and we'll kind of like keep working our way through this so the pattern this time is six again the one diff this time is five which i think might be a nine right the difference between one and nine is going to be one two three four oh no it's four okay so the difference between one and six here is one two three four five okay so the one diff should be five and the length so pattern.length is a six so we should have like six five and then nothing four um for a six okay so then let's go back over to the number six six five that should give us our answer i think okay and then thinking about this again that one is fine the two so if we're looking at a two the diff between a one and a two is going to be um one two three four okay so that should be a four and then the diff between a four and a two is going to be one two three okay and then for a three the diff between a one and a three is three and the diff between a four and a three is two um two the diff between a four and a four is nothing five okay that has six segments and the diff between a five and a four is two and the diff between a five and a one is one two three four okay so at the end of the day too we wanna make sure that none of these are duplicates and some of them are probably like unnecessary like we have more than we actually need um but yeah so the length of a nine is a six and then the diff between a nine and a one is one two three four and the diff between a nine and a four is one two all right so let's see if this works as expected all right um oh okay so uh exception no matching pattern five four two five four two so five four we have five three two but no five four two okay so these are the only two that could be um five something right those are the only two that have a five segment thing or it is a five i'll say hold on a second so one two three four five oh yeah five this should be a five four two all right let's try this again okay hey we got we got super close here so i think we might actually be i think we might actually be cooking with gas here so let's return let's return mapping and see what we get so let's see so if we return mapping down here the other thing is that like they're not in a set but we can do we can do something like um set dot new of this thing dot chars uh ooh there was just one different what is this c e b f o wait what we thought it was a zero but it was actually a nine okay so close so close so we thought it was a zero but it was actually a nine so six four all right so what is the difference between four and zero four and zero so four is one two three so that should be a three all right let's run this again hey we've got it passing all right so this is super cool all right so the pattern this pattern matching thing okay so we didn't talk about a couple things one is like what is the deal with an underscore so the underscore says ignore ignore that part um so here what we're doing is we're creating a pattern so our pattern is like what is the length of what is the length of the incoming segment what is the difference between our set segment and whatever the pattern is that we're considering our one what is the difference between r1 and the pattern we're considering what is the difference between the four and the pattern we're considering and then by doing pattern matching based on those different lengths we're able to uniquely identify all of these there was another solution that i saw that was pretty neat they mapped each of the each of the letters to a certain value and then multiplied or something and like the multiplied the the like product of all of the different possible segments gave you some pattern and then you can use that to derive the other numbers whatever okay so this is uh all right so we've got our mappings let's just say that's that's uh like looking good and that was super fun so i've never used pattern matching before in ruby and this is uh i don't know it seems seems cool um and like very rusty or elixir pattern matching is much more common in functional programming um okay so the idea is that now we have to take each of these mappings and we have to convert our like output number thing into some actual number thing so i think we also want these to be mapped into um set.new of like the characters for the numbers um just simplify this a little bit with the numbered whatever okay so we've got our numbers now we want like our output number and the way this works is we're going to go over uh we're going to go we're going to iterate over all the all of the numbers and that's going to give us our result is this thing result dot join i guess like we want to we want to yeah we want to create a string value based on these mappings so mappings at um at n i don't know if that's going to work if the set if the order of the set or something is different um let's see so mapping at n dot 2 s dot yeah so we want to map the numbers to their number value and then we want to join them i don't know maybe this will work just like this uh dot 2 i i don't know so let's see uh okay so given yeah given this same thing we should expect that display dot output number two equal i think it's like um five three five three let's see if that's what we get mappings also we don't want to call mappings every single time so we're going to like let's cache this so let's call this app mapping and app mapping and we will um so if we'll just like return at mapping if at mapping and run this again okay so that gives us the right answer so now what we want to do is go over we want to iterate over all of those and figure out what those numbers are supposed to be and then add them up so based on um yeah let's just run it based on our input so instead of simple numbers we want to do output number and just see what we get here crashed uninitialized constant display set so i thought require set i'm pretty sure i don't know why it's available in pry but it's not available elsewhere all right so what is this all right this is our number one million thirty one thousand five hundred fifty three so we are correctly solving part two we sort of breezed through that a bit um like sort of parts of the solution but i also wanted to just show you what my actual my like original solution was just so that i can show you like how to how i'm doing these a little bit differently or how i how i like learned um this new way and did it a little differently so the first time i solved this segment thing i i did create a mapping but the mapping was like okay i'm going to call each of these methods 0 through 9 and each method i'm going to try to figure out like is this a zero or not so i had a method called four and a method called three in a method called two in etc etc but each method i was also um trying to figure out like oh does it include the bottom left or the top right or the the other side and so i kind of worked my way through a bunch of different like possibilities and the uh the outcome was this massive solution 156 lines which i'm not again like i'm not super proud of this one is 67 lines is pretty if you can figure out this mapping situation it's like a much easier to read and follow so um yeah much more excited about this pattern matching thing also pattern matching in ruby is cool so we learned about sets today we learned about pattern matching um and like yeah set intersection and set difference and whatever so this was a fun it was a fun and challenging exercise so hopefully you enjoyed that and uh if so would appreciate a like and otherwise we'll see you in the next one cheers [Music] you
Info
Channel: CJ Avilla
Views: 65
Rating: undefined out of 5
Keywords: cjav_dev, web development tutorials, web development for beginners, vim, ruby, rails, pattern matching, set, advent of code, advent of code 2021
Id: k4zSyiAwWiI
Channel Id: undefined
Length: 43min 13sec (2593 seconds)
Published: Mon Dec 13 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.