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.

  • 0 Posts
  • 21 Comments
Joined 2 years ago
cake
Cake day: June 20th, 2023


  • There would need to be a way to guarantee that only the browser could do this, or at least some way to tell exactly what the source was.

    I don’t think there’s a way to do that. Let’s say browsers implemented this. I could then just take a copy of Firefox source code and make my own version, which is exactly the same than normal FF except the fancy screenshot tool has been slightly modified to allow editing the page before taking the screenshot.


  • The website (Telegram in this case, but can be any website) adds a specifically crafted text to the clipboard and then tricks the user into pasting that text into the Windows Run dialog, which can be used to execute any command(s), basically like a command prompt.

    The text the attacker places in the clipboard is actually a command to download and execute an executable file from the internet, giving the attacker remote access to the system or whatever the payload happens to be.

    It’s a pretty clever trick. Perhaps MS should consider adding a warning before allowing pasting into the Run dialog or cmd for the first time. They already have this in the Edge browser console.












  • I had one of the earlier Lumia models, maybe the 900. The software was missing many standard features (even for the time), and there were like no apps available. Edge wasn’t a thing yet and instead it rocked Internet Explorer… And I don’t think there were any other browsers available, at least not in the beginning.

    That said, I do remember kinda liking the UI. On PC (Windows 8) it was terrible, but it worked quite well on a device it was actually designed for. Another thing, it had a really good camera. The hardware I mean, sadly the software for it was seriously lacking, and there just weren’t any third-party camera apps available yet





  • TypeScript

    Not very optimized code today. Basically just a recursive function

    Code
    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!");
    }
    

  • TypeScript

    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:

    The Christmas tree

    Picture

    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.

    The code
    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;
    }
    

  • Java

    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

    The code
    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) {}
    }