3d12's recent activity

  1. Comment on Justin Timberlake: Tiny Desk Concert (2024) in ~music

    3d12
    Link Parent
    100% this. Never would have described myself as a "fan," but this was legitimately good. And SexyBack was a friggin' bop. I got a serious "uncanny valley" vibe from JT the first few times he...

    I enjoyed this tiny desk a lot more than I expected!

    100% this. Never would have described myself as a "fan," but this was legitimately good. And SexyBack was a friggin' bop.

    I got a serious "uncanny valley" vibe from JT the first few times he smiles into the camera though. It looks like he was maybe feeling a bit stiff or awkward at the start but definitely eased up as the set went on.

    3 votes
  2. Comment on Tell me about your weird religious beliefs in ~humanities

    3d12
    Link
    Here is a poem I wrote a few years ago, when I was regularly meeting up with an acoustic jam at my local library. It's called "Music is my Religion," and it's an expression of the spiritual...

    Here is a poem I wrote a few years ago, when I was regularly meeting up with an acoustic jam at my local library. It's called "Music is my Religion," and it's an expression of the spiritual sensations I found being part of that group.

    Music is my Religion

    Each morning, I find myself deep in worship
    There is no heresy here but silence
    No "dangerous other"
    Everyone is invited to sing praises to the gospel that resonates through us all

    Love is a wavelength
    Compassion is a time signature
    And the beat of human life is carried by the supernatural power embodied in the music

    I am not spiritual
    I am not a believer
    I am a musician
    And knowing this opens my soul to possibilities beyond what any god could provide me

    People bring their little ones to worship
    in the hall,
    in the audience,
    on the floor,
    wherever they can find room

    To join, or to listen
    To receive the melody's blessing
    To take part in the communion of our spirits
    with the music that surrounds us all

    Nobody comes to find value,
    or worth,
    or validation
    We gather to praise the keys and scales from which we compose our essence

    And when our composition is done, we disperse
    back to our normal lives
    made whole again by the timbre and tone
    carrying the beat with us

    Until the time comes to share our song again
    And we make beautiful harmony with each other
    Soothing to the ear,
    calming to the spirit,
    the tunes ring out,
    the catechism of our lives

    Music is my religion

    Background/Context

    I was raised loosely Lutheran, then started experimenting with different spiritual and philosophical beliefs all through college, before finally settling on an agnostic flavor of hard determinism. In essence, I don't think we'll ever definitively know whether there's a god, but I think it's far more probable that if they exist they are more of an absent watchmaker than an omnipresent overseer. And I believe free will is an illusion, just our perception of the natural forces guiding our actions.

    All that said, meeting up with this acoustic group was an incredibly intimidating prospect. I'd never played with other actual musicians before, just jammed around in a friend's garage. But to find such a welcoming environment, full of diverse and wonderfully talented people, was an incredible joy. It opened my eyes to the delight of playing music with others, and for the first time in my life I found myself enjoying listening to (and playing along to) Christian hymnals -- a favorite of many of the group's older members.

    I remember coming back to my apartment after a session and just pouring this whole poem out in one long stream of consciousness. I haven't had an opportunity to play with a group like that since, but I'm looking into options of starting a group in my area. If I can create an environment that can inspire even one person to feel like I did when I wrote this, I would consider that very meaningful.

    5 votes
  3. Comment on Palworld could be a delight if it wasn't so invested in being awful in ~games

    3d12
    Link Parent
    Gonna stump for just a moment and say I had a great time with Cassette Beasts. For $20 it's a very solid competitor in this genre. The story, while still quite silly and "quirky" at times, hit...

    I think people want a fully fledged game with character arcs, mechanics beyond "fire beats leaf beats water beats fire."

    Gonna stump for just a moment and say I had a great time with Cassette Beasts. For $20 it's a very solid competitor in this genre. The story, while still quite silly and "quirky" at times, hit some serious tones too which surprised me, and the overworld is genuinely a delight to explore. The mechanics are also absolutely batshit bonkers, like this goes way beyond STAB and into genuinely interesting strategic territory. And basically every fight being a duo (2v2) means lots of ability to synchronize abilities for maximum effect.

    My personal favorite change? Reusable TMs. That scratched such an itch that I audibly sighed out loud in happiness when I realized. It's so much less stressful to be able to experiment with new movesets without needing to involve hours of grinding/breeding.

    10 votes
  4. Comment on Armored Core VI discussion in ~games

    3d12
    Link Parent
    Not a dig, but this comment made me genuinely curious. According to this page the global achievement stats on Steam have a 93.6% on the achievement for clearing the tutorial boss. You wouldn't be...

    Not gonna make excuses, I know plenty people got past this point.

    Not a dig, but this comment made me genuinely curious. According to this page the global achievement stats on Steam have a 93.6% on the achievement for clearing the tutorial boss. You wouldn't be counted in this figure, having refunded the game, and I'm sure you're not the only one. But out of only the people who haven't refunded the game yet, more than 5% haven't gotten past that point either.

    I don't really have a point. You keep being you, you're beautiful. 🙂

    1 vote
  5. Comment on What are your favorite ridiculous/absurd/campy Christmas movies? in ~movies

    3d12
    Link Parent
    This one is a tradition around my house, too. So many amazing casting choices all around, but usually all I have to say to get someone to watch it is "Andy Dick as evil santa." They also released...

    This one is a tradition around my house, too. So many amazing casting choices all around, but usually all I have to say to get someone to watch it is "Andy Dick as evil santa."

    They also released a trailer for the sequel back in 2017, alongside a slightly successful crowdfunding campaign and simultaneously launched a merch store... But it doesn't seem like anything else has come of the project lately.

    2 votes
  6. Comment on I've been looking into self-hosting, what's the best cost-efficient option? in ~tech

    3d12
    Link
    I can't offer much advice, since everyone's situation is a little different, but keep in mind that the Pis have an ARM processor, and not all the software you want to use may be compatible. If...

    I can't offer much advice, since everyone's situation is a little different, but keep in mind that the Pis have an ARM processor, and not all the software you want to use may be compatible. If source is available you can usually compile for your specific target, but don't expect software to work out-of-the-box as much as using x86-based hardware.

    For what it's worth, I would trust a refurb from some place like woot.com more than a rando on FB/Craigslist. Or better yet, a local PC shop if you have one that won't price-gouge you on some last-gen tech.

    Power consumption is definitely a good consideration too, my oldest server was a desktop from 2008-ish that pulls 6A (!) which is way too much for a headless server, so it got replaced this year with something a lot more power-efficient. But of course, you can switch out the power supply on whatever chassis you find if you're so inclined, and that could be a good way to mix-and-match your way to some savings. 🤷

    6 votes
  7. Comment on Starter tool set for someone starting out in ~life.home_improvement

    3d12
    Link Parent
    Interesting you put DeWalt as good, when they were acquired by Black and Decker in 1960. In my own experience, they're great tools.

    Interesting you put DeWalt as good, when they were acquired by Black and Decker in 1960.

    In my own experience, they're great tools.

    1 vote
  8. Comment on Any popular game genres you just can't get into? in ~games

    3d12
    Link Parent
    Another place where Dota 2 shines. Not only are many heroes focused on multi-unit gameplay (Enigma, Naga Siren, Terrorblade, just to name a few) but a common item progression for pushing includes...

    Another place where Dota 2 shines. Not only are many heroes focused on multi-unit gameplay (Enigma, Naga Siren, Terrorblade, just to name a few) but a common item progression for pushing includes Manta Style, which creates controllable copies (illusions) of your hero.

    Also, see the infamous "Puppey block" of TI2 where 2 summons (Lycan's wolves) were used to block a hero's pathing so a teammate could catch up for a very early kill.

  9. Comment on Any popular game genres you just can't get into? in ~games

    3d12
    Link Parent
    Dota 2 is the clear winner here. It's the only MOBA I'm aware of that doesn't sell playable characters. So no gameplay is money-gated, only cosmetics.

    Dota 2 is the clear winner here. It's the only MOBA I'm aware of that doesn't sell playable characters. So no gameplay is money-gated, only cosmetics.

    3 votes
  10. Comment on Is it possible to run a Linux app that requires USB/OTG support from an Android device? in ~tech

    3d12
    Link
    I'm going to lean pretty far towards "no" on this one. Just from a quick peek at the install.sh file included in the linux distribution of this program, it seems like the setup wouldn't be...

    I'm going to lean pretty far towards "no" on this one.

    Just from a quick peek at the install.sh file included in the linux distribution of this program, it seems like the setup wouldn't be compatible with Android's filesystem to begin with. Even if that's not the case, or Android symlinks those locations to more appropriate locations in the filesystem, it doesn't include an "app" per se, like Android is expecting. Just the files needed to run the application. That sounds like a a contradiction, but Android apps require a specific "manifest" file which tells the OS what the app is capable of, what permissions it needs, etc. The install.sh file doesn't include any of that. So while setup might "work" (or appear to), I don't think you have a built-in way to tell Android "hey, go take those files I just copied and run them" -- but that said, with a little tinkering in Android Studio, it might be possible to create a "wrapper" app that does exactly that.

    Of course, the best way to confirm this compatibility would probably be to contact their support, or try posting on the forums. I searched the forums for "android" and got no hits, and "linux" only got two hits.

    Realistically, your best bet might just be to put Ubuntu on a laptop or VM and use that to tweak the settings. At least the settings are saved on the device, so you wouldn't need a computer connected every time you want to use it.

    4 votes
  11. Comment on How do you imagine society would develop if dragons existed? in ~talk

    3d12
    Link
    This is one of the premises of the Shadowrun RPG setting. Basically, dragons are powerful enough to manipulate people from behind the scenes, and rich enough to own global corporations. It's kind...

    This is one of the premises of the Shadowrun RPG setting. Basically, dragons are powerful enough to manipulate people from behind the scenes, and rich enough to own global corporations. It's kind of like when people speculate what a sentient and godlike AI would look like these days.

    4 votes
  12. Comment on Paramount Plus and Showtime become ‘Paramount Plus with Showtime’ in ~tv

    3d12
    Link Parent
    Paramount+ with Showtime: Deluxe Ultimate Complete Streaming Service of the Year Edition

    Paramount+ with Showtime: Deluxe Ultimate Complete Streaming Service of the Year Edition

    2 votes
  13. Comment on This shall be my last post about MUD games in ~games

    3d12
    Link Parent
    Yeah, that's the site for ATS. They've got ship systems, some kind of commodity-based economy, and automated trading vessels going between stations; lots of really interesting mechanics which lend...

    Yeah, that's the site for ATS. They've got ship systems, some kind of commodity-based economy, and automated trading vessels going between stations; lots of really interesting mechanics which lend themselves well to players trying to generate their own content instead of having it randomly generated for them.

    The social interaction is difficult sometimes, yeah. It's especially hard to get people onto the same timeframe for multiple scenes over an arc. But it just takes getting involved and putting yourself out there, just like any other community.

    2 votes
  14. Comment on This shall be my last post about MUD games in ~games

    3d12
    Link
    Great write-up, lou! I used to be really into MU* games, spending most of that time on a Star Trek MUSH around 2009. What really struck me about that type of game is the freedom and creativity...

    Great write-up, lou!

    I used to be really into MU* games, spending most of that time on a Star Trek MUSH around 2009. What really struck me about that type of game is the freedom and creativity allowed in building and describing things.

    As an analogy, some games will give me color sliders to change the RGB values of my character's armor. But no game (that I know of) gives me the ability to import my own meshes and textures to make my armor look like literally whatever I want. That's the kind of freedom given to text.

    Aside from just description, you make an excellent point about mechanics. The MUSH I played on used Aspace (originally from and still used by Among the Stars TrekMUSH) and that system is so incredible from a ship systems and command perspective. Easily the most fun I've had piloting a spaceship in any game.

    3 votes
  15. Comment on Advent of Code 2022 post-mortem discussion in ~comp

    3d12
    Link
    Confirmed, there is no day 26. Instead, how about a brief post-mortem from participants? Favorite/least favorite problems, any interesting workarounds, or something new you learned? I struggled...

    Confirmed, there is no day 26. Instead, how about a brief post-mortem from participants? Favorite/least favorite problems, any interesting workarounds, or something new you learned?

    I struggled immensely with day 15, and that threw my rhythm off completely for the rest of the event. I also didn't bother posting any of my solutions in the threads this year, but since my wife is now learning Python, I tried doing the problems in Python so she could follow along. I took much better advantage of list comprehension this time, to great effect too, since I liked the map() functionality of Javascript so much when I used that last year.

    My favorite problem this year was the CRT display one (day 10) just because I love the problems with a visual solution (see also: transparent origami, AoC 2021 Day 13)

    My repo of solution code: https://github.com/3d12/adventofcode2022-py

    I also admit I had to peek at the thread for day 13 to find the solution to part 2. My math skills were failing me and I tried a bunch of different modulo numbers but didn't even think of the lowest common multiple.

    2 votes
  16. Comment on The next (monthly, one-month-long) Linux Upskill Challenge starts this Monday in ~comp

    3d12
    Link Parent
    Thanks for bringing this up, Eric_the_Cerise :) I am also following along with this, but I don't feel like logging into reddit to participate. Will it be too noisy if we create daily threads to...

    Thanks for bringing this up, Eric_the_Cerise :) I am also following along with this, but I don't feel like logging into reddit to participate. Will it be too noisy if we create daily threads to share our progress on this event? Would weekly perhaps work better?

    As for my day 1, I actually went ahead and added the SSH key for login on day 0 (setup) since Digital Ocean offers that instead of a password-based login. So I ended up getting a little ahead and not having much to do today. But I'm looking forward to learning more sysadmin stuff! Specifically, I'm not so good with systemd yet or networking & sockets, so I'm hoping this event will touch on those topics.

    1 vote
  17. Comment on Day 13: Transparent Origami in ~comp

    3d12
    Link
    Friggin' yeesh. With everyone saying how easy this year is compared to previous, it's no wonder I couldn't hang in this far in previous years. This did end up being a very fun (and visually...

    Friggin' yeesh. With everyone saying how easy this year is compared to previous, it's no wonder I couldn't hang in this far in previous years.

    This did end up being a very fun (and visually pleasing!) problem once I worked out the kinks. I knew I'd get in trouble for "standardizing" my test folds to a centralized axis, so that did come back to bite me. But I went ahead and coded for all the folds since the problem implied that was the next step, and ended up getting some fun map/filter practice to backwardly-derive my answer to part 1.

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseGrid(input) {
    	let gridPoints = [];
    	for (const line of input) {
    		let regex = /(\d+),(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let pointX = parseInt(found[1]);
    			let pointY = parseInt(found[2]);
    			gridPoints.push({ x: pointX, y: pointY });
    		}
    	}
    	let foldInstructions = [];
    	for (const line of input) {
    		let regex = /fold along ([x|y])=(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let foldDirection = found[1];
    			let foldDistance = parseInt(found[2]);
    			foldInstructions.push({ direction: foldDirection, distance: foldDistance });
    		}
    	}
    	return { gridPoints: gridPoints, foldInstructions: foldInstructions };
    }
    
    function mapGrid(input) {
    	let output = [];
    	console.log("DEBUG: input.map(e => e.x) = " + input.map(e => e.x).sort((a,b) => b-a)[0]);
    	let length = input.map(e => e.x).sort((a,b) => b-a)[0];
    	let depth = input.map(e => e.y).sort((a,b) => b-a)[0];
    	console.log("DEBUG: length = " + length + ", depth = " + depth);
    	for (let y = 0; y <= depth; y++) {
    		let currentLine = [];
    		for (let x = 0; x <= length; x++) {
    			if (input.filter(e => e.x === x).filter(e => e.y === y).length > 0) {
    				currentLine.push('#');
    			} else {
    				currentLine.push('.');
    			}
    		}
    		output.push(currentLine);
    	}
    	return output;
    }
    
    function foldGrid(input,direction,distance) {
    	let output = [];
    	if (direction === 'x') {
    		let inputCopy = input.map(e => e);
    		for (let row of inputCopy) {
    			row[distance] = '|';
    		}
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let row of inputCopy) {
    			let newRow = [];
    			for (let i = 0; i < distance; i++) {
    				let char1 = row[i];
    				let char2 = row[i+((distance-i)*2)];
    				if (char1 === '#' || char2 === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else if (direction === 'y') {
    		let foldRow = [];
    		for (let i = 0; i<input[distance].length; i++) {
    			foldRow.push('-');
    		}
    		let inputCopy = input.map(e => e);
    		inputCopy[distance] = foldRow;
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let i = 0; i < distance; i++) {
    			let currentRow = inputCopy[i];
    			let compareRow = [];
    			let distanceOffset = i+((distance-i)*2);
    			if (distanceOffset < inputCopy.length) {
    				compareRow = inputCopy[i+((distance-i)*2)];
    			} else {
    				compareRow = currentRow;
    			}
    			let newRow = [];
    			for (let charIndex = 0; charIndex < currentRow.length; charIndex++) {
    				let currentChar = currentRow[charIndex];
    				let compareChar = compareRow[charIndex];
    				if (currentChar === '#' || compareChar === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else {
    		return new Exception("Invalid direction passed: " + direction);
    	}
    	return output;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let parsedGrid = parseGrid(inputArr);
    	console.log(parsedGrid);
    	let fold = 0;
    	let foldedMap = mapGrid(parsedGrid.gridPoints);
    	console.log("DEBUG: fold = " + fold + ", direction = " + parsedGrid.foldInstructions[0].direction + ", distance = " + parsedGrid.foldInstructions[0].distance);
    	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	console.log('');
    	foldedMap = foldGrid(
    	 			foldedMap,
    	 			parsedGrid.foldInstructions[0].direction,
    	 			parsedGrid.foldInstructions[0].distance
    	 		);
    	// for (const instruction of parsedGrid.foldInstructions) {
    	// 	fold++;
    	// 	console.log("DEBUG: fold = " + fold + ", direction = " + instruction.direction + ", distance = " + instruction.distance);
    	// 	foldedMap = foldGrid(
    	// 			foldedMap,
    	// 			instruction.direction,
    	// 			instruction.distance
    	// 		);
    	// 	console.log('');
    	// 	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	// 	console.log('');
    	// }
    	let totalDots = foldedMap
    		.filter(e => e.includes('#'))
    		.map(e => e.filter(f => f === '#').length)
    		.reduce((a,b) => a + b);
    	console.log("Answer found! " + totalDots);
    })();
    
    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseGrid(input) {
    	let gridPoints = [];
    	for (const line of input) {
    		let regex = /(\d+),(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let pointX = parseInt(found[1]);
    			let pointY = parseInt(found[2]);
    			gridPoints.push({ x: pointX, y: pointY });
    		}
    	}
    	let foldInstructions = [];
    	for (const line of input) {
    		let regex = /fold along ([x|y])=(\d+)/;
    		let found = line.match(regex);
    		if (found) {
    			let foldDirection = found[1];
    			let foldDistance = parseInt(found[2]);
    			foldInstructions.push({ direction: foldDirection, distance: foldDistance });
    		}
    	}
    	return { gridPoints: gridPoints, foldInstructions: foldInstructions };
    }
    
    function mapGrid(input) {
    	let output = [];
    	console.log("DEBUG: input.map(e => e.x) = " + input.map(e => e.x).sort((a,b) => b-a)[0]);
    	let length = input.map(e => e.x).sort((a,b) => b-a)[0];
    	let depth = input.map(e => e.y).sort((a,b) => b-a)[0];
    	console.log("DEBUG: length = " + length + ", depth = " + depth);
    	for (let y = 0; y <= depth; y++) {
    		let currentLine = [];
    		for (let x = 0; x <= length; x++) {
    			if (input.filter(e => e.x === x).filter(e => e.y === y).length > 0) {
    				currentLine.push('#');
    			} else {
    				currentLine.push('.');
    			}
    		}
    		output.push(currentLine);
    	}
    	return output;
    }
    
    function foldGrid(input,direction,distance) {
    	let output = [];
    	if (direction === 'x') {
    		let inputCopy = input.map(e => e);
    		for (let row of inputCopy) {
    			row[distance] = '|';
    		}
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let row of inputCopy) {
    			let newRow = [];
    			for (let i = 0; i < distance; i++) {
    				let char1 = row[i];
    				let char2 = row[i+((distance-i)*2)];
    				if (char1 === '#' || char2 === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else if (direction === 'y') {
    		let foldRow = [];
    		for (let i = 0; i<input[distance].length; i++) {
    			foldRow.push('-');
    		}
    		let inputCopy = input.map(e => e);
    		inputCopy[distance] = foldRow;
    		console.log(inputCopy.map(e => e.join('')).join('\n'));
    		for (let i = 0; i < distance; i++) {
    			let currentRow = inputCopy[i];
    			let compareRow = [];
    			let distanceOffset = i+((distance-i)*2);
    			if (distanceOffset < inputCopy.length) {
    				compareRow = inputCopy[i+((distance-i)*2)];
    			} else {
    				compareRow = currentRow;
    			}
    			let newRow = [];
    			for (let charIndex = 0; charIndex < currentRow.length; charIndex++) {
    				let currentChar = currentRow[charIndex];
    				let compareChar = compareRow[charIndex];
    				if (currentChar === '#' || compareChar === '#') {
    					newRow.push('#');
    				} else {
    					newRow.push('.');
    				}
    			}
    			output.push(newRow);
    		}
    	} else {
    		return new Exception("Invalid direction passed: " + direction);
    	}
    	return output;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let parsedGrid = parseGrid(inputArr);
    	console.log(parsedGrid);
    	let fold = 0;
    	let foldedMap = mapGrid(parsedGrid.gridPoints);
    	console.log("DEBUG: fold = " + fold + ", direction = " + parsedGrid.foldInstructions[0].direction + ", distance = " + parsedGrid.foldInstructions[0].distance);
    	console.log(foldedMap.map(e => e.join('')).join('\n'));
    	for (const instruction of parsedGrid.foldInstructions) {
    		fold++;
    		console.log("DEBUG: fold = " + fold + ", direction = " + instruction.direction + ", distance = " + instruction.distance);
    		foldedMap = foldGrid(
    				foldedMap,
    				instruction.direction,
    				instruction.distance
    			);
    		console.log('');
    		console.log(foldedMap.map(e => e.join('')).join('\n'));
    		console.log('');
    	}
    })();
    
    3 votes
  18. Comment on Day 12: Passage Pathing in ~comp

    3d12
    Link Parent
    Really bad. Like, over a minute? But, I didn't try it with all the console.log statements removed.

    Really bad. Like, over a minute? But, I didn't try it with all the console.log statements removed.

    3 votes
  19. Comment on Day 12: Passage Pathing in ~comp

    3d12
    Link
    Like others, I also stared at this problem for many minutes before writing any code. In fact, I thought about it so long, I fell asleep and decided to tackle it today instead. I'm not great at...

    Like others, I also stared at this problem for many minutes before writing any code. In fact, I thought about it so long, I fell asleep and decided to tackle it today instead.

    I'm not great at recursive functions, so imagine my surprise when the problem circumvented my expectations (and the reason I parsed the upper-cased-ness of the letters into a boolean flag in part 1) which led to my business side coding kicking in, and implementing a horrible "exclusion rule" which is used both in the selection criteria, then again in the actual recursion step in part 2. Hey, at least it worked. ¯\_(ツ)_/¯

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseCaves(input) {
    	let cavesArr = [];
    	for (const line of input) {
    		let lineRegex = /(\w+)-(\w+)/;
    		let found = line.match(lineRegex);
    		let dest1 = found[1];
    		let dest2 = found[2];
    		console.log("DEBUG: line parsed, dest1 = " + dest1 + ", dest2 = " + dest2);
    		let findDest1 = cavesArr.filter(e => e.name === dest1);
    		if (findDest1.length === 0) {
    			let multiplePassThrough = false;
    			if (dest1 === dest1.toUpperCase()) {
    				multiplePassThrough = true;
    			}
    			cavesArr.push({ name: dest1, leadsTo: [ dest2 ], multiplePassThrough: multiplePassThrough });
    		} else {
    			findDest1[0].leadsTo.push(dest2);
    		}
    		let findDest2 = cavesArr.filter(e => e.name === dest2);
    		if (findDest2.length === 0) {
    			let multiplePassThrough = false;
    			if (dest2 === dest2.toUpperCase()) {
    				multiplePassThrough = true;
    			}
    			cavesArr.push({ name: dest2, leadsTo: [ dest1 ], multiplePassThrough: multiplePassThrough});
    		} else {
    			findDest2[0].leadsTo.push(dest1);
    		}
    	}
    	return cavesArr;
    }
    
    function findPaths(mapArr, startRoom=mapArr.filter(e => e.name === 'start')[0], currentPath=[], pathsArr=[]) {
    	console.log("DEBUG: entering findPaths, startRoom is " + startRoom.name + " and currentPath is " + currentPath.map(e => e.name).join(','));
    	if (startRoom.name === 'end') {
    		console.log("DEBUG: ending findPaths, found end room");
    		let tempPath = currentPath.map(e => e);
    		tempPath.push(startRoom);
    		pathsArr.push(tempPath);
    		return pathsArr;
    	}
    	if (startRoom.name === 'start') {
    		console.log("DEBUG: starting findPaths, found start room");
    		currentPath.push(startRoom);
    	}
    	let dests = startRoom.leadsTo;
    	let destObjects = [];
    	for (const dest of dests) {
    		destObjects.push(mapArr.filter(e => e.name === dest)[0]);
    	}
    	let eligibleDests = destObjects.filter(e => (currentPath.filter(f => e.name === f.name).length === 0) || e.multiplePassThrough === true);
    	//console.log("DEBUG: eligible dests: " + eligibleDests.map(e => e.name).join(','));
    	for (const dest of eligibleDests) {
    		let tempPath = currentPath.map(e => e);
    		if (dest.name != 'end') {
    			tempPath.push(dest);
    		}
    		//console.log("DEBUG: about to recurse, startRoom = " + startRoom.name + ", dest = " + dest.name + ", currentPath = " + tempPath.map(e => e.name).join(',') + " and pathsArr = " + pathsArr.map(e => e.map(f => f.name).join(',')).join(';'))
    		findPaths(mapArr, dest, tempPath, pathsArr);
    	}
    	return pathsArr;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let cavesArr = parseCaves(inputArr);
    	console.log(cavesArr);
    	let paths = findPaths(cavesArr);
    	console.log(paths);
    	console.log(paths.length);
    })();
    
    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function parseCaves(input) {
    	let cavesArr = [];
    	for (const line of input) {
    		let lineRegex = /(\w+)-(\w+)/;
    		let found = line.match(lineRegex);
    		let dest1 = found[1];
    		let dest2 = found[2];
    		console.log("DEBUG: line parsed, dest1 = " + dest1 + ", dest2 = " + dest2);
    		let findDest1 = cavesArr.filter(e => e.name === dest1);
    		if (findDest1.length === 0) {
    			let multiplePassThrough = false;
    			if (dest1 === dest1.toUpperCase()) {
    				multiplePassThrough = true;
    			}
    			cavesArr.push({ name: dest1, leadsTo: [ dest2 ], multiplePassThrough: multiplePassThrough });
    		} else {
    			findDest1[0].leadsTo.push(dest2);
    		}
    		let findDest2 = cavesArr.filter(e => e.name === dest2);
    		if (findDest2.length === 0) {
    			let multiplePassThrough = false;
    			if (dest2 === dest2.toUpperCase()) {
    				multiplePassThrough = true;
    			}
    			cavesArr.push({ name: dest2, leadsTo: [ dest1 ], multiplePassThrough: multiplePassThrough});
    		} else {
    			findDest2[0].leadsTo.push(dest1);
    		}
    	}
    	return cavesArr;
    }
    
    function findPaths(mapArr, startRoom=mapArr.filter(e => e.name === 'start')[0], currentPath=[], pathsArr=[], smallRoomDoubled=false) {
    	console.log("DEBUG: entering findPaths, startRoom is " + startRoom.name + ", smallRoomDoubled is " + smallRoomDoubled + ", and currentPath is " + currentPath.map(e => e.name).join(','));
    	if (startRoom.name === 'end') {
    		console.log("DEBUG: ending findPaths, found end room");
    		let tempPath = currentPath.map(e => e);
    		tempPath.push(startRoom);
    		pathsArr.push(tempPath);
    		return pathsArr;
    	}
    	if (startRoom.name === 'start') {
    		console.log("DEBUG: starting findPaths, found start room");
    		currentPath.push(startRoom);
    	}
    	let dests = startRoom.leadsTo;
    	let destObjects = [];
    	for (const dest of dests) {
    		destObjects.push(mapArr.filter(e => e.name === dest)[0]);
    	}
    	let eligibleDests = destObjects.filter(e =>
    		(
    		currentPath.filter(f => e.name === f.name).length === 0
    		||
    		e.multiplePassThrough === true
    		||
    		(
    			e.name === e.name.toLowerCase()
    			&& currentPath.filter(f => e.name === f.name).length === 1
    			&& smallRoomDoubled === false
    			&& e.name != 'start'
    			&& e.name != 'end'
    		)
    	));
    	//console.log("DEBUG: eligible dests: " + eligibleDests.map(e => e.name).join(','));
    	for (const dest of eligibleDests) {
    		let tempPath = currentPath.map(e => e);
    		let tempSmallRoomDoubled = smallRoomDoubled;
    		if (dest.name != 'end') {
    			tempPath.push(dest);
    		}
    		if (
    				dest.name === dest.name.toLowerCase()
    				&& currentPath.filter(f => dest.name === f.name).length === 1
    				&& tempSmallRoomDoubled === false
    				&& dest.name != 'start'
    				&& dest.name != 'end'
    			) {
    				findPaths(mapArr, dest, tempPath, pathsArr, true);
    		} else {
    			//console.log("DEBUG: about to recurse, startRoom = " + startRoom.name + ", dest = " + dest.name + ", currentPath = " + tempPath.map(e => e.name).join(',') + " and pathsArr = " + pathsArr.map(e => e.map(f => f.name).join(',')).join(';'))
    			findPaths(mapArr, dest, tempPath, pathsArr, smallRoomDoubled);
    		}
    	}
    	return pathsArr;
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let cavesArr = parseCaves(inputArr);
    	console.log(cavesArr);
    	let paths = findPaths(cavesArr);
    	console.log(paths);
    	console.log(paths.length);
    })();
    
    2 votes
  20. Comment on Day 11: Dumbo Octopus in ~comp

    3d12
    Link
    Yay for reusable functions! Just had to add diagonals to my findNeighbors function from day9, and it was ready to be re-used! On a side note, part 2's output was extremely visually satisfying to...

    Yay for reusable functions! Just had to add diagonals to my findNeighbors function from day9, and it was ready to be re-used! On a side note, part 2's output was extremely visually satisfying to me. :)

    Oh! And I'm extremely proud of my solve time between the two parts. Part 2 only took +3m from part 1, because I was already returning the count of flashes from each iteration, and the comparison for that is a calculable constant!

    Part 1
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function findNeighbors(x,y,octopusGrid) {
    	let neighbors = [];
    	let rowBoundary = octopusGrid.length-1;
    	let colBoundary = octopusGrid[0].length-1;
    	// left
    	if (x-1 >= 0) {
    		neighbors.push({ x: x-1, y: y });
    	}
    	// right
    	if (x+1 <= colBoundary) {
    		neighbors.push({ x: x+1, y: y });
    	}
    	// up
    	if (y-1 >= 0) {
    		neighbors.push({ x: x, y: y-1 });
    	}
    	// down
    	if (y+1 <= rowBoundary) {
    		neighbors.push({ x: x, y: y+1 });
    	}
    	// up-left
    	if (x-1 >= 0 && y-1 >= 0) {
    		neighbors.push({ x: x-1, y: y-1 });
    	}
    	// up-right
    	if (x+1 <= colBoundary && y-1 >= 0) {
    		neighbors.push({ x: x+1, y: y-1 });
    	}
    	// down-left
    	if (x-1 >= 0 && y+1 <= rowBoundary) {
    		neighbors.push({ x: x-1, y: y+1 });
    	}
    	// down-right
    	if (x+1 <= colBoundary && y+1 <= rowBoundary) {
    		neighbors.push({ x: x+1, y: y+1 });
    	}
    	return neighbors;
    }
    
    function simulateStep(octopusGrid) {
    	let flashedThisStep = [];
    	let newGrid = [];
    	// serialize into objects
    	for (let y = 0; y < octopusGrid.length; y++) {
    		let currentRow = octopusGrid[y];
    		let newRow = [];
    		for (let x = 0; x < currentRow.length; x++) {
    			newRow.push({ numericValue: octopusGrid[y][x] });
    		}
    		newGrid.push(newRow);
    	}
    	// increment every octopus' value by 1
    	for (let y = 0; y < newGrid.length; y++) {
    		let currentRow = newGrid[y];
    		for (let x = 0; x < currentRow.length; x++) {
    			newGrid[y][x].numericValue++;
    		}
    	}
    	// check for flashes
    	for (let y = 0; y < newGrid.length; y++) {
    		let currentRow = newGrid[y];
    		for (let x = 0; x < currentRow.length; x++) {
    			let currentOctopus = currentRow[x];
    			// on flash, if not already flashed this step
    			if (currentOctopus.numericValue > 9 && flashedThisStep.filter(e => e.x === x && e.y === y).length === 0) {
    				let flashesToResolve = [ { x: x, y: y } ];
    				while (flashesToResolve.length > 0) {
    					//console.log("DEBUG: flashesToResolve = " + flashesToResolve.map(e => e.x + ',' + e.y).join(';'));
    					let resolvingFlash = flashesToResolve.pop();
    					//console.log("DEBUG: " + resolvingFlash.x + "," + resolvingFlash.y + " (" + newGrid[resolvingFlash.y][resolvingFlash.x].numericValue + ") flashed");
    					// add to flashedThisStep
    					flashedThisStep.push({ x: resolvingFlash.x, y: resolvingFlash.y });
    					let neighbors = findNeighbors(resolvingFlash.x,resolvingFlash.y,newGrid);
    					//console.log("DEBUG: neighbors: " + neighbors.map(e => e.x + "," + e.y).join(';'));
    					// increase all neighbors
    					for (const neighbor of neighbors) {
    						//console.log("DEBUG: increasing neighbor " + neighbor.x + "," + neighbor.y + " (" + newGrid[neighbor.y][neighbor.x].numericValue + ") -> (" + (newGrid[neighbor.y][neighbor.x].numericValue + 1) + ")");
    						newGrid[neighbor.y][neighbor.x].numericValue++;
    						// check for flashes
    						if (newGrid[neighbor.y][neighbor.x].numericValue > 9
    								&& flashedThisStep.filter(e => e.x === neighbor.x && e.y === neighbor.y).length === 0
    								&& flashesToResolve.filter(e => e.x === neighbor.x && e.y === neighbor.y).length === 0) {
    							//console.log("DEBUG: adding " + neighbor.x + "," + neighbor.y + " to flashesToResolve");
    							// add to flashesToResolve
    							flashesToResolve.push(neighbor);
    						}
    					}
    				}
    			}
    		}
    	}
    	// all flashed octopi have their energy reset
    	for (const flashed of flashedThisStep) {
    		newGrid[flashed.y][flashed.x].numericValue = 0;
    	}
    	return { updatedGrid: newGrid.map(e => e.map(f => f.numericValue)), flashedThisStep: flashedThisStep };
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let octopusArr = [];
    	let totalFlashes = 0;
    	for (const line of inputArr) {
    		let tempLine = [];
    		for (const char of line) {
    			tempLine.push(parseInt(char));
    		}
    		octopusArr.push(tempLine);
    	}
    	//console.log(octopusArr.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: start of step 1: ");
    	console.log(octopusArr.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: totalFlashes = " + totalFlashes);
    	let updatedArr = simulateStep(octopusArr);
    	totalFlashes += updatedArr.flashedThisStep.length;
    	console.log("DEBUG: end of step 1: ");
    	console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: totalFlashes = " + totalFlashes);
    	for (let i = 0; i < 99; i++) {
    		console.log("DEBUG: start of step " + (i+2) + ": ");
    		console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    		console.log("DEBUG: totalFlashes = " + totalFlashes);
    		updatedArr = simulateStep(updatedArr.updatedGrid);
    		totalFlashes += updatedArr.flashedThisStep.length;
    		console.log("DEBUG: end of step " + (i+2) + ": ");
    		console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    		console.log("DEBUG: totalFlashes = " + totalFlashes);
    	}
    })();
    
    Part 2
    const fs = require('fs');
    const readline = require('readline');
    
    let inputArr = [];
    
    async function openFileForReading(file) {
    	const fileStream = fs.createReadStream(file);
    
    	const rl = readline.createInterface({
    		input: fileStream,
    		crlfDelay: Infinity
    	});
    
    	for await (const line of rl) {
    		try {
    			inputArr.push(line);
    		} catch(e) {
    			console.error(e);
    		}
    	}
    }
    
    function findNeighbors(x,y,octopusGrid) {
    	let neighbors = [];
    	let rowBoundary = octopusGrid.length-1;
    	let colBoundary = octopusGrid[0].length-1;
    	// left
    	if (x-1 >= 0) {
    		neighbors.push({ x: x-1, y: y });
    	}
    	// right
    	if (x+1 <= colBoundary) {
    		neighbors.push({ x: x+1, y: y });
    	}
    	// up
    	if (y-1 >= 0) {
    		neighbors.push({ x: x, y: y-1 });
    	}
    	// down
    	if (y+1 <= rowBoundary) {
    		neighbors.push({ x: x, y: y+1 });
    	}
    	// up-left
    	if (x-1 >= 0 && y-1 >= 0) {
    		neighbors.push({ x: x-1, y: y-1 });
    	}
    	// up-right
    	if (x+1 <= colBoundary && y-1 >= 0) {
    		neighbors.push({ x: x+1, y: y-1 });
    	}
    	// down-left
    	if (x-1 >= 0 && y+1 <= rowBoundary) {
    		neighbors.push({ x: x-1, y: y+1 });
    	}
    	// down-right
    	if (x+1 <= colBoundary && y+1 <= rowBoundary) {
    		neighbors.push({ x: x+1, y: y+1 });
    	}
    	return neighbors;
    }
    
    function simulateStep(octopusGrid) {
    	let flashedThisStep = [];
    	let newGrid = [];
    	// serialize into objects
    	for (let y = 0; y < octopusGrid.length; y++) {
    		let currentRow = octopusGrid[y];
    		let newRow = [];
    		for (let x = 0; x < currentRow.length; x++) {
    			newRow.push({ numericValue: octopusGrid[y][x] });
    		}
    		newGrid.push(newRow);
    	}
    	// increment every octopus' value by 1
    	for (let y = 0; y < newGrid.length; y++) {
    		let currentRow = newGrid[y];
    		for (let x = 0; x < currentRow.length; x++) {
    			newGrid[y][x].numericValue++;
    		}
    	}
    	// check for flashes
    	for (let y = 0; y < newGrid.length; y++) {
    		let currentRow = newGrid[y];
    		for (let x = 0; x < currentRow.length; x++) {
    			let currentOctopus = currentRow[x];
    			// on flash, if not already flashed this step
    			if (currentOctopus.numericValue > 9 && flashedThisStep.filter(e => e.x === x && e.y === y).length === 0) {
    				let flashesToResolve = [ { x: x, y: y } ];
    				while (flashesToResolve.length > 0) {
    					//console.log("DEBUG: flashesToResolve = " + flashesToResolve.map(e => e.x + ',' + e.y).join(';'));
    					let resolvingFlash = flashesToResolve.pop();
    					//console.log("DEBUG: " + resolvingFlash.x + "," + resolvingFlash.y + " (" + newGrid[resolvingFlash.y][resolvingFlash.x].numericValue + ") flashed");
    					// add to flashedThisStep
    					flashedThisStep.push({ x: resolvingFlash.x, y: resolvingFlash.y });
    					let neighbors = findNeighbors(resolvingFlash.x,resolvingFlash.y,newGrid);
    					//console.log("DEBUG: neighbors: " + neighbors.map(e => e.x + "," + e.y).join(';'));
    					// increase all neighbors
    					for (const neighbor of neighbors) {
    						//console.log("DEBUG: increasing neighbor " + neighbor.x + "," + neighbor.y + " (" + newGrid[neighbor.y][neighbor.x].numericValue + ") -> (" + (newGrid[neighbor.y][neighbor.x].numericValue + 1) + ")");
    						newGrid[neighbor.y][neighbor.x].numericValue++;
    						// check for flashes
    						if (newGrid[neighbor.y][neighbor.x].numericValue > 9
    								&& flashedThisStep.filter(e => e.x === neighbor.x && e.y === neighbor.y).length === 0
    								&& flashesToResolve.filter(e => e.x === neighbor.x && e.y === neighbor.y).length === 0) {
    							//console.log("DEBUG: adding " + neighbor.x + "," + neighbor.y + " to flashesToResolve");
    							// add to flashesToResolve
    							flashesToResolve.push(neighbor);
    						}
    					}
    				}
    			}
    		}
    	}
    	// all flashed octopi have their energy reset
    	for (const flashed of flashedThisStep) {
    		newGrid[flashed.y][flashed.x].numericValue = 0;
    	}
    	return { updatedGrid: newGrid.map(e => e.map(f => f.numericValue)), flashedThisStep: flashedThisStep };
    }
    
    (async function mainExecution() {
    	await openFileForReading('input.txt');
    	let octopusArr = [];
    	let totalFlashes = 0;
    	for (const line of inputArr) {
    		let tempLine = [];
    		for (const char of line) {
    			tempLine.push(parseInt(char));
    		}
    		octopusArr.push(tempLine);
    	}
    	//console.log(octopusArr.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: start of step 1: ");
    	console.log(octopusArr.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: totalFlashes = " + totalFlashes);
    	let updatedArr = simulateStep(octopusArr);
    	totalFlashes += updatedArr.flashedThisStep.length;
    	console.log("DEBUG: end of step 1: ");
    	console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    	console.log("DEBUG: totalFlashes = " + totalFlashes);
    	let i = 1;
    	while (updatedArr.flashedThisStep.length < (updatedArr.updatedGrid.length * updatedArr.updatedGrid[0].length)) {
    		console.log("DEBUG: start of step " + (i+1) + ": ");
    		console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    		console.log("DEBUG: totalFlashes = " + totalFlashes);
    		updatedArr = simulateStep(updatedArr.updatedGrid);
    		totalFlashes += updatedArr.flashedThisStep.length;
    		console.log("DEBUG: end of step " + (i+1) + ": ");
    		console.log(updatedArr.updatedGrid.map(e => e.join('')).join('\n'));
    		console.log("DEBUG: totalFlashes = " + totalFlashes);
    		i++;
    	}
    	console.log("Answer found! They synchronize on step " + i);
    })();
    
    4 votes