What do they cure with this treatment?
Just a lvl 27 guy from 🇫🇮 Finland. Full-stack web developer and Scrum Master by trade, but more into server-side programming, networking, and sysadmin stuff.
During the summer, I love trekking, camping, and going on long hiking adventures. Also somewhat of an avgeek and a huge Lego fanatic.
What do they cure with this treatment?
You’re absolutely right. Good for me then that the inputs weren’t any larger
Not very optimized code today. Basically just a recursive function
import fs from "fs";
type Point = {x: number, y: number};
enum Direction {
UP = '^',
DOWN = 'v',
LEFT = '<',
RIGHT = '>'
}
const input = fs.readFileSync("./15/input.txt", "utf-8").split(/[\r\n]{4,}/);
const warehouse: string[][] = input[0]
.split(/[\r\n]+/)
.map(row => row.split(""));
const movements: Direction[] = input[1]
.split("")
.map(char => char.trim())
.filter(Boolean)
.map(char => char as Direction);
// Part 1
console.info("Part 1: " + solve(warehouse, movements));
// Part 2
const secondWarehouse = warehouse.map(row => {
const newRow: string[] = [];
for (const char of row) {
if (char === '#') { newRow.push('#', '#'); }
else if (char === 'O') { newRow.push('[', ']'); }
else if (char === '.') { newRow.push('.', '.'); }
else { newRow.push('@', '.'); }
}
return newRow;
});
console.info("Part 2: " + solve(secondWarehouse, movements));
function solve(warehouse: string[][], movements: Direction[]): number {
let _warehouse = warehouse.map(row => [...row]); // Take a copy to avoid modifying the original
const robotLocation: Point = findStartLocation(_warehouse);
for (const move of movements) {
// Under some very specific circumstances in part 2, tryMove returns false, but the grid has already been modified
// "Fix" the issue ba taking a copy so we can easily revert all changes made
// Slow AF of course but rest of this code isn't optimized either, so...
const copy = _warehouse.map(row => [...row]);
if (tryMove(robotLocation, move, _warehouse)) {
if (move === Direction.UP) { robotLocation.y--; }
else if (move === Direction.DOWN) { robotLocation.y++; }
else if (move === Direction.LEFT) { robotLocation.x--; }
else { robotLocation.x++; }
} else {
_warehouse = copy; // Revert changes
}
}
// GPS
let result = 0;
for (let y = 0; y < _warehouse.length; y++) {
for (let x = 0; x < _warehouse[y].length; x++) {
if (_warehouse[y][x] === "O" || _warehouse[y][x] === "[") {
result += 100 * y + x;
}
}
}
return result;
}
function tryMove(from: Point, direction: Direction, warehouse: string[][], movingPair = false): boolean {
const moveWhat = warehouse[from.y][from.x];
if (moveWhat === "#") {
return false;
}
let to: Point;
switch (direction) {
case Direction.UP: to = {x: from.x, y: from.y - 1}; break;
case Direction.DOWN: to = {x: from.x, y: from.y + 1}; break;
case Direction.LEFT: to = {x: from.x - 1, y: from.y}; break;
case Direction.RIGHT: to = {x: from.x + 1, y: from.y}; break;
}
const allowMove = warehouse[to.y][to.x] === "."
|| (direction === Direction.UP && tryMove({x: from.x, y: from.y - 1}, direction, warehouse))
|| (direction === Direction.DOWN && tryMove({x: from.x, y: from.y + 1}, direction, warehouse))
|| (direction === Direction.LEFT && tryMove({x: from.x - 1, y: from.y}, direction, warehouse))
|| (direction === Direction.RIGHT && tryMove({x: from.x + 1, y: from.y}, direction, warehouse));
if (allowMove) {
// Part 1 logic handles horizontal movement of larger boxes just fine. Needs special handling for vertical movement
if (!movingPair && (direction === Direction.UP || direction === Direction.DOWN)) {
if (moveWhat === "[" && !tryMove({x: from.x + 1, y: from.y}, direction, warehouse, true)) {
return false;
}
if (moveWhat === "]" && !tryMove({x: from.x - 1, y: from.y}, direction, warehouse, true)) {
return false;
}
}
// Make the move
warehouse[to.y][to.x] = moveWhat;
warehouse[from.y][from.x] = ".";
return true;
}
return false;
}
function findStartLocation(warehouse: string[][]): Point {
for (let y = 0; y < warehouse.length; y++) {
for (let x = 0; x < warehouse[y].length; x++) {
if (warehouse[y][x] === "@") {
return {x,y};
}
}
}
throw new Error("Could not find start location!");
}
Part 2 was a major curveball for sure. I was expecting something like the grid size and number of seconds multiplying by a large amount to make iterative solutions unfeasible.
First I was baffled how we’re supposed to know what shape we’re looking for exactly. I just started printing out cases where many robots were next to each other and checked them by hand and eventually found it. For my input the correct picture looked like this:
Later it turned out that a much simpler way is to just check for the first time none of the robots are overlapping each other. I cannot say for sure if this works for every input, but I suspect the inputs are generated in such a way that this approach always works.
import fs from "fs";
type Coord = {x: number, y: number};
type Robot = {start: Coord, velocity: Coord};
const SIZE: Coord = {x: 101, y: 103};
const input: Robot[] = fs.readFileSync("./14/input.txt", "utf-8")
.split(/[\r\n]+/)
.map(row => /p=(-?\d+),(-?\d+)\sv=(-?\d+),(-?\d+)/.exec(row))
.filter(matcher => matcher != null)
.map(matcher => {
return {
start: {x: parseInt(matcher[1]), y: parseInt(matcher[2])},
velocity: {x: parseInt(matcher[3]), y: parseInt(matcher[4])}
};
});
console.info("Part 1: " + safetyFactor(input.map(robot => calculatePosition(robot, SIZE, 100)), SIZE));
// Part 2
// Turns out the Christmas tree is arranged the first time none of the robots are overlapping
for (let i = 101; true; i++) {
const positions = input.map(robot => calculatePosition(robot, SIZE, i));
if (positions.every((position, index, arr) => arr.findIndex(pos => pos.x === position.x && pos.y === position.y) === index)) {
console.info("Part 2: " + i);
break;
}
}
function calculatePosition(robot: Robot, size: Coord, seconds: number): Coord {
return {
x: ((robot.start.x + robot.velocity.x * seconds) % size.x + size.x) % size.x,
y: ((robot.start.y + robot.velocity.y * seconds) % size.y + size.y) % size.y
};
}
function safetyFactor(positions: Coord[], size: Coord): number {
const midX = Math.floor(size.x / 2);
const midY = Math.floor(size.y / 2);
let quadrant0 = 0; // Top-left
let quadrant1 = 0; // Top-right
let quadrant2 = 0; // Bottom-left
let quadrant3 = 0; // Bottom-right
for (const {x,y} of positions) {
if (x === midX || y === midY) { continue; }
if (x < midX && y < midY) { quadrant0++; }
else if (x > midX && y < midY) { quadrant1++; }
else if (x < midX && y > midY) { quadrant2++; }
else if (x > midX && y > midY) { quadrant3++; }
}
return quadrant0 * quadrant1 * quadrant2 * quadrant3;
}
Today was pretty easy one but for some reason I spent like 20 minutes overthinking part 2 when all it needed was one new else if
. I initially through the concatenation operator would take precedence even tho it clearly says “All operators are still evaluated left-to-right” in the instructions…
I’m sure there are optimizations to do but using parallelStreams it only takes around 300ms total on my machine so there’s no point really
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
public class Day7 {
public static void main(final String[] _args) throws IOException {
final List<Equation> equations = Files.readAllLines(Path.of("2024\\07\\input.txt"), StandardCharsets.UTF_8).stream()
.map(line -> line.split(":\\s"))
.map(line -> new Equation(
Long.parseLong(line[0]),
Arrays.stream(line[1].split("\\s"))
.map(Integer::parseInt)
.toArray(Integer[]::new)
)
).toList();
final char[] part1Operators = {'+', '*'};
System.out.println("Part 1: " + equations.parallelStream()
.mapToLong(equation -> getResultIfPossible(equation, part1Operators))
.sum()
);
final char[] part2Operators = {'+', '*', '|'};
System.out.println("Part 2: " + equations.parallelStream()
.mapToLong(equation -> getResultIfPossible(equation, part2Operators))
.sum()
);
}
private static Long getResultIfPossible(final Equation equation, final char[] operators) {
final var permutations = Math.pow(operators.length, equation.values.length - 1);
for (int i = 0; i < permutations; i++) {
long result = equation.values[0];
int count = i;
for (int j = 0; j < equation.values.length - 1; j++) {
// If the result is already larger than the expected one, we can short circuit here to save some time
if (result > equation.result) {
break;
}
final char operator = operators[count % operators.length];
count /= operators.length;
if (operator == '+') { result += equation.values[j + 1]; }
else if (operator == '*') { result *= equation.values[j + 1]; }
else if (operator == '|') { result = Long.parseLong(String.valueOf(result) + String.valueOf(equation.values[j + 1])); }
else {
throw new RuntimeException("Unsupported operator " + operator);
}
}
if (result == equation.result) {
return result;
}
}
return 0L;
}
private static record Equation(long result, Integer[] values) {}
}
import fs from "fs";
enum GuardDirection {
UP = "^",
RIGHT = ">",
DOWN = "v",
LEFT = "<",
};
const originalGrid: string[][] = fs.readFileSync("./06/input.txt", "utf-8")
.split(/[\r\n]+/)
.filter(Boolean)
.map(row => row.split(""))
.map(row => row.map(char => char === "." ? "" : char));
const gridWidth = originalGrid[0].length;
const startingPosition = getStartPosition(originalGrid);
const startingDirection = originalGrid[startingPosition.y][startingPosition.x] as GuardDirection;
originalGrid[startingPosition.y][startingPosition.x] = "";
// Part 1
const grid = getGridCopy(originalGrid);
doGuardMovement(grid);
console.info("Part 1: " + grid.flatMap(row => row).filter(cell => cell.startsWith("X")).length);
// Part 2
let part2Result = 0;
for (let y = 0; y < originalGrid.length; y++) {
for (let x = 0; x < originalGrid.length; x++) {
if (!originalGrid[y][x].length && grid[y][x].startsWith("X")) {
// Cell is empty AND was visited during part 1 => Should place an obstacle here
const gridCopy = getGridCopy(originalGrid);
gridCopy[y][x] = "#";
if (!doGuardMovement(gridCopy)) { part2Result++; }
}
}
}
console.info("Part 2: " + part2Result);
function doGuardMovement(grid: string[][]): boolean { // Returns false if loop detected
let [x, y, guard] = [startingPosition.x, startingPosition.y, startingDirection];
while (y >= 0 && y < grid.length && x >= 0 && x < gridWidth) {
// Check for loop
if (grid[y][x].length > 3) { return false; }
grid[y][x] += "X"; // Mark each visitation with X
// If there is something directly in front of you, turn right 90 degrees
if (guard === GuardDirection.UP && y > 0 && grid[y - 1][x] === "#") { guard = GuardDirection.RIGHT; }
else if (guard === GuardDirection.RIGHT && x < gridWidth - 1 && grid[y][x + 1] === "#") { guard = GuardDirection.DOWN; }
else if (guard === GuardDirection.DOWN && y < grid.length - 1 && grid[y + 1][x] === "#") { guard = GuardDirection.LEFT; }
else if (guard === GuardDirection.LEFT && x > 0 && grid[y][x - 1] === "#") { guard = GuardDirection.UP; }
// Otherwise, take a step forward
else if (guard === GuardDirection.UP) { y--; }
else if (guard === GuardDirection.RIGHT) { x++; }
else if (guard === GuardDirection.DOWN) { y++; }
else if (guard === GuardDirection.LEFT) { x--; }
else { throw new Error("Something went wrong"); }
}
return true; // Exited the grid
}
function getGridCopy(grid: string[][]): string[][] {
return grid.map(row => [...row]);
}
function getStartPosition(grid: string[][]): {x: number, y: number} {
for (let y = 0; y < grid.length; y++) {
for (let x = 0; x < grid.length; x++) {
if (Object.values(GuardDirection).some(char => grid[y][x] === char)) {
return {x, y};
}
}
}
throw new Error("Could not find starting position");
}
Part 2 was an interesting one and my solution kinda feels like cheating. What I did I only changed the validation method from part 1 to return the indexes of incorrectly placed pages and then randomly swapped those around in a loop until the validation passed. I was expecting this to not work at all or take forever to run but surprisingly it only takes three to five seconds to complete.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
public class Day05 {
private static final Random random = new Random();
public static void main(final String[] args) throws IOException {
final String input = Files.readString(Path.of("2024\\05\\input.txt"), StandardCharsets.UTF_8);
final String[] inputSplit = input.split("[\r\n]{4,}");
final List<PageOrderingRule> rules = Arrays.stream(inputSplit[0].split("[\r\n]+"))
.map(row -> row.split("\\|"))
.map(row -> new PageOrderingRule(Integer.parseInt(row[0]), Integer.parseInt(row[1])))
.toList();
final List<ArrayList<Integer>> updates = Arrays.stream(inputSplit[1].split("[\r\n]+"))
.map(row -> row.split(","))
.map(row -> Arrays.stream(row).map(Integer::parseInt).collect(Collectors.toCollection(ArrayList::new)))
.toList();
System.out.println("Part 1: " + updates.stream()
.filter(update -> validate(update, rules).isEmpty())
.mapToInt(update -> update.get(update.size() / 2))
.sum()
);
System.out.println("Part 2: " + updates.stream()
.filter(update -> !validate(update, rules).isEmpty())
.map(update -> fixOrder(update, rules))
.mapToInt(update -> update.get(update.size() / 2))
.sum()
);
}
private static Set<Integer> validate(final List<Integer> update, final List<PageOrderingRule> rules) {
final Set<Integer> invalidIndexes = new HashSet<>();
for (int i = 0; i < update.size(); i++) {
final Integer integer = update.get(i);
for (final PageOrderingRule rule : rules) {
if (rule.x == integer && update.contains(rule.y) && i > update.indexOf(rule.y)) {
invalidIndexes.add(i);
}
else if (rule.y == integer && update.contains(rule.x) && i < update.indexOf(rule.x)) {
invalidIndexes.add(i);
}
}
}
return invalidIndexes;
}
private static List<Integer> fixOrder(final List<Integer> update, final List<PageOrderingRule> rules) {
List<Integer> invalidIndexesList = new ArrayList<>(validate(update, rules));
// Swap randomly until the validation passes
while (!invalidIndexesList.isEmpty()) {
Collections.swap(update, random.nextInt(invalidIndexesList.size()), random.nextInt(invalidIndexesList.size()));
invalidIndexesList = new ArrayList<>(validate(update, rules));
}
return update;
}
private static record PageOrderingRule(int x, int y) {}
}
Sure. I’m not recommending anything, just stating what has worked for me. For simple use cases, I think most of the DDNS services are pretty much the same anyway and it’s easy to switch to an another one if one stops working for some reason.
I’ve been using No-IP free plan for years without issues. Inputted the credentials to my routers DDNS client and then basically forgot about it. Free users need to confirm their account once a month via email but that’s just one click.
If your domain registrar happens to have an API to update DNS entries, you could implement DDNS yourself by writing a simple automated script to check the external IP (e.g. via ipify.org) and if it’s changed from the last check then call the API to update the DNS entries.
Mistä uudempien ydinvoimaloiden polttoaine tulee ja mitä eroa niillä on? Mun tietämys ydinvoimaloista on tasoa “sisään menee uraania ja ulos tulee sähköä”, joten siksi kyselen tyhmiä
I’ve been working through my backlog on Steam
I finished Twin Mirror last night and today I started Kona II. I’m couple of hours in and so far it’s been a little meh. Graphics are great and all but it just doesn’t have the same atmosphere than the first Kona had
Varsinkin kun pikkukunnissa monessa asiassa edetään otteella “poikien kanssa tässä vähän puuhaillaan”, eikä ison maailman hömpötykset paina.
Todettakoon kuitenkin, että se Perähikiän kunnan vesihuolto ei luultavasti ole sabotöörien ykköskohde, kun mahdolliset vaikutukset olisi aivan mitättömät. Mutta tämä ei tietenkään ole syy jättää varautumatta
Työpaikan kahvipöydässä kuulin, että niksi on tehdä numeronsiirto kilpailijalle ja odottaa, että nykyiseltä operaattorilta tulee ns. winback-tarjous, yleensä tekstiviestillä. Mutta ekaan tarjoukseen ei kannata vielä tarttua, vaan odottaa että tulee vielä parempi tarjous tai soittavat perään. Ja tämä sitten uudestaan joka kerta kun tarjoushintainen kausi on päättymässä.
Itsellä ei kyllä todellakaan ole mielenkiintoa moiseen rumpaan.
My main issue with CVEs nowadays is that it seems one gets generated even when 99% of the use cases for the software in question are not vulnerable as the vulnerability requires a very specific configuration/circumstances/etc. to be exploitable. In large projects with lots of dependencies this adds a lot of noice and there’s a risk that actual important CVEs go unnoticed.
That or live cd (well, most likely live usb nowadays)
I was going to give the example of the Carnival cruise ship that sank in the 2010s (I think) largely due to the captain’s incompetence[…]
That’s Costa Concordia. It received extra media attention and is mostly known due to the awful behavior of the captain who first directly caused the accident and then fled the ship before most of his passengers.
Well, just by looking at responses in this thread, the controversy most definitely still exists. Some seem to like it and others hate it fiercely.
Cool, thanks for the explanation.
a single application that gets bundled with all necessary dependencies including versioning
Does that mean that if I were to install Application A and Application B that both have dependency to package C version 1.2.3 I then would have package C (and all of its possible sub dependencies) twice on my disk? I don’t know how much external dependencies applications on Linux usually have but doesn’t that have the potential to waste huge amounts of disk space?
Sorry to ask, I’m not really familiar with Linux desktop nowadays: I’ve seen Flatpak and Flathub talked about a lot lately and it seems to be kinda a controversial topic. Anyone wanna fill me in what’s all the noice about? It’s some kind of cross-distro “app store” thingy?
Yep, I wouldn’t mind being in his place. Or being the one tickling, for that matter.