scarecrw's recent activity

  1. Comment on Day 18: RAM Run in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Fastest day so far! That's not saying too much, as I wasn't particularly going for speed this year, but I'm happy that I'm getting more fluent with Pharo and not having to spend time searching...

    Fastest day so far! That's not saying too much, as I wasn't particularly going for speed this year, but I'm happy that I'm getting more fluent with Pharo and not having to spend time searching around too much.

    I just used BFS, which was fast enough when combined with a binary search for part 2. Thankfully, I've learned from previous years not to use an actual 2D grid when possible, and instead just use borders and a set of obstacles, which made running the search with a different collection of obstacles trivial.

    Smalltalk Solution
    Class {
    	#name : 'Day18Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'gridSize', 'bytes', 'part1Size' ],
    	#category : 'AoCDay18',
    	#package : 'AoCDay18'
    }
    
    Day18Solver >> parseRawData [
    	| coords |
    	bytes := rawData lines collect: [ :line |
    		         coords := (line splitOn: $,) collect: #asInteger.
    		         coords first @ coords second ]
    ]
    
    Day18Solver >> pathLengthWithFallen: fallenCount [
    	| steps queue currPos fallen |
    	fallen := (bytes first: fallenCount) asSet.
    	steps := Dictionary newFromPairs: { 0 @ 0 . 0 }.
    	queue := { 0 @ 0 } asOrderedCollection.
    	[ queue isNotEmpty ] whileTrue: [
    		currPos := queue removeFirst.
    		currPos fourNeighbors
    			select: [ :neighbor | self validPosition: neighbor withFallen: fallen ]
    			thenDo: [ :neighbor |
    				(steps includesKey: neighbor) ifFalse: [
    					steps at: neighbor put: (steps at: currPos) + 1.
    					queue add: neighbor ] ] ].
    	^ steps at: gridSize @ gridSize ifAbsent: 0
    ]
    
    Day18Solver >> solvePart1 [
    	^ self pathLengthWithFallen: part1Size
    ]
    
    Day18Solver >> solvePart2 [
    	| lower upper mid |
    	lower := part1Size.
    	upper := bytes size.
    	[ lower < upper ] whileTrue: [
    		mid := upper + lower // 2.
    		(self pathLengthWithFallen: mid) = 0
    			ifTrue: [ upper := mid - 1 ]
    			ifFalse: [ lower := mid + 1 ] ].
    	^ bytes at: lower
    ]
    
    Day18Solver >> validPosition: pos withFallen: fallen [
    	^ (pos between: 0 @ 0 and: gridSize @ gridSize) & (fallen includes: pos) not
    ]
    
    1 vote
  2. Comment on Day 17: Chronospatial Computer in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Pretty fun day! Very reminiscent of a previous year (I can't remember which one) which also relied on interpreting the given instructions. I think that day was about optimizing them, whereas today...

    Pretty fun day! Very reminiscent of a previous year (I can't remember which one) which also relied on interpreting the given instructions. I think that day was about optimizing them, whereas today the only necessary information was how the A register was iterated over 3 bits of at a time (I'm assuming this was the same in everyone's input, or at least similar).

    I'm still getting burned by indexing from 1... Today it was that findNext: returns a 1-based index, or 0 on failure.

    Computer Simulator
    Class {
    	#name : 'Day17Computer',
    	#superclass : 'Object',
    	#instVars : [ 'a', 'b', 'c', 'program', 'ip', 'halted', 'output' ],
    	#category : 'AoCDay17',
    	#package : 'AoCDay17'
    }
    
    Day17Computer >> initialize [
    	ip := 1.
    	halted := false.
    	output := OrderedCollection new.
    ]
    
    Day17Computer >> step [
    	| opcode operations combo literal |
    	(ip between: 1 and: program size) ifFalse: [ halted := true. ^ self ].
    	opcode := program at: ip.
    	literal := program at: (ip + 1).
    	combo := { 0 . 1 . 2 . 3 . a . b . c } at: ((program at: (ip + 1)) + 1).
    	operations := Dictionary newFromPairs: {
    		0 . [ a := a >> combo ] .
    		1 . [ b := b bitXor: literal ] .
    		2 . [ b := combo % 8 ] .
    		3 . [ a ~= 0 ifTrue: [ ip := literal - 1 ] ] .
    		4 . [ b := b bitXor: c ] .
    		5 . [ output add: combo % 8 ] .
    		6 . [ b := a >> combo ] .
    		7 . [ c := a >> combo ]
    	}.
    	(operations at: opcode) value.
    	ip := ip + 2
    ]
    
    Solver
    Class {
    	#name : 'Day17Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'aInitial', 'bInitial', 'cInitial', 'program' ],
    	#category : 'AoCDay17',
    	#package : 'AoCDay17'
    }
    
    Day17Solver >> parseRawData [
    	| nums |
    	nums := ('\d+' asRegex matchesIn: rawData) collect: #asInteger.
    	aInitial := nums first.
    	bInitial := nums second.
    	cInitial := nums third.
    	program := nums allButFirst: 3
    ]
    
    Day17Solver >> solvePart1 [
    	^ $, join: ((self runComputer: aInitial) collect: #asString)
    ]
    
    Day17Solver >> solvePart2 [
    	^ self solve2Helper: 0 finished: 0
    ]
    
    Day17Solver >> runComputer: aStart [
    	| computer |
    	computer := Day17Computer new a: aStart; b: bInitial; c: cInitial; program: program.
    	[ computer halted ] whileFalse: [ computer step ].
    	^ computer output
    ]
    
    Day17Solver >> solve2Helper: a finished: n [
    	| output target recResult |
    	n = program size ifTrue: [ ^ a ].
    	target := program at: program size - n.
    	0 to: 7 do: [ :x |
    		output := self runComputer: a << 3 + x.
    		output first = target ifTrue: [
    			recResult := self solve2Helper: a << 3 + x finished: n + 1.
    			recResult > 0 ifTrue: [ ^ recResult ] ] ].
    	^ 0
    ]
    
    1 vote
  3. Comment on Day 16: Reindeer Maze in ~comp.advent_of_code

    scarecrw
    Link
    While exploring Pharo before AoC started, I did notice that there are some pathfinding algorithms programmed in. I actually found them because they seem to be where the generalized graph data...

    While exploring Pharo before AoC started, I did notice that there are some pathfinding algorithms programmed in. I actually found them because they seem to be where the generalized graph data structure is also contained. I didn't end up using them, but I might go back and try to re-solve with that approach to learn how those work.

    I ended up storing the nodes as 3D points, with the regular position in x and y and the z storing the direction. I mostly did this to avoid having to write a new class with its own hash function, but it worked quite well. I've also gotten more comfortable adding methods to base classes: today I extended the x@y syntax for creating a Point to x@y@z to create a G3DCoordinates as well as added atPoint:put: for Array2D which I had been wanting in previous days.

    Pharo Smalltalk Solution

  4. Comment on Day 15: Warehouse Woes in ~comp.advent_of_code

    scarecrw
    Link
    Definitely my longest day as far as LOC, but that's mostly because there's lots of repeated code for the slightly different cases. Paired with the way Pharo outputs to text files it's nearing 200...

    Definitely my longest day as far as LOC, but that's mostly because there's lots of repeated code for the slightly different cases. Paired with the way Pharo outputs to text files it's nearing 200 lines...

    Other than getting tripped up again with input parsing (I've updated my tools to avoid it going forward), this was a pretty smooth day.

    Pharo Smalltalk Solution

    1 vote
  5. Comment on Day 14: Restroom Redoubt in ~comp.advent_of_code

    scarecrw
    Link
    Continuing to find more new tools that Pharo has built-in! Today was quadrantOf: which was helpful for part 1 and fourNeighbors which I could have avoided writing myself in some previous days and...

    Continuing to find more new tools that Pharo has built-in! Today was quadrantOf: which was helpful for part 1 and fourNeighbors which I could have avoided writing myself in some previous days and helped for my approach to part 2.

    If I have time this weekend I might look a bit more into Morphs and how to set up some useful displays for future days. In the past I always just relied on ASCII displays, but Morphs seem to make generating simple visualizations very straightforward, so worth checking out.

    Pharo Smalltalk Solution

    1 vote
  6. Comment on Day 13: Claw Contraption in ~comp.advent_of_code

    scarecrw
    Link Parent
    You're absolutely right that the tools to edit the environment and built-ins is probably the coolest part. Numerous times now I've found myself thinking "oh man, I wish that X had a Y feature..."...

    You're absolutely right that the tools to edit the environment and built-ins is probably the coolest part. Numerous times now I've found myself thinking "oh man, I wish that X had a Y feature..." before realizing, I can just add that! That and the tests/debugging definitely set it apart from other languages in how you work.

    I've definitely been learning a lot as I go! Most days I'm kicking myself as I find a tool that would have been very helpful on a previous problem. I looked at those MOOC courses myself, and started through the first couple weeks of one, but ended up finding this book more useful: Pharo By Example.

    The only pain point so far is that finding/guessing method names and what they do has not been very intuitive. The aim of having the names flow more like natural language is neat, but ends up making it tough to get used to, coming from other languages.

    1 vote
  7. Comment on Day 13: Claw Contraption in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Probably one of the easier days so far for most of the logic. I guess if you haven't dealt with math in a bit this could be tricky, but compared to some previous AoC math-y problems this was...

    Probably one of the easier days so far for most of the logic. I guess if you haven't dealt with math in a bit this could be tricky, but compared to some previous AoC math-y problems this was fairly tame.

    I spent most of my time trying to get regex working properly.

    Smalltalk Solution
    Class {
    	#name : 'Day13Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'machines'
    	],
    	#category : 'AoCDay13',
    	#package : 'AoCDay13'
    }
    
    Day13Solver >> parseRawData [
    	machines := ('<n><n>' expandMacros split: rawData) collect: [ :machineString | Day13Machine newFromString: machineString ]
    ]
    
    Day13Solver >> solvePart1 [
    	^ (machines collect: [ :machine | machine tokensToWin]) sum
    ]
    
    Day13Solver >> solvePart2 [
    	^ (machines collect: [ :machine | machine tokensToWinAlt]) sum
    ]
    
    Class {
    	#name : 'Day13Machine',
    	#superclass : 'Object',
    	#instVars : [
    		'aButton',
    		'bButton',
    		'prizeLocation',
    		'prizeLocationAlt'
    	],
    	#category : 'AoCDay13',
    	#package : 'AoCDay13'
    }
    
    Day13Machine class >> newFromString: aString [
    	| machine re nums |
    	machine := self new.
    	re := '\d+' asRegex.
    	nums := (re matchesIn: aString) collect: #asInteger.
    	machine aButton: nums first @ nums second.
    	machine bButton: nums third @ nums fourth.
    	machine prizeLocation: nums fifth @ nums sixth.
    	machine prizeLocationAlt: machine prizeLocation + 10000000000000.
    	^ machine
    ]
    
    Day13Machine >> tokensToWin [
    	0 to: 100 do: [ :aPresses |
    		0 to: 100 do: [ :bPresses |
    			aPresses * aButton + (bPresses * bButton) = prizeLocation ifTrue: [
    				^ 3 * aPresses + bPresses ] ] ].
    	^ 0
    ]
    
    Day13Machine >> tokensToWinAlt [
    	| l1 l2 result |
    	l1 := GLine a: aButton x b: bButton x c: prizeLocationAlt x negated.
    	l2 := GLine a: aButton y b: bButton y c: prizeLocationAlt y negated.
    	result := (l1 intersectionsWith: l2) first asPoint.
    	^ result isIntegerPoint
    		  ifTrue: [ 3 * result x + result y ]
    		  ifFalse: [ 0 ]
    ]
    
    2 votes
  8. Comment on Day 12: Garden Groups in ~comp.advent_of_code

    scarecrw
    Link
    Ugh, first day truly annoyed by Pharo... I had the logic quickly, but ran into so many issues just trying to store a grid. There's a built in Array2D, but it doesn't have a neat way to create it...

    Ugh, first day truly annoyed by Pharo... I had the logic quickly, but ran into so many issues just trying to store a grid. There's a built in Array2D, but it doesn't have a neat way to create it from the original data and is also deprecated. I found Containers-Array2D, which has CTArray2D with fromRows, but I spent far too long before realizing that it uses points as x-y instead of row-column... but it does include CTAlternateArray2D which uses row-column, but doesn't have fromRows!

    I like a lot of what Pharo has as far as tools, but to have something as basic as a 2D array have 3+ inconsistent versions is just ridiculous. I haven't even looked at CTNewArray2D, CTNewArray2DRowsAndColumns, or CTNewArray2DXAndY...

    Pharo Smalltalk Solution

  9. Comment on Day 11: Plutonian Pebbles in ~comp.advent_of_code

    scarecrw
    Link
    Probably would have been my fastest day yet, but I could not get the caching working on the recursive function. Pharo Dictionaries have a at:ifAbsentPut: which seemed perfect for implementing a...

    Probably would have been my fastest day yet, but I could not get the caching working on the recursive function. Pharo Dictionaries have a at:ifAbsentPut: which seemed perfect for implementing a simple cache, but I haven't been able to figure out why I couldn't get it to work. I ended up using mutual recursion and it worked alright, but I'll have to dig deeper to figure out what's up.

    I miss python's @cache...

    Smalltalk Solution
    Class {
    	#name : 'Day11Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'stones',
    		'cache'
    	],
    	#category : 'AoCDay11',
    	#package : 'AoCDay11'
    }
    
    Day11Solver >> blink: blinkCount timesStone: stone [
    	| s firstHalf secondHalf |
    	blinkCount = 0 ifTrue: [ ^ 1 ].
    	stone = 0 ifTrue: [ ^ self cachedBlink: blinkCount - 1 timesStone: 1 ].
    	s := stone asString.
    	s size even ifTrue: [
    		firstHalf := (s first: s size // 2) asInteger.
    		secondHalf := (s allButFirst: s size // 2) asInteger.
    		^ (self cachedBlink: blinkCount - 1 timesStone: firstHalf)
    		  + (self cachedBlink: blinkCount - 1 timesStone: secondHalf) ].
    	^ self cachedBlink: blinkCount - 1 timesStone: stone * 2024
    ]
    
    Day11Solver >> cachedBlink: blinkCount timesStone: stone [
    	| key |
    	key := { blinkCount . stone }.
    	(cache includesKey: key)
    		ifTrue: [ ^ cache at: key ]
    		ifFalse: [ ^ cache at: key put: (self blink: blinkCount timesStone: stone). ]
    ]
    
    Day11Solver >> parseRawData [
    	cache := Dictionary new.
    	stones := (rawData splitOn: ' ') collect: #asInteger.
    ]
    
    Day11Solver >> solvePart1 [
    	^ (stones collect: [ :stone | self cachedBlink: 25 timesStone: stone ]) sum
    ]
    
    Day11Solver >> solvePart2 [
    	^ (stones collect: [ :stone | self cachedBlink: 75 timesStone: stone ]) sum
    ]
    
    1 vote
  10. Comment on Day 10: Hoof It in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Managed to keep things simple enough for today, including reusing my code from Part 1 for Part 2. I'm finding a bunch of tools that I wished I'd found previous days like flatCollect: and...

    Managed to keep things simple enough for today, including reusing my code from Part 1 for Part 2. I'm finding a bunch of tools that I wished I'd found previous days like flatCollect: and digitValue.

    Smalltalk Solution
    Class {
    	#name : 'Day10Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'trailMap',
    		'trailHeads'
    	],
    	#category : 'AoCDay10',
    	#package : 'AoCDay10'
    }
    
    Day10Solver >> parseRawData [
    	| temp |
    	temp := rawData lines collect: [ :row | row collect: #digitValue as: Array ].
    	trailMap := Array2D
    		            rows: temp size
    		            columns: (temp at: 1) size
    		            tabulate: [ :r :c | (temp at: r) at: c ].
    	trailHeads := OrderedCollection new.
    	trailMap withIndicesDo: [ :x :r : c | x = 0 ifTrue: [ trailHeads add: r @ c ] ]
    ]
    
    Day10Solver >> solvePart1 [
    	^ (trailHeads collect: [ :trailHead | self numPathsStartingAt: trailHead unique: false ]) sum
    ]
    
    Day10Solver >> solvePart2 [
    	^ (trailHeads collect: [ :trailHead | self numPathsStartingAt: trailHead unique: true ]) sum
    ]
    
    Day10Solver >> isPointInRange: aPoint [ 
    	^ aPoint > (0@0) and: aPoint <= (trailMap numberOfRows @ trailMap numberOfColumns)
    ]
    
    Day10Solver >> neighborsOf: aPoint [
    	| neighbors height |
    	height := trailMap atPoint: aPoint.
    	neighbors := {
    		             (aPoint + (1 @ 0)).
    		             (aPoint + (0 @ 1)).
    		             (aPoint - (1 @ 0)).
    		             (aPoint - (0 @ 1)) }.
    	^ neighbors select: [ :p | (self isPointInRange: p) and: [ (trailMap atPoint: p) = (height + 1) ] ]
    ]
    
    Day10Solver >> numPathsStartingAt: aPoint unique: aBoolean [
    	| curr pathCount |
    	curr := aBoolean
    		        ifTrue: [ OrderedCollection new ]
    		        ifFalse: [ Set new ].
    	curr add: aPoint.
    	pathCount := 0.
    	[ curr isEmpty ] whileFalse: [
    		curr := curr flatCollect: [ :p |
    			        (trailMap atPoint: p) = 9
    				        ifTrue: [ pathCount := pathCount + 1. #(  ) ]
    				        ifFalse: [ self neighborsOf: p ] ] ].
    	^ pathCount
    ]
    
    3 votes
  11. Comment on Day 9: Disk Fragmenter in ~comp.advent_of_code

    scarecrw
    Link
    Didn't run into too many problems with the solution itself, but lots of bugs along the way. Before coming up with a better solution I was trying to just get a slow version working inserting with...

    Didn't run into too many problems with the solution itself, but lots of bugs along the way. Before coming up with a better solution I was trying to just get a slow version working inserting with insert:before:, not realizing that it's a private method and doesn't actually allow for insertions...

    Both reasonably efficient (~0.01 and ~1 seconds), but I'm still thinking of faster approaches for part 2.

    Part 2 Spoilers

    Since all the gap sizes are 1-9, I suspect you could speed things up by storing separate lists of each size, sorted by position in memory. Then just take the head of each list from the needed size and above and pick the one with the minimum position. I haven't implemented this, but I think it should work and reduce the time substantially.

    Pharo Smalltalk Solution

    1 vote
  12. Comment on Day 8: Resonant Collinearity in ~comp.advent_of_code

    scarecrw
    Link
    Nothing too special today. I'm enjoying having built-in points and rectangles, though I'm still getting tripped up indexing from 1. I made use of combinations:atATimeDo: which, along with other...

    Nothing too special today. I'm enjoying having built-in points and rectangles, though I'm still getting tripped up indexing from 1. I made use of combinations:atATimeDo: which, along with other combinatorics tools, will likely come back in future days.

    Pharo Smalltalk Solution

    5 votes
  13. Comment on Day 7: Bridge Repair in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    First day actually feeling good about my solution! I'm sure I'm still doing plenty of unidiomatic things, but most of the solution came together nicely. Two things I'm happy with: Managing to pass...

    First day actually feeling good about my solution! I'm sure I'm still doing plenty of unidiomatic things, but most of the solution came together nicely. Two things I'm happy with:

    • Managing to pass operators to reuse the same method for both parts
    • Being able to just add a new operator to the Integer class

    The whole setup of Pharo with images is weird, but seems to really encourage doing things like messing with built-in classes. It's very fun to just decide "I guess Integers do that now!"

    Smalltalk Solution
    Class {
    	#name : 'Day7Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'equations'
    	],
    	#category : 'AoCDay7',
    	#package : 'AoCDay7'
    }
    
    Day7Solver >> parseRawData [
    	equations := rawData lines collect: [ :eqString | Day7Equation newFromString: eqString ]
    ]
    
    Day7Solver >> solvePart1 [
    	^ ((equations select: [ :eq | eq canBeSolved: { #+. #* } ]) collect: #testValue) sum
    ]
    
    Day7Solver >> solvePart2 [
    	^ ((equations select: [ :eq | eq canBeSolved: { #+. #*. #|| } ]) collect: #testValue) sum
    ]
    
    Class {
    	#name : 'Day7Equation',
    	#superclass : 'Object',
    	#instVars : [
    		'testValue',
    		'terms'
    	],
    	#category : 'AoCDay7',
    	#package : 'AoCDay7'
    }
    
    Day7Equation class >> newFromString: aString [ 
    	| parts terms |
    	parts := aString splitOn: ': '.
    	terms := (parts second splitOn: ' ') collect: #asInteger.
    	^ self new testValue: parts first asInteger; terms: terms
    ]
    
    Day7Equation >> canBeSolved: operators [
    	| totals |
    	totals := { terms first }.
    	terms allButFirstDo: [ :term |
    		totals := (operators collect: [ :op |
    			           totals collect: [ :prev | prev perform: op with: term ] ])
    			          flattened select: [ :x | x <= testValue ] ].
    	^ totals includes: testValue
    ]
    
    Integer >> || anInteger
    	^ (self asString , anInteger asString) asInteger
    
    3 votes
  14. Comment on Day 6: Guard Gallivant in ~comp.advent_of_code

    scarecrw
    Link
    Did the brute-force the same as others; about 35 seconds to run. Not super proud to be getting slow execution times already at day 6, but I wasn't aiming for time this year so we'll push onward. I...

    Did the brute-force the same as others; about 35 seconds to run. Not super proud to be getting slow execution times already at day 6, but I wasn't aiming for time this year so we'll push onward.

    I figured out how to make a class hashable, which I suspect will be a useful tool, and it seems like Pharo's clone is sufficient for copying at least non-nested objects. I'd like to explore more about the debugger tools. Being able to live edit methods is super cool, but I suspect there's a lot more I'm missing.

    Pharo Smalltalk Solution

    1 vote
  15. Comment on Day 5: Print Queue in ~comp.advent_of_code

    scarecrw
    Link
    Sigh... I spent probably a third of my time on parsing because when I copied the sample data it just used carriage returns but my actual data used carriage returns and line feeds. I was glad I...

    Sigh... I spent probably a third of my time on parsing because when I copied the sample data it just used carriage returns but my actual data used carriage returns and line feeds.

    I was glad I caught that the ruleset was complete. I almost started working out how to order based on a chain of rules (I think that was part of a previous year's puzzle) but correctly guessed that was overkill for day 5.

    Pharo Smalltalk Solution

    1 vote
  16. Comment on Day 4: Ceres Search in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    I'm very much enjoying having a Point class with simple syntax, but I can't say it's making up for the rest of Pharo's syntax being kind of a pain. It's tough to go from: arr[r-1][c-1] + arr[r][c]...

    I'm very much enjoying having a Point class with simple syntax, but I can't say it's making up for the rest of Pharo's syntax being kind of a pain. It's tough to go from:

    arr[r-1][c-1] + arr[r][c] + arr[r+1][c+1]
    

    for finding a diagonal string, to:

    String withAll: { (self at: aPoint - (1 @ 1)) . (self at: aPoint) . (self at: aPoint + (1 @ 1)) }
    

    Maybe I'm just not finding the cleanest approaches, but it's frustrating.

    I have, however, been enjoying the pressure to heavily decompose tasks and easily add tests. I feel like I'm leaving each day in a state that would be easily refactored or improved.

    Pharo Smalltalk Solution

    2 votes
  17. Comment on Day 3: Mull It Over in ~comp.advent_of_code

    scarecrw
    Link Parent
    Intcode! that was the first year I participated, and definitely got me hooked. I feel like it's been teased a few times since then but we haven't gotten anything quite the same.

    Intcode! that was the first year I participated, and definitely got me hooked. I feel like it's been teased a few times since then but we haven't gotten anything quite the same.

    2 votes
  18. Comment on Day 3: Mull It Over in ~comp.advent_of_code

    scarecrw
    Link
    Well this was frustrating. No problems solving it, but every step felt like I was going against the grain. I couldn't get regex groups to work so just fell back on string manipulation and my part...

    Well this was frustrating. No problems solving it, but every step felt like I was going against the grain. I couldn't get regex groups to work so just fell back on string manipulation and my part 2 didn't feel great.

    I'm going to have to dive deeper into Pharo's regex tools, as I'm sure many future days are going to be a pain if I can't get that working well.

    Smalltalk Solution
    Class {
    	#name : 'Day3Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'operations'
    	],
    	#category : 'AoCDay3',
    	#package : 'AoCDay3'
    }
    
    Day3Solver >> evalMul: aString [ 
    	| idx val1 val2 |
    	idx := aString indexOf: $,.
    	val1 := (aString copyFrom: 5 to: idx) asNumber.
    	val2 := (aString copyFrom: (idx + 1) to: (aString size - 1)) asNumber.
    	^ val1 * val2
    ]
    
    Day3Solver >> parseRawData [
    	operations := rawData regex: 'mul\(\d{1,3},\d{1,3}\)|do\(\)|don''t\(\)' matchesCollect: [ :x | x ]
    ]
    
    Day3Solver >> solvePart1 [
    	| mulOps |
    	mulOps := operations select: [ :op | op beginsWith: 'mul' ].
    	^ (mulOps collect: [ :mulOp | self evalMul: mulOp ]) sum
    ]
    
    Day3Solver >> solvePart2 [
    	| total shouldMul |
    	total := 0.
    	shouldMul := true.
    	operations do: [ :op |
    		((op beginsWith: 'mul') and: shouldMul)
    			ifTrue: [ total := total + (self evalMul: op) ]
    			ifFalse: [ shouldMul := op = 'do()' ] ].
    	^ total
    ]
    
    2 votes
  19. Comment on Day 2: Red-Nosed Reports in ~comp.advent_of_code

    scarecrw
    Link Parent
    I don't know if there's that much of an easier way, but the main idiomatic difference would be using list comprehension instead of mapping. Here's a one-liner if you want which would leave you...

    I don't know if there's that much of an easier way, but the main idiomatic difference would be using list comprehension instead of mapping.

    Here's a one-liner if you want which would leave you with a list of lists of ints:

    reports = [[int(x) for x in row.split()] for row in input.split('\n')]
    

    You can also use map, as you've done, though I'll note you can actually just pass int as a function, rather than writing a lambda:

    levels = list(map(int, report.split(' '))
    

    Last little idiomatic tip, you probably want to avoid using input as a variable name, as it overshadows the built-in function of the same name.

    3 votes
  20. Comment on Day 2: Red-Nosed Reports in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    So far keeping up OOP and TDD! This took... far longer than it should have, but I'm definitely learning more about Smalltalk. Discovering overlappingPairsCollect and copyWithoutIndex handled a lot...

    So far keeping up OOP and TDD! This took... far longer than it should have, but I'm definitely learning more about Smalltalk. Discovering overlappingPairsCollect and copyWithoutIndex handled a lot of the heavy lifting here. Also, Pharo shows little green dots when the tests pass, and getting them all to light up is a wonderful way to tie problem completion to dopamine release.

    I think I'll have to swap to just posting a github link and snippets going forward. Getting to a somewhat reasonable length means cutting out the comments, protocols, accessors, tests...

    Smalltalk Solution
    Class {
    	#name : 'Day2Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [
    		'reports'
    	],
    	#category : 'AoCDay2',
    	#package : 'AoCDay2'
    }
    
    Day2Solver >> parseRawData [
    	reports := rawData collect: [ :s | Day2Report newFromString: s].
    ]
    
    Day2Solver >> solvePart1 [
    	^ reports count: [ :report | report isSafe ]
    ]
    
    Day2Solver >> solvePart2 [
    	^ reports count: [ :report | report isNearlySafe ]
    ]
    
    Class {
    	#name : 'Day2Report',
    	#superclass : 'OrderedCollection',
    	#category : 'AoCDay2',
    	#package : 'AoCDay2'
    }
    
    Day2Report class >> newFromString: aString [ 
    	^ self newFrom: ((aString findTokens: ' ') collect: [ :x | x asNumber ])
    ]
    
    Day2Report >> isDecreasing [
    	^ (self overlappingPairsCollect: [ :a :b | a > b ]) allSatisfy: [ :x | x ].
    ]
    
    Day2Report >> isIncreasing [
    	^ (self overlappingPairsCollect: [ :a :b | a < b ]) allSatisfy: [ :x | x ].
    ]
    
    Day2Report >> isNearlySafe [
    	^ (1 to: self size) anySatisfy: [ :idx | (self copyWithoutIndex: idx) isSafe]
    ]
    
    Day2Report >> isSafe [
    	^ (self isIncreasing or: self isDecreasing) and: self levelsAreSimilar
    ]
    
    Day2Report >> levelsAreSimilar [
    	^ (self overlappingPairsCollect: [ :a :b | (a - b) abs between: 1 and: 3]) allSatisfy: [ :x | x ].
    ]
    
    3 votes