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

help-circle

  • 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.


  • I don’t think this can really be answered until after the fact. Anything that I (and I suspect most) people could say about an artstyle are going to be particular to an instance of that artsyle. If I’d give advice as someone who is neither an artist nor a game designer, what attracts me more than anything is a unique artstyle, which, if I’m gonna give a brutal opinion, starting from a vague category like ‘pixel’, ‘hand drawn’ or ‘3D’ probably won’t get you there.

    I feel like I even struggle to answer your question at face value because it doesn’t align well at all with how I conceptualize game art. For example, Cruelty Squad is a game that I don’t think I’d have gotten if not for it’s artsyle. Like, sure, it’s 3D, but it’s a lot more like a PilotRedSun animation than it is a game like TF2. Or take a game like Factorio: most of the assets of that game are pre-rendered 3D sprites, so despite being artisticly unique in a way that interests me it doesn’t fit into the categories you’ve asked about. The best I can say is “I dunno”, and I don’t think anyone else can answer it further than that.


  • There is the Anno series of games, which are technically RTS games but if I’m honest I find them the most fun when I go out of my way to avoid combat/micromanagement. I’ve only played 1404, 2070, and 2205, 2070 being the best in my opinion, but it has a bad history with DRM so I’d suggest 1404 (known as “Dawn of Discovery” in the US because us americans are afraid of numbers apparently).

    Edit: looking at the steam page it looks like they decided to take 1404 down and made a new page where the game is (mostly) unchanged besides requiring you to jump through all the BS hoops that 2070 did, so I’d say if you’re gonna spend money get 1404 on GOG, or if you are willing to do unspeakable things go with 2070.




  • One of the reasons I really disliked Reddit and stopped using it years ago was this way of using the voting system. If I make a post, and it gets voted something like +4-10, and a reply that is some rewording of “that’s a dumb statement”, what am I to think? I’m certainly not going to change my mind, no one gave me a good reason to.

    If one is voting because they feel they can’t stand behind their opinion if they expanded it in text… I don’t know what to tell ya.

    I’m inclined to believe a lot of people do this. This is not to say they are terrible for doing this, it’s that it’s human nature. Replying to someone with a well thought out post takes effort and, from my experience, makes the me realize i don’t know shit about the subject. Point is, this way of using the voting system breeds half-thought opinions which is a host of a lot of other problems.


  • I think wikis have already gotten there, at least for games. All of the game wikis have gotten consolidated into fandom/Wikia, which, from my experience, has enshittification levels that makes viewing Reddit from a phone browser feel likea slick experience. You can’t avoid it either. Wikis that used to be very good (at least compared to fandom, like gamepedia), have somehow gotten all pulled into the enshittification vacuum.

    A few days ago I was on the Minecraft wiki, but I was playing b1.7.3 so I was viewing it on wayback. And holy shit, before fandom bought out gamepedia (albeit I was looking at the pre-gamepedia wiki), the wiki was actually usable.