···11import fileinput
22-from string import maketrans
33-from itertools import permutations
22+from collections import Counter
435465UNIQUES = {
···2322 9: 'abcdfg',
2423}
25242626-SEGMENTS_MAP = {v: str(k) for k, v in DIGIT_MAP.items()}
2525+REVERSE_MAP = {v: k for k, v in DIGIT_MAP.items()}
27262828-VALID_SEGMENTS = set(v for v in DIGIT_MAP.values())
2727+# a/c and d/g can both be diambiguated by 4,
2828+# who has a unique segument count, and also
2929+# contains c & d but not a & g.
3030+SEGMENT_FREQ = {
3131+ # 'a': 8,
3232+ 'b': 6,
3333+ # 'c': 8,
3434+ # 'd': 7,
3535+ 'e': 4,
3636+ 'f': 9,
3737+ # 'g': 7,
3838+}
29393030-ALL_SEGMENTS = 'abcdefg'
4040+REVERSE_FREQ = {v: k for k, v in SEGMENT_FREQ.items()}
31413242part_1 = 0
3343part_2 = 0
···4151 part_1 += 1
42524353 # Solve part 2
4444- for perm in permutations('abcdefg', 7):
4545- table = maketrans(ALL_SEGMENTS, perm)
5454+ for word in input.split():
5555+ if len(word) == 4:
5656+ four_segments = word
5757+5858+ frequencies = Counter(input.replace(' ', ''))
5959+6060+ print frequencies.most_common()
6161+ # break
46624747- for word in input.split():
4848- new_word = word.translate(table)
4949- new_word = ''.join(sorted(new_word))
6363+ mapping = {}
6464+ for segment, count in frequencies.most_common():
6565+ if count in REVERSE_FREQ:
6666+ mapping[segment] = REVERSE_FREQ[count]
6767+ elif count == 7:
6868+ if segment in four_segments:
6969+ mapping[segment] = 'd'
7070+ else:
7171+ mapping[segment] = 'g'
7272+ elif count == 8:
7373+ if segment in four_segments:
7474+ mapping[segment] = 'c'
7575+ else:
7676+ mapping[segment] = 'a'
50775151- if new_word not in VALID_SEGMENTS:
5252- break
5353- else:
5454- ans = ''
5555- for word in output.split():
5656- new_word = word.translate(table)
5757- new_word = ''.join(sorted(new_word))
5858- ans += SEGMENTS_MAP[new_word]
7878+ code = ''
7979+ for word in output.split():
8080+ new_word = ''.join(sorted(mapping[l] for l in word))
8181+ code += str(REVERSE_MAP[new_word])
59826060- part_2 += int(ans)
8383+ part_2 += int(code)
618462856386print "Part 1:", part_1
+59
2021/day09.py
···11+import fileinput
22+from collections import Counter, defaultdict, deque, namedtuple # NOQA
33+from utils import Point, mul
44+55+# Read in problem input.
66+WIDTH = HEIGHT = 0
77+BOARD = {}
88+99+for y, line in enumerate(fileinput.input()):
1010+ line = line.strip()
1111+ for x, c in enumerate(line):
1212+ BOARD[Point(x, y)] = int(c)
1313+ WIDTH = x + 1
1414+1515+ HEIGHT = y + 1
1616+1717+1818+LOW_POINTS = set()
1919+part_1 = 0
2020+2121+# Solve part 1.
2222+for y in range(HEIGHT):
2323+ for x in range(WIDTH):
2424+ p = Point(x, y)
2525+ val = BOARD[p]
2626+2727+ for n in p.neighbours_4():
2828+ if n in BOARD:
2929+ if BOARD[n] <= val:
3030+ break
3131+ else:
3232+ LOW_POINTS.add(p)
3333+ part_1 += (val + 1)
3434+3535+print "Part 1:", part_1
3636+3737+3838+# Solve part 2.
3939+def compute_basin_size(board, start):
4040+ """Return the set of all explored points from a given point."""
4141+ seen = set()
4242+4343+ def _dfs(node):
4444+ val = board[node]
4545+ seen.add(node)
4646+ for n in node.neighbours_4():
4747+ if n in board and n not in seen:
4848+ if val <= board[n] and board[n] != 9:
4949+ _dfs(n)
5050+5151+ _dfs(start)
5252+ return len(seen)
5353+5454+BASIN_SIZES = {}
5555+for p in LOW_POINTS:
5656+ BASIN_SIZES[p] = compute_basin_size(BOARD, p)
5757+5858+print "Part 2:", mul(sorted(BASIN_SIZES.values(), reverse=True)[:3])
5959+