Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 00a4370

Browse files
Update 5 and 6
1 parent 4c21bf7 commit 00a4370

File tree

4 files changed

+124
-57
lines changed

4 files changed

+124
-57
lines changed

‎.gemini/commands/aoc/doc.toml‎

4 Bytes
Binary file not shown.

‎docs/2016/5.md‎

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ tags:
99
link: https://en.wikipedia.org/wiki/MD5
1010
- name: hashlib
1111
link: https://docs.python.org/3/library/hashlib.html
12-
- name: String Manipulation
13-
link: /python/string_manipulation # Assuming an internal link for string manipulation
14-
- name: Looping
15-
link: /python/looping # Assuming an internal link for looping
1612
---
1713

14+
## Page Navigation
15+
16+
- [Problem Intro](#problem-intro)
17+
- [Part 1](#part-1)
18+
- [Part 2](#part-2)
19+
- [Results](#results)
20+
1821
## Problem Intro
1922

2023
In this Advent of Code puzzle, we are tasked with finding an eight-character password for a security door. The password is generated by repeatedly hashing a combination of a "Door ID" (our puzzle input) and an incrementing integer (nonce). A character is revealed if the hexadecimal representation of the MD5 hash starts with five zeroes.
@@ -24,7 +27,9 @@ For example, if the Door ID is `abc`:
2427

2528
Our goal is to find this eight-character password.
2629

27-
## Part 1: Simple Password Construction
30+
## Part 1
31+
32+
**Given the actual Door ID, what is the password?**
2833

2934
The first part requires us to construct the password by taking the sixth character of each valid hash (those starting with five zeroes) in the order they are found.
3035

@@ -63,7 +68,9 @@ def main():
6368
# ...
6469
```
6570

66-
## Part 2: Positional Password Construction
71+
## Part 2
72+
73+
**Given the actual Door ID and a new hashing method, what is the password??**
6774

6875
The second part introduces a twist: the password characters are placed at specific positions. The sixth character of a valid hash now indicates the *position* (0-7) in the password, and the seventh character is the actual character to be placed at that position. If a position is already filled, we ignore subsequent attempts to fill it.
6976

@@ -108,6 +115,74 @@ def main():
108115
# ...
109116
```
110117

111-
## Conclusion
118+
## Results
119+
120+
The final code is as follows:
121+
122+
```python
123+
import logging
124+
import os
125+
import time
126+
import hashlib
127+
128+
# pylint: disable=logging-fstring-interpolation
129+
130+
SCRIPT_DIR = os.path.dirname(__file__)
131+
INPUT_FILE = "input/input.txt"
132+
SAMPLE_INPUT_FILE = "input/sample_input.txt"
133+
134+
def main():
135+
logging.basicConfig(level=logging.INFO, format="%(asctime)s:%(levelname)s: %(message)s")
136+
137+
# input_file = os.path.join(SCRIPT_DIR, SAMPLE_INPUT_FILE)
138+
input_file = os.path.join(SCRIPT_DIR, INPUT_FILE)
139+
with open(input_file, mode="rt") as f:
140+
door_id = f.read()
141+
142+
logging.info(f"Door ID: {door_id}")
143+
144+
# Part 1
145+
pwd = ""
146+
nonce = 0
147+
while len(pwd) < 8:
148+
data = door_id + str(nonce)
149+
# Create byte equivalent of input string, then generate md5 hexdigest.
150+
hash_hex = hashlib.md5(data.encode()).hexdigest()
151+
152+
if hash_hex.startswith("00000"):
153+
pwd = pwd + hash_hex[5]
154+
logging.debug(f"Found {hash_hex} with data {data}.")
155+
logging.info(f"Pwd is: {pwd}")
156+
157+
nonce += 1
158+
159+
# Part 2
160+
pwd = "________"
161+
nonce = 0
162+
while "_" in pwd:
163+
data = door_id + str(nonce)
164+
# Create byte equivalent of input string, then generate md5 hexdigest.
165+
hash_hex = hashlib.md5(data.encode()).hexdigest()
166+
167+
if hash_hex.startswith("00000"):
168+
position = hash_hex[5]
169+
if position in "01234567":
170+
position = int(position)
171+
# Check we haven't already filled this position
172+
if pwd[position] == "_":
173+
pwd = pwd[:position] + hash_hex[6] + pwd[position+1:]
174+
175+
logging.debug(f"Found {hash_hex} with data {data}.")
176+
logging.info(f"{pwd}")
177+
178+
nonce += 1
179+
180+
181+
if __name__ == "__main__":
182+
t1 = time.perf_counter()
183+
main()
184+
t2 = time.perf_counter()
185+
print(f"Execution time: {t2 - t1:0.4f} seconds")
186+
```
112187

113-
This puzzle is a straightforward application of MD5 hashing and string manipulation. The core challenge lies in efficiently generating and checking a large number of hashes. Part 2 adds a layer of complexity by introducing positional requirements and the need to handle already-filled positions, making it a good exercise in careful state management during iteration. The `hashlib` module in Python provides a convenient way to perform the necessary cryptographic hashing operations.
188+
This puzzle is a straightforward application of MD5 hashing and string manipulation. The core challenge lies in efficiently generating and checking a large number of hashes. Part 2 adds a layer of complexity by introducing positional requirements and the need to handle already-filled positions, making it a good exercise in careful state management during iteration. The `hashlib` module in Python provides a convenient way to perform the necessary cryptographic hashing operations.

‎docs/2016/6.md‎

Lines changed: 41 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,21 @@
22
day: 6
33
title: Day 6
44
main_img:
5-
name: Signals and Noise
5+
name: "Signals and Noise"
66
link: /assets/images/2016-06.png
7-
tags:
7+
tags:
88
- name: collections.Counter
99
link: https://docs.python.org/3/library/collections.html#collections.Counter
1010
- name: zip
11-
link: https://docs.python.org/3/library/functions.html#zip
12-
- name: String Manipulation
13-
link: /python/string_manipulation
14-
- name: Logging
15-
link: /python/logging
11+
link: https://realpython.com/python-zip-function/
12+
- name: lambda
13+
link: /python/functions#lambda-functions
1614
---
17-
1815
## Problem Intro
1916

20-
The year is 2016, and we're helping Santa's communication system. Due to a local electromagnetic anomaly, messages are being corrupted. We receive a series of messages, but each character in the message has been sent many times, and the most frequent character in each position is the correct one.
17+
We're receiving a garbled message from Santa. It seems to be a simple repetition code, where the same message is sent over and over. Our job is to decode it.
2118

22-
Our input consists of a list of corrupted messages, one per line. For example:
19+
The input data is a series of lines, each representing a received message. For example:
2320

2421
```text
2522
eedadn
@@ -33,26 +30,26 @@ rasrtv
3330
nssdts
3431
ntnada
3532
svetve
36-
tesnvf
33+
tesnvt
3734
vntsnd
3835
vrdear
3936
dvrsen
4037
enarar
4138
```
4239

43-
## Part 1
40+
To decode the message, we need to find the most common character in each column.
4441

45-
**Given the corrupted messages, what is the error-corrected version of the message?**
42+
## Part 1
4643

47-
To solve this, we need to determine the most frequent character in each column of the input data. The problem can be broken down into these steps:
44+
**Given the recording in your puzzle input, what is the error-corrected version of the message being sent?**
4845

49-
1. Read all the lines of the input data.
50-
2. "Transpose" the data so that we can easily access all characters in a given column.
51-
3. For each column, count the occurrences of each character.
52-
4. Identify the most frequent character in that column.
53-
5. Combine these characters to form the error-corrected message.
46+
My strategy is to:
47+
1. Read all the lines of the input file.
48+
2. Transpose the data, so that columns become rows.
49+
3. For each new "row" (which was a column), find the most common character.
50+
4. Concatenate these characters to form the message.
5451

55-
Here's the core logic from the solution:
52+
Here's the Python code that implements this:
5653

5754
```python
5855
import logging
@@ -62,28 +59,27 @@ from collections import Counter
6259

6360
SCRIPT_DIR = os.path.dirname(__file__)
6461
INPUT_FILE = "input/input.txt"
62+
SAMPLE_INPUT_FILE = "input/sample_input.txt"
6563

6664
def main():
67-
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s:%(levelname)s:\t%(message)s")
65+
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s:%(levelname)s:%(message)s")
6866

67+
# input_file = os.path.join(SCRIPT_DIR, SAMPLE_INPUT_FILE)
6968
input_file = os.path.join(SCRIPT_DIR, INPUT_FILE)
7069
with open(input_file, mode="rt") as f:
7170
data = f.read().splitlines()
7271

73-
# First, we need to transpose columns to rows.
74-
# zip(*data) takes an iterable of iterables and aggregates the elements
75-
# from each of the iterables. For example, if data = ['abc', 'def'],
76-
# then zip(*data) would produce [('a', 'd'), ('b', 'e'), ('c', 'f')].
72+
# First, we need to transpose columns to rows
7773
transposed = list(zip(*data))
7874

7975
most_common_chars = [] # Part 1
80-
76+
8177
for line in transposed:
8278
char_counts = Counter(line)
83-
# Counter.most_common(1) returns a list of the 1 most common elements
84-
# and their counts, e.g., [('e', 5)]. We want just the character.
85-
most_common_chars.append(char_counts.most_common(1)[0][0])
79+
# Get the most frequent char
80+
most_common_chars.append(max(char_counts.items(), key=lambda x: x[1])[0])
8681

82+
# Convert to str representation
8783
most_common = "".join(str(char) for char in most_common_chars)
8884

8985
logging.info(f"Part 1 message: {most_common}")
@@ -95,44 +91,40 @@ if __name__ == "__main__":
9591
print(f"Execution time: {t2 - t1:0.4f} seconds")
9692
```
9793

98-
The key steps here are:
94+
The key parts of the code are:
9995

100-
- `zip(*data)`: This elegant Python idiom is used to transpose the rows and columns. If `data` is a list of strings (each string representing a row), `zip(*data)` treats each string as an iterable and groups the characters at the same index across all strings. The `list()` conversion then turns this into a list of tuples, where each tuple represents a column.
101-
- `collections.Counter`: For each transposed "column" (which is now a tuple of characters), `Counter` efficiently counts the occurrences of each character.
102-
- `char_counts.most_common(1)[0][0]`: This retrieves the most common character. `most_common(1)` returns a list of the single most common element and its count (e.g., `[('e', 5)]`). We then access the character itself using `[0][0]`.
96+
- `zip(*data)`: This is a neat trick to transpose a list of lists (or in this case, a list of strings). The `*` operator unpacks the `data` list, so each string is passed as a separate argument to `zip`. `zip`then aggregates the elements from each of the iterables.
97+
- `collections.Counter`: This is a specialized dictionary subclass for counting hashable objects. It's perfect for this task.
98+
- `max(char_counts.items(), key=lambda x: x[1])[0]`: This finds the item in the `Counter` with the highest count. `char_counts.items()` gives us `(character, count)` pairs. The `key=lambda x: x[1]` tells `max` to use the count (the second element of the tuple) for comparison. Finally, `[0]` gets the character from the `(character, count)` tuple.
10399

104100
## Part 2
105101

106-
**As a second part of the communication system, we also need to find the original message if the communication system uses a modified repetition code where the character that appears *least* frequently in each positionis the correct one.**
102+
**Now, find the least common character in each position. What is the modified message?**
107103

108-
Part 2 is a slight variation of Part 1. Instead of finding the *most* frequent character, we need to find the *least* frequent character in each column. The `collections.Counter` object doesn't have a direct `least_common` method, but we can achieve this by sorting the items by their counts and picking the first one, or by using `min` with a custom key.
104+
This is a simple modification of Part 1. Instead of finding the `max`character count, we need to find the `min`.
109105

110-
Here's how the solution extends to handle Part 2:
106+
Here's the updated code within the `main` function:
111107

112108
```python
113-
# ... (previous code for reading data and transposing)
114-
115-
most_common_chars = [] # Part 1
116109
least_common_chars = [] # Part 2
117110

118111
for line in transposed:
119112
char_counts = Counter(line)
120-
# Get the most frequent char (Part 1)
121-
most_common_chars.append(char_counts.most_common(1)[0][0])
122-
123-
# Get the least frequent char (Part 2)
124-
# We find the minimum count using a lambda function as the key for min()
113+
# Get the least frequent char
125114
least_common_chars.append(min(char_counts.items(), key=lambda x: x[1])[0])
126115

127116
# Convert to str representation
128117
least_common = "".join(str(char) for char in least_common_chars)
129-
most_common = "".join(str(char) for char in most_common_chars)
130118

131-
logging.info(f"Part 1 message: {most_common}")
132119
logging.info(f"Part 2 message: {least_common}")
133120
```
134121

135-
The key change for Part 2 is the line:
136-
- `least_common_chars.append(min(char_counts.items(), key=lambda x: x[1])[0])`: This finds the character with the minimum count. `char_counts.items()` returns key-value pairs (character, count). The `key=lambda x: x[1]` tells `min()` to compare these pairs based on their second element (the count). We then extract the character (`[0]`) from the resulting `(character, count)` tuple.
122+
The only change is using `min()` instead of `max()`.
137123

138-
This approach efficiently solves both parts of the puzzle by leveraging Python's built-in `zip` function for transposition and `collections.Counter` for frequency analysis.
124+
The final output looks like this:
125+
126+
```text
127+
20:39:29:INFO: Part 1 message: zcreqgiv
128+
20:39:29:INFO: Part 2 message: pljvorrk
129+
Execution time: 0.0010 seconds
130+
```

‎docs/assets/images/2016-06.png‎

66.6 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /