Skip to content

Commit bc1327c

Browse files
committed
2023 d1: Add walkthrough
1 parent 8dbed01 commit bc1327c

File tree

2 files changed

+273
-1
lines changed

2 files changed

+273
-1
lines changed

2023/README.md

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
Advent of Code 2023 walkthrough
2+
===============================
3+
4+
**Note**: in the hope of speeding up the process of writing walkthroughs each
5+
day, this year I am *not* going to give a brief summary of the "part 1" problem
6+
statement at the beginning of each day. Instead, I will jump right at the
7+
solution. The official problem statements are linked throughout the document for
8+
reference.
9+
10+
Table of Contents
11+
-----------------
12+
13+
- [Day 1 - Trebuchet?!][d01]
14+
<!--
15+
- [Day 2 - ][d02]
16+
- [Day 3 - ][d03]
17+
- [Day 4 - ][d04]
18+
- [Day 5 - ][d05]
19+
- [Day 6 - ][d06]
20+
- [Day 7 - ][d07]
21+
- [Day 8 - ][d08]
22+
- [Day 9 - ][d09]
23+
- [Day 10 - ][d10]
24+
- [Day 11 - ][d11]
25+
- [Day 12 - ][d12]
26+
- [Day 13 - ][d13]
27+
- [Day 14 - ][d14]
28+
- [Day 15 - ][d15]
29+
- [Day 16 - ][d16]
30+
- [Day 17 - ][d17]
31+
- [Day 18 - ][d18]
32+
- [Day 19 - ][d19]
33+
- [Day 20 - ][d20]
34+
- [Day 21 - ][d21]
35+
- [Day 22 - ][d22]
36+
- [Day 23 - ][d23]
37+
- [Day 24 - ][d24]
38+
- [Day 25 - ][d25]
39+
-->
40+
41+
42+
Day 1 - Trebuchet?!
43+
-------------------
44+
45+
[Problem statement][d01-problem][Complete solution][d01-solution][Back to top][top]
46+
47+
### Part 1
48+
49+
Task seems easy enough. How do you find out if a character is a digit? Simply
50+
check [`char.isdigit()`][py-str-isdigit]. We can do this for each character of
51+
each line of input, first iterating forward to find the first, and then
52+
iterating backwards (using `[::-1]`) to find the last. The digits we find will
53+
need to be converted to `int`, and the first one will need to also be multiplied
54+
by `10`.
55+
56+
```python
57+
fin = open(...)
58+
total = 0
59+
60+
for line in fin:
61+
for char in line:
62+
if char.isdigit():
63+
total += 10 * int(char)
64+
break
65+
66+
for char in line[::-1]:
67+
if char.isdigit():
68+
total += int(char)
69+
break
70+
```
71+
72+
We can simplify this with the help of the [`filter()`][py-builtin-filter]
73+
built-in function: just filter out any character that satisfies `str.isdigit()`.
74+
To only extrac the first such character from the iterator returned by `filter()`
75+
we can simply use [`next()`][py-builtin-next].
76+
77+
```python
78+
for line in fin:
79+
digit1 = next(filter(str.isdigit, line))
80+
digit2 = next(filter(str.isdigit, line[::-1]))
81+
total += 10 * int(digit1) + int(digit2)
82+
83+
print('Part 1:', total)
84+
```
85+
86+
### Part 2
87+
88+
Things get more complex and this is probably the "hardest" day 1 problem I have
89+
seen so far. We need to also consider English *words* when checking each line of
90+
input. The first and last digits to appear either as a digit or as an english
91+
word need to be found.
92+
93+
There isn't much to do except checking each spelled out English digit for each
94+
line. We can simplify things by building a `dict` to use as a lookup table:
95+
96+
```python
97+
DIGITS = {
98+
'zero' : 0,
99+
'one' : 1,
100+
'two' : 2,
101+
'three': 3,
102+
'four' : 4,
103+
'five' : 5,
104+
'six' : 6,
105+
'seven': 7,
106+
'eight': 8,
107+
'nine' : 9,
108+
}
109+
```
110+
111+
Now the check is a bit more annoying, so let's create a function for it: it will
112+
take a string and will check whether the first character is a digit (and in that
113+
case return it) or whether the string starts with a spelled-out English digit
114+
(and in that case convert and return it). We'll return `0` in case of no match
115+
for simplicity.
116+
117+
```python
118+
def check_digit(string):
119+
if string[0].isdigit():
120+
return int(string[0])
121+
122+
for d in DIGITS:
123+
if string.startswith(d):
124+
return DIGITS[d]
125+
126+
return 0
127+
```
128+
129+
The second loop above can again be simplified with the use of `filter()` +
130+
`next()`, but since this time we are not guaranteed to find an English word in
131+
`string`, we need to pass a second argument to `next()` for the default value to
132+
return in case `filter()` does not match anything.
133+
134+
```python
135+
def check_digit(char, string):
136+
if string[0].isdigit():
137+
return int(string[0])
138+
139+
d = next(filter(string.startswith, DIGITS), None)
140+
return DIGITS.get(d, 0)
141+
```
142+
143+
We can now integrate the above function in the loop we wrote for part 1, using a
144+
second variable for the total. This time, we'll have to iterate over each
145+
possible substring manually, first forward and then backwards. We can easily use
146+
[`range()`][py-builtin-range] for that.
147+
148+
```python
149+
total1 = total2 = 0
150+
151+
for line in fin:
152+
# Part 1
153+
total1 += 10 * int(next(filter(str.isdigit, line)))
154+
total1 += int(next(filter(str.isdigit, line[::-1])))
155+
156+
# Part 2
157+
for i in range(len(line)):
158+
digit1 = check_digit(char, line[i:])
159+
if digit1:
160+
break
161+
162+
for i in range(len(line) - 1, -1, -1):
163+
digit2 = check_digit(line[i], line[i:])
164+
if digit2:
165+
break
166+
167+
total2 += 10 * digit1 + digit2
168+
169+
print('Part 1:', total1)
170+
print('Part 2:', total2)
171+
```
172+
173+
There is technically a way to "simplify" this even more, again with the use of
174+
`filter()` + `next()`, but it does not really help anything. However, here it
175+
is, just for the fun of it:
176+
177+
```python
178+
for line in fin:
179+
total2 += 10 * next(filter(None, map(check_digit, (line[i:] for i in range(len(line))))))
180+
total2 += next(filter(None, map(check_digit, (line[i:] for i in range(len(line) -1, -1, -1)))))
181+
```
182+
183+
First two starts of the year done. Welcome to Advent of Code 2024!
184+
185+
---
186+
187+
*Copyright &copy; 2023 Marco Bonelli. This document is licensed under the [Creative Commons BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) license.*
188+
189+
![License icon](https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png)
190+
191+
[top]: #advent-of-code-2023-walkthrough
192+
[d01]: #day-1---trebuchet
193+
[d02]: #day-2---
194+
[d03]: #day-3---
195+
[d04]: #day-4---
196+
[d05]: #day-5---
197+
[d06]: #day-6---
198+
[d07]: #day-7---
199+
[d08]: #day-8---
200+
[d09]: #day-9---
201+
[d10]: #day-10---
202+
[d11]: #day-11---
203+
[d12]: #day-12---
204+
[d13]: #day-13---
205+
[d14]: #day-14---
206+
[d15]: #day-15---
207+
[d16]: #day-16---
208+
[d18]: #day-18---
209+
[d19]: #day-19---
210+
[d20]: #day-20---
211+
[d21]: #day-21---
212+
[d22]: #day-22---
213+
[d24]: #day-24---
214+
[d25]: #day-25---
215+
216+
[d01-problem]: https://adventofcode.com/2023/day/1
217+
[d02-problem]: https://adventofcode.com/2023/day/2
218+
[d03-problem]: https://adventofcode.com/2023/day/3
219+
[d04-problem]: https://adventofcode.com/2023/day/4
220+
[d05-problem]: https://adventofcode.com/2023/day/5
221+
[d06-problem]: https://adventofcode.com/2023/day/6
222+
[d07-problem]: https://adventofcode.com/2023/day/7
223+
[d08-problem]: https://adventofcode.com/2023/day/8
224+
[d09-problem]: https://adventofcode.com/2023/day/9
225+
[d10-problem]: https://adventofcode.com/2023/day/10
226+
[d11-problem]: https://adventofcode.com/2023/day/11
227+
[d12-problem]: https://adventofcode.com/2023/day/12
228+
[d13-problem]: https://adventofcode.com/2023/day/13
229+
[d14-problem]: https://adventofcode.com/2023/day/14
230+
[d15-problem]: https://adventofcode.com/2023/day/15
231+
[d16-problem]: https://adventofcode.com/2023/day/16
232+
[d18-problem]: https://adventofcode.com/2023/day/18
233+
[d19-problem]: https://adventofcode.com/2023/day/19
234+
[d20-problem]: https://adventofcode.com/2023/day/20
235+
[d21-problem]: https://adventofcode.com/2023/day/21
236+
[d22-problem]: https://adventofcode.com/2023/day/22
237+
[d24-problem]: https://adventofcode.com/2023/day/24
238+
[d25-problem]: https://adventofcode.com/2023/day/25
239+
240+
[d01-solution]: solutions/day01.py
241+
[d02-solution]: solutions/day02.py
242+
[d03-solution]: solutions/day03.py
243+
[d04-solution]: solutions/day04.py
244+
[d05-solution]: solutions/day05.py
245+
[d06-solution]: solutions/day06.py
246+
[d07-solution]: solutions/day07.py
247+
[d08-solution]: solutions/day08.py
248+
[d09-solution]: solutions/day09.py
249+
[d10-solution]: solutions/day10.py
250+
[d11-solution]: solutions/day11.py
251+
[d12-solution]: solutions/day12.py
252+
[d13-solution]: solutions/day13.py
253+
[d14-solution]: solutions/day14.py
254+
[d15-solution]: solutions/day15.py
255+
[d16-solution]: solutions/day16.py
256+
[d18-solution]: solutions/day18.py
257+
[d19-solution]: solutions/day19.py
258+
[d20-solution]: solutions/day20.py
259+
[d21-solution]: solutions/day21.py
260+
[d22-solution]: solutions/day22.py
261+
[d24-solution]: solutions/day24.py
262+
[d25-solution]: solutions/day25.py
263+
264+
[py-builtin-filter]: https://docs.python.org/3/library/functions.html#filter
265+
[py-builtin-next]: https://docs.python.org/3/library/functions.html#next
266+
[py-builtin-range]: https://docs.python.org/3/library/functions.html#range
267+
[py-str-isdigit]: https://docs.python.org/3/library/stdtypes.html#str.isdigic

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ Personal repository of [Advent of Code](#about-advent-of-code) solutions.
66
### Quick links
77

88
- **[How to run my solutions on your inputs][how-to-run]**
9-
- **AoC 2022: [walkthrough][2022-wal], [clean][2022-sol] / [original][2022-ori] solutions, complete [calendar][2022-cal] and [leaderboard][2022-lea]**
9+
- **AoC 2023: [walkthrough][2023-wal], [clean][2023-sol] / [original][2023-ori] solutions**
10+
- AoC 2022: [walkthrough][2022-wal], [clean][2022-sol] / [original][2022-ori] solutions, complete [calendar][2022-cal] and [leaderboard][2022-lea]
1011
- AoC 2021: [walkthrough][2021-wal], [clean][2021-sol] / [original][2021-ori] solutions, complete [calendar][2021-cal] and [leaderboard][2021-lea]
1112
- AoC 2020: [walkthrough][2020-wal], [clean][2020-sol] / [original][2020-ori] solutions, complete [calendar][2020-cal] and [leaderboard][2020-lea]
1213
- AoC 2019: [walkthrough][2019-wal], [clean][2019-sol] / [original][2019-ori] solutions, complete [calendar][2019-cal] and [leaderboard][2019-lea]
@@ -117,6 +118,10 @@ license, which you can find in the file
117118
*Copyright &copy; 2018-2023 Marco Bonelli.*
118119

119120

121+
[2023-wal]: 2023/README.md
122+
[2023-sol]: 2023/solutions
123+
[2023-ori]: 2023/original_solutions
124+
120125
[2022-wal]: 2022/README.md
121126
[2022-sol]: 2022/solutions
122127
[2022-ori]: 2022/original_solutions

0 commit comments

Comments
 (0)