-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.json
1 lines (1 loc) · 99.4 KB
/
index.json
1
[{"content":"Question\nWe are stuck at a river between two mountains, our communication device can\u0026rsquo;t get a decent signal but don\u0026rsquo;t worry THIS IS FINE!! we can get through this if we only find a high enough place to get a signal out\ncool cool cool, no doubt no doubt no doubt stay optimistic, and don\u0026rsquo;t panic!!!\nThe one thing going for us at the moment is that we have a height map from our device, we just need to get to the heighst point and send a distress signal\nOur map looks like this\n1 2 3 4 5 Sabqponm abcryxxl accszExk acctuvwj abdefghi Our current location is marked as S and our destination is marked as E We are also told that the height of each location is the ASCII value of each letter The value of S = a and E = z\nThis smells like a graph problem\u0026hellip;\nParsing We could have used the structure of the input as is but I wanted to make it a bit more meaningful so we will parse it into a matrix of points! First, we will create a new Point struct\n1 2 3 4 5 6 7 8 9 10 11 type Point struct { i, j, v int } func (p *Point) id() string { return fmt.Sprintf(\u0026#34;(%d,%d)\u0026#34;, p.i, p.j) } func newPoint(i, j, v int) *Point { return \u0026amp;Point{i: i, j: j, v: v} } Each point location is kept with i and j, and the value is stored in v. We also create a struct method, id that we will probably need some time down the line\nAlthough I\u0026rsquo;m by no means a go expert, having a new function for a struct seems like the standard. Probably because it\u0026rsquo;s a lot less verbose.\nNow let\u0026rsquo;s change our input matrix into a matrix of Points\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func createPoint(i, j int, r rune) *Point { switch r { case \u0026#39;S\u0026#39;: return newPoint(i, j, 0) case \u0026#39;E\u0026#39;: return newPoint(i, j, int(\u0026#39;z\u0026#39;)-int(\u0026#39;a\u0026#39;)) default: return newPoint(i, j, int(r)-int(\u0026#39;a\u0026#39;)) } } func parse(raw string) (matrix [][]*Point, start *Point, dest *Point) { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) matrix = make([][]*Point, len(lines)) for i := range matrix { matrix[i] = make([]*Point, len(lines[0])) rows := []*Point{} for j, c := range lines[i] { if c == \u0026#39;S\u0026#39; { start = createPoint(i, j, c) } if c == \u0026#39;E\u0026#39; { dest = createPoint(i, j, c) } rows = append(rows, createPoint(i, j, c)) } matrix[i] = rows } return matrix, start, dest } The first thing we do is to split our input on each \\n this will give us an array of lines AKA rows. We will split each of these \u0026ldquo;rows\u0026rdquo; to get each \u0026ldquo;cell\u0026rdquo; in our input For each cell, we will create a new point and push it into the current row eventually we will end up with a matrix of points\nPart 1 We are asked to get from S to E with the least amount of steps. Ohh and to make sure we won\u0026rsquo;t get too tired along the way we can only travel from p1 to p2 if the value in p2 is at most one higher than the value in p1.\nReading the first line, \u0026ldquo;with the least amount of step\u0026rdquo; this is a dead giveaway that we can and should use BFS\nSince we need a Queue to easily implement BFS and Go doesn\u0026rsquo;t have anything built-in, we will need to create our Queue first Create a new package named, well, queue and add the following logic and struct\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package queue import \u0026#34;fmt\u0026#34; type Queue[T comparable] struct { items []T } // Enqueue - Adds an item T to the Q func (q *Queue[T]) Enqueue(item T) { q.items = append(q.items, item) } // Dequeue - Removes an element from the Q - FIFO func (q *Queue[T]) Dequeue() T { if q.IsEmpty() { fmt.Println(\u0026#34;Trying to Dequeue from an empty Queue\u0026#34;) } firstItem := q.items[0] q.items = q.items[1:] return firstItem } func (q *Queue[T]) Peek() T { return q.items[0] } func (q *Queue[T]) NumberOfItems() int { return len(q.items) } func (q *Queue[T]) IsEmpty() bool { return len(q.items) == 0 } There is nothing fancy here, just your regular Q\u0026hellip; Armed with our new Queue we can start implementing BFS The gist of BFS is\nStart from node X and push all its neighbors to the \u0026ldquo;processing\u0026rdquo; Queue To avoid duplicates in that Queue we will also maintain a Set of \u0026ldquo;seen\u0026rdquo; nodes, for that we will use our simple set from day 6 The last thing we need is to keep track of how we got to each node, this means for point p1 who got it inside the Queue that is the currently processed point. Let\u0026rsquo;s look at some code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 func BFS(graph [][]*Point, s *Point, destination *Point) int { // Creating a Queue Q := queue.Queue[*Point]{} // Adding the start node to the Queue Q.Enqueue(s) // Keeping track of who got us into the Queue backTrack := map[string]string{} // Nodes we have seen seen := set.NewSimpleSet[string]() // Mark the starting node as seen to avoid pushing it into the Queue again seen.Add(s.id()) // We keep going over the \u0026#34;graph\u0026#34; while there are nodes in the Queue for !Q.IsEmpty() { // The node we are currently processing currentNode := Q.Dequeue() if currentNode == destination { // Yay we got to our destination, we can stop searching break } // Get all valid neighbors from our current node // we will go over the implementation of getting neighbors in a minute or two neighbors := getNeighbors(graph, currentNode) for _, v := range neighbors { // For each neighbor, if we haven\u0026#39;t seen it before, do the following // 1. mark it as seen // 2. mark the node that got to him i.e our currently processed node // 3. push it to the Queue if !seen.Has(v.id()) { seen.Add(v.id()) backTrack[v.id()] = currentNode.id() Q.Enqueue(v) } } } // go over the route to the destination node and count the number of steps it took us return count(backTrack, destination.id()) } // simple recursive function that uses the node id to jump from p1 to p2 where p2 is the node that got p1 into the Queue func count(backTrack map[string]string, id string) int { v, ok := backTrack[id] if ok { return 1 + count(backTrack, v) } return 0 } func getNeighbors(graph [][]*Point, sink *Point) (neighbors []*Point) { // moves represent our up, right, down, and left options moves := [][]int{{-1, 0}, {0, 1}, {1, 0}, {0, -1}} for _, move := range moves { // add the current move to the origin point di, dj := move[0]+sink.i, move[1]+sink.j // if the new indexes are inbound of our matrix/graph if di \u0026gt;= 0 \u0026amp;\u0026amp; di \u0026lt; len(graph) \u0026amp;\u0026amp; dj \u0026gt;= 0 \u0026amp;\u0026amp; dj \u0026lt; len(graph[0]) { delta := graph[di][dj].v - sink.v // We were also told in the question that we need to make sure we make moves of at most 1 if delta \u0026lt;= 1 { neighbors = append(neighbors, graph[di][dj]) } } } return neighbors } I tried making everything as clear as possible but if there is something you are struggling with, hit me up in the comment section\nOur final solution looks like this\n1 2 3 4 5 func Part1(raw string) int { graph, start, dest := parse(raw) steps := BFS(graph, start, dest) return steps } Part 2 Part 2 is where things gets interesting, we are now asked to find the shortest path to our destination point but we can use every a in our map as a starting position and S We can take a naive approach here and see if it works, what\u0026rsquo;s the naive approach you ask? Find all starting nodes and from each of those run our BFS and keep track of the minimum value This also means that we need to change our parsing function to collect all starting points\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // start is now an array of points func parse2(raw string) (matrix [][]*Point, start []*Point, dest *Point) { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) matrix = make([][]*Point, len(lines)) for i := range matrix { matrix[i] = make([]*Point, len(lines[0])) rows := []*Point{} for j, c := range lines[i] { if c == \u0026#39;S\u0026#39; || c == \u0026#39;a\u0026#39; { start = append(start, createPoint(i, j, c)) } if c == \u0026#39;E\u0026#39; { dest = createPoint(i, j, c) } rows = append(rows, createPoint(i, j, c)) } matrix[i] = rows } return matrix, start, dest } And our solution for part 2 will look like this\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func getMinValue(arr []int) (m int) { for i, e := range arr { if i == 0 || e \u0026lt; m \u0026amp;\u0026amp; e != 0 { m = e } } return md } func Part2NaiveApproach(raw string) int { graph, start, dest := parse2(raw) steps := []int{} for _, s := range start { res := BFS(graph, s, dest) if res \u0026gt; 0 { steps = append(steps, res) } } return getMinValue(steps) } This works for the question input which surprised me but it takes about 3 seconds to complete, we can do better!\nThere\u0026rsquo;s one thing that comes to mind, why do we need to do a BFS from each node, can\u0026rsquo;t we just add all those starting nodes to our Queue as starting nodes? Well, we can! its called multi-source BFS, I happen to know this approach from solving the monster question on CSES, give it a try.\nBefore we start tweaking things and trying new approaches, let\u0026rsquo;s write some benchmarks so we can make sure what we are doing have an impact.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func BenchmarkPart1(b *testing.B) { input := util.ReadFile(\u0026#34;./input.txt\u0026#34;) for n := 0; n \u0026lt; b.N; n++ { Part1(input) } } func BenchmarkPart2NaiveApproach(b *testing.B) { input := util.ReadFile(\u0026#34;./input.txt\u0026#34;) for n := 0; n \u0026lt; b.N; n++ { Part2NaiveApproach(input) } } Run go test bench=. -count 2\n1 2 3 4 BenchmarkPart1-8 183 7225891 ns/op BenchmarkPart1-8 182 7127653 ns/op BenchmarkPart2NaiveApproach-8 1 1530454249 ns/op BenchmarkPart2NaiveApproach-8 1 1476970916 ns/op ophh\u0026hellip; our naive approach is slow. it only run ones and took 1530454249ns which is roughly 1.53 seconds compared to our part 1 results, this is just awful!\nOur Multi-Source BFS approach is similar to our original BFS but there are changes in the first couple of lines\n1 2 3 4 5 6 7 8 9 10 11 // s is now an array of starting points func MultiSourceBFS(graph [][]*Point, s []*Point, destination *Point) int { Q := queue.Queue[*Point]{} seen := set.NewSimpleSet[string]() // for each starting point push int into the Queue and mark it as seen for _, v := range s { Q.Enqueue(v) seen.Add(v.id()) } ... ... See the complete code here\nLet\u0026rsquo;s write a benchmark for our Multi-Source BFS approach\n1 2 3 4 5 6 7 func BenchmarkPart2MultiSourceBfs(b *testing.B) { input := util.ReadFile(\u0026#34;./input.txt\u0026#34;) // run the Fib function b.N times for n := 0; n \u0026lt; b.N; n++ { Part2MultiSourceBfs(input) } } Wow, what a difference! Our Multi-source BFS run ~205 times and each run took only ~5841816ns which is roughly 0.0058 seconds! That\u0026rsquo;s about 274 times faster!\n1 2 3 4 5 6 BenchmarkPart1-8 184 7306437 ns/op BenchmarkPart1-8 181 6429481 ns/op BenchmarkPart2NaiveApproach-8 1 1475075477 ns/op BenchmarkPart2NaiveApproach-8 1 1450753723 ns/op BenchmarkPart2MultiSourceBfs-8 205 5841816 ns/op BenchmarkPart2MultiSourceBfs-8 204 5818908 ns/op So we had a fun graph problem and we have seen a few approaches to solve it, what a fun day! hopefully, you learned something new from this day post.\nAs a final note I\u0026rsquo;ll leave you all with a question, is there an even better way of doing what we just did? Can we perhaps change our logic a bit and start from a different node and get our desired answer in an even more efficient way? Let me know in the comments if you have any ideas :)\nThat\u0026rsquo;s it for today, see you tomorrow ⭐️\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-12/","summary":"Question\nWe are stuck at a river between two mountains, our communication device can\u0026rsquo;t get a decent signal but don\u0026rsquo;t worry THIS IS FINE!! we can get through this if we only find a high enough place to get a signal out\ncool cool cool, no doubt no doubt no doubt stay optimistic, and don\u0026rsquo;t panic!!!\nThe one thing going for us at the moment is that we have a height map from our device, we just need to get to the heighst point and send a distress signal","title":"Hill Climbing Algorithm"},{"content":"Question\nCrap seems like our rope physics knowledge from yesterday didn\u0026rsquo;t help and we wound up in the river\u0026hellip; The elves are nowhere to be found and our communication system is spitting out weird signals and noise Luckily we can probably get this device up and running in no time we just need to make a drop-in replacement for the device\u0026rsquo;s video system. To do that we need to decode the instructions from the CPU and the register value first\nWe are given the CPU output and told that each operation takes Y cycles to complete, for example:\n1 2 3 noop addx 3 addx -5 addx - takes two rounds, add the right-hand value to X noop - take one round, does nothing Parsing First, Create a new Instruction struct\n1 2 3 4 type Instruction struct { cycles int value int } Compared to previous days the parsing here is a piece of cake\n1 2 3 4 5 6 7 8 9 10 11 12 13 func parse(raw string) (instructions []*Instruction) { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) for _, l := range lines { if strings.Contains(l, \u0026#34;noop\u0026#34;) { instructions = append(instructions, \u0026amp;Instruction{cycles: 1, value: 0}) } else { parts := strings.Split(l, \u0026#34; \u0026#34;) instructions = append(instructions, \u0026amp;Instruction{cycles: 2, value: util.ParseInt(parts[1])}) } } return } you might notice that our function signature is a bit weird, it ends with (instructions []Instruction) and we return nothing at the end of the function. This syntax creates a variable at the top of our function, and the last return statement is called a \u0026ldquo;naked\u0026rdquo; return, which returns instructions by default. In my opinion, it shouldn\u0026rsquo;t be used for any function with more than a couple of lines of code, instead, we should use the named variable and return instructions but for the sake of learning new things we will stick with the \u0026ldquo;naked\u0026rdquo; return (I kind of like that terminology)\nPart 1 We need to sample the value in register X*ticks in various CPU cycles, more precisly during the 20th, 60th, 100th, 140th, 180th, and 220th cycles and sum them up\nThere is no kind of gotchas in these sort of questions (usually), most simulation question just needs to be carefully read and then directly apply the instructions in the code\nDirectly from AoC addx V takes two cycles to complete. After two cycles, the X register is increased by the value V. (V can be negative. noop takes one cycle to complete. It has no other effect.\nthe thing to note here is that the instruction value takes effect only after the specific number of cycles has passed\nSo what does our solution needs to do?\nGo over each instruction Keep track of the system ticks (not the same as cycle) Keep track of X Sample X in each one of the intervals Run each instruction Y number of cycles Update X after each instruction We are adding a notion of ticks, ticks happen on every run regardless of the number of cycles an instructions should take\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func Part1(raw string) int { instructions := parse(raw) x := 1 result := 0 ticks := 0 for _, ci := range instructions { for j := 0; j \u0026lt; ci.cycles; j++ { ticks++ // updating ticks on every cycle if ticks%20 == 0 \u0026amp;\u0026amp; ticks%40 != 0 { result += x * ticks } } x += ci.value } return result } Part 2 using the value of X during each tick we need to draw some stuff to the screen Our screen is 40 pixels wide and its height is 6 pixels\nThere is no notion of vertical positions, meaning that our x value needs to be translated to the range we defined above 40*6\nWe are told there is a sprite 3 pixels long and the x value determines her center position. During each cycle we bump our location in the screen, if the current position includes the currently drawn pixel we say it lit and draw # otherwise its dark and we draw .\nThe output of this should be a sequence of chars and that will be our answer! How cool is that right?\nThis question is a bit trickier than part 1 but still, the main thing is to read the instructions carefully and translate them back into code\nFirst, lets create a screen!\n1 2 3 4 5 6 7 8 9 10 11 12 13 func printCrt(crt [][]string) { for i, r := range crt { fmt.Println(i, r) } } func makeCrt() [][]string { crt := make([][]string, 6) for i, _ := range crt { crt[i] = make([]string, 40) } return crt } Next comes our logic\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func Part2(raw string) { instructions := parse(raw) crt := makeCrt() x := 1 ticks := 0 for _, ci := range instructions { for j := 0; j \u0026lt; ci.cycles; j++ { row := int(ticks / 40) col := ticks % 40 d := util.Abs(col - x) if d \u0026lt; 2 { crt[row][col] = \u0026#34;#\u0026#34; } else { crt[row][col] = \u0026#34;.\u0026#34; } ticks++ } x += ci.value } print(crt) } Not everything here is obvious so let\u0026rsquo;s go over the tricky lines one by one:\nrow := int(ticks / 40) - we know that each row is 40 long, so we can divide the number ticks by the width of each row to determine in each row we should be relative to our CRT e.g 30/40 -\u0026gt; 0, 90/40 -\u0026gt; 2 etc..\ncol := ticks%40 - we have a \u0026ldquo;window\u0026rdquo; with a length of 40 and a value that is increasing but we still want to fall into that bucker of values e.g 30%40 -\u0026gt; 30, 50%40 -\u0026gt; 10 (second row 10th pixel), 220%40 -\u0026gt; 20 etc\u0026hellip;\nd := util.Abs(col - x) - our delta from the center of the sprite, if its smaller than 2 (remember that x is the center of the sprite) we draw a lit pixel, otherwise we draw a dark pixel\nWith my input I got the following output, what about you? That\u0026rsquo;s it for today, see you tomorrow ⭐️\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-10/","summary":"Question\nCrap seems like our rope physics knowledge from yesterday didn\u0026rsquo;t help and we wound up in the river\u0026hellip; The elves are nowhere to be found and our communication system is spitting out weird signals and noise Luckily we can probably get this device up and running in no time we just need to make a drop-in replacement for the device\u0026rsquo;s video system. To do that we need to decode the instructions from the CPU and the register value first","title":"Cathode-Ray Tube"},{"content":"Question AkA doing weird rope physics!\nYou come across an old rope bridge and you are not sure it can hold your fat a** so you decide to model some rope physics to distract yourself (like that\u0026rsquo;s going to help\u0026hellip;)\nDirectly from AoC Consider a rope with a knot at each end; these knots mark the head and the tail of the rope. If the head moves far enough away from the tail, the tail is pulled toward the head.\nWe are given a series of moves to be done by the head, for example:\n1 2 3 4 5 6 7 8 R 4 U 4 L 3 D 1 R 4 D 1 L 5 R 2 Parsing Defining an Instruction struct, each instruction has a direction L,R,U,D and steps \u0026lt;number of steps to perform\u0026gt; Iterating over our input we can map each line to our new instruction struct as follows\n1 2 3 4 5 6 7 8 9 10 11 12 13 type Instruction struct { direction string steps int } func parse(raw string) (instructions []Instruction) { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) for _, l := range lines { parts := strings.Split(l, \u0026#34; \u0026#34;) instructions = append(instructions, Instruction{direction: parts[0], steps: util.ParseInt(parts[1])}) } return instructions } Part 1 We are asked to simulate the position of the tail after each instruction execution We will treat both the head and tail as if they are starting from position 0,0\nWe know that the head should change its x or y value based on the current instruction direction. Let\u0026rsquo;s create a struct to represent a Point and expose a method move that mutates the point accordingly\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type Point struct { x, y int } func (p *Point) move(direction string) { switch direction { case \u0026#34;L\u0026#34;: p.x-- case \u0026#34;R\u0026#34;: p.x++ case \u0026#34;U\u0026#34;: p.y-- case \u0026#34;D\u0026#34;: p.y++ } } func newPoint(x, y int) *Point { return \u0026amp;Point{x, y} } Now lets start writing our solution for part 1\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 instructions := parse(raw) head, tail := newPoint(0, 0), newPoint(0, 0) visited := set.NewSimpleSet[string]() for _, ci := range instructions { for i := 0; i \u0026lt; ci.steps; i++ { head.move(ci.direction) // we need to adjust the tail point according to the head location } } return visited.Size() } Next, adjust the tail point according to the head point From the question description\nDue to the aforementioned Planck lengths, the rope must be quite short; in fact, the head (H) and tail (T) must always be touching (diagonally adjacent and even overlapping both counts as touching):\nAs you can see, we need to \u0026ldquo;touch\u0026rdquo; the head at any point in time, or in other words, the distance between the points x and y cords is always less than 2\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func (p *Point) adjust(leadingPoint *Point) { dx := util.Abs(leadingPoint.x - p.x) dy := util.Abs(leadingPoint.y - p.y) if dx \u0026gt;= 2 || dy \u0026gt;= 2 { if leadingPoint.x \u0026gt; p.x { p.x++ } else if leadingPoint.x \u0026lt; p.x { p.x-- } if leadingPoint.y \u0026gt; p.y { p.y++ } else if leadingPoint.y \u0026lt; p.y { p.y-- } } } Armed with our new point structure we can implement the actual logic for part 1 We will use our SimpleSet from day 6 to keep track of the number of points the tail visited The size of that set will be the answer for this part\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func Part1(raw string) int { instructions := parse(raw) head, tail := newPoint(0, 0), newPoint(0, 0) visited := set.NewSimpleSet[string]() for _, ci := range instructions { for i := 0; i \u0026lt; ci.steps; i++ { visited.Add(tail.id()) head.move(ci.direction) tail.adjust(head) } } return visited.Size() } Part 2 Crap the rope just snaps and for some reason that does not make any sense we now have 10 knots to simulate\u0026hellip; that\u0026rsquo;s what happens when you combine elves and rope physics.\nAt first glance, this seems complicated but in reality, we just need to think about the new requirements as an array of points where points[j] is the tail of points[j+1] and for each move adjust all tail points according to their relative heads\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 func Part2(raw string) int { instructions := parse(raw) // 1 point for the leading edge + 9 tail points knots := make([]*Point, 10) // All points start from 0,0 for i, _ := range knots { knots[i] = newPoint(0, 0) } visited := set.NewSimpleSet[string]() for _, ci := range instructions { for i := 0; i \u0026lt; ci.steps; i++ { // Move the actual head knots[0].move(ci.direction) // Adjust each point relative to its head for j := 0; j \u0026lt; len(knots)-1; j++ { head, tail := knots[j], knots[j+1] tail.adjust(head) } visited.Add(knots[len(knots)-1].id()) } } return visited.Size() } Same as part one, we keep track of the points we visited using our SimpleSet\nThat\u0026rsquo;s it for today, see you tomorrow ⭐️\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-9/","summary":"Question AkA doing weird rope physics!\nYou come across an old rope bridge and you are not sure it can hold your fat a** so you decide to model some rope physics to distract yourself (like that\u0026rsquo;s going to help\u0026hellip;)\nDirectly from AoC Consider a rope with a knot at each end; these knots mark the head and the tail of the rope. If the head moves far enough away from the tail, the tail is pulled toward the head.","title":"Rope Bridge"},{"content":"Question\nThe elves come to you with a problem, they want to build a tree house but they want to make sure that they have enough cover.\nWe are given a map of the forest with a number representing each tree\u0026rsquo;s height.\n1 2 3 4 5 30373 25512 65332 33549 35390 Parsing We care about the location of each tree and its height. All of the above is available to us if we parse our map into a matrix [][]int\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func parse(raw string) [][]int { rows := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) matrix := make([][]int, len(rows)) for i, row := range rows { matrix[i] = make([]int, len(row)) } for i, row := range rows { for j, c := range row { matrix[i][j] = util.ParseInt(string(c)) } } return matrix } Part 1 We are tasked with determining how many visible trees there are on the map.\nA tree is visible from a given direction (up, right, down, and left) only if all the trees directly on that line and to the edge are shorter than him.\nFrom that definition, we can deduce that every tree on the edge of the map is visible and an initial amount of visible trees from the get-go\n1 2 3 // (len(mat)-2)*2 top and bottom rows - the shared elements with the columns // len(mat[0])*2 right and left most columns visibleTrees := (len(mat)-2)*2 + len(mat[0])*2 Now for every other tree we need to check if its visible from some direction To do that, we will write 4 functions, each to check a different direction\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 func checkLeft(mat [][]int, i, j int) bool { for k := j - 1; k \u0026gt;= 0; k-- { if mat[i][k] \u0026gt;= mat[i][j] { return false } } return true } func checkRight(mat [][]int, i, j int) bool { for k := j + 1; k \u0026lt; len(mat[0]); k++ { if mat[i][k] \u0026gt;= mat[i][j] { return false } } return true } func checkUp(mat [][]int, i, j int) bool { for k := i - 1; k \u0026gt;= 0; k-- { if mat[k][j] \u0026gt;= mat[i][j] { return false } } return true } func checkDown(mat [][]int, i, j int) bool { for k := i + 1; k \u0026lt; len(mat[0]); k++ { if mat[k][j] \u0026gt;= mat[i][j] { return false } } return true } For each direction, we are going all the way until the edge, if at some point one of the trees is taller than our current tree we return false and try another direction.\nLet\u0026rsquo;s see the complete solution for part one\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 func Part1(raw string) int { mat := parse(raw) visibleTrees := (len(mat)-2)*2 + len(mat[0])*2 for i, row := range mat { if i == 0 || i == len(mat)-1 { continue } for j, _ := range row { if j == 0 || j == len(row)-1 { continue } if checkLeft(mat, i, j) { visibleTrees++ continue } if checkRight(mat, i, j) { visibleTrees++ continue } if checkUp(mat, i, j) { visibleTrees++ continue } if checkDown(mat, i, j) { visibleTrees++ continue } } } return visibleTrees } We have an if statement at the beginning of each loop to make sure we are not counting the edges again.\nThis solution is very verbose and there are a lot of ways we can optimize it, for example keeping track of the tallest tree in each column and row and with that information skipping some of the checks.\nBut\u0026hellip;the current solution is fast enough and run\u0026rsquo;s in 0.35s on my local machine with my AoC input.\nPart 2 Calculate how many trees are visible from each tree in every direction, multiply the numbers and find the maximum value tree in our map\nWe are going to take our check functions and create a similar counterpart, named calc\u0026lt;Direction\u0026gt;\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func calcLeft(mat [][]int, i, j int) int { vis := 0 for k := j - 1; k \u0026gt;= 0; k-- { if mat[i][k] \u0026gt;= mat[i][j] { return vis + 1 } vis++ } return vis } func calcRight(mat [][]int, i, j int) int { vis := 0 for k := j + 1; k \u0026lt; len(mat[0]); k++ { if mat[i][k] \u0026gt;= mat[i][j] { return vis + 1 } vis++ } return vis } func calcUp(mat [][]int, i, j int) int { vis := 0 for k := i - 1; k \u0026gt;= 0; k-- { if mat[k][j] \u0026gt;= mat[i][j] { return vis + 1 } vis++ } return vis } func calcDown(mat [][]int, i, j int) int { vis := 0 for k := i + 1; k \u0026lt; len(mat[0]); k++ { if mat[k][j] \u0026gt;= mat[i][j] { return vis + 1 } vis++ } return vis } Each calc function returns the number of trees that are visible from a point on the map.\nOur solution now is fairly simple, for each tree we calculate a score based on the instructions. We then compare that score with our current max and swap them if needed.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 func Part2(raw string) int { mat := parse(raw) max := 0 for i, row := range mat { for j, _ := range row { score := calcLeft(mat, i, j) * calcRight(mat, i, j) * calcUp(mat, i, j) * calcDown(mat, i, j) if score \u0026gt; max { max = score } } } return max } That\u0026rsquo;s it for today, see you tomorrow ⭐️\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-8/","summary":"Question\nThe elves come to you with a problem, they want to build a tree house but they want to make sure that they have enough cover.\nWe are given a map of the forest with a number representing each tree\u0026rsquo;s height.\n1 2 3 4 5 30373 25512 65332 33549 35390 Parsing We care about the location of each tree and its height. All of the above is available to us if we parse our map into a matrix [][]int","title":"Treetop Tree House"},{"content":"Question This was a fun day! we finally got something challenging.\nWe got a device from the elves when trying to update that device we got an error that notifies us that there isn\u0026rsquo;t enough memory on our device\n1 2 $ system-update --please --pretty-please-with-sugar-on-top Error: No space left on the device To determine what\u0026rsquo;s going on we start exploring the device file system and record the output (our puzzle input) For example\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ cd / $ ls dir a 14848514 b.txt 8504156 c.dat dir d $ cd a $ ls dir e 29116 f 2557 g 62596 h.lst $ cd e $ ls 584 i $ cd .. $ cd .. $ cd d $ ls 4060174 j 8033020 d.log 5626152 d.ext 7214296 k represent the following structure\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 - / (dir) - a (dir) - e (dir) - i (file, size=584) - f (file, size=29116) - g (file, size=2557) - h.lst (file, size=62596) - b.txt (file, size=14848514) - c.dat (file, size=8504156) - d (dir) - j (file, size=4060174) - d.log (file, size=8033020) - d.ext (file, size=5626152) - k (file, size=7214296) When I first solved it I iterated over all commands and didn\u0026rsquo;t create any kind of structure and just kept track of the current dir and update its parent size once we are done with it. I thought about trying something a bit more interesting for this post, let\u0026rsquo;s create a file system representation from our input and use it later to answer today\u0026rsquo;s puzzle\nFirst, we need to make sense of each line of our input, for that we are going to create a tokenizer! our tokens\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // token/token.go package token type TokenType string const ( Cd TokenType = \u0026#34;Cd\u0026#34; Ls TokenType = \u0026#34;Ls\u0026#34; File TokenType = \u0026#34;File\u0026#34; Dir TokenType = \u0026#34;Dir\u0026#34; ) type Token struct { Type TokenType Literal string } We have 4 types of tokens, one for each type of output line. In addition, we also save the raw data in literal so we can make use of it later on.\nNow let\u0026rsquo;s write our tokenizer, its responsibility is to get our input and transform it into a list of meaningful tokens\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // token/tokenizer.go package token import \u0026#34;strings\u0026#34; func newToken(tokenType TokenType, literal string) Token { return Token{ Type: tokenType, Literal: literal, } } func Tokenize(raw string) []Token { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) tokens := []Token{} for _, l := range lines { parts := strings.Split(l, \u0026#34; \u0026#34;) // process commands if parts[0] == \u0026#34;$\u0026#34; { if parts[1] == \u0026#34;ls\u0026#34; { tokens = append(tokens, newToken(Ls, l)) } else { tokens = append(tokens, newToken(Cd, l)) } // process ls output } else { if parts[0] == \u0026#34;dir\u0026#34; { tokens = append(tokens, newToken(Dir, l)) } else { tokens = append(tokens, newToken(File, l)) } } } return tokens } Create a struct to represent our file system\n1 2 3 4 5 6 7 8 9 10 11 type FileSystem struct { root *FileSystemNode } type FileSystemNode struct { Size int Parent *FileSystemNode Token token.Token Name string Dirs map[string]*FileSystemNode } Now to the fun part, going over our tokens and constructing a tree like structure based on that\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 func newFileSystemNode(name string, token token.Token, parent *FileSystemNode) *FileSystemNode { return \u0026amp;FileSystemNode{Name: name, Parent: parent, Token: token, Dirs: map[string]*FileSystemNode{}} } func NewFileSystem(tokens []token.Token) *FileSystem { root := newFileSystemNode(\u0026#34;/\u0026#34;, token.Token{Type: token.Dir, Literal: \u0026#34;/\u0026#34;}, nil) fileSystem := \u0026amp;FileSystem{root} current := root for _, t := range tokens { switch t.Type { case token.File: current.Size += CreateFileNode(t.Literal).Size case token.Dir: node := CreateDirNode(t.Literal) current.Dirs[node.Name] = newFileSystemNode(node.Name, t, current) case token.Ls: continue case token.Cd: cdNode := CreateCdNode(t.Literal) if cdNode.To == \u0026#34;..\u0026#34; { current.Parent.Size += current.Size current = current.Parent } else { _, ok := current.Dirs[cdNode.To] if ok { current = current.Dirs[cdNode.To] } } default: fmt.Println(\u0026#34;Unexpected token!\u0026#34;, t) } } // In case we are not ending at the root node for current != root { current.Parent.Size += current.Size current = current.Parent } return fileSystem } We iterate over our tokens and perform some operations depending on the type of token we got\nfile token: add the file size to the current dir dir token: create a new directory in the current dir and name it based on the token literal (the dir name) ls token: we don\u0026rsquo;t care about it and just continue our loop cd token: \u0026ldquo;..\u0026rdquo; literal: we change current to be `current.parent and add the size of the current dir to the parent else, its some dir that we have seen before using ls and we change the current dir to be current.Dirs[dirName] There are different kinds of file nodes, they are used to take the token literal and parse it into a meaningful structure. For example, the cd node looks like this\n1 2 3 4 5 6 7 8 9 10 type CdNode struct { To string } func CreateCdNode(str string) CdNode { parts := strings.Split(str, \u0026#34; \u0026#34;) return CdNode{ To: parts[2], } } At the end of the function we backtrack to our root directory and add each directory size in that path to its parent, this is because our output does not contain .. commands back to the top.\nNow that we have got our file system creation process all dialed in we can start implementing the first part solution\nPart 1 We are tasked to find all directories with size \u0026lt;= 100,000 To do that we need to have a way to walk over each directory in our file system structure. Let\u0026rsquo;s add methods to support that capability\n1 2 3 4 5 6 7 8 9 10 11 func (tree *FileSystem) Walk(visitor func(t *FileSystemNode)) { tree.root.Walk(visitor) } func (node *FileSystemNode) Walk(visitor func(t *FileSystemNode)) { visitor(node) for _, t := range node.Dirs { t.Walk(visitor) } } Both FileSystem and FileSystemNode get a Walk method\nWe pass in a function that will be called on each node in our file system. Using the above method our solution is now as simple as\n1 2 3 4 5 6 7 8 9 10 11 func Part1(raw string) int { fs := parse(raw) sum := 0 fs.Walk(func(node *fileSystem.FileSystemNode) { if node.Size \u0026lt;= 100000 { sum += t.Size } }) return sum } Part 2 In part 2 we are tasked with increasing the amount of free space to at least 3,000,000 we also know that the total memory on our device is 7,000,000 We need to find the smallest directory that we can delete that will increase the amount of free memory \u0026gt;= 3,000,000\nFor example (directly from the question)\n_In our example, you have the following options:\nDelete directory e, which would increase unused space by 584. Delete directory a, which would increase unused space by 94853. Delete directory d, which would increase unused space by 24933642. Delete directory /, which would increase unused space by 48381165. Directories e and a are both too small; deleting them would not free up enough space. However, directories d and / are both big enough! Between these, choose the smallest: d, increasing unused space by 24933642._\nThe logic to solve part 2 is also fairly straightforward but we do need to expose the size of our fileSystem first\n1 2 3 func (tree *FileSystem) Size() int { return tree.root.Size } Part 2 solution\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func Part2(raw string) int { fs := parse(raw) const OS_MEM = 70000000 const THRESHOLD = 30000000 unusedSpace := OS_MEM - fs.Size() min := OS_MEM fs.Walk(func(node *fileSystem.FileSystemNode) { if unusedSpace+node.Size \u0026gt; THRESHOLD { if min \u0026gt; node.Size { min = node.Size } } }) return min } For each directory, we check if by deleting it we have enough free memory unusedSpace+t.Size \u0026gt; THRESHOLD if it does we check to see if it\u0026rsquo;s less than our current smallest directory.\nPheww\u0026hellip;That\u0026rsquo;s it for day 7! I know this approach is a bit too structured for an AoC problem and initially, I solved it without building the entire structure, tokens etc\u0026hellip; For the sake of this blog post, I thought I\u0026rsquo;ll make things a bit more structured and interesting\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-7/","summary":"Question This was a fun day! we finally got something challenging.\nWe got a device from the elves when trying to update that device we got an error that notifies us that there isn\u0026rsquo;t enough memory on our device\n1 2 $ system-update --please --pretty-please-with-sugar-on-top Error: No space left on the device To determine what\u0026rsquo;s going on we start exploring the device file system and record the output (our puzzle input) For example","title":"No Space Left On Device"},{"content":"Question Finally, we move out of camp! As we start heading out of the camp one of the elves gives us a communication device, it will come as no surprise that we got the only malfunctioning device\u0026hellip;\nThe device receives streams of signals that are built from a list of chars, to fix our device we need to be able to find the start-of-packet marker.\nPart 1 We are told that the start of the packet is defined as a series of four different consecutive chars. Given our signle, we need to determine how many chars should be processed before the first marker appears or in other words the index at the end of our marker.\nFor example, mjq\u0026lt;start\u0026gt;jpqm\u0026lt;end\u0026gt;gbljsphdztnvjfqwrcgsmlb\n\u0026lt;start\u0026gt; and \u0026lt;end\u0026gt; represents the start and end of the marker accordingly\nmeaning the answer should be 7 (index of m)\nBasically what we need to accomplish here is to find 4 consecutive chars in a row, there are multiple ways of doing this but we will use Sets to count the number of unique chars within a given range i.e i -\u0026gt; i+4 In each position of that range, we will take the char and add it to our set, at the end of the range if the set size is 4 then we got a winner and we can return our current index i + 4.\nSet is a data structure that guarantees the uniqueness of each key, in Go, there isn\u0026rsquo;t a built-in type for that but we can easily create a set using a map[rune]bool type to make this a bit more interesting let\u0026rsquo;s create a generic set package\nSet We will create a package called set and in it, a struct named SimpleSet that will support a basic set of operations\nAdd Has Size Here is the code for our SimpleSet 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package set type SimpleSet[T comparable] struct { items map[T]bool } func NewSimpleSet[T comparable](values ...T) *SimpleSet[T] { m := make(map[T]bool) return \u0026amp;SimpleSet[T]{ items: m, } } func (s *SimpleSet[T]) Add(key T) { s.items[key] = true } func (s *SimpleSet[T]) Has(key T) bool { _, ok := s.items[key] return ok } func (s *SimpleSet[T]) Size() int { return len(s.items) } I\u0026rsquo;m using generics to have this set useful in days to come, you can read more about it here\nI don\u0026rsquo;t get how come Go didn\u0026rsquo;t have generics until recently, imagine repeating the same code for our set for every type!\nArmed with our new set, lets solve part 1!\n1 2 3 4 5 6 7 8 9 10 11 12 13 func Part1(raw string) int { for i := range raw { set := set.NewSimpleSet[rune]() for _, c := range raw[i : i+4] { set.Add(c) } if set.Size() == 4 { return i + 4 } } return -1 } Part 2 Exactly like part 1 but now the marker needs to be 14 consecutive chars We can take our part 1 solution and have it accept an offset to fit both parts\n1 2 3 4 5 6 7 8 9 10 11 12 13 func Part1(raw string, offset int) int { for i := range raw { set := set.NewSimpleSet[rune]() for _, c := range raw[i : i+offset] { set.Add(c) } if set.Size() == offset { return i + offset } } return -1 } Our part 2 solution will be Part1(input, 14.\nThe solution can be optimized a bit by returning early if a char is already in our set, before we do anything we first need to measure our current solution.\nThis can be easily done using Go benchmarks capabilities.\nCreate a new file main_test.go and write our benchmarks there\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package main import ( \u0026#34;testing\u0026#34; \u0026#34;github.com/galElmalah/aoc-2022/util\u0026#34; ) func BenchmarkPart1(b *testing.B) { input := util.ReadFile(\u0026#34;./input.txt\u0026#34;) // run the Fib function b.N times for n := 0; n \u0026lt; b.N; n++ { Part1(input, 4) } } func BenchmarkPart2(b *testing.B) { input := util.ReadFile(\u0026#34;./input.txt\u0026#34;) // run the Fib function b.N times for n := 0; n \u0026lt; b.N; n++ { Part1(input, 14) } } Running go test -bench=. -count 3 results in\n1 2 3 4 5 6 BenchmarkPart1-8 3819 285783 ns/op BenchmarkPart1-8 3873 285734 ns/op BenchmarkPart1-8 4021 284767 ns/op BenchmarkPart2-8 1086 1083411 ns/op BenchmarkPart2-8 1083 1073575 ns/op BenchmarkPart2-8 1046 1075867 ns/op Now let\u0026rsquo;s add the following if to our inner loop\n1 2 3 if set.Has(c) { break } re-run the benchmark\n1 2 3 4 5 6 BenchmarkPart1-8 3607 296341 ns/op BenchmarkPart1-8 3748 294505 ns/op BenchmarkPart1-8 3734 289663 ns/op BenchmarkPart2-8 2490 465996 ns/op BenchmarkPart2-8 2526 454670 ns/op BenchmarkPart2-8 2541 451878 ns/op we can see that for part 1 there is barely an improvement but for part 2 that early return does make a noticeable difference, awesome!\nThat\u0026rsquo;s it for today, we created our very own Set data structure and used go benchmarks to optimize our solution.\nI hoped you enjoyed and learned something new cause I sure did!\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-6/","summary":"Question Finally, we move out of camp! As we start heading out of the camp one of the elves gives us a communication device, it will come as no surprise that we got the only malfunctioning device\u0026hellip;\nThe device receives streams of signals that are built from a list of chars, to fix our device we need to be able to find the start-of-packet marker.\nPart 1 We are told that the start of the packet is defined as a series of four different consecutive chars.","title":"Tuning Trouble"},{"content":"Question\nWelcome to day 5 of AoC, aka, annoying AF parsing day.\nWe are crane operators! What do we want?! More stacks and crates!\nAnyhow, we are charged to execute a series of delicate manuvers using our insane crane skills\nOur input is given to us as follows\n1 2 3 4 5 6 7 8 9 [D] [N] [C] [Z] [M] [P] 1 2 3 move 1 from 2 to 1 move 3 from 1 to 3 move 2 from 2 to 1 move 1 from 1 to 2 The first half represents the current state of stacks and crates. The second one is a series of moves we need to perform.\nCrates can only be moved one at a time!\nParsing How can we parse that input into something meaningful? Well first let\u0026rsquo;s seprate both halves using\n1 2 3 parts := strings.Split(raw, \u0026#34;\\n\\n\u0026#34;) rawStacks := parts[0] rawMoves := parts[1] Now let\u0026rsquo;s tackle parsing the crates!\nOne way we can go about it is simply copy-pasting our input into an object by hand but where is the fun in that?!\nIf we look closely at our input we might notice that the letters fit in chunks of four chars, for example\n1 2 3 4 5 6 7 8 9 0 | 1 | 2 | // positions in chunks array xxxxxxxxxxxx [D] [N] [C] [Z] [M] [P] 1 2 3 // row 1 col 1,2,3 chunks -\u0026gt; [ [\u0026#34; \u0026#34;,\u0026#34;[D] \u0026#34;,\u0026#34; \u0026#34;] ...] Armed with that information we can write the following code to prase crates and classify them into the right stack\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func parseStacks(crates []string) [][]string { stacks := make([][]string, 9) for _, row := range crates { rowOfCrates := chunkBy(strings.Split(row, \u0026#34;\u0026#34;), 4) for crateNo, crateCandidate := range rowOfCrates { for _, char := range crateCandidate { if char \u0026gt;= \u0026#34;A\u0026#34; \u0026amp;\u0026amp; char \u0026lt;= \u0026#34;Z\u0026#34; { //pre-appending an element to array stacks[crateNo] = append([]string{char}, stacks[crateNo]...) } } } } return stacks } Let us look at the list of instructions, each line represents one command move x from y to z, lets\u0026rsquo;s create a struct to populate exactly that and turn our list into a list of those structs\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 type Instruction struct { amount int from int to int } func toInts(fromStrings []string) (result []int) { for _, n := range fromStrings { num, _ := strconv.Atoi(n) result = append(result, num) } return result } func parseInstructions(rawInstructions []string) (instructions []Instruction) { matcher := regexp.MustCompile(`move (\\d+) from (\\d+) to (\\d+)`) for _, line := range rawInstructions { match := toInts(matcher.FindStringSubmatch(line)[1:]) instructions = append(instructions, Instruction{ amount: match[0], from: match[1] - 1, to: match[2] - 1, }) } return instructions } Combining parseInstructions and parseStacks to get our parse function\n1 2 3 4 5 6 7 func parse(raw string) ([][]string, []Instruction) { chunks := strings.Split(string(raw), \u0026#34;\\n\\n\u0026#34;) rawCrates := strings.Split(chunks[0], \u0026#34;\\n\u0026#34;) rawInstructions := strings.Split(chunks[1], \u0026#34;\\n\u0026#34;) return parseStacks(rawCrates), parseInstructions(rawInstructions) } In go you can have more than one return value.\nPart 1 Perform the list of instructions then construct a string built from the top crate of each stack, for example in our current example its NDP but after the instructions are applied its CMZ\nUsing our parsed instructions and stacks, the solution becomes quite trivial\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func Part1(raw string) string { stacks, instructions := parse(string(raw)) // applying instructions for _, instruction := range instructions { from := instruction.from to := instruction.to // another approach is to create a slice of size amount from `moveFrom` // reverse that slice and push it to `moveTo` but the approach here is much simpler to reason about for i := 0; i \u0026lt; instruction.amount; i++ { stacks[to] = append(stacks[to], stacks[from][len(stacks[from])-1]) stacks[from] = stacks[from][:len(stacks[from])-1] } } answer := \u0026#34;\u0026#34; for _, stack := range stacks { if len(stack) \u0026gt; 0 { answer += stack[len(stack)-1] } } return answer } And that\u0026rsquo;s it for part one, besides the parsing it was quite simple.\nPart 2 Something was off with our stacks, we went to make sure that our crane is CrateMover 9000 but it was the 9001 model! this means we can move multiple crates at once.\nThe meaning of this in the context of the code we wrote is that we don\u0026rsquo;t need to preserve a stack-like order when moving crates, the top element from our source crane will stay on top instead of being at the bottom of the crates we are moving\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func Part2(raw string) string { stacks, instructions := parse(string(raw)) // mutating the stacks for _, instruction := range instructions { from := instruction.from to := instruction.to amount := instruction.amount takeRange := len(stacks[from]) - amount // take items from `takeRange` until the end of the slice and append them to the target stack stacks[to] = append(stacks[to], stacks[from][takeRange:]...) // remove items that come after the `takeRange` from our source crate stacks[from] = stacks[from][:takeRange] } answer := \u0026#34;\u0026#34; for _, stack := range stacks { if len(stack) \u0026gt; 0 { answer += stack[len(stack)-1] } } return answer } That\u0026rsquo;s it for day 5 of AoC, hoped you enjoyed reading it, and feel free to suggest improvements to my poor Go or solution\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-5/","summary":"Question\nWelcome to day 5 of AoC, aka, annoying AF parsing day.\nWe are crane operators! What do we want?! More stacks and crates!\nAnyhow, we are charged to execute a series of delicate manuvers using our insane crane skills\nOur input is given to us as follows\n1 2 3 4 5 6 7 8 9 [D] [N] [C] [Z] [M] [P] 1 2 3 move 1 from 2 to 1 move 3 from 1 to 3 move 2 from 2 to 1 move 1 from 1 to 2 The first half represents the current state of stacks and crates.","title":"Supply Stacks"},{"content":"Question Seems like this time we are tasked with optimizing the elves\u0026rsquo; cleaning tasks! those elves can improve their lists\u0026hellip; The cleaning tasks are assigned to each pair of elves, for example:\n1 2 3 4 5 6 2-4,6-8 2-3,4-5 5-7,7-9 2-8,3-7 6-6,4-6 2-6,4-8 For the first few pairs, this list means:\nWithin the first pair of Elves, the first Elf was assigned sections 2-4 (sections 2, 3, and 4), while the second Elf was assigned sections 6-8 (sections 6, 7, 8). The Elves in the second pair were each assigned two sections. As always, let\u0026rsquo;s start with parsing our list of assignments to something we can work with\nParsing First, let\u0026rsquo;s decide what we would like our output to look like. we can create a struct that represents each pair task but that seems a bit redundant, let\u0026rsquo;s keep it simple and create something like [[st1, et1, st2, et2] , ... ] where st1 and et1 is the first elf range of sections and st2 and et2 are the second elf range.\nWe will call a range of sections a \u0026ldquo;task\u0026rdquo; from now on.\nTo get that we can go with splitting on various delimiters but today I finally had an excuse to use a Regex in go.\nWe will create a regex, containing capture groups for each t and use FindStringSubmatch to iterate over the resulting matches.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func parse(raw string) [][]int { lines := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) pairs := [][]int{} r := regexp.MustCompile(`(\\d+)-(\\d+),(\\d+)-(\\d+)`) for _, l := range lines { match := r.FindStringSubmatch(l) pair := []int{} for _, m := range match[1:] { num, _ := strconv.Atoi(m) pair = append(pair, int(num)) } pairs = append(pairs, pair) } return pairs } Its worth noting that the first match is the entire string we are passing and that\u0026rsquo;s why we are skipping it when looping over the results range match[1:]\nPart 1 The elves are really smart and noticed that some tasks entirely overlap with their partner tasks. We are tasked with finding and counting those overlaps.\nLet\u0026rsquo;s draw this out so it will be a bit clearer\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .234..... 2-4 // no overlapping .....678. 6-8 .23...... 2-3 // no overlapping ...45.... 4-5 ....567.. 5-7 // just a partial overlapping NOT GOOD ENOUGH ......789 7-9 .2345678. 2-8 ..34567.. 3-7 // contained in 2-8 .....6... 6-6 // contained in 4-6 ...456... 4-6 .23456... 2-6 // again, partial overlapping ...45678. 4-8 From the above, we can say that if one of the elves\u0026rsquo; starting tasks is greater or equal to the other elf starting task AND the final task is smaller or equal, then the assignment is contained. We now write a solution to part 1\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func part1(raw []byte) int { var assignments = parse(string(raw)) count := 0 for _, pairAssignmentRange := range assignments { st1 := pairAssignmentRange[0] et1 := pairAssignmentRange[1] st2 := pairAssignmentRange[2] et2 := pairAssignmentRange[3] if (st1 \u0026gt;= st2 \u0026amp;\u0026amp; et1 \u0026lt;= et2) || (st2 \u0026gt;= st1 \u0026amp;\u0026amp; et2 \u0026lt;= et1) { count++ } } return count } If you know a better way to take those values from pairAssignmentRange let me know in the comment section\nPart 2 We are asked to count overlapping tasks of any kind meaning its enough that we will have one overlapping \u0026ldquo;section\u0026rdquo; in our range for it to count as an overlap What does that mean in code? it means that we are looking for tasks that are ending inside the range of the other task. For example\n1 2 ....567.. 5-7 ......789 7-9 These tasks overlap over \u0026ldquo;section\u0026rdquo; 7. We can now write our part 2 solution as follows\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func part2(raw []byte) int { var assignments = parse(string(raw)) count := 0 for _, pairAssignmentRange := range assignments { st1 := pairAssignmentRange[0] et1 := pairAssignmentRange[1] st2 := pairAssignmentRange[2] et2 := pairAssignmentRange[3] if (et1 \u0026gt;= st2 \u0026amp;\u0026amp; et1 \u0026lt;= et2) || (et2 \u0026gt;= st1 \u0026amp;\u0026amp; et2 \u0026lt;= et1) { count++ } } return count } The only thing we changed was the if statement to look at the ending task \u0026ldquo;section\u0026rdquo;\nTo sum things up, each day I feel more and more confident with Go, and the task\u0026rsquo;s code starts flowing.\nI think that one of Go\u0026rsquo;s upsides is that the language is fairly small without any fancy concepts so the learning curve is not that long or hard.\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-4/","summary":"Question Seems like this time we are tasked with optimizing the elves\u0026rsquo; cleaning tasks! those elves can improve their lists\u0026hellip; The cleaning tasks are assigned to each pair of elves, for example:\n1 2 3 4 5 6 2-4,6-8 2-3,4-5 5-7,7-9 2-8,3-7 6-6,4-6 2-6,4-8 For the first few pairs, this list means:\nWithin the first pair of Elves, the first Elf was assigned sections 2-4 (sections 2, 3, and 4), while the second Elf was assigned sections 6-8 (sections 6, 7, 8).","title":"Camp Cleanup"},{"content":"Question We are going into the jungle! We put Legolas in charge of packing the supplies for our journey but it seems like his packing abilities are nothing like his bow-aiming skills and he kind of sucks at packing\u0026hellip; We do however have a manifest of the items in each rucksack, that list looks as follows\n1 2 3 4 5 6 vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw Each line represents one rucksack and each half of the line represents a compartment inside a rucksack. Items are identifiable by their chars, and yes, \u0026ldquo;A\u0026rdquo; and \u0026ldquo;a\u0026rdquo; are not the same items. For example The first rucksack contains the items vJrwpWtwJgWrhcsFMMfFFhFp, which means its first compartment contains the items vJrwpWtwJgWr, while the second compartment contains the items hcsFMMfFFhFp. The only item type that appears in both compartments is lowercase p.\nTo help prioritize item rearrangement, every item type can be converted to a priority:\nLowercase item types a through z have priorities 1 through 26. Uppercase item types A through Z have priorities 27 through 52.\nIn the above example, the priority of the item type that appears in both compartments of each rucksack is 16 (p), 38 (L), 42 (P), 22 (v), 20 (t), and 19 (s); the sum of these is 157.\nPart 1 In part one we are tasked with summing up the priority of items that appears in both compartments\nLet\u0026rsquo;s start with parsing our input\nParsing Ideally, we would like to have an array containing a tuple with a set for each half for example vJrwpWtwJgWrhcsFMMfFFhFp -\u0026gt; [ [set(vJrwpWtwJgWr), set(hcsFMMfFFhFp)]... ] We are choosing a set DS here to have the ability to easily answer the question \u0026ldquo;is the letter X in set Y?\u0026rdquo; later on\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // go doesn\u0026#39;t have a built in Set DS so we need to roll our own here func makeSet(chars string) map[rune]bool { set := map[rune]bool{} for _, c := range chars { set[c] = true } return set } func parse() [][]map[rune]bool { data, _ := os.ReadFile(\u0026#34;./input.txt\u0026#34;) lines := strings.Split(string(data), \u0026#34;\\n\u0026#34;) rucksacks := [][]map[rune]bool{} for _, line := range lines { c1 := makeSet(line[:len(line)/2]) c2 := makeSet(line[len(line)/2:]) w := []map[rune]bool{c1, c2} rucksacks = append(rucksacks, w) } return rucksacks } Interesting to note that we are getting each char as a rune which is the ASCII value of said char\nWe are using : to slice our line into two parts, line[x:] means slice from x until the end, and lines[:x] means slice from the beginning of the array until x\nSolution Now that we are done preparing our data we can write the following code to calc the priority of each char\n1 2 3 4 5 6 7 func calcPriority(c rune) int { if c \u0026gt;= \u0026#39;a\u0026#39; \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;z\u0026#39; { return int(c) - \u0026#39;a\u0026#39; + 1 } else { return int(c) - \u0026#39;A\u0026#39; + 27 } } Basically, we are taking our rune, deducting the base value e.g \u0026lsquo;a\u0026rsquo; or \u0026lsquo;A\u0026rsquo;, and adding the range from the question, meaning, lowercase from 1 to 26 and uppercase from 27 to 52\nThis code won\u0026rsquo;t work if I use double quotes in the comparison since you can\u0026rsquo;t compare strings and runes, the single quotes actually define \u0026lsquo;a\u0026rsquo; as having the type rune\nOk we are all ready to go now, let\u0026rsquo;s solve part 1 We are going to iterate over all rucksacks and for each one of those find the letter that is in the first compartment and the second one. When we find one, we pass it along to our calcPriority function and accumulate its value\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func pt1() int { groups := parse() sum := 0 for _, group := range groups { s1 := group[0] // first compartment s2 := group[1] // second compartment for k, _ := range s1 { if s2[k] { sum += calcPriority(k) } } } return sum } cool cool cool, we\u0026rsquo;re all done with part 1 let\u0026rsquo;s see what part 2 got in store for us\nPart 2 We are not tasked with finding the priority of the group badges, a badge is defined to be an item that is contained in 3 consecutive rucksacks\nFrom the example input above we can draw the following example\n1 2 3 vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg And the second group\u0026rsquo;s rucksacks are the next three lines:\n1 2 3 wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw In the first group, the only item type that appears in all three rucksacks is lowercase r; this must be their badges. In the second group, their badge item type must be Z.\nPriorities for these items must still be found to organize the sticker attachment efforts: here, they are 18 (r) for the first group and 52 (Z) for the second group. The sum of these is 70.\nWe will need to tweak our parsing code a bit to create sets for chunks of 3 rows at a time, this looks like this\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func chunkInto(s []string, size int) [][]string { results := [][]string{} for i := 0; i \u0026lt; len(s); i += size { results = append(results, s[i:i+size]) } return results } func parse2() [][]map[rune]bool { data, _ := os.ReadFile(\u0026#34;./input.txt\u0026#34;) rows := strings.Split(string(data), \u0026#34;\\n\u0026#34;) groups := chunkInto(rows, 3) rucksacks := [][]map[rune]bool{} for _, chunk := range groups { w := []map[rune]bool{} for _, c := range chunk { w = append(w, makeSet(c)) } rucksacks = append(rucksacks, w) } return rucksacks } We can now solve part 2 very similarly to part 1\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func pt2() int { groups := parse2() sum := 0 for _, group := range groups { s1 := group[0] s2 := group[1] s3 := group[2] for k, _ := range s1 { if s2[k] \u0026amp;\u0026amp; s3[k] { sum += calcPriority(k) } } } return sum } And that\u0026rsquo;s it for today boys and girls, I hope you are enjoying AoC as much as I do so far 🙂\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-3/","summary":"Question We are going into the jungle! We put Legolas in charge of packing the supplies for our journey but it seems like his packing abilities are nothing like his bow-aiming skills and he kind of sucks at packing\u0026hellip; We do however have a manifest of the items in each rucksack, that list looks as follows\n1 2 3 4 5 6 vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw Each line represents one rucksack and each half of the line represents a compartment inside a rucksack.","title":"Rucksack Reorganization"},{"content":"Question\nThe Elves begin to set up camp on the beach. To decide whose tent gets to be closest to the snack storage, a giant Rock Paper Scissors tournament is already in progress.\nGod the question\u0026rsquo;s description is always pure gold\u0026hellip;now let\u0026rsquo;s jump right in.\nWe are given a string, our \u0026ldquo;strategy guide\u0026rdquo; that represents a rock, paper, scissors turn decision. The string is divided into two columns, the first represents the first player\u0026rsquo;s decision and the second ours. For example:\n1 2 3 A Y B X C Z We are also told the following: For player 1 - A = Rock, B = Paper, C = Scissors For player 2 - X = Rock, Y = Paper, Z = Scissors We are also given a score table for the outcome and the decision we made, for example, a win is 6 points and choosing paper is 2 points, etc\u0026hellip;\nLike previous questions, we\u0026rsquo;ll start by parsing our input\nParsing We can go (see what I did there?) with several options to represent our game but in my opinion, an array of tuples is the simplest option\n1 2 3 4 5 6 7 8 9 10 11 func parse(raw string) [][]string { chunks := strings.Split(string(raw), \u0026#34;\\n\u0026#34;) pairs := make([][]string, len(chunks)) for i := range pairs { pairs[i] = strings.Split(chunks[i], \u0026#34; \u0026#34;) } return pairs } // example output // [ [A, Y], [B, X], [C, Z] ] The make function allocates a piece of memory in a certain, specified size for our array\nPart 1 We are asked to provide our total score if we play exactly as instructed in the strategy guide. Let\u0026rsquo;s think about this for a bit, there are several ways we can solve this, we can use a bunch of if statements or some fancy pattern matching, since go does not have pattern matching and I don\u0026rsquo;t want to write a ton of if statements we will go with a hybrid approach. We will create 3 different mappings:\nRepresents the points we get for our choice e.g rock, paper, or scissors Winning state, meaning If we choose X what does the other player need to choose for us to Win Tie state, essentially the same as .2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 scores := map[string]int{ \u0026#34;X\u0026#34;: 1, \u0026#34;Y\u0026#34;: 2, \u0026#34;Z\u0026#34;: 3, } // If I choose X(Rock) I need him to choose C(scissors) in order to win win := map[string]string{ \u0026#34;X\u0026#34;: \u0026#34;C\u0026#34;, \u0026#34;Y\u0026#34;: \u0026#34;A\u0026#34;, \u0026#34;Z\u0026#34;: \u0026#34;B\u0026#34;, } tie := map[string]string{ \u0026#34;X\u0026#34;: \u0026#34;A\u0026#34;, \u0026#34;Y\u0026#34;: \u0026#34;B\u0026#34;, \u0026#34;Z\u0026#34;: \u0026#34;C\u0026#34;, } We don\u0026rsquo;t take into account the losing state since its essentially a no-op (0 points)\nBuilding on top of these maps and our parsing logic, we can now solve the first part with the following code\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 func part1(raw []byte) int { pairs := parse(string(raw)) // X Rock, Y Paper, Z Scissors scores := map[string]int{ \u0026#34;X\u0026#34;: 1, \u0026#34;Y\u0026#34;: 2, \u0026#34;Z\u0026#34;: 3, } win := map[string]string{ \u0026#34;X\u0026#34;: \u0026#34;C\u0026#34;, \u0026#34;Y\u0026#34;: \u0026#34;A\u0026#34;, \u0026#34;Z\u0026#34;: \u0026#34;B\u0026#34;, } tie := map[string]string{ \u0026#34;X\u0026#34;: \u0026#34;A\u0026#34;, \u0026#34;Y\u0026#34;: \u0026#34;B\u0026#34;, \u0026#34;Z\u0026#34;: \u0026#34;C\u0026#34;, } score := 0 for _, pair := range pairs { his := pair[0] my := pair[1] score += scores[my] if win[my] == his { score += WINNING_POINTS } if tie[my] == his { score += TIE_POINTS } } return score } // output for part 1 based on the example is // 15 -\u0026gt; (8 + 1 + 6) At each loop iteration we first add the points based on our choice score += scores[my] then we check if his move is what we need based on our player choice, to win or get a tie, and if it is we add the necessary points to our total score.\nPart 2 In part two the sneaky elves switch things up a bit. Instead of our column representing our moves, it represents the turn outcome where X = lose, Y = tie, and Z = win and we need to choose our choice accordingly. For example, let\u0026rsquo;s look at the first turn A Y, the new meaning of this pair is \u0026ldquo;player one chose Rock, and the game ended in a tie\u0026rdquo; building on this information we can create new mappings, the new mappings will be between player 1 choice and the choice player 2 need to make to get to a certain state e.g winning, losing, tie, etc\u0026hellip; Since it\u0026rsquo;s pretty similar to part 1, we will jump right ahead and look at part 2 as a whole\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 func part2(raw []byte) int { var pairs = parse(string(raw)) // X Lose, Y Tie, Z Win scores := map[string]int{ \u0026#34;X\u0026#34;: 1, \u0026#34;Y\u0026#34;: 2, \u0026#34;Z\u0026#34;: 3, } win := map[string]string{ \u0026#34;C\u0026#34;: \u0026#34;X\u0026#34;, \u0026#34;A\u0026#34;: \u0026#34;Y\u0026#34;, \u0026#34;B\u0026#34;: \u0026#34;Z\u0026#34;, } tie := map[string]string{ \u0026#34;A\u0026#34;: \u0026#34;X\u0026#34;, \u0026#34;B\u0026#34;: \u0026#34;Y\u0026#34;, \u0026#34;C\u0026#34;: \u0026#34;Z\u0026#34;, } lose := map[string]string{ \u0026#34;A\u0026#34;: \u0026#34;Z\u0026#34;, \u0026#34;B\u0026#34;: \u0026#34;X\u0026#34;, \u0026#34;C\u0026#34;: \u0026#34;Y\u0026#34;, } score := 0 for _, pair := range pairs { hisMove := pair[0] myMove := pair[1] // we lose if myMove == \u0026#34;X\u0026#34; { score += scores[lose[hisMove]] } // we end in a tie if myMove == \u0026#34;Y\u0026#34; { score += TIE_POINTS score += scores[tie[hisMove]] } // we win if myMove == \u0026#34;Z\u0026#34; { score += WINNING_POINTS score += scores[win[hisMove]] } } return score } For each desired state we check what move we need to do based on player 2 choice and pass it down to the scores map.\nThat\u0026rsquo;s it we are all done with paper, rock, scissors and I must admit that I didn\u0026rsquo;t think it can be so confusing 🤣\nYou can find the complete code here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-2/","summary":"Question\nThe Elves begin to set up camp on the beach. To decide whose tent gets to be closest to the snack storage, a giant Rock Paper Scissors tournament is already in progress.\nGod the question\u0026rsquo;s description is always pure gold\u0026hellip;now let\u0026rsquo;s jump right in.\nWe are given a string, our \u0026ldquo;strategy guide\u0026rdquo; that represents a rock, paper, scissors turn decision. The string is divided into two columns, the first represents the first player\u0026rsquo;s decision and the second ours.","title":"Rock Paper Scissors 🪨🧻✂"},{"content":"The moment we have all been waiting for, December first. The end of the year is upon us and so those Advent of Code!\nTo those of you who don\u0026rsquo;t know, AoC is a yearly programming competition where we try to help Santa do some funky sh**. The competition itself is speed and time based, you can read more about it here.\nQuestion\nPart 1 We are given a list of numbers where each consecutive block of numbers represents the calories an elf is carrying with him. For example\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 1000 - \\ 2000 - -\u0026gt; an elf carrying 6000 calories 3000 - / 4000 5000 6000 7000 - \\ 8000 - -\u0026gt; an elf carrying 24000 calories - the Maximum in our list 9000 - / 10000 This problem seems simple enough, we need to split the string on empty lines then take each chunk and sum the numbers in it. Once we obtained the various chunks (elf calories) we can search for the maximum value and we have our answer.\nParsing Reading our input from a file named \u0026ldquo;input.txt\u0026rdquo;\n1 2 3 4 5 6 7 8 func parse() []string { data, _ := os.ReadFile(\u0026#34;./input.txt\u0026#34;) chunks := strings.Split(string(data), \u0026#34;\\n\\n\u0026#34;) return chunks } // example output // [\u0026#34;1000\\n2000\\n3000\u0026#34;, \u0026#34;4000\u0026#34;, \u0026#34;5000\\n6000\u0026#34;, ....] Why are we splitting the string on \u0026ldquo;\\n\\n\u0026rdquo;? The only char in an empty line is \u0026ldquo;\\n\u0026rdquo;, combining this with the line before we get \u0026ldquo;\\n\\n\u0026rdquo;\nSumming the Chunks Adding a new function that gets a string representing a chunk e.g 1000\\n2000\\n3000 and returns the sum of those numbers\n1 2 3 4 5 6 7 8 9 10 func sumChunk(chunk string) int { sum := 0 for _, num := range strings.Split(chunk, \u0026#34;\\n\u0026#34;) { v, _ := strconv.Atoi(num) sum += v } return sum } // example output // [6000, 4000, 11000, ....] to convert a string to an int we need to import the strconv package and use the atoi function (ascii to int). Now its probably a good point to mention that in Go you deal with errors by returning them, in the function above the _ returned from atoi represent a possible error that we should deal with but since AoC is all about speed we can just tell the compiler to ignore it by naming it _.\nPutting it All Together 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;sort\u0026#34; \u0026#34;strconv\u0026#34; \u0026#34;strings\u0026#34; ) func main() { chunks := parse() // result := []int{} for _, chunk := range chunks { result = append(result, sumChunk(chunk)) } sort.Ints(result) fmt.Println(\u0026#34;Part 1\u0026#34;) fmt.Println(result[len(result)-1]) } Again for the sake of speed we will just store all the results, sort them in ascending order, using Go sort package and use the last position as our answer.\nI know, I know I could have found the max entry by comparing them inside the for loop but you\u0026rsquo;ll see it will be worth it in part 2.\nPart 2 In part 2 we are asked to return the sum of the 3 elves carrying the most calories i.e take our sorted array and sum the last 3 elements We can easily tweak our answer to accommodate these new requirements by adding this line\n1 2 fmt.Println(\u0026#34;Part 2\u0026#34;) fmt.Println(result[len(result)-1] + result[len(result)-2] + result[len(result)-3]) I used sorting to get the biggest N elements in the array but there are other more performant approaches here like using a max-heap data structure BUT AoC is about keeping it simple and getting a valid answer ASAP so unless you know you\u0026rsquo;ll have a performance problem it\u0026rsquo;s better to go with the simpler and more naive solution most often.\nYou can find a complete code example here Thanks for reading!\n","permalink":"https://galelmalah.com/posts/advent-of-code/2022/day-1/","summary":"The moment we have all been waiting for, December first. The end of the year is upon us and so those Advent of Code!\nTo those of you who don\u0026rsquo;t know, AoC is a yearly programming competition where we try to help Santa do some funky sh**. The competition itself is speed and time based, you can read more about it here.\nQuestion\nPart 1 We are given a list of numbers where each consecutive block of numbers represents the calories an elf is carrying with him.","title":"Calorie Counting"},{"content":"Hey there, I\u0026rsquo;m Gal, a software team lead from Israel. I\u0026rsquo;m also a big fantasy books fan, runner, and MTB enthusiast. My background is mainly in JS but I did get to dabble with different programming languages in uni \u0026amp; work so the notion of compiled statically typed languages is familiar to me (just a bit)\nWhat A series of posts showcasing various coding questions, AoC problems, general programming in Go, and probably a random thought here and there\nWhy The purpose of these blog posts is mainly to make me think critically and dive into specific aspects of the Go language. Getting some harsh, honest feedback is always great, and what better way to get it than posting my code on the internet for everyone to talk shit about?! I do hope that this series will help different people to learn Go or just have fun with various coding questions\nWho Who is this for? Well, just about anyone. You can be a complete beginner or an experienced veteran, either way, you might learn something new and just have fun following along\n","permalink":"https://galelmalah.com/posts/learning-go/intro/","summary":"Hey there, I\u0026rsquo;m Gal, a software team lead from Israel. I\u0026rsquo;m also a big fantasy books fan, runner, and MTB enthusiast. My background is mainly in JS but I did get to dabble with different programming languages in uni \u0026amp; work so the notion of compiled statically typed languages is familiar to me (just a bit)\nWhat A series of posts showcasing various coding questions, AoC problems, general programming in Go, and probably a random thought here and there","title":"Learning Go Series"},{"content":"Following my previous blog post 5 Cli Tools That Will Increase Your Dev Velocity And Code Quality I wrote a quick guide on how to write and publish a CLI.\nWhat\u0026rsquo;s in it for you? Write a cool as f*** CLI tool. Learn how to set up a project using Typescript. Publish your new shiny CLI to npm. setup We will use Scaffolder to generate all the boilerplate we need for our shiny CLI.\n1 npx scaffolder-cli interactive --from-github https://github.com/galElmalah/ts-cli-scaffolder.git --template cli Scaffolder makes creating and sharing boilerplate code a breeze, Check it out!\nOnce npm has finished installing all of our dependencies, we should have a clean, greenfield project.\nLet\u0026rsquo;s have a quick look at the package.json file.\nFirst of all, as you can see we got a postfix to our name field, I added this to prevent naming conflicts with existing packages 😄\nSecond, we got a bin field. bin field tells npm that this package has an executable that should be invoked using the coolGroup command.\n1 2 3 \u0026#34;bin\u0026#34; : { \u0026#34;coolGroup\u0026#34; : \u0026#34;./dist/cli.js\u0026#34; } Finally, we have commander as a dependency. We are going to use it to register commands for our cli to act on.\nIn a gist commander makes creating CLI\u0026rsquo;s a breeze\nNow Let\u0026rsquo;s quickly go over the tsconfig.json file.\n1 2 3 4 5 6 7 8 9 10 { \u0026#34;compilerOptions\u0026#34;: { \u0026#34;module\u0026#34;: \u0026#34;commonJs\u0026#34;, // Module code generation \u0026#34;target\u0026#34;: \u0026#34;es6\u0026#34;, // Target a specific ECMAScript version \u0026#34;outDir\u0026#34;: \u0026#34;dist/\u0026#34;, // The TSC compiler will output our files to the ./dist folder \u0026#34;lib\u0026#34;: [\u0026#34;es6\u0026#34;] // Specify library files to be included in the compilation step }, \u0026#34;files\u0026#34;: [\u0026#34;src/cli.ts\u0026#34;], // Mark cli.ts as our entry point \u0026#34;exclude\u0026#34;: [\u0026#34;node_modules\u0026#34;] } We mentioned ./dist/cli.js in the bin field. We can do that because we tell typescript to compile our code into a dist folder.\nIf you want to learn more about Typescript or tsconfig.json, I recommend this free book.\nWe are finally done going over our boilerplate. Let\u0026rsquo;s get down to business. We are going to write a simple CLI that does the following:\nGo over all the files in a directory and get their extension. Create a folder for each type of file extension. Move all the files to their matching folders. 0.5. Some imports for later 1 2 import { readdirSync, existsSync, statSync, mkdirSync, renameSync } from \u0026#39;fs\u0026#39;; import { join } from \u0026#39;path\u0026#39;; 1. Go over all the files in a directory and get their extension. 1 2 3 4 5 6 7 8 9 // `getPath` is a little helper that will make more sense when we will look at the whole file. const getPath = (...paths) =\u0026gt; join(sourcePath, ...paths); const toFileExtension = (fromFileName: string) =\u0026gt; fromFileName.split(\u0026#39;.\u0026#39;).pop(); const isFile = (aFile: string) =\u0026gt; statSync(getPath(aFile)).isFile(); const files = readdirSync(sourcePath).filter(isFile); const getWorkingDirectoryFileExtensions = (): string[] =\u0026gt; Array.from(new Set(files.map(toFileExtension))); 2. Create a folder for each type of file extension. If the folder already exists, then skip its creation to avoid errors.\n1 2 3 4 5 6 7 8 const createDirectory = (aFileExtension: string) =\u0026gt; mkdirSync(getPath(aFileExtension)); const shouldCreateFolder = (aFileExtension: string) =\u0026gt; !existsSync(getPath(aFileExtension)); getWorkingDirectoryFileExtensions() .filter(shouldCreateFolder) .forEach(createDirectory); 3. Move all the files to their matching folders. 1 2 3 4 const moveToFileExtensionFolder = (aFile) =\u0026gt; renameSync(getPath(aFile), getPath(toFileExtension(aFile), aFile)); files.forEach(moveToFileExtensionFolder); Putting it all together We are going to put all of this logic inside a file named groupFilesByExtensions.ts\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { readdirSync, existsSync, statSync, mkdirSync, renameSync } from \u0026#39;fs\u0026#39;; import { join } from \u0026#39;path\u0026#39;; export const groupFilesByExtensions = (sourcePath: string) =\u0026gt; { const getPath = (...paths: string[]) =\u0026gt; join(sourcePath, ...paths); const toFileExtension = (fromFileName: string) =\u0026gt; fromFileName.split(\u0026#39;.\u0026#39;).pop(); const isFile = (aFile: string) =\u0026gt; statSync(getPath(aFile)).isFile(); const files = readdirSync(sourcePath).filter(isFile); const getWorkingDirectoryFileExtensions = () =\u0026gt; Array.from(new Set(files.map(toFileExtension))); const createDirectory = (aFileExtension) =\u0026gt; mkdirSync(getPath(aFileExtension)); const shouldCreateFolder = (aFileExtension) =\u0026gt; !existsSync(getPath(aFileExtension)); getWorkingDirectoryFileExtensions() .filter(shouldCreateFolder) .forEach(createDirectory); const moveToFileExtensionFolder = (aFile: string) =\u0026gt; renameSync(getPath(aFile), getPath(toFileExtension(aFile), aFile)); files.forEach(moveToFileExtensionFolder); }; We got all of our logic in working condition. Now, let\u0026rsquo;s wire this thing up.\nWhat will be a reasonable workflow for this CLI? Let\u0026rsquo;s write it up as a user story.\n1. As a user, I want to type coolGroup in my cli and have all files in my current working directory grouped. By importing our groupFilesByExtensions function into cli.ts file.\nWe add a shebang(#!/usr/bin/env node) to specify the script interpreter that\u0026rsquo;s used to execute our code.\n1 2 3 4 5 6 #!/usr/bin/env node import { groupFilesByExtensions } from \u0026#39;./groupFilesByExtensions\u0026#39;; // process.cwd() give us back the current working directory groupFilesByExtensions(process.cwd()); Let\u0026rsquo;s introduce another requirement and see we can adjust to it.\n2. As a user, I to be able to specify the folder coolGroup will work on. For example coolGroup --entry-point ./group/this/folder\nChange the cli.ts file to accommodate this change\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/usr/bin/env node import * as commander from \u0026#39;commander\u0026#39;; import { groupFilesByExtensions } from \u0026#39;./groupFilesByExtensions\u0026#39;; commander .option( \u0026#39;--entry-point [value]\u0026#39;, \u0026#39;Relative path to a folder you want to group.\u0026#39; ) .action((command) =\u0026gt; { /* commander parses the input for us. The options we specify then get exposed via the `command` argument - command.\u0026lt;our-option\u0026gt; */ const groupFolderPath = command.entryPoint ? join(process.cwd(), command.entryPoint) : process.cwd(); groupFilesByExtensions(groupFolderPath); }) .parse(process.argv); Now our users can specify a path to the folder they want to group.\nAs a bonus, we get a nice help section out of the box!\nrun npm run build and then node ./dist/cli.js to see it in action locally (or use npm link)\nShare it with the world! We got a cool working CLI but it only exists on our local machine.\nLet\u0026rsquo;s share this brilliant creation with the world by publishing it to npm.\nBefore moving to the next section, if you don\u0026rsquo;t have an npm user follow this guide to create one and set up the credentials.\nTo publish our package all we need is to run npm publish and you should be good to go!\nFor a more polished publish flow check np out.\nIf everything went well you should see something like this.\ncheck it out by running npx \u0026lt;your-module-name-here\u0026gt; inside whatever folder you like.\nwoohoo, we are all done.\n","permalink":"https://galelmalah.com/posts/create-and-publish-your-first-cli-using-typescript/","summary":"Following my previous blog post 5 Cli Tools That Will Increase Your Dev Velocity And Code Quality I wrote a quick guide on how to write and publish a CLI.\nWhat\u0026rsquo;s in it for you? Write a cool as f*** CLI tool. Learn how to set up a project using Typescript. Publish your new shiny CLI to npm. setup We will use Scaffolder to generate all the boilerplate we need for our shiny CLI.","title":"Create and Publish Your First CLI Using Typescript"},{"content":"Everyone likes CLI\u0026rsquo;s (or maybe it\u0026rsquo;s just me).\nI have compiled a list of the CLI\u0026rsquo;s I use on a daily basis, some are well known some are less so, but all of them help me get things done faster and some even help me keep my code cleaner.\nThis list is ordered from most used to least used on a daily basis.\n1. z - Go from A to Z with ease This one will make you look like a ninja and help you boost your productivity! In a gist z helps you navigate your file system faster.\nBasically, it ranks all the directories you visit, and given a query, it will take you to the directory that will most likely match your query.\n2. Scaffolder - Generate boilerplate code with ease Hate copy-pasting files? You want to make sure all of your modules follow the same structure? You want to generate boilerplate code i.e react components or a complete project set up in an intuitive and easy way? Scaffolder got your back, you can define templates with dynamic parameters and generate them easily with a CLI or a vscode extension.\nIn essence, it will save you tons of time\nThere are tons of other features as well, like sharing templates via GitHub or defining your own functions to be run inside your templates.\nP.S, I wrote it, so I\u0026rsquo;m kind of biased. Feel free to leave a star or a feature request\n3. tldr-pages - Just show me how to use this From the project repo:\n\u0026ldquo;The tldr-pages project is a collection of community-maintained help pages for command-line tools, that aims to be a simpler, more approachable complement to traditional man pages.\u0026rdquo;\nEssentially you\u0026rsquo;ll get a bunch of useful examples for the command you specified\n4. np - Publish npm packages like a pro Hate going through the same routine each time you publish your npm package? np will help you automate this process while adding many other goodies like GitHub release tags and multiple versions of increment strategies. It\u0026rsquo;s described well in the project repo \u0026ldquo;A better npm publish\u0026rdquo;.\n5. lerna - One repo to rule them all Did you ever develop multiple packages that depend on one another? if so, then you know the pain of npm links, building, testing, and publishing all of the relevant packages. If you didn\u0026rsquo;t get the pleasure of managing this stuff manually, then let me save you the trouble and introduce you to lerna your new best friend\nYou might also want to check yarn workspaces\nGo ahead and give these CLI\u0026rsquo;s a try and tell me what you think in the comment section You might be interested in the following posts\nCreate and Publish Your First CLI Using Typescript\nGot any CLI tools that help you increase your dev velocity and quality? Leave a comment and share them with the rest of us\n","permalink":"https://galelmalah.com/posts/5-cli-tools-that-will-increase-your-velocity-and-code-quality/","summary":"Everyone likes CLI\u0026rsquo;s (or maybe it\u0026rsquo;s just me).\nI have compiled a list of the CLI\u0026rsquo;s I use on a daily basis, some are well known some are less so, but all of them help me get things done faster and some even help me keep my code cleaner.\nThis list is ordered from most used to least used on a daily basis.\n1. z - Go from A to Z with ease This one will make you look like a ninja and help you boost your productivity!","title":"5 CLI Tools That Will Increase Your Velocity and Code Quality"},{"content":"For most developers, bubble sort is one of the first algorithms we learn. Therefore, visualizing it can be highly satisfying and feels a bit like meeting an old friend after a long time.\nThis article will take you through visualizing the bubble sort algorithm using HTML5 canvas API.\nIf you would like to jump straight to the results and have a look at the code, here is a codepen.\nIn the meantime, below is a little sneak peek of what we are going to accomplish here. If you want to follow along, run the following command to get the initial boilerplate code generated into your working directory.\n1 npx scaffolder-cli i --from-github https://github.com/galElmalah/scaffolder-canvas-template.git --template canvas \u0026amp;\u0026amp; cd visualizing-bubble-sort This will leverage Scaffolder to create the initial setup.\nOr create a basic index.html file and script.js.\nNow lets jump right ahead and start coding All of the javascript code is written in script.js.\nThe first thing we will need is an unsorted array to sort. Let\u0026rsquo;s write a helper function for creating shuffled arrays.\nCool. Now, we will write a simple implementation of the bubble sort algorithm.\nNext, we\u0026rsquo;ll get our canvas and create a context.\nSo we got all the basics covered, and now it\u0026rsquo;s up to us to decide how to visualize the array. The most straightforward way is to just draw a rectangle corresponding to each array element, and set the height according to that element value (the higher the value the higher the rectangle will be).\nHere is a representation of our custom rectangle.\nLet\u0026rsquo;s test that everything is working as expected, by drawing our shuffled array.\nMultiply each height param by 5 to get a nice scale, so each pixel will equal 5 pixels.\nWe can make the height and width of the rectangle dynamic, by making it span the full height and width of the screen. Try doing this yourself. Here is a working example for the lazy ones (notice the calcMembersHeightScale and calcMembersWidth functions).\nIf all goes well, you should see something similar to the following in your browser. Now, let\u0026rsquo;s go back to our sorting function. What are the actions and states we care about? compare, swap, and sort. Let\u0026rsquo;s add a custom action dictionary.\nChange our bubble sort function to accept an onAction callback, and call it when an action is made in our bubble sort with the appropriate action.\nWe are almost done so hang in there! What should we do in each action? Give the members a different color based on the action, and \u0026ldquo;move\u0026rdquo; them if necessary - which will just be swapping their values. Now let\u0026rsquo;s create an action map, according to our known actions.\nWe seem to have all of the parts needed in order to visualize this nifty little thing! Let\u0026rsquo;s give it a try.\nI\u0026rsquo;ll be damned! it seems like we got only the fully sorted state. How can we solve this? we need to time our painting somehow. Let\u0026rsquo;s add two variables, speed which will determine how much time will pass between each step, and ticks to count each call to our onAction callback. A couple of things you should notice in the above example:\nClearing the canvas on each iteration. Resetting the color property for all of our rectangles on each iteration. Now putting it all together, we should end up with something like this.\nAnd there you have it, we just visualized this cool algorithm in 5 minutes!\nHope you enjoyed this little blog post! If you liked this visualization, check out some more sorting algorithms visualizations I created.\n","permalink":"https://galelmalah.com/posts/visualizing-bubble-sort-with-html-canvas/","summary":"For most developers, bubble sort is one of the first algorithms we learn. Therefore, visualizing it can be highly satisfying and feels a bit like meeting an old friend after a long time.\nThis article will take you through visualizing the bubble sort algorithm using HTML5 canvas API.\nIf you would like to jump straight to the results and have a look at the code, here is a codepen.\nIn the meantime, below is a little sneak peek of what we are going to accomplish here.","title":"Visualizing Bubble Sort in 5 Minutes Using HTML5 Canvas API"},{"content":"As a developer, I always reflect upon the code I write and read. Through this process, I have collected a bunch of useful tips. In this post, I\u0026rsquo;m going to share those tips that relate to array methods.\nAlthough I\u0026rsquo;m talking about array methods, these tips apply in other situations. Keep them in mind while you code.\nThe tips Some are better naming conventions and rules on when to apply them. Some are little tricks to make your code cleaner. Most of them are very opinionated :smile:.\nI tried categorizing them into general and specific tips to make it easier to digest and use as a reference.\nGeneral tips Name your functions Future readers of the code shouldn\u0026rsquo;t have to think about what\u0026rsquo;s that function is doing. Be a nice human and use meaningful names.\n1 2 3 4 5 6 7 8 const numbers = [1,2,3,4]; // BAD - I need to think about what this function is doing numbers.filter(num =\u0026gt; num % 2 === 0); // GOOD - I can read this line and immediately tell what\u0026#39;s going on. const isEven = num =\u0026gt; num % 2 === 0); numbers.filter(isEven); Don\u0026rsquo;t pass arguments from one function to another Array methods call functions that were sent to them with specific arguments. There is no need to explicitly pass those arguments through another function.\n1 2 3 4 5 6 7 8 const numbers = [1, 2, 3, 4]; const multiplyByTwo = (num) =\u0026gt; num * 2; // BAD - There is no need to explicitly pass num. numbers.map((num) =\u0026gt; multiplyByTwo(num)); // GOOD numbers.map(multiplyByTwo); Use partial application Do you need more than whats passed to you by the array method? Use partial application.\ndon\u0026rsquo;t know what\u0026rsquo;s a partial application? check this out.\n1 2 3 4 5 6 7 8 9 10 11 12 13 const numbers = [1, 2, 3, 4]; // BAD const multiplyBy = (num, multiplier) =\u0026gt; num * multiplier; numbers.map((num) =\u0026gt; multiplyBy(num, 2)); const multiplyBy = (multiplier) =\u0026gt; (num) =\u0026gt; num * multiplier; // GOOD numbers.map(multiplyBy(2)); // GOOD - more verbose const multiplyByTwo = multiplyBy(2); numbers.map(multiplyByTwo); Break long chains or Assign them to a variable/function When I see 3 or 4 levels of array methods chained together without anything indicating the result of that chain, I ask myself, Why? Why do I have to go over each line and figure out what the result is going to be? There are two ways we can solve this.\nBreak the chain - assign the result of each line to a variable and operate on that variable. Assign the result to a function or a variable with a meaningful name. let\u0026rsquo;s say we want to find all employees that are above 18, give them a random bonus and then get the sum of their salaries.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const employees = [{name:\u0026#34;bruce banner\u0026#34;, age:21, salary: 1500}, ...]; const isAboveEighteen = (employ) =\u0026gt; employ.age \u0026gt; 18; const addRandomBonus = (employ) =\u0026gt; ({...employ, salary: employ.salary*(Math.random() + 1)}); const toSumOfSalaries = (sum, employ) =\u0026gt; sum + employ.salary; // BAD - I need to think how each line effect the next one and what will be the outcome employees .filter(isAboveEighteen) .map(addRandomBonus) .reduce(toSumOfSalaries); // Breaking the chain const aboveEighteenEmployees = employees.filter(isAboveEighteen); const salariesWithBonus = aboveEighteenEmployees.map(addRandomBonus); const sumOfSalaries = salariesWithBonus.reduce(toSumOfSalaries); // Assign the result // *If we need to reuse this then we would use a function const aboveEighteenAfterBonusSumOfSalaries = employees .filter(isAboveEighteen) .map(addRandomBonus) .reduce(toSumOfSalaries); Map tips When transforming from type A to B use \u0026ldquo;toB\u0026rdquo; as the function name and \u0026ldquo;A\u0026rdquo; or \u0026ldquo;fromA\u0026rdquo; as the function argument For example, let\u0026rsquo;s say we want to transform error codes into human-readable error messages.\nIn this example A is errorCode and B is errorMessage.\n1 2 3 4 5 const errorCodes = [1, 2, 3]; const errorCodesMessages = {1: \u0026#34;your code is great!\u0026#34;, 2: \u0026#34;your code is awesome!\u0026#34;.... }; const toErrorMessage = (fromErrorCode) =\u0026gt; errorCodesMessages[fromErrorCode]; errorCodes.map(toErrorMessage); In this example, it\u0026rsquo;s clear from our code what we intend to do. toErrorMessage function conveys that we are transforming to B. Our array should tell us that we are operating on error codes. But, if we screw up the naming of the array, then it\u0026rsquo;s clear from the function argument we are operating on A.\nPerforming actions We can use the same convention we used in the above example, but it feels a bit awkward and overly verbose. Instead, for actions, we will just state the action we are performing.\nLet\u0026rsquo;s say we want to add a unique id to an array of users\n1 2 3 const users = [{name: \u0026#34;john doe\u0026#34;, email: \u0026#34;johnDoe@evilcorp.com\u0026#34;}, ....]; const addId = obj =\u0026gt; ({...obj, id: uuid()}); users.map(addId); Filter tips Use it when it\u0026rsquo;s the right tool for the job When is filter the right tool for the job? When you want to get a subset of an array based on some condition. In other cases, where you want to get a specific member, assert that a condition holds for at least one member or for all of them, use find, some, or every - know your tools and when to use them.\nDon\u0026rsquo;t really know these methods? Read about them find, some and every\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const peoples = [{ name: \u0026#34;Bruce Wayne\u0026#34;, country: \u0026#34;USA\u0026#34;, city: \u0026#34;Gotham\u0026#34;,... }, ...]; // find const isBruceWayne = person =\u0026gt; person.name === \u0026#34;Bruce Wayne\u0026#34;; peoples.filter(isBruceWayne)[0]; // BAD peoples.find(isBruceWayne); // GOOD // some const isFromTheUSA = person =\u0026gt; person.country === \u0026#34;USA\u0026#34;; // has peoples from USA? !!peoples.filter(isFromTheUSA)[0]; // BAD peoples.some(isFromTheUSA); // GOOD // every const isNotFromTheUSA = person =\u0026gt; person.country !== \u0026#34;USA\u0026#34;; // everyone from the USA? !peoples.filter(isNotFromTheUSA)[0] // BAD peoples.every(isFromTheUSA) // GOOD Make it sound like a question This one applies to all conditional statements. If the return value of our function is a boolean i.e true or false, then we should write our function in a way that will read like a question.\n1 2 3 4 5 6 7 8 9 const numbers = [1,2,3,4] // BAD - Reads like a statment const even = num =\u0026gt; num % 2 === 0); numbers.filter(even); // GOOD - Reads like a question const isEven = num =\u0026gt; num % 2 === 0); numbers.filter(isEven); Some common prefixes are is, has, should\u0026hellip;\nCheck for multiple conditions in one pass If you want to check for multiple conditions in one pass, use ramda anyPass and allPass functions when you want to combine multiple conditions while adhering to SRP.\nFor example, let\u0026rsquo;s say we want to get all the even numbers that are bigger than 10 OR odd numbers that are smaller than 10.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import R from \u0026#39;ramda\u0026#39;; const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; // This is a simple example but you can imagine how it can get out of control. const isAValidNumber = (num) =\u0026gt; { if (num % 2 === 0 \u0026amp;\u0026amp; num \u0026gt; 10) { return true; } if (num % 2 \u0026amp;\u0026amp; num \u0026lt; 10) { return true; } }; // Good - We\u0026#39;ve split our logic into small, reusable functions, that do one thing. const isEven = (num) =\u0026gt; num % 2 === 0; const isOdd = (num) =\u0026gt; !isEven(num); const isBiggerThanTen = (num) =\u0026gt; num \u0026gt; 10; const isSmallerThanTen = (num) =\u0026gt; num \u0026lt; 10; const isValidNumber = R.anyPass([ R.allPass([isBiggerThanTen, isEven]), R.allPass([isSmallerThanTen, isOdd]), ]); numbers.filter(isValidNumber); We added some code, but look at it, it\u0026rsquo;s so clear what we want to achieve!\nIf you don\u0026rsquo;t like the functional approach or your team is not familiar with ramda, there are other ways to get the same result, like plugging the functions we wrote into the if statements in the first implementation of isValidNumber.\nWant to apply multiple functions in one pass using map? check out ramda pipe or compose.\nGot any tips to share? Those were my greatest hits for using array methods. Got any tips of your own? Found something new or interesting in this post? Leave a comment and share this post with your fellow developers:smile:\n","permalink":"https://galelmalah.com/posts/9-array-methods-tips/","summary":"As a developer, I always reflect upon the code I write and read. Through this process, I have collected a bunch of useful tips. In this post, I\u0026rsquo;m going to share those tips that relate to array methods.\nAlthough I\u0026rsquo;m talking about array methods, these tips apply in other situations. Keep them in mind while you code.\nThe tips Some are better naming conventions and rules on when to apply them.","title":"9 Great Tips To Use Javascript Array Methods Like a Pro!"}]