29 votes

Day 1: Trebuchet?!

33 comments

  1. JRandomHacker
    Link
    Here we go again! I'll try to remember to post my solution in the morning, but for now I'll just say - wow that was a trickier Day 1 than I expected

    Here we go again! I'll try to remember to post my solution in the morning, but for now I'll just say - wow that was a trickier Day 1 than I expected

    8 votes
  2. csos95
    (edited )
    Link
    Rust use aoc_runner_derive::aoc; use regex::Regex; #[aoc(day1, part1)] fn part1(input: &str) -> usize { input .lines() .map(|line| { let mut digits = line.chars().filter(char::is_ascii_digit); let...
    Rust
    use aoc_runner_derive::aoc;
    use regex::Regex;
    
    #[aoc(day1, part1)]
    fn part1(input: &str) -> usize {
        input
            .lines()
            .map(|line| {
                let mut digits = line.chars().filter(char::is_ascii_digit);
    
                let first = digits.next().unwrap();
                let last = match digits.last() {
                    Some(last) => last,
                    None => first,
                };
                let n: usize = format!("{first}{last}").parse().unwrap();
                n
            })
            .sum()
    }
    
    #[aoc(day1, part2)]
    fn part2(input: &str) -> usize {
        let digit_re = Regex::new("([1-9]|one|two|three|four|five|six|seven|eight|nine)").unwrap();
        input
            .lines()
            .map(|line| {
                let mut digits = Vec::new();
                let mut i = 0;
                while i < line.len() {
                    if let Some(m) = digit_re.find_at(line, i) {
                        digits.push(match m.as_str() {
                            "one" => "1",
                            "two" => "2",
                            "three" => "3",
                            "four" => "4",
                            "five" => "5",
                            "six" => "6",
                            "seven" => "7",
                            "eight" => "8",
                            "nine" => "9",
                            c => c,
                        });
                        i = m.start() + 1;
                    } else {
                        break;
                    }
                }
    
                let first = digits.first().unwrap();
                let last = match digits.last() {
                    Some(last) => last,
                    None => first,
                };
                let n: usize = format!("{first}{last}").parse().unwrap();
                n
            })
            .sum()
    }
    

    I'm using cargo-aoc to download my inputs and generate runners.

    There was a little "gotcha" with the second part, that I had to search through my input to figure out.

    Part 2 Hint The spelled out digits can overlap, so if you're using a non-overlapping regex iterator, it'll miss things.

    The furthest I've gotten in AOC before being distracted by something else is day 10, lets see how far I get this year. :D

    EDIT: Went back and did part 1 with nom to refresh myself on it in case it's needed later on.
    I quickly remembered how annoyingly opaque its errors are and have decided not to bother with it for future puzzles unless I absolutely need to.
    I'll stick to regex and logos otherwise.

    Rust - Nom Part 1
    use aoc_runner_derive::aoc;
    use nom::{
        bytes::complete::{take_till, take_while1},
        error::Error,
        multi::many1,
        sequence::preceded,
    };
    
    #[aoc(day1, part1, nom)]
    fn part1_nom(input: &str) -> usize {
        input
            .lines()
            .map(|line| {
                let (_, digits) = many1::<_, _, Error<_>, _>(preceded(
                    take_till(|c: char| c.is_ascii_digit()),
                    take_while1(|c: char| c.is_ascii_digit()),
                ))(line)
                .unwrap();
    
                let first = digits.first().unwrap();
                let last = digits.last().unwrap();
                let n: usize = format!("{first}{last}").parse().unwrap();
                n
            })
            .sum()
    }
    
    6 votes
  3. tomf
    (edited )
    Link
    welp, I'm doing Google Sheets again because I have done fuck all to learn anything. I think I did it basically the same way as everyone else... ish. Part 1 =ARRAYFORMULA( LET(...

    welp, I'm doing Google Sheets again because I have done fuck all to learn anything. I think I did it basically the same way as everyone else... ish.

    Part 1
    =ARRAYFORMULA(
      LET(
       x,IFERROR(REGEXREPLACE(TO_TEXT(A2:A),"\D","")),
       SUM((--(LEFT(x)&RIGHT(x))))))
    
    Part 2
    =ARRAYFORMULA(
      LET(
       _w,{"one";"two";"three";"four";"five";"six";"seven";"eight";"nine"},
       _r,IF(ISBLANK(A2:A),,
           REGEXREPLACE(
            REDUCE(
             A2:A,ROW(1:9),
             LAMBDA(
              a,x,
              SUBSTITUTE(
               a,
               INDEX(_w,x),
               INDEX(_w&ROW(1:9)&_w,x)))),
            "\D",)),
       SUM(--(LEFT(_r)&RIGHT(_r)))))
    
    4 votes
  4. [2]
    first-must-burn
    (edited )
    Link
    Gasp! I forgot it was December 1. This is why I never get and sleep in December. Is anyone running a Tildes leaderboard? Edit to add: Got the solutions, my repo is here:...

    Gasp! I forgot it was December 1. This is why I never get and sleep in December. Is anyone running a Tildes leaderboard?

    Edit to add: Got the solutions, my repo is here: https://github.com/firstmustburn/aoc

    I think I did something a little different than most people for part 2...

    Part 2 spoilers

    Unlike many of the solutions I've seen people post, I did not use regexes. I have a function that looks for a match at the beginning of the string and returns the remaining string if it finds one. I hit the problem that most people hit (overlapping number names), but it was a simple as tweaking the algorithm to only consume the first letter of the name when it matches on it, leaving the others to match for the second letter.

    4 votes
  5. tjf
    Link
    The year went by so quick, but here we are again! As usual I am using Python: Part 1 #!/usr/bin/env pypy3 total = 0 for line in open(0): digits = [*filter(str.isdigit, line.strip())] calibration =...

    The year went by so quick, but here we are again! As usual I am using Python:

    Part 1
    #!/usr/bin/env pypy3
    
    total = 0
    for line in open(0):
        digits = [*filter(str.isdigit, line.strip())]
        calibration = int(digits[0] + digits[-1])
        total += calibration
    
    print(total)
    
    Regular expressions came in handy here.
    Part 2
    #!/usr/bin/env pypy3
    
    import re
    
    spellings = ('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine')
    pattern = r'(?=(' + r'|'.join(spellings + tuple('123456789')) + r'))'
    
    total = 0
    for line in open(0):
        digits = re.findall(pattern, line.strip())
        calibration = int(''.join(map(str, (int(d) if d.isdigit() else spellings.index(d) + 1
                                            for d in (digits[0], digits[-1])))))
        total += calibration
    
    print(total)
    

    Bonus: I wrote a little script so I don't have to remember how to write out these comments with <details> and all.

    tildes-aoc-comment.py
    #!/usr/bin/env python3
    
    import pathlib
    import datetime
    
    now = datetime.datetime.now()
    path = pathlib.Path(f'~/src/advent-of-code-{now.year}/day{now.day:02}').expanduser()
    part1 = open(path / '1.py').read()
    part2 = open(path / '2.py').read()
    
    input_blue = lambda s: input(f'\033[34m{s}\033[0m')
    part1_remarks = input_blue('Any remarks before part one?: ')
    part2_remarks = input_blue('Any remarks before part two?: ')
    final_remarks = input_blue('Any final remarks?: ')
    
    print(f'''\
    {part1_remarks}
    <details>
    <summary>Part 1</summary>
    
    ```python
    {part1}
    ```
    </details>
    {part2_remarks}
    <details>
    <summary>Part 2</summary>
    
    ```python
    {part2}
    ```
    </details>
    {final_remarks}''')
    
    4 votes
  6. akk
    (edited )
    Link
    I started using Java since the last year, so this year I did them in Java. Other people have posted a repo, so here's mine:...

    I started using Java since the last year, so this year I did them in Java. Other people have posted a repo, so here's mine: https://michaeleisemann.com/source/aoc23/browse/master/src/main/java/com/michaeleisemann/aoc23/services/

    If other people have pointers about my somewhat rudimentary Spring Boot webapp layout, I would love feedback on that, too.

    Everything I've done so far with Java has been spring boot, so my AoC this year is a web app using Spring Boot.

    Java
    package com.michaeleisemann.aoc23.services;
    
    import com.michaeleisemann.aoc23.interfaces.DayInterface;
    import org.apache.commons.lang3.ArrayUtils;
    import org.springframework.stereotype.Service;
    
    // https://adventofcode.com/2023/day/1
    
    @Service
    public class Day1Service implements DayInterface {
    
        private final String[] wordToNumbers = {
            "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"
        };
    
        // Fundamentally, we have to just get the first and last digit of the String.
        // The first and last number can be the same. e.g. asdf1asdf produces the first and last number as 1.
        public String day1Part1(String puzzleInput) {
            logger.info("Day 1 Part 1");
            logger.debug("Puzzle Input:\n{}", puzzleInput);
            int sum = 0;
            for (String line : puzzleInput.split("\n")) {
                // remove all non-digits
                String digits = line.replaceAll("\\D+", "");
                int calibrationValue = getFirstAndLastDigits(digits);
                logger.debug("Calibration Value: {}", calibrationValue);
                sum += calibrationValue;
            }
            return String.valueOf(sum);
        }
    
        public String day1Part2(String puzzleInput) {
            logger.info("Day 1 Part 2");
            logger.debug("Puzzle Input:\n{}", puzzleInput);
            int sum = 0;
            for (String line : puzzleInput.split("\n")) {
                // search through the line for either a digit or the word for a digit
                StringBuilder realDigits = new StringBuilder();
                StringBuilder tempString = new StringBuilder();
                for (String letter : line.split("")) {
                    if (letter.matches("\\d")) {
                        realDigits.append(letter);
                        tempString = new StringBuilder();
                        continue;
                    }
                    tempString.append(letter);
                    for (String wordNumber : wordToNumbers) {
                        if (tempString.toString().contains(wordNumber)) {
                            realDigits.append(ArrayUtils.indexOf(wordToNumbers, wordNumber));
                            tempString = new StringBuilder();
                            tempString.append(letter);
                            break;
                        }
                    }
                }
                String digits = realDigits.toString();
                int calibrationValue = getFirstAndLastDigits(digits);
                logger.debug("Calibration Value: {}", calibrationValue);
                sum += calibrationValue;
            }
            return String.valueOf(sum);
        }
    
        private int getFirstAndLastDigits(String digits) {
            String firstDigit = String.valueOf(digits.charAt(0));
            String lastDigit = String.valueOf(digits.charAt(digits.length() - 1));
            return Integer.parseInt(firstDigit + lastDigit);
        }
    }
    
    4 votes
  7. [2]
    spit-evil-olive-tips
    Link
    in previous years I've always used Python for AoC. this year I'm pushing myself to use Golang, because we use it at $dayjob and I'm comfortable with it, but not as fluent as I would like to be....

    in previous years I've always used Python for AoC. this year I'm pushing myself to use Golang, because we use it at $dayjob and I'm comfortable with it, but not as fluent as I would like to be.

    I've previously attempted using Go, but always got annoyed at how much boilerplate was required. so this year I'm making myself a scaffold that will simplify successive daily problems, using the Cobra library:

    main.go
    package main
    
    import (
    	"fmt"
    	"os"
    
    	"github.com/spf13/cobra"
    
    	"spit-evil-olive.tips/aoc2023/day01"
    )
    
    var rootCmd = &cobra.Command{}
    
    func init() {
    	rootCmd.AddCommand(day01.CommandA)
    	rootCmd.AddCommand(day01.CommandB)
    }
    
    func main() {
    	if err := rootCmd.Execute(); err != nil {
    		fmt.Println(err)
    		os.Exit(1)
    	}
    }
    

    I also started a "shared library" to start collecting all my "wow, that's really verbose in Go, I miss Python" snippets:

    common/files.go
    package common
    
    import (
    	"bufio"
    	"os"
    )
    
    func ReadFileLines(path string) ([]string, error) {
    	file, err := os.Open(path)
        if err != nil {
            return nil, err
        }
        defer file.Close()
    
    	scanner := bufio.NewScanner(file)
    
    	var lines []string
    
    	for scanner.Scan() {
    		lines = append(lines, scanner.Text())
        }
    
        return lines, nil
    }
    
    part 1
    package day01
    
    import (
    	"fmt"
    	"regexp"
    	"strconv"
    
    	"github.com/spf13/cobra"
    
    	"spit-evil-olive.tips/aoc2023/common"
    )
    
    var digitRegex = regexp.MustCompile(`\d`)
    
    var CommandA = &cobra.Command{
    	Use:  "01a",
    	Args: cobra.ExactArgs(1),
    	RunE: func(cmd *cobra.Command, args []string) error {
    		lines, err := common.ReadFileLines(args[0])
    		if err != nil {
    			return err
    		}
    
    		sum := 0
    
    		for _, line := range lines {
    			matches := digitRegex.FindAllString(line, -1)
    
    			first, err := strconv.Atoi(matches[0])
    			if err != nil {
    				return err
    			}
    
    			last, err := strconv.Atoi(matches[len(matches)-1])
    			if err != nil {
    				return err
    			}
    
    			value := (first * 10) + last
    			sum += value
    		}
    
    		fmt.Println(sum)
    
    		return nil
    	},
    }
    
    discussion of part 2 and a hint

    this was easy, because I was already using a regex...or so I thought.

    I had a line in my puzzle input with an edge case that wasn't in the example input. specifically, 6oneighthlf, where the one and the eight overlap with each other.

    the regexp library in the Go stdlib just doesn't handle overlapping pattern matches at all. so I ended up needing to use a more fully-featured regex library, as well as rolling my own "give me the last match, possibly taking overlaps into account" logic.

    part 2
    var expandedDigitRegex = regexp2.MustCompile(`(\d|one|two|three|four|five|six|seven|eight|nine)`, regexp2.None)
    
    var digitValues = map[string]int{
    	"one":   1,
    	"two":   2,
    	"three": 3,
    	"four":  4,
    	"five":  5,
    	"six":   6,
    	"seven": 7,
    	"eight": 8,
    	"nine":  9,
    }
    
    func parseDigit(input string) (int, error) {
    	if len(input) == 1 {
    		return strconv.Atoi(input)
    	} else {
    		value, ok := digitValues[input]
    		if !ok {
    			return 0, fmt.Errorf("could not parse %s", input)
    		}
    		return value, nil
    	}
    }
    
    func findFirstAndLastMatch(re *regexp2.Regexp, line string) (string, string, error) {
    	firstMatch, err := re.FindStringMatch(line)
    	if err != nil {
    		return "", "", err
    	}
    
    	var lastMatch *regexp2.Match
    	prevMatch := firstMatch
    	for {
    		start := prevMatch.Index + 1
    		match, err := re.FindStringMatchStartingAt(line, start)
    		if err != nil {
    			return "", "", err
    		}
    
    		if match == nil {
    			lastMatch = prevMatch
    			break
    		}
    
    		prevMatch = match
    	}
    
    	return firstMatch.String(), lastMatch.String(), nil
    }
    
    var CommandB = &cobra.Command{
    	Use:  "01b",
    	Args: cobra.ExactArgs(1),
    	RunE: func(cmd *cobra.Command, args []string) error {
    		lines, err := common.ReadFileLines(args[0])
    		if err != nil {
    			return err
    		}
    
    		sum := 0
    
    		for _, line := range lines {
    			firstMatch, lastMatch, err := findFirstAndLastMatch(expandedDigitRegex, line)
    			if err != nil {
    				return err
    			}
    
    			first, err := parseDigit(firstMatch)
    			if err != nil {
    				return err
    			}
    
    			last, err := parseDigit(lastMatch)
    			if err != nil {
    				return err
    			}
    
    			value := (first * 10) + last
    			sum += value
    		}
    
    		fmt.Println(sum)
    
    		return nil
    	},
    }
    
    3 votes
    1. first-must-burn
      Link Parent
      I am doing the same thing (trying to force myself to use Go). It's hard because Python is just so fast and flexible. But my golang has improved enough this past year that I think I can complete...

      I am doing the same thing (trying to force myself to use Go). It's hard because Python is just so fast and flexible. But my golang has improved enough this past year that I think I can complete it, even if I don't win any prizes for speed.

  8. pnutzh4x0r
    (edited )
    Link
    Once again, I'm doing it in Python: Repo Link Part 1 This was pretty straightforward: just used a list comprehension to extract the digits. def read_values(stream=sys.stdin) -> list[list[str]]:...

    Once again, I'm doing it in Python: Repo Link

    Part 1

    This was pretty straightforward: just used a list comprehension to extract the digits.

    def read_values(stream=sys.stdin) -> list[list[str]]:
        return [
            list(filter(str.isdigit, line))
            for line in stream
        ]
    
    def main(stream=sys.stdin) -> None:
        values = read_values(stream)
        total  = sum(
            int(digits[0] + digits[-1]) for digits in values
        )
        print(total)
    
    Part 2

    This was trickier than expected as you had to account for overlapping words such as eightwo. Fortunately, I only had to change my read_values function.

    def read_values(stream=sys.stdin) -> list[list[str]]:
        values = []
        for line in stream:
            letters = line.strip()
            digits  = []
    
            for index, letter in enumerate(letters):
                if letter.isdigit():
                    digits.append(letter)
    
                for value, digit in enumerate(DIGITS, 1):
                    if letters[index:].startswith(digit):
                        digits.append(str(value))
                        break
    
            values.append(digits)
        return values
    
    Part 2 (Update)

    While showering, I thought of a way to make Part 2 more functional by using map to convert the words to digits before filtering as I did in Part 1:

    def word_to_digit(s: str, index: int) -> str:
        for value, digit in enumerate(DIGITS, 1): 
            if s[index:].startswith(digit):
                return str(value)
        return s[index]
    
    def read_values(stream=sys.stdin) -> list[list[str]]:
        return [
            list(filter(str.isdigit, map(lambda p: word_to_digit(line, p[0]), enumerate(line))))
            for line in stream
        ] 
    
    3 votes
  9. Crestwave
    Link
    Part 2 was much trickier than I expected. As usual, here's my hacky AWK solutions. Part 1 #!/usr/bin/awk -f { gsub(/[^0-9]/, "") total += substr($0, 1, 1) substr($0, length($0 - 1)) } END { print...

    Part 2 was much trickier than I expected. As usual, here's my hacky AWK solutions.

    Part 1
    #!/usr/bin/awk -f
    {
    	gsub(/[^0-9]/, "")
    	total += substr($0, 1, 1) substr($0, length($0 - 1))
    }
    
    END { print total }
    
    Part 2

    After spotting the overlapping problem, I initially just decided to parse it from left to right for some reason. The worst part is that it worked perfectly for the sample but not for my input... made things a lot harder for myself.

    #!/usr/bin/awk -f
    {
    	trans["one"] = 1
    	trans["two"] = 2
    	trans["three"] = 3
    	trans["four"] = 4
    	trans["five"] = 5
    	trans["six"] = 6
    	trans["seven"] = 7
    	trans["eight"] = 8
    	trans["nine"] = 9
    
    	do
    		for (k in trans)
    			if ($0 ~ "^[0-9]*" k)
    				sub(k, trans[k] k)
    	while (sub(/[^0-9]/, ""))
    
    	total += substr($0, 1, 1) substr($0, length($0 - 1))
    }
    
    END { print total }
    
    3 votes
  10. [4]
    alp
    Link
    What a tricky Day 1 compared to previous years! Plus, as somebody using this year to try and brush up a bit on my old beloved C, it feels like today's second part was designed to make me sad :(...

    What a tricky Day 1 compared to previous years! Plus, as somebody using this year to try and brush up a bit on my old beloved C, it feels like today's second part was designed to make me sad :( While I completed today, I didn't get the second star thanks to a misinterpretation that I hope that I'm not alone in having read: given the input 6eightwo, my reading of the challenge led me to believe that the expected value therefor was 68, and not 62, and so that caused my final solution to be wrong. To change that I'd need to totally reïmplement, so I suppose that that's that.

    Anyway, as I haven't seen anybody else having a crack at C, I thought I'd post mine here!

    Advent of Code Day 1 C implementation ```c #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h>

    #define MAX_STR_LENGTH 128

    // a lookup table of number spellings and numerals, grouped by spelling length
    char* const DIGIT_NAMES[3][9][2] = {
    {
    { "one", "1" },
    { "two", "2" },
    { "six", "6" },
    }, {
    { "four", "4" },
    { "five", "5" },
    { "nine", "9" },
    }, {
    { "three", "3" },
    {. "seven", "7" },
    { "eight", "8" }
    }
    };

    // replace a substring at a given index with another
    void insertNumeral(char* srcString, int startIndex, int endIndex, char num) {

    char builtString[MAX_STR_LENGTH];
    
    // load up the leadin...
    strncpy(
            builtString,
            srcString,
            startIndex
    );
    
    // ...sneak in our little numeral package...
    builtString[startIndex] = num;
    
    // ...and then top off with the rest!
    strncpy(
            &builtString[startIndex + 1],
            srcString + endIndex,
            MAX_STR_LENGTH - endIndex
    );
    
    // donezo; now just copy back to the source :)
    strcpy(srcString, builtString);
    

    }

    // pass through a given string, replacing spelled numbers with numerals
    void sanitiseDigits(char* srcString) {

    for     (int start     = 0; start < (MAX_STR_LENGTH-5);    start++    )
        for (int numLen    = 0; numLen < 3;                    numLen++) {
    
            if (!isalnum(srcString[start+numLen+2]))
                break;
    
            // get current substring to query
            char* srcSubstr = (char*) malloc( (numLen+3) * sizeof(char) );
            memcpy(
                srcSubstr, 
                srcString + start    * sizeof(char),
                (numLen + 3)    * sizeof(char)
            );
    
            // check for numeral hits
            for (int i = 0; i < 3; i++)
                if (!strcmp(srcSubstr, DIGIT_NAMES[numLen][i][0]))
                    insertNumeral(
                        srcString, 
                        start,
                        (start + numLen + 3),
                        DIGIT_NAMES[numLen][i][1][0]
                    );
    
            free(srcSubstr);
    
        }
    

    }

    int main(int argc, char* argv[]) {

    // first, let's open up our input file...
    char const* const fn = argv[1];
    FILE* f = fopen(fn, "r");
    char curLine[MAX_STR_LENGTH];
    
    // ..and finally we can iterate through its lines!
    int        noOfDigits;
    char    curTerminals[2];
    int        runningTotal = 0;
    while ( fgets(curLine, sizeof(curLine), f) ) {
    
        // firstly let's sanitise our spelled digits as a first pass
        printf("raw str is %s...", curLine);
        sanitiseDigits(curLine);
        printf(" and sanitised str is %s!\n", curLine);
    
    
    
        // now all there is to do is to extract the digits...
        int* curDigits = (int*) malloc(MAX_STR_LENGTH * sizeof(int));
        noOfDigits = 0;
        
        for (int i = 0; i < (int)sizeof(curLine); i++) {
            
            if (!isalnum(curLine[i]))
                break;
            
            if (curLine[i]>='0' && curLine[i]<='9') {
                // (treat as single-character int by subtracting 0 literal)
                curDigits[noOfDigits] = (curLine[i] - '0');
                noOfDigits++;
            }
        
        }
    
        // ...and build the calibrators!
        curTerminals[0] = '0' + curDigits[0];
        curTerminals[1] = '0' + curDigits[noOfDigits-1];
        runningTotal += atoi(curTerminals);
    
        free(curDigits);
    }
    
    
    
    fclose(f);
    
    // lovely stuff. :)
    printf("FINAL TOTAL IS %d. End.", runningTotal);
    
    return 0;
    

    }

    </details>
    
    3 votes
    1. [2]
      guissmo
      Link Parent
      But you can still get a second star right? Even despite giving a wrong answer initially.

      But you can still get a second star right? Even despite giving a wrong answer initially.

      1 vote
    2. lily
      (edited )
      Link Parent
      You inspired me to take a crack at a C solution myself. It's pretty much a port of my Python solution, but I think it's pretty efficient? I'm not entirely sure, I'm not the most experienced with...

      You inspired me to take a crack at a C solution myself. It's pretty much a port of my Python solution, but I think it's pretty efficient? I'm not entirely sure, I'm not the most experienced with C.

      Solution
      // Advent of Code 2023
      // Day 1: Trebuchet?!
      
      #include <ctype.h>
      #include <stdio.h>
      #include <string.h>
      
      int main(void) {
          FILE *file = fopen("inputs/day_1.txt", "r");
      
          if (file == NULL) {
              puts("Couldn't read file!");
              return 1;
          }
      
          int result_part_1 = 0;
          int result_part_2 = 0;
      
          for (char line[64]; fgets(line, 64, file);) {
              int first_digit_part_1 = -1;
              int last_digit_part_1 = -1;
              int first_digit_part_2 = -1;
              int last_digit_part_2 = -1;
      
              for (size_t i = 0; line[i] != '\n'; ++i) {
                  int digit = -1;
      
                  if (isdigit(line[i])) {
                      digit = line[i] - '0';
      
                      if (digit != -1) {
                          if (first_digit_part_1 == -1) {
                              first_digit_part_1 = digit;
                          }
      
                          last_digit_part_1 = digit;
                      }
                  } else {
                      char check[5];
                      strncpy(check, line + i, 5);
      
                      if (strncmp(check, "one", 3) == 0) {
                          digit = 1;
                      } else if (strncmp(check, "two", 3) == 0) {
                          digit = 2;
                      } else if (strncmp(check, "three", 5) == 0) {
                          digit = 3;
                      } else if (strncmp(check, "four", 4) == 0) {
                          digit = 4;
                      } else if (strncmp(check, "five", 4) == 0) {
                          digit = 5;
                      } else if (strncmp(check, "six", 3) == 0) {
                          digit = 6;
                      } else if (strncmp(check, "seven", 5) == 0) {
                          digit = 7;
                      } else if (strncmp(check, "eight", 5) == 0) {
                          digit = 8;
                      } else if (strncmp(check, "nine", 4) == 0) {
                          digit = 9;
                      }
                  }
      
                  if (digit != -1) {
                      if (first_digit_part_2 == -1) {
                          first_digit_part_2 = digit;
                      }
      
                      last_digit_part_2 = digit;
                  }
              }
      
              result_part_1 += first_digit_part_1 * 10 + last_digit_part_1;
              result_part_2 += first_digit_part_2 * 10 + last_digit_part_2;
          }
      
          printf("Part 1: %i\nPart 2: %i\n", result_part_1, result_part_2);
      }
      
      1 vote
  11. asciipip
    Link
    As usual for me, I do my solutions in Common Lisp. Here's my code for today. I started the problem at midnight EST and did more from the REPL than from writing and running functions. I...

    As usual for me, I do my solutions in Common Lisp. Here's my code for today.

    I started the problem at midnight EST and did more from the REPL than from writing and running functions. I accidentally submitted the wrong answer for part one twice in a row, so I ended up getting a recorded solution at 00:14 or so. The code linked above is a more formalized version of what I was typing by hand in the REPL.

    My solution, as with many other peoples', involved regexes. So part two bit me, as with many others, by having overlapping digit names. My quick and dirty solution was to reverse all of the regex string except the \d, match against the reversed string, and then re-reverse the match for parsing.

    3 votes
  12. wycy
    Link
    Rust Rust use std::env; use std::io::{self, prelude::*, BufReader}; use std::fs::File; extern crate regex; use regex::Regex; fn calibration_value(input: &str) -> usize { let re =...

    Rust

    Rust
    use std::env;
    use std::io::{self, prelude::*, BufReader};
    use std::fs::File;
    
    extern crate regex;
    use regex::Regex;
    
    fn calibration_value(input: &str) -> usize {
        let re = Regex::new(r"(\d)").unwrap();
        let matches: Vec<_> = re
            .find_iter(input)
            .map(|x| x.as_str().parse::<usize>().ok())
            .collect();
        10 * matches[0].unwrap() + matches[matches.len()-1].unwrap()
    }
    
    fn calibration_value_2(input: &str) -> usize {
        let re = Regex::new(r"(\d|one|two|three|four|five|six|seven|eight|nine)").unwrap();
        let matches: Vec<_> = re
            .find_iter(input)
            .map(|x| x.as_str())
            .collect();
        10 * actual_value(matches[0]) + actual_value(matches[matches.len()-1])
    }
    
    fn actual_value(input: &str) -> usize {
        match input {
            "1" | "one"   => 1,
            "2" | "two"   => 2,
            "3" | "three" => 3,
            "4" | "four"  => 4,
            "5" | "five"  => 5,
            "6" | "six"   => 6,
            "7" | "seven" => 7,
            "8" | "eight" => 8,
            "9" | "nine"  => 9,
            _ => panic!("Unexpected input to actual_value: {input}"),
        }
    }
    
    fn solve(input: &str) -> io::Result<()> {
        let file = File::open(input).expect("Input file not found.");
        let reader = BufReader::new(file);
    
        // Input
        let input: Vec<String> = match reader.lines().collect() {
            Err(err) => panic!("Unknown error reading input: {}", err),
            Ok(result) => result,
        };
    
        // Part 1
        let part1 = input
            .iter()
            .map(|x| calibration_value(x))
            .sum::<usize>();
        println!("Part 1: {part1}"); // 54561
    
        // Part 2
        let part2 = input
            .iter()
            .map(|x| calibration_value_2(x))
            .sum::<usize>();
        println!("Part 2: {part2}"); // 54076
    
        Ok(())
    }
    
    fn main() {
        let args: Vec<String> = env::args().collect();
        let filename = &args[1];
        solve(&filename).unwrap();
    }
    
    2 votes
  13. infinitesimal
    Link
    Decided to use Kotlin and see how far I get this year. I got tripped up on part 2 and had to switch from simple replacement to regex: import java.util.regex.Pattern fun main() { /* For each line,...

    Decided to use Kotlin and see how far I get this year. I got tripped up on part 2 and had to switch from simple replacement to regex:

    import java.util.regex.Pattern
    
    fun main() {
        /*
        For each line, filter the digits, concatenate the first and last digits, and then add it to the result.
         */
        fun part1(input: List<String>): Int {
            var result = 0
            for (line in input) {
                val digits = line.filter { it.isDigit() }.map { it.digitToInt() }
                result += digits[0] * 10 + digits[digits.size - 1]
            }
            return result
        }
    
        check(part1(readExample(1, 1)) == 142)
        val output1 = part1(readInput(1))
        println(output1)
        check(output1 == 53334)
    
        /*
        For each line, generate digits using a map, concatenate the first and last digits, and then add it to the result.
         */
        fun part2(input: List<String>): Int {
            var result = 0
            val map = mapOf(
                "one" to 1,
                "two" to 2,
                "three" to 3,
                "four" to 4,
                "five" to 5,
                "six" to 6,
                "seven" to 7,
                "eight" to 8,
                "nine" to 9
            ).plus((0 until 10).map { it.toString() to it })
            val pattern = Pattern.compile(map.keys.joinToString("|"))
            for (line in input) {
                val digits = arrayListOf<Int>()
                val matcher = pattern.matcher(line)
                while (matcher.find()) {
                    digits.addLast(map[matcher.group()])
                }
                result += digits[0] * 10 + digits[digits.size - 1]
            }
            return result
        }
    
        check(part2(readExample(1, 2)) == 281)
        val output2 = part2(readInput(1))
        println(output2)
        check(output2 == 52834)
    }
    
    2 votes
  14. kari
    Link
    Here's my code in Rust. It's not particularly elegant (or fast, or good, or idiomatic, or...), but it gets the job done. I'm going on a work trip/vacation for the next 8 days so I probably won't...

    Here's my code in Rust. It's not particularly elegant (or fast, or good, or idiomatic, or...), but it gets the job done. I'm going on a work trip/vacation for the next 8 days so I probably won't be able to keep up but can't wait to come check out these threads after the fact.

    2 votes
  15. Macil
    (edited )
    Link
    I solved it with Typescript and Deno, using my aocd library for handling fetching the inputs and submitting answers. Part 1 was very straight forward by using a regex to remove all non-numeric...

    I solved it with Typescript and Deno, using my aocd library for handling fetching the inputs and submitting answers. Part 1 was very straight forward by using a regex to remove all non-numeric characters from the line. Part 2 required me to throw that out and iterate character by character to see if it was a digit or was the position a digit's name started.

    My first attempt at part 2 passed the test but failed on the real data because as an optimization I skipped to the end of the digit's name in the line when I recognized a digit's name, but it turns out some of the lines actually have the digit names overlapping, like in "eightwothree" where you're meant to read both "eight" and "two" despite them sharing a letter. I deleted the line I had that advanced i by the word's length and then my code worked fine.

    Typescript
    import { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts";
    import { runPart } from "https://deno.land/x/aocd@v1.5.1/mod.ts";
    
    function parse(input: string) {
      return input.trimEnd().split("\n");
    }
    
    function part1(input: string): number {
      const items = parse(input);
      return items.map((line) => {
        const onlyNumbers = line.replace(/[^0-9]+/g, "");
        const firstDigit = onlyNumbers[0];
        const lastDigit = onlyNumbers[onlyNumbers.length - 1];
        return Number(firstDigit + lastDigit);
      }).reduce((a, b) => a + b, 0);
    }
    
    const digits = [
      "zero",
      "one",
      "two",
      "three",
      "four",
      "five",
      "six",
      "seven",
      "eight",
      "nine",
    ];
    
    function part2(input: string): number {
      const items = parse(input);
      return items.map((line) => {
        const digitsInLine: number[] = [];
        for (let i = 0; i < line.length; i++) {
          if (line[i] >= "0" && line[i] <= "9") {
            digitsInLine.push(Number(line[i]));
          } else {
            for (
              let digitIndex = 0;
              digitIndex < digits.length;
              digitIndex++
            ) {
              if (line.startsWith(digits[digitIndex], i)) {
                digitsInLine.push(digitIndex);
                break;
              }
            }
          }
        }
        const firstDigit = digitsInLine[0];
        const lastDigit = digitsInLine[digitsInLine.length - 1];
        return firstDigit * 10 + lastDigit;
      }).reduce((a, b) => a + b, 0);
    }
    
    if (import.meta.main) {
      runPart(2023, 1, 1, part1);
      runPart(2023, 1, 2, part2);
    }
    
    const TEST_INPUT = `\
    1abc2
    pqr3stu8vwx
    a1b2c3d4e5f
    treb7uchet
    `;
    
    Deno.test("part1", () => {
      assertEquals(part1(TEST_INPUT), 142);
    });
    
    const TEST_INPUT2 = `\
    two1nine
    eightwothree
    abcone2threexyz
    xtwone3four
    4nineeightseven2
    zoneight234
    7pqrstsixteen
    `;
    
    Deno.test("part2", () => {
      assertEquals(part2(TEST_INPUT2), 281);
    });
    
    2 votes
  16. [2]
    guissmo
    Link
    Wrote about the first problem on my blog. Dunno if I can keep up lol. First part I did with a text editor with regex, and then an Excel file. Second part was tricky. I gave up on Excel and whipped...

    Wrote about the first problem on my blog. Dunno if I can keep up lol.

    First part I did with a text editor with regex, and then an Excel file.

    Second part was tricky. I gave up on Excel and whipped out my trusty Python.

    Invested time on a function that finds the first recognized digit then copy pasted that into the same function but with the relevant strings reversed.

    More info here:
    https://guissmo.com/blog/advent-of-code-2023-day-1/

    2 votes
    1. first-must-burn
      Link Parent
      I know what you meant, but .... if you got the answer with the second meaning of this phrase, that would be truly impressive. And yes, part of me apparently is still 12.

      whipped out my trusty Python

      I know what you meant, but .... if you got the answer with the second meaning of this phrase, that would be truly impressive. And yes, part of me apparently is still 12.

      1 vote
  17. xavdid
    Link
    AoC is routinely my favorite event of the year. Day 1 had some fun edge cases, but nothing too crazy. If anyone gets stuck (not necessarily today, but at some point in here), I'll be posting...

    AoC is routinely my favorite event of the year. Day 1 had some fun edge cases, but nothing too crazy.

    If anyone gets stuck (not necessarily today, but at some point in here), I'll be posting explanations of each puzzle and my Python solution on my website: https://advent-of-code.xavd.id/writeups/2023/day/1/

    Should be a fun year!

    2 votes
  18. thorondir
    Link
    I'm trying out Guile Scheme, this year. Didn't get to it yesterday, sadly, but such is life. I wrote a "utils.scm" library, to do things I'll have to do over and over again: utils.scm For now,...

    I'm trying out Guile Scheme, this year.
    Didn't get to it yesterday, sadly, but such is life.

    I wrote a "utils.scm" library, to do things I'll have to do over and over again:

    utils.scm

    For now, it's just a function that reads in the input-file line by line, hands back a list of lines.

    (use-modules (ice-9 rdelim))
    
    (define (slurp filename)
      (call-with-input-file filename
                            (lambda (p)
                              (let loop ([line (read-line p)]
                                         [result '()])
                                (if (eof-object? line)
                                  result
                                  (loop (read-line p) (cons line result)))))))
    
    Part 1

    Nothing particularly fancy, just regex-ing it.

    (use-modules (ice-9 regex)
                 (ice-9 format))
    (load "utils.scm")
    
    (define beginning-digit-re "^[^1-9]*([1-9])")
    (define end-digit-re "([1-9])[^1-9]*$")
    
    (define (process-line line)
      (let ([begin-match (string-match beginning-digit-re line)]
            [end-match   (string-match end-digit-re line)])
        (string-join (list (match:substring begin-match 1)
                           (match:substring end-match 1))
                     "")))
    
    (define (proc-task-01a filename)
      (define puzzle-input (slurp filename))
      (apply + (map string->number (map process-line puzzle-input))))
    
    (if (= (proc-task-01a "tiny_input_01a") 142)
      (format #t "Task 01, part 1: ~a\n" (proc-task-01a "input_01"))
      (error "tiny-task-01a didn't pass sanity checks"))
    

    Second part was, as everyone already mentioned, a bit trickier.
    Still regex-ing it, though.

    Part 2
    (define (front-splicer str matcher)
      (let [(m (string-match matcher str))]
        (if (regexp-match? m)
          m
          (front-splicer (string-drop str 1) matcher))))
    (define (back-splicer str matcher)
      (let [(m (string-match matcher str))]
        (if (regexp-match? m)
          m
          (back-splicer (string-drop-right str 1) matcher))))
    
    (define (digit-getter m)
      (cond
        [(or (string=? "1" m) (string=? "one" m))   "1"]
        [(or (string=? "2" m) (string=? "two" m))   "2"]
        [(or (string=? "3" m) (string=? "three" m)) "3"]
        [(or (string=? "4" m) (string=? "four" m))  "4"]
        [(or (string=? "5" m) (string=? "five" m))  "5"]
        [(or (string=? "6" m) (string=? "six" m))   "6"]
        [(or (string=? "7" m) (string=? "seven" m)) "7"]
        [(or (string=? "8" m) (string=? "eight" m)) "8"]
        [(or (string=? "9" m) (string=? "nine" m))  "9"]
        [else
          (error (format #f "we matched something else? ~a" m))]) )
    
    (define (grab-first line)
      (define matcher "^(1|2|3|4|5|6|7|8|9|one|two|three|four|five|six|seven|eight|nine)")
      (let [(m (match:substring (front-splicer line matcher) 1))]
        (digit-getter m)))
    
    (define (grab-last line)
      (define matcher "(1|2|3|4|5|6|7|8|9|one|two|three|four|five|six|seven|eight|nine)$")
      (let [(m (match:substring (back-splicer line matcher) 1))]
        (digit-getter m)))
    
    (define (proc-line line)
      (let [(front (grab-first line))
            (back  (grab-last  line))]
        (string->number (format #f "~a~a" front back))))
    
    (define (proc-task-01b filename)
      (define puzzle-input (slurp filename))
      (apply + (map proc-line puzzle-input)))
    
    (if (= (proc-task-01b "tiny_input_01b") 281)
      (format #t "Task 01, part 2: ~a\n" (proc-task-01b "input_01"))
      (error "tiny-task-01b didn't pass sanity checks"))
    

    The solutions don't feel particularly... scheme-y, but then again, I don't know what that would feel like, exactly. xD

    2 votes
  19. jzimbel
    (edited )
    Link
    Elixir Breaking out the binary pattern matching on day 1, hoo-wee 🤠 Parts 1 and 2 I got tripped up by the fact that spelled-out digits can overlap in part 2. At first, I had my code jump ahead by...

    Elixir

    Breaking out the binary pattern matching on day 1, hoo-wee 🤠

    Parts 1 and 2

    I got tripped up by the fact that spelled-out digits can overlap in part 2. At first, I had my code jump ahead by length(digit_string) every time I found one, so I was missing any spelled-out digits that started within that first one. Annoyingly, this issue only came up in the real puzzle input and not the examples.

    defmodule AdventOfCode.Solution.Year2023.Day01 do
      def part1(input), do: solve(input, &digits_part1/1)
      def part2(input), do: solve(input, &digits_part2/1)
    
      defp solve(input, get_digits_fn) do
        input
        |> String.split("\n", trim: true)
        |> Enum.map(&(&1 |> get_digits_fn.() |> Integer.undigits()))
        |> Enum.sum()
      end
    
      defguardp is_digit(char) when char in ?0..?9
    
      defp digits_part1(line, acc \\ [])
      defp digits_part1("", acc), do: [List.first(acc), List.last(acc)]
    
      defp digits_part1(<<char, rest::binary>>, acc) when is_digit(char) do
        digits_part1(rest, put_digit(acc, char - ?0))
      end
    
      defp digits_part1(<<_, rest::binary>>, acc) do
        digits_part1(rest, acc)
      end
    
      ###
    
      defp digits_part2(line, acc \\ [])
      defp digits_part2("", acc), do: [List.first(acc), List.last(acc)]
    
      defp digits_part2(<<char, rest::binary>>, acc) when is_digit(char) do
        digits_part2(rest, put_digit(acc, char - ?0))
      end
    
      defp digits_part2(<<_, rest::binary>> = line, acc) do
        acc =
          case find_leading_word_digit(line) do
            {:ok, digit} -> put_digit(acc, digit)
            :error -> acc
          end
    
        digits_part2(rest, acc)
      end
    
      defp put_digit([], first_digit), do: [first_digit]
      defp put_digit([first_digit | _], digit), do: [first_digit, digit]
    
      for {str, n} <- Enum.zip(~w[one two three four five six seven eight nine], 1..9) do
        defp find_leading_word_digit(unquote(str) <> _), do: {:ok, unquote(n)}
      end
    
      defp find_leading_word_digit(_), do: :error
    end
    

    P.S. This is the cleaned up version of my solution—my initial code was a big ol' mess

    Edit

    Shorter and (in certain cases) faster solution

    I realized you don't need to search through the entirety of each line—you can look for the first digit from the left end and the first digit from the right end, and potentially skip a large amount of text in the middle of the line.

    Seems like the Erlang VM is optimized for parsing through binary data from left to right though, so even though this does "less work" by stopping as soon as it finds a digit on either end, instead of always iterating through the whole line, the code that works backward from the end of the string tends to run slowly enough to almost cancel out the benefit. In fact, part 1 now runs a few hundred μs slower!

    However I still like this approach better because it's a bit easier to understand what the code is doing.

    defmodule AdventOfCode.Solution.Year2023.Day01 do
      def part1(input), do: solve(input, &find_digit_part1/1)
      def part2(input), do: solve(input, &find_digit_part2/1)
    
      defp solve(input, finder_fn) do
        input
        |> String.split("\n", trim: true)
        |> Enum.map(fn line ->
          [find_first_digit(line, finder_fn), find_last_digit(line, finder_fn)]
          |> Integer.undigits()
        end)
        |> Enum.sum()
      end
    
      defguardp is_digit(char) when char in ?0..?9
    
      defp find_first_digit(<<_, rest::binary>> = str, finder_fn) do
        case finder_fn.(str) do
          {:ok, d} -> d
          :error -> find_first_digit(rest, finder_fn)
        end
      end
    
      defp find_last_digit(line, finder_fn), do: find_last_digit(line, finder_fn, byte_size(line) - 1)
    
      defp find_last_digit(line, finder_fn, drop_from_front) do
        <<_::binary-size(drop_from_front), str::binary>> = line
    
        case finder_fn.(str) do
          {:ok, d} -> d
          :error -> find_last_digit(line, finder_fn, drop_from_front - 1)
        end
      end
    
      defp find_digit_part1(<<char, _::binary>>) when is_digit(char), do: {:ok, char - ?0}
      defp find_digit_part1(_), do: :error
    
      defp find_digit_part2(<<char, _::binary>>) when is_digit(char), do: {:ok, char - ?0}
    
      for {str, n} <- Enum.zip(~w[one two three four five six seven eight nine], 1..9) do
        defp find_digit_part2(<<unquote(str), _::binary>>), do: {:ok, unquote(n)}
      end
    
      defp find_digit_part2(_), do: :error
    end
    
    1 vote
  20. Johz
    Link
    Rust, with the aim being pure speed - my repo is here. According to Criterion, on a Macbook Pro with M2, I'm getting around 18 µs for part one, and 28 µs for part two, which I'm fairly happy with....

    Rust, with the aim being pure speed - my repo is here.

    According to Criterion, on a Macbook Pro with M2, I'm getting around 18 µs for part one, and 28 µs for part two, which I'm fairly happy with.

    Notes I started by just parsing all the values I could see and then returning the first and last ones, which seems to be a common solution. (I also got stuck on the overlapping numbers issue and was really confused for a long time why all my tests were working but the main solution kept on showing up as wrong. The subreddit definitely helped today! 😅)

    Then I realised I didn't need to do all that parsing, and it was only the first and last numbers that were interesting, which meant I could skip all the overlapping stuff, and made the parsing a lot easier, although I did end up needing to do it twice to handle the forwards case and the backwards case.

    If I were going to improve this, I'd probably look at the regex crate, or better yet some of its internals. The full power of regex is probably overkill in this situation, but Burntsushi knows his way around string matching, so maybe there's something there that'll handle the parsing better. But I think I'm happy for now.

    1 vote
  21. whs
    Link
    My Rust solution. Seems that many people are advocating that the intended solution is you stop scanning once you hit the first number, and I should've done that instead of find & replace approach...

    My Rust solution. Seems that many people are advocating that the intended solution is you stop scanning once you hit the first number, and I should've done that instead of find & replace approach that I did. It might be even simplier. The Rust code also have a bug that it should've use is_digit(10) instead of is_numeric() which make the app crash when encountering non-ascii number.

    Maybe I'll try different language tomorrow.

    use std::io;
    use std::io::Read;
    
    // The newly-improved calibration document consists of lines of text
    // each line originally contained a specific calibration value that the Elves now need to recover
    // On each line, the calibration value can be found by combining the first digit and the last digit (in that order) to form a single two-digit number.
    fn solve(text: &str) -> u32 {
        text.lines().map(|v| {
            let first_digit = v.chars().find(|c| c.is_numeric()).map(|c| c.to_digit(10)).flatten().unwrap();
            let last_digit = v.chars().rfind(|c| c.is_numeric()).map(|c| c.to_digit(10)).flatten().unwrap();
    
            (first_digit * 10) + last_digit
        }).fold(0u32, |a, b| a + b)
    }
    
    struct Replacement {
        len: usize,
        text: &'static [u8; 5],
        replacement: &'static [u8; 1],
    }
    
    const NUMBERS: [Replacement; 9] = [
        Replacement { len: 3, text: b"one  ", replacement: b"1" },
        Replacement { len: 3, text: b"two  ", replacement: b"2" },
        Replacement { len: 5, text: b"three", replacement: b"3" },
        Replacement { len: 4, text: b"four ", replacement: b"4" },
        Replacement { len: 4, text: b"five ", replacement: b"5" },
        Replacement { len: 3, text: b"six  ", replacement: b"6" },
        Replacement { len: 5, text: b"seven", replacement: b"7" },
        Replacement { len: 5, text: b"eight", replacement: b"8" },
        Replacement { len: 4, text: b"nine ", replacement: b"9" },
    ];
    
    fn solve2(text: &str) -> u32 {
        text.lines().map(|v| {
            let mut new_v = Vec::<u8>::new();
            let raw = v.as_bytes();
            let mut i = 0;
            let mut last_covered = 0;
            'outer: while i < raw.len() {
                for pattern in &NUMBERS {
                    if i + pattern.len > raw.len() {
                        continue;
                    }
                    if raw[i..i + pattern.len] == pattern.text[0..pattern.len] {
                        last_covered = i + pattern.len;
                        new_v.extend_from_slice(pattern.replacement);
                        i += 1;
                        continue 'outer;
                    }
                }
                if last_covered <= i {
                    new_v.push(raw[i]);
                }
                i += 1;
            }
            let new_v = unsafe { String::from_utf8_unchecked(new_v) };
            let first_digit = new_v.chars().find(|c| c.is_numeric()).map(|c| c.to_digit(10)).flatten().unwrap();
            let last_digit = new_v.chars().rfind(|c| c.is_numeric()).map(|c| c.to_digit(10)).flatten().unwrap();
    
            (first_digit * 10) + last_digit
        }).fold(0u32, |a, b| a + b)
    }
    
    fn main() {
        let mut stdin = io::stdin();
        let mut input = String::new();
        stdin.read_to_string(&mut input).unwrap();
        println!("{}", solve2(&input));
    }
    
    #[cfg(test)]
    mod tests {
        use crate::*;
    
        #[test]
        fn test_example() {
            let input = "1abc2
    pqr3stu8vwx
    a1b2c3d4e5f
    treb7uchet";
            assert_eq!(solve(input), 142);
        }
    
        #[test]
        fn test_example2() {
            let input = "two1nine
    eightwothree
    abcone2threexyz
    xtwone3four
    4nineeightseven2
    zoneight234
    7pqrstsixteen";
            assert_eq!(solve2(input), 281);
        }
    
        #[test]
        fn test_example2_reddit() {
            let input = "eighthree
    sevenine";
            assert_eq!(solve2(input), 83 + 79);
        }
    }
    
    1 vote
  22. Crespyl
    Link
    Back to Ruby for this year, I want to see if I can get further this time than I have the last couple. Day 1 - Ruby #!/usr/bin/env ruby require 'benchmark' require 'minitest' require 'pry-byebug'...

    Back to Ruby for this year, I want to see if I can get further this time than I have the last couple.

    Day 1 - Ruby
    #!/usr/bin/env ruby
    
    require 'benchmark'
    require 'minitest'
    require 'pry-byebug'
    
    TEST_STR = "\
    1abc2
    pqr3stu8vwx
    a1b2c3d4e5f
    treb7uchet
    "
    
    TEST_STR_2 = "\
    two1nine
    eightwothree
    abcone2threexyz
    xtwone3four
    4nineeightseven2
    zoneight234
    7pqrstsixteen
    "
    
    class Test < MiniTest::Test
      def test_p1
        assert_equal(142, compute_p1(TEST_STR))
      end
    
      def test_p2
        assert_equal(281, compute_p2(TEST_STR_2))
      end
    
      def test_p2_overlap
        assert_equal(51, compute_p2("jjhxddmg5mqxqbgfivextlcpnvtwothreetwonerzk"))
      end
    end
    
    def compute_p1(input)
      input.lines.map { |line|
        numbers = line
                    .chars
                    .filter { |c| c.match(/[[:digit:]]/) }
        [numbers.first, numbers.last]
          .join
          .to_i
      }.sum
    end
    
    def compute_p2(input)
      input.lines.map { |line|
        numbers = line
          .scan(/(?=(\d|one|two|three|four|five|six|seven|eight|nine))/)
          .flatten
          .map { |num|
            case num
            when "one"
              1
            when "two"
              2
            when "three"
              3
            when "four"
              4
            when "five"
              5
            when "six"
              6
            when "seven"
              7
            when "eight"
              8
            when "nine"
              9
            else
              num
            end
        }
    
        [numbers.first, numbers.last]
          .join
          .to_i
      }.sum
    end
    
    if MiniTest.run
      puts 'Test case OK, running...'
    
      @input = File.read(ARGV[0] || 'input')
      do_p2 = defined?(compute_p2)
    
      Benchmark.bm do |bm|
        bm.report('Part 1:') { @p1 = compute_p1(@input) }
        bm.report('Part 2:') { @p2 = compute_p2(@input) } if do_p2
      end
    
      puts "\nResults:"
      puts 'Part 1: %i' % @p1
      puts 'Part 2: %i' % @p2 if do_p2
    
    else
      puts 'Test case ERR'
    end
    
    1 vote
  23. lily
    Link
    Should've posted this earlier, but I didn't realize there was a thread here. We're certainly off to a tricky start this year - I was hoping for a leaderboard spot, but was a couple minutes off for...

    Should've posted this earlier, but I didn't realize there was a thread here. We're certainly off to a tricky start this year - I was hoping for a leaderboard spot, but was a couple minutes off for part 1 and even further away for part 2. Enjoyable problem, though! Looking forward to tonight's.

    Solution
    # Advent of Code 2023
    # Day 1: Trebuchet?!
    
    spelled_digits = {
        "one": 1,
        "two": 2,
        "three": 3,
        "four": 4,
        "five": 5,
        "six": 6,
        "seven": 7,
        "eight": 8,
        "nine": 9,
        "ten": 10
    }
    
    result_part_1 = 0
    result_part_2 = 0
    
    with open("inputs/day_1.txt") as file:
        for line in file:
            digits_part_1 = []
            digits_part_2 = []
    
            for pos in range(len(line)):
                if line[pos].isdigit():
                    digit = int(line[pos])
                    digits_part_1.append(digit)
                    digits_part_2.append(digit)
    
                for spelled in spelled_digits:
                    if line[pos:].startswith(spelled):
                        digits_part_2.append(spelled_digits[spelled])
    
            result_part_1 += digits_part_1[0] * 10 + digits_part_1[-1]
            result_part_2 += digits_part_2[0] * 10 + digits_part_2[-1]
    
    print(f"Part 1: {result_part_1}")
    print(f"Part 2: {result_part_2}")
    
    1 vote
  24. zazowoo
    Link
    This is my first year doing AoC after hearing about it from coworkers for a while. Curious to see how long I can stick with it! Here's my Elixir solution for part 2. Day 1 - Elixir defmodule...

    This is my first year doing AoC after hearing about it from coworkers for a while. Curious to see how long I can stick with it! Here's my Elixir solution for part 2.

    Day 1 - Elixir
    defmodule Solution do
      def run(filename) do
        File.stream!(filename, [:read])
        |> Enum.reduce(0, fn line, sum -> 
          sum + get_number_from_line(line)
        end)
        |> IO.puts()
      end
    
      defp get_number_from_line(line) do
        case Regex.run(~r/^.*?(\d|one|two|three|four|five|six|seven|eight|nine).*(\d|one|two|three|four|five|six|seven|eight|nine).*?$/, line) do
          [_, first_digit, last_digit] -> create_number(first_digit, last_digit)
          nil -> case Regex.run(~r/^.*(\d|one|two|three|four|five|six|seven|eight|nine).*$/, line) do
            [_, digit] -> create_number(digit, digit)
          end
        end
      end
    
      defp create_number(first_digit, second_digit) do
        to_integer(first_digit) * 10 + to_integer(second_digit)
      end
    
      defp to_integer(number) do
        case number do
          "one" -> 1
          "two" -> 2
          "three" -> 3
          "four" -> 4
          "five" -> 5
          "six" -> 6
          "seven" -> 7
          "eight" -> 8
          "nine" -> 9
          number -> String.to_integer(number)
        end
      end
    end
    
    Solution.run("./input")
    
    1 vote
  25. Toric
    Link
    Part 1 was straight forward, but had to rely on a really hacky solution for day 2. Doing it in rust again this year, its the language that I find fun....

    Part 1 was straight forward, but had to rely on a really hacky solution for day 2. Doing it in rust again this year, its the language that I find fun.

    https://github.com/gabevenberg/advent-of-code-2023

    1 vote
  26. Eji1700
    (edited )
    Link
    Always running behind on these. 19 loc in F# for part 1. Didn't want to use regex because I suck at it and I like to future proof against part 2 which sometimes is much harder to do in regex. Part...

    Always running behind on these. 19 loc in F# for part 1. Didn't want to use regex because I suck at it and I like to future proof against part 2 which sometimes is much harder to do in regex.

    Part 1
    open System.IO
    open System
    
    let inputData =  File.ReadLines @".\input.txt"
    
    let getNumberFromString (input:string) = 
        input
        |> Seq.filter Char.IsNumber
        |> fun numbers  -> 
            let first = Seq.head numbers
            let last =  Seq.last numbers 
            (first.ToString() + last.ToString()) |> int
    
    
    let result =
        inputData
        |> Seq.map getNumberFromString
        |> Seq.sum
    
    result
    
    Annnd i was wrong, regex would've been fine, but I'm stubborn and lazy so rather than spend 5 minutes on regexr i'll spend 20 mocking this up. Got it in under 50 lines ignoring my rage debugging.
    Part 2
    open System.IO
    open System
    
    let inputData =  File.ReadLines @".\input.txt"
    
    let numberWords =
        [   "one", 1
            "two", 2
            "three", 3
            "four", 4
            "five", 5
            "six", 6
            "seven", 7
            "eight", 8
            "nine", 9 ]
    
    type NumberIndex = 
        {   Index: int
            Value: string }
    
    let getNumberFromWord (input:string) = 
        numberWords
        |> List.collect(fun (number, value) -> 
            let first = input.IndexOf number 
            let last = input.LastIndexOf number
            [   { Index = first; Value = value.ToString() }
                { Index = last; Value = value.ToString()} ] ) 
        |> List.filter(fun indexes -> indexes.Index >= 0)
        |> List.sortBy(fun index -> index.Value)
        
    let getNumberFromCharacter (input:string) = 
        input
        |> Seq.mapi(fun i c ->
            match Char.IsNumber c with 
            | true -> Some { Index = i; Value = c.ToString() } 
            | false -> None
        )
        |> Seq.choose id
        |> Seq.toList
    
    let result =
        inputData
        |> Seq.map( fun line ->
            getNumberFromWord line @ getNumberFromCharacter line
            |> List.sortBy _.Index
            |> fun indexes -> (List.head indexes).Value + (List.last indexes).Value |> int
        )
        |> Seq.toList
        |> Seq.sum
    
    // Ye ole fashion debugging.
    // result 
    // |> Seq.map _.ToString()
    // |> fun data -> File.WriteAllLines(@".\test.txt", data)
    
    1 vote
  27. knocklessmonster
    Link
    I'm already behind, but got Part 1 done in Bash. An arbitrary goal I've set is pure GNU/Bash, since bash is fairly limited in what it can do, but has fairly robust logic. I used sed and awk for...

    I'm already behind, but got Part 1 done in Bash. An arbitrary goal I've set is pure GNU/Bash, since bash is fairly limited in what it can do, but has fairly robust logic. I used sed and awk for this, for example, to extract the data, and the arithmetic was fairly simple to get work, like any other language.

    Part 1 ````bash

    #!/bin/bash

    INPUT=$(sed ./input.txt -e 's/[a-z]//g' | awk '{print substr($0,0,1) substr($0,length($0),1)}')
    declare -a NUMS=()
    NUM=0
    #echo -e "$INPUT"

    for LINE in $INPUT; do
    #echo $LINE
    let NUM+=$LINE
    done

    echo "$NUM"

    </details>
    
    1 vote