# Day 1: Trebuchet?!

1. JRandomHacker
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

2. csos95
(edited )
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()
}
``````

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()
}
``````
3. tomf
(edited )
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. [2]
first-must-burn
(edited )
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.

1. fxgn
5. tjf
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()

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}''')
``````
6. akk
(edited )
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;

@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);
}
}
``````
7. [2]
spit-evil-olive-tips
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() {
}

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 {
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 {
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
},
}
``````
1. first-must-burn
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 )
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:
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]

return [
list(filter(str.isdigit, map(lambda p: word_to_digit(line, p[0]), enumerate(line))))
for line in stream
]
``````
9. Crestwave
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 }
``````
10. [4]
alp
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];

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>
``````
1. [2]
guissmo
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 )
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) {
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
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.

12. wycy
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::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<()> {

// 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();
}
``````
13. infinitesimal
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
}

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()) {
}
result += digits[0] * 10 + digits[digits.size - 1]
}
return result
}

println(output2)
check(output2 == 52834)
}
``````
14. kari
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.

15. Macil
(edited )
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);
});
``````
16. [2]
guissmo
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.

1. first-must-burn
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
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!

18. thorondir
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)
[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))

(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 puzzle-input (slurp filename))
(apply + (map string->number (map process-line puzzle-input))))

(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 puzzle-input (slurp filename))
(apply + (map proc-line puzzle-input)))

(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

19. jzimbel
(edited )
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 =
{: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

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
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
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;

// 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();
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
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...'

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

1 vote
26. Eji1700
(edited )
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 getNumberFromString (input:string) =
input
|> Seq.filter Char.IsNumber
|> fun 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 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
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