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.
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)
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 := 0for 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})
deferdelete(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 {
iflen(s) == 1 {
num, ok := strconv.parse_int(s)
return num
} else do switch s {
case"one" : return1case"two" : return2case"three": return3case"four" : return4case"five" : return5case"six" : return6case"seven": return7case"eight": return8case"nine" : return9
}
return-1
}
total := 0for 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, -1for {
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.
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 } } }