• 0 Posts
  • 16 Comments
Joined 1 year ago
cake
Cake day: June 20th, 2023

help-circle
  • I wouldn’t say that it’d be strictly impossible, however if it can be done then it would come at a considerable cost to useability, versatility, etc.

    One adjacent concept that comes to mind is the use of the :visited CSS tag to extract a user’s browsing habits. I remember seeing a demonstration of this where an “are you human” captcha was shown but the choice of image in each box was controlled by the :visited tag. I can’t find that post, but this medium article demonstrates a similer concept. There are mitigations to this luckily, but a fullproof solution would be to remove the tag’s functionality altogether, which would make certain websites (like the one we’re on right now!) much more inconvenient to use.

    It seems trivial to me for a website to detect user behaviors that indicate the use of an adblocker. For example, if a request for a page is immediately followed by a request for a video on that page, rather than after 5-60 seconds, then they’re likey using an adblocker. If there is an ad placed between two paragaphs in an article, but two distant paragraphs are visible at the same time, it is more likely (although not guaranteed) that they are using an adblocker. If a user triggers an abnormal amount of those heuristics then they get flagged as an adblocking user.


  • I agree pretty strongly with this generally. The farside has a way of having jokes that are so simple on it’s face that I’m left thinking “surely I’ve missed something?” Usually it turns out that no, in fact, I got the joke and was just vastly underwhelmed.

    For whatever reason I found this one to be mildly funny. Couldn’t tell you why. Perhaps it’s the idea that the people who built the atomic bomb weren’t that smart after all?





  • The thing that finally got businesses to finally get off IE wasn’t from the browser being worse than every other option. Heck, it wasn’t even because it was a decrepit piece of software that lost it’s former market dominance (and if anything businesses see that as a positive, not a negative).

    What finally did that was microsoft saying there won’t be any security updates. That’s what finally got them off their ass; subtly threatening them with data breaches, exploits, etc. if they continue to use it. I don’t see google doing this anytime soon, at least not without a “sequel” like microsoft had with edge.






  • Odin

    When I read the problem description I expected the input to also be 2 digit numbers. When I looked at it I just had to say “huh.”

    Second part I think you definitely have to do in reverse (edit: if you are doing a linear search for the answer), as that allows you to nope out as soon as you find a match, whereas with doing it forward you have to keep checking just in case.

    Formatted code

    package day5
    
    import "core:fmt"
    import "core:strings"
    import "core:slice"
    import "core:strconv"
    
    Range :: struct {
        dest: int,
        src: int,
        range: int,
    }
    
    Mapper :: struct {
        ranges: []Range,
    }
    
    parse_range :: proc(s: string) -> (ret: Range) {
        rest := s
    
        parseLen := -1
    
        destOk: bool
        ret.dest, destOk = strconv.parse_int(rest, 10, &parseLen)
        rest = strings.trim_left_space(rest[parseLen:])
    
        srcOk: bool
        ret.src, srcOk = strconv.parse_int(rest, 10, &parseLen)
        rest = strings.trim_left_space(rest[parseLen:])
    
        rangeOk: bool
        ret.range, rangeOk = strconv.parse_int(rest, 10, &parseLen)
    
        return
    }
    
    parse_mapper :: proc(ss: []string) -> (ret: Mapper) {
        ret.ranges = make([]Range, len(ss)-1)
        for s, i in ss[1:] {
            ret.ranges[i] = parse_range(s)
        }
    
        return
    }
    
    parse_mappers :: proc(ss: []string) -> []Mapper {
        mapsStr := make([dynamic][]string)
        defer delete(mapsStr)
    
        restOfLines := ss
        isLineEmpty :: proc(s: string)->bool {return len(s)==0}
    
        for i, found := slice.linear_search_proc(restOfLines, isLineEmpty); 
            found; 
            i, found  = slice.linear_search_proc(restOfLines, isLineEmpty) {
            
            append(&mapsStr, restOfLines[:i])
            restOfLines = restOfLines[i+1:]
        }
        append(&mapsStr, restOfLines[:])
    
        return slice.mapper(mapsStr[1:], parse_mapper)
    }
    
    apply_mapper :: proc(mapper: Mapper, num: int) -> int {
        for r in mapper.ranges {
            if num >= r.src && num - r.src < r.range do return num - r.src + r.dest
        }
    
        return num
    }
    
    p1 :: proc(input: []string) {
        maps := parse_mappers(input)
        defer {
            for m in maps do delete(m.ranges)
            delete(maps)
        }
    
        restSeeds := input[0][len("seeds: "):]
        min := 0x7fffffff
    
        for len(restSeeds) > 0 {
            seedLen := -1
            seed, seedOk := strconv.parse_int(restSeeds, 10, &seedLen)
            restSeeds = strings.trim_left_space(restSeeds[seedLen:])
    
            fmt.print(seed)
            for m in maps {
                seed = apply_mapper(m, seed)
                fmt.print(" ->", seed)
            }
            fmt.println()
    
            if seed < min do min = seed
        }
    
        fmt.println(min)
    }
    
    apply_mapper_reverse :: proc(mapper: Mapper, num: int) -> int {
        for r in mapper.ranges {
            if num >= r.dest && num - r.dest < r.range do return num - r.dest + r.src
        }
    
        return num
    }
    
    p2 :: proc(input: []string) {
        SeedRange :: struct {
            start: int,
            len: int,
        }
    
        seeds := make([dynamic]SeedRange)
        restSeeds := input[0][len("seeds: "):]
    
        for len(restSeeds) > 0 {
            seedLen := -1
            seedS, seedSOk := strconv.parse_int(restSeeds, 10, &seedLen)
            restSeeds = strings.trim_left_space(restSeeds[seedLen:])
    
            seedL, seedLOk := strconv.parse_int(restSeeds, 10, &seedLen)
            restSeeds = strings.trim_left_space(restSeeds[seedLen:])
    
            append(&seeds, SeedRange{seedS, seedL})
        }
    
        maps := parse_mappers(input)
        defer {
            for m in maps do delete(m.ranges)
            delete(maps)
        }
    
        for i := 0; true; i += 1 {
            rseed := i
            #reverse for m in maps {
                rseed = apply_mapper_reverse(m, rseed)
            }
    
            found := false
            for sr in seeds {
                if rseed >= sr.start && rseed < sr.start + sr.len {
                    found = true
                    break
                }
            }
            if found {
                fmt.println(i)
                break
            }
        }
    }
    



  • Did this in Odin

    Here’s a tip: if you are using a language / standard library that doesn’t have a set, you can mimic it with a map from your key to a nullary (in this case an empty struct)

    formatted code

    package day3
    
    import "core:fmt"
    import "core:strings"
    import "core:unicode"
    import "core:strconv"
    
    flood_get_num :: proc(s: string, i: int) -> (parsed: int, pos: int) {
        if !unicode.is_digit(rune(s[i])) do return -99999, -1
    
        pos = strings.last_index_proc(s[:i+1], proc(r:rune)->bool{return !unicode.is_digit(r)})
        pos += 1
    
        ok: bool
        parsed, ok = strconv.parse_int(s[pos:])
    
        return parsed, pos
    }
    
    p1 :: proc(input: []string) {
        // wow what a gnarly type
        foundNumSet := make(map[[2]int]struct{})
        defer delete(foundNumSet)
    
        total := 0
    
        for y in 0..

  • Did mine in Odin. Found this day’s to be super easy, most of the challenge was just parsing.

    package day2
    
    import "core:fmt"
    import "core:strings"
    import "core:strconv"
    import "core:unicode"
    
    Round :: struct {
        red: int,
        green: int,
        blue: int,
    }
    
    parse_round :: proc(s: string) -> Round {
        ret: Round
    
        rest := s
        for {
            nextNumAt := strings.index_proc(rest, unicode.is_digit)
            if nextNumAt == -1 do break
            rest = rest[nextNumAt:]
    
            numlen: int
            num, ok := strconv.parse_int(rest, 10, &numlen)
            rest = rest[numlen+len(" "):]
    
            if rest[:3] == "red" {
                ret.red = num
            } else if rest[:4] == "blue" {
                ret.blue = num
            } else if rest[:5] == "green" {
                ret.green = num
            }
        }
    
        return ret
    }
    
    Game :: struct {
        id: int,
        rounds: [dynamic]Round,
    }
    
    parse_game :: proc(s: string) -> Game {
        ret: Game
    
        rest := s[len("Game "):]
    
        idOk: bool
        idLen: int
        ret.id, idOk = strconv.parse_int(rest, 10, &idLen)
        rest = rest[idLen+len(": "):]
    
        for len(rest) > 0 {
            endOfRound := strings.index_rune(rest, ';')
            if endOfRound == -1 do endOfRound = len(rest)
    
            append(&ret.rounds, parse_round(rest[:endOfRound]))
            rest = rest[min(endOfRound+1, len(rest)):]
        }
    
        return ret
    }
    
    is_game_possible :: proc(game: Game) -> bool {
        for round in game.rounds {
            if round.red   > 12 ||
               round.green > 13 ||
               round.blue  > 14 {
                return false
            }
        }
        return true
    }
    
    p1 :: proc(input: []string) {
        totalIds := 0
    
        for line in input {
            game := parse_game(line)
            defer delete(game.rounds)
    
            if is_game_possible(game) do totalIds += game.id
        }
    
        fmt.println(totalIds)
    }
    
    p2 :: proc(input: []string) {
        totalPower := 0
    
        for line in input {
            game := parse_game(line)
            defer delete(game.rounds)
    
            minRed   := 0
            minGreen := 0
            minBlue  := 0
            for round in game.rounds {
                minRed   = max(minRed  , round.red  )
                minGreen = max(minGreen, round.green)
                minBlue  = max(minBlue , round.blue )
            }
    
            totalPower += minRed * minGreen * minBlue
        }
    
        fmt.println(totalPower)
    }
    

  • Did this in Odin (very hashed together, especially finding the last number in part 2):

    spoiler
    package day1
    
    import "core:fmt"
    import "core:strings"
    import "core:strconv"
    import "core:unicode"
    
    p1 :: proc(input: []string) {
        total := 0
    
        for line in input {
            firstNum := line[strings.index_proc(line, unicode.is_digit):][:1]
            lastNum := line[strings.last_index_proc(line, unicode.is_digit):][:1]
    
            calibrationValue := strings.concatenate({firstNum, lastNum})
            defer delete(calibrationValue)
    
            num, ok := strconv.parse_int(calibrationValue)
    
            total += num
        }
    
        // daggonit thought it was the whole numbers
        /*
        for line in input {
            firstNum := line
    
            fFrom := strings.index_proc(firstNum, unicode.is_digit)
            firstNum = firstNum[fFrom:]
    
            fTo := strings.index_proc(firstNum, proc(r:rune)->bool {return !unicode.is_digit(r)})
            if fTo == -1 do fTo = len(firstNum)
            firstNum = firstNum[:fTo]
    
    
            lastNum := line
            lastNum = lastNum[:strings.last_index_proc(lastNum, unicode.is_digit)+1]
            lastNum = lastNum[strings.last_index_proc(lastNum, proc(r:rune)->bool {return !unicode.is_digit(r)})+1:]
    
            calibrationValue := strings.concatenate({firstNum, lastNum})
            defer delete(calibrationValue)
    
            num, ok := strconv.parse_int(calibrationValue, 10)
            if !ok {
                fmt.eprintf("%s could not be parsed from %s", calibrationValue, line)
                return
            }
    
            total += num;
        }
        */
    
        fmt.println(total)
    }
    
    p2 :: proc(input: []string) {
        parse_wordable :: proc(s: string) -> int {
            if len(s) == 1 {
                num, ok := strconv.parse_int(s)
                return num
            } else do switch s {
                case "one"  : return 1
                case "two"  : return 2
                case "three": return 3
                case "four" : return 4
                case "five" : return 5
                case "six"  : return 6
                case "seven": return 7
                case "eight": return 8
                case "nine" : return 9
            }
    
            return -1
        }
    
        total := 0
    
        for line in input {
            firstNumI, firstNumW := strings.index_multi(line, {
                "one"  , "1",
                "two"  , "2",
                "three", "3",
                "four" , "4",
                "five" , "5",
                "six"  , "6",
                "seven", "7",
                "eight", "8",
                "nine" , "9",
            })
            firstNum := line[firstNumI:][:firstNumW]
    
    
            // last_index_multi doesn't seem to exist, doing this as backup
            lastNumI, lastNumW := -1, -1
            for {
                nLastNumI, nLastNumW := strings.index_multi(line[lastNumI+1:], {
                    "one"  , "1",
                    "two"  , "2",
                    "three", "3",
                    "four" , "4",
                    "five" , "5",
                    "six"  , "6",
                    "seven", "7",
                    "eight", "8",
                    "nine" , "9",
                })
    
                if nLastNumI == -1 do break
    
                lastNumI += nLastNumI+1
                lastNumW  = nLastNumW
            }
            lastNum := line[lastNumI:][:lastNumW]
    
            total += parse_wordable(firstNum)*10 + parse_wordable(lastNum)
        }
    
        fmt.println(total)
    }
    

    Had a ton of trouble with part 1 until I realized I misinterpreted it. Especially annoying because the example was working fine. So paradoxically part 2 was easier than 1.