scarecrw's recent activity

  1. Comment on What games have you been playing, and what's your opinion on them? in ~games

    scarecrw
    Link Parent
    I hope you enjoy it! It was one of my favorite game experiences. Something about the game made it much more natural to embrace the role-playing aspect of an RPG. I think it was mostly a nice...

    I hope you enjoy it! It was one of my favorite game experiences.

    Something about the game made it much more natural to embrace the role-playing aspect of an RPG. I think it was mostly a nice reprieve from all the games where you're "The Hero" who will inevitably be better and stronger and more special than everyone around you. Your character certainly grows and develops, but it stays much more down to earth and let's you be part of the world instead of treating it like a sandbox.

    I'll be curious if you have a take on the monastery quest. I can't say I enjoyed it, but I recall it was fairly devisive.

    4 votes
  2. Comment on What programming/technical projects have you been working on? in ~comp

    scarecrw
    Link Parent
    This is super cool! Do you have a link to any of the papers you used as reference? I'd love to learn about how this works.

    This is super cool! Do you have a link to any of the papers you used as reference? I'd love to learn about how this works.

  3. Comment on United States: What personal (non-business) tax software/program do you use? in ~finance

    scarecrw
    Link Parent
    The IRS system was available in my state last year, but it only covered federal taxes, not state. That meant I was going to have to use another service anyways to file state taxes, and it wasn't...

    The IRS system was available in my state last year, but it only covered federal taxes, not state. That meant I was going to have to use another service anyways to file state taxes, and it wasn't worth doing everything twice. I suppose if you're in a state without income tax then it might be worth it, but otherwise I didn't really see the benefit.

    5 votes
  4. Comment on Randomized trial shows AI tutoring effective in Nigeria in ~tech

    scarecrw
    Link
    I've been eagerly waiting to see how some of these AI-assisted educational programs perform, especially those focused on broadening access to education. A lot of the overly-rosy perspectives tend...

    I've been eagerly waiting to see how some of these AI-assisted educational programs perform, especially those focused on broadening access to education. A lot of the overly-rosy perspectives tend to compare outcomes to students who received no treatment whatsoever, which is a disingenuous way to report an approach's effectiveness, so it was very confidence inspiring to see that this also outperformed the majority of comparable interventions.

    I was also pleased to see from their videos how they seemed to have made the experience a social activity, involving interaction with the teacher and other students. A lot of failed attempts at using technology in education seem tailored for some mythical, emotionless, entirely self-motivated student.

    There's some draw to the idea that, if only the explanations were perfect and practice exercises just-so, every student would breeze through any topic. Maybe the AI optimists are correct, and we'll see some revolution coming, but from my experience with students in the present, a lot of the effectiveness of educational approaches stems from fostering an environment conducive for learning and building students' confidence in their own abilities. While current LLMs seem far from providing this on their own, I hope more efforts like this one find ways to use them in conjunction with more human approaches.

    7 votes
  5. Comment on Day 25: Code Chronicle in ~comp.advent_of_code

    scarecrw
    Link Parent
    Part 2 was definitely this year's "don't generalize, just inspect your input" problem. I can say that while I at some point knew how adders were set up, the only information that was necessary is...

    Part 2 was definitely this year's "don't generalize, just inspect your input" problem. I can say that while I at some point knew how adders were set up, the only information that was necessary is that each bit in the output is calculated in roughly the same way.

    minor spoiler

    For example, after sorting all the connections that led to a z output, I could see:

    ...
    gmw XOR cqn -> z08
    jgj XOR gff -> z09
    x10 AND y10 -> z10 // that's weird
    fgb XOR bmd -> z11
    qbw XOR gvj -> z12
    bgh XOR ctc -> z13
    pvs XOR wgc -> z14
    ...
    

    Just the obvious stand-outs accounted for 3/4 of the swaps for my input. Finding what to replace them with was a bit more challenging, but the same premise of "follow the pattern" still holds.

    Btw, thanks for sharing your Jai work! I'd never heard of the language, and while I'm not sure if I'm interested in trying it, I watched one of the creator's videos and the ideas seemed super interesting!

    1 vote
  6. Comment on Day 25: Code Chronicle in ~comp.advent_of_code

    scarecrw
    Link
    Short and sweet to finish! I just hardcoded all the dimensions, but it's the last day, so I'm happy enough with it. Smalltalk Solution Class { #name : 'Day25Solver', #superclass : 'AoCSolver',...

    Short and sweet to finish! I just hardcoded all the dimensions, but it's the last day, so I'm happy enough with it.

    Smalltalk Solution
    Class {
    	#name : 'Day25Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'locks', 'keys' ],
    	#category : 'AoCDay25',
    	#package : 'AoCDay25'
    }
    
    Day25Solver >> getHeights: aString [
       | heights |
       heights := OrderedCollection new: 5 withAll: 0.
       aString lines do: [ :row |
          row withIndexDo: [ :c :i |
             c = $# ifTrue: [ heights at: i put: (heights at: i) + 1 ] ] ].
       ^ heights
    ]
    
    Day25Solver >> parseRawData [
       locks := OrderedCollection new.
       keys := OrderedCollection new.
       (rawData splitOn: String cr , String cr) do: [ :schematic |
          (schematic startsWith: '#')
             ifTrue: [ locks add: (self getHeights: schematic) ]
             ifFalse: [ keys add: (self getHeights: schematic) ] ]
    ]
    
    Day25Solver >> solvePart1 [
    	^ locks collectAndSum: [ :lock | keys count: [ :key | (lock + key) max <= 7 ] ]
    ]
    

    I've certainly come a long with Pharo since the beginning of the month! I can't say I expect to use it much going forward, but I'm pleased to have gotten through AoC with it and definitely feel like I've gotten the "experience" of working in a language like this. I caught myself the other day writting in another language and thinking "oh man, I wish I could do it like Pharo..." which is always a great indicator that something's started to rub off.

    Always more puzzles to be had, but this year's AoC was a wonderful time!

    1 vote
  7. Comment on Day 24: Crossed Wires in ~comp.advent_of_code

    scarecrw
    Link
    This was a fantastic problem for using Pharo! I still don't have a generalized solution to Part 2, but this was a great opportunity to showcase the difference between Pharo and traditional...

    This was a fantastic problem for using Pharo! I still don't have a generalized solution to Part 2, but this was a great opportunity to showcase the difference between Pharo and traditional programming environments. Instead of writing an algorithm to solve, I focused on building objects and tools and just working in the inspector.

    I can't really share this process in a neat way, but here's a screenshot to give a sense of what this looks like during the process: https://i.imgur.com/3Sq9t8E.png

    Once I had a way to inspect the connections, swap outputs, and test the result, actually finding the solutions was fairly trivial: just look for the odd ones out that don't follow the pattern of every other connection.

    Pharo Smalltalk Solution

    1 vote
  8. Comment on Day 23: LAN Party in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    I'm sure at some point I knew some proper clique finding algorithm, but I couldn't remember anything. IIRC a generalized solution is NP-hard, but I took a look at the graph and noticed all the...

    I'm sure at some point I knew some proper clique finding algorithm, but I couldn't remember anything. IIRC a generalized solution is NP-hard, but I took a look at the graph and noticed all the nodes have the same degree, and just counted down from this upper bound on the clique size.

    Similar to yesterday, I think one of the most useful skills I've most developed for these types of problems is being able to quickly eyeball what approaches are going to fit the input size. I can definitely see myself in the past getting lost in the weeds on an approach that was never going to be efficient enough.

    I didn't do much refactoring, so code's pretty ugly today, with some goofy deep nesting.

    Pharo Smalltalk Solution
    Class {
    	#name : 'Day23Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'computers' ],
    	#category : 'AoCDay23',
    	#package : 'AoCDay23'
    }
    
    Day23Solver >> cliquesOfSize: size [
       | potentialCliques |
       potentialCliques := OrderedCollection new.
       computers keysAndValuesDo: [ :comp :neighbors |
          neighbors combinations: size - 1 atATimeDo: [ :comb |
             potentialCliques add: comb asOrderedCollection , { comp } ] ].
       ^ potentialCliques select: [ :x | self isClique: x ]
    ]
    
    Day23Solver >> isClique: aCollection [
       aCollection withIndexDo: [ :v :i |
          ((computers at: v) includesAll: (aCollection allButFirst: i)) ifFalse: [ ^ false ] ].
       ^ true
    ]
    
    Day23Solver >> parseRawData [
       | comps |
       computers := Dictionary new.
       rawData lines do: [ :line |
          comps := line splitOn: '-'.
          (computers at: comps first ifAbsentPut: OrderedCollection new) add: comps second.
          (computers at: comps second ifAbsentPut: OrderedCollection new) add: comps first ]
    ]
    
    Day23Solver >> solvePart1 [
       ^ ((self cliquesOfSize: 3) count: [ :clique | clique anySatisfy: [ :x | x first = $t ] ]) // 3
    ]
    
    Day23Solver >> solvePart2 [
       | maxSize cliques |
       maxSize := computers values first size + 1.
       maxSize to: 1 by: -1 do: [ :size |
          cliques := self cliquesOfSize: size.
          cliques ifNotEmpty: [ ^ ',' join: cliques first sorted ] ].
    ]
    

    Edit: simplified code a bit

    2 votes
  9. Comment on Day 22: Monkey Market in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Not super proud of the result, but solving went smoothly. I didn't make any optimizations on the PRNG, though I suspect there may be some. My part 2 takes ~12 seconds to run, so if I have some...

    Not super proud of the result, but solving went smoothly. I didn't make any optimizations on the PRNG, though I suspect there may be some. My part 2 takes ~12 seconds to run, so if I have some time I might see if I can fix that. I think the issue is in storing and using the delta sequence as a key in the dictionary; that or in merging dictionaries.

    Pharo Smalltalk Solution:

    PRNG
    Class {
    	#name : 'Day22PRNG',
    	#superclass : 'Object',
    	#instVars : [ 'startingNumber', 'currentNumber', 'deltaHistory' ],
    	#category : 'AoCDay22',
    	#package : 'AoCDay22'
    }
    
    Day22PRNG >> initialize [
       startingNumber := 0.
       deltaHistory := 0
    ]
    
    Day22PRNG >> next [
       | oldPrice delta |
       oldPrice := self price.
       currentNumber := (currentNumber << 6 bitXor: currentNumber) % (1 << 24).
       currentNumber := (currentNumber >> 5 bitXor: currentNumber) % (1 << 24).
       currentNumber := (currentNumber << 11 bitXor: currentNumber) % (1 << 24).
       delta := self price - oldPrice.
       deltaHistory := (deltaHistory * 18 + delta + 9) % (18 ** 4).
       ^ currentNumber
    ]
    
    Day22PRNG >> price [
    	^ currentNumber rem: 10
    ]
    
    Day22PRNG >> startingNumber: anObject [
    	startingNumber := anObject.
    	currentNumber := anObject
    ]
    
    Solver
    Class {
    	#name : 'Day22Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'startingNumbers' ],
    	#category : 'AoCDay22',
    	#package : 'AoCDay22'
    }
    
    Day22Solver >> parseRawData [
    	startingNumbers := rawData lines collect: #asInteger
    ]
    
    Day22Solver >> solvePart1 [
       | prng |
       ^ startingNumbers collectAndSum: [ :startingNum |
            prng := Day22PRNG new startingNumber: startingNum.
            2000 timesRepeat: [ prng next ].
            prng currentNumber ]
    ]
    
    Day22Solver >> solvePart2 [
       | sums prng key totalSums |
       totalSums := Dictionary new.
       startingNumbers do: [ :startingNum |
          sums := Dictionary new.
          prng := Day22PRNG new startingNumber: startingNum.
          prng next; next; next; next.
    		1996 timesRepeat: [ 
    			key := prng deltaHistory.
             sums at: key put: (sums at: key ifAbsent: prng price).
             prng next ].
          sums keysAndValuesDo: [ :k :v | totalSums at: k update: [ :curr | curr + v ] initial: v ] ].
       ^ totalSums values max
    ]
    

    Edit: Sorted out the delta history hashing! Part 2 now runs in ~2.5 seconds which is good enough for me!

    1 vote
  10. Comment on Day 21: Keypad Conundrum in ~comp.advent_of_code

    scarecrw
    Link
    Definitely a step up in difficulty from the previous few days! I was shocked to almost make the leaderboard for part 1, but definitely struggled a bit more with part 2. I spent some time after...

    Definitely a step up in difficulty from the previous few days! I was shocked to almost make the leaderboard for part 1, but definitely struggled a bit more with part 2. I spent some time after refactoring to generalize the keypads a bit, though I still wouldn't describe today's solution as particularly comprehensible.

    Solution Logic

    The core insight was that, while each level was different, the best move sequence to get from one key to another was always the same for a given level. With 5 keys to move between and 25 levels, this leads to a manageable number of options to test.

    Pharo Smalltalk Solution

    1 vote
  11. Comment on Day 20: Race Condition in ~comp.advent_of_code

    scarecrw
    Link
    Came up with the approach I ended up using fairly quickly, but made some silly mistakes along the way. Part 2 still takes ~5 seconds to run, so I'm hoping to see someone else with a better...

    Came up with the approach I ended up using fairly quickly, but made some silly mistakes along the way. Part 2 still takes ~5 seconds to run, so I'm hoping to see someone else with a better solution.

    Smalltalk Solution
    Class {
    	#name : 'Day20Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'startPos', 'endPos', 'walls', 'goal', 'width', 'height', 'endDistance' ],
    	#category : 'AoCDay20',
    	#package : 'AoCDay20'
    }
    
    Day20Solver >> parseRawData [
    	height := rawData lines size.
    	width := (rawData lines at: 1) size.
    	walls := Set new.
    	rawData lines withIndexDo: [ :row :r |
    		row withIndexDo: [ :val :c |
    			val = $# ifTrue: [ walls add: r @ c ].
    			val = $S ifTrue: [ startPos := r @ c ].
    			val = $E ifTrue: [ endPos := r @ c ] ] ].
    	self buildEndDistance
    ]
    
    Day20Solver >> buildEndDistance [
    	| curr dist |
    	endDistance := Dictionary new.
    	curr := Set newFrom: { endPos }.
    	dist := 0.
    	[ curr isEmpty ] whileFalse: [
    		curr do: [ :pos | endDistance at: pos put: dist ].
    		dist := dist + 1.
    		curr := ((curr flatCollect: [ :pos | pos fourNeighbors ])
    			select: [ :pos | self canMove: pos ]) 
    			reject: [ :pos | endDistance includesKey: pos ] ]
    ]
    
    Day20Solver >> canMove: pos [
    	^ (pos between: 1 @ 1 and: height @ width) and: [ (walls includes: pos) not ]
    ]
    
    Day20Solver >> neighborsOf: aPoint distance: dist [
    	| neighbors |
    	neighbors := (aPoint x - dist to: aPoint x + dist) flatCollect: [ :r |
    		             (aPoint y - dist to: aPoint y + dist) collect: [ :c | r @ c ] ].
    	^ neighbors select: [ :pos | (self canMove: pos) and: [ (aPoint manhattanDistanceTo: pos) <= dist ] ]
    ]
    
    Day20Solver >> solveWithJumpSize: jumpSize [
    	| cheatValues jumpStarts |
    	jumpStarts := (1 to: height) flatCollect: [ :r | (1 to: width) collect: [ :c | r @ c ] ].
    	jumpStarts := jumpStarts select: [ :pos | self canMove: pos ].
    	cheatValues := jumpStarts flatCollect: [ :jumpStart |
    		               (self neighborsOf: jumpStart distance: jumpSize) collect: [ :jumpEnd |
    				               (endDistance at: jumpStart)
    				               - (endDistance at: jumpEnd)
    				               - (jumpStart manhattanDistanceTo: jumpEnd) ] ].
    	^ cheatValues count: [ :cheatVal | cheatVal >= goal ]
    ]
    
    Day20Solver >> solvePart1 [
    	^ self solveWithJumpSize: 2
    ]
    
    Day20Solver >> solvePart2 [
    	^ self solveWithJumpSize: 20
    ]
    
    4 votes
  12. Comment on Day 19: Linen Layout in ~comp.advent_of_code

    scarecrw
    (edited )
    Link
    Hard to complain, but this felt far easier than previous day 19s. I think I might just be biased having done enough coding puzzles in the past that I've probably done something similar a half...

    Hard to complain, but this felt far easier than previous day 19s. I think I might just be biased having done enough coding puzzles in the past that I've probably done something similar a half dozen times.

    Nothing special about the solution, just recursively solving each string by testing all the prefixes, caching results for part 2.

    Smalltalk Solution
    Class {
    	#name : 'Day19Solver',
    	#superclass : 'AoCSolver',
    	#instVars : [ 'patterns', 'designs', 'cache' ],
    	#category : 'AoCDay19',
    	#package : 'AoCDay19'
    }
    
    Day19Solver >> canBuild: design [ 
    	design ifEmpty: [ ^ true ].
    	^ (patterns select: [ :pattern | design beginsWith: pattern ])
    		  anySatisfy: [ :pattern | self canBuild: (design allButFirst: pattern size) ]
    ]
    
    Day19Solver >> countBuilds: aString [
    	| options ans |
    	(cache includesKey: aString) ifTrue: [ ^ cache at: aString ].
    	aString isEmpty ifTrue: [ ^ 1 ].
    	options := patterns select: [ :pattern | aString beginsWith: pattern ].
    	ans := (options collect: [ :option | self countBuilds: (design allButFirst: option size) ]) sumNumbers.
    	cache at: aString put: ans.
    	^ ans
    ]
    
    Day19Solver >> initialize [
    	cache := Dictionary new.
    ]
    
    Day19Solver >> parseRawData [
    	patterns := rawData lines first splitOn: ', '.
    	designs := rawData lines allButFirst: 2.
    ]
    
    Day19Solver >> solvePart1 [
    	^ designs count: [ :design | self canBuild: design ]
    ]
    
    Day19Solver >> solvePart2 [
    	^ (designs collect: [ :design | self countBuilds: design ]) sum
    ]
    
    2 votes
  13. 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
  14. 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
  15. 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

  16. 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
  17. 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
  18. 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
  19. 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
  20. 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

    1 vote