First part
This puzzle is a little more complicated than usual. I did not take too long to find a solution that would fit the examples,
def get_three_sections(
line: str
) -> list[str, str, str]:
LEFT_BRACKET = line.index('[')
RIGHT_BRACKET = line.index(']')
return [line[:LEFT_BRACKET],
line[LEFT_BRACKET + 1:RIGHT_BRACKET],
line[RIGHT_BRACKET + 1:]]
def get_all_substrings_from_section(s: str):
return list(map(lambda i: s[i:i + 4],
range(0, len(s) - 3)))
def substring_is_abba(s: str):
return (s[0] == s[3] and
s[1] == s[2] and
s[0] != s[1])
def abba_exists(section: str) -> bool:
return any(map(substring_is_abba,
get_all_substrings_from_section(section)))
def has_tls(line: str):
left, middle, right = get_three_sections(line)
return not abba_exists(middle) and \
(abba_exists(left) or \
abba_exists(right))
print(list(map(has_tls,
EXAMPLE.splitlines())))
but, of course, I overlooked the proper format of the input, which is of the form (xxx[yyy])+xxx
; the xxx[yyy]
blocks appear at least once, and then there's surely one last block outside of square brackets. This means that not only the get_three_sections
function doesn't work anymore, I will have to abstract the code a little further to make it work for any given length of input. This is not necessarily a big problem, but I'm also thinking of further optimising the code; however, I'll try to progress as much as I can without committing to early optimisations, which can bring problems very early. I will, however, still try to do this functionally.
def get_all_substrings_from_section(
s: str
) -> list[str]:
return list(map(lambda i: s[i:i + 4],
range(0, len(s) - 3)))
def substring_is_abba(s: str) -> bool:
return (s[0] == s[3] and
s[1] == s[2] and
s[0] != s[1])
def produce_list_of_sections(
line: str
) -> list[str]:
"""given a line of the form ([a-z]+\[[a-z]+\])+[a-z]+,
returns a list of all strings outside and inside
the square brackets, in the pattern
[inside, outside,...,inside]"""
return re.split(r'\[|\]', line)
def abba_exists_in_section(section: str) -> bool:
return any(map(substring_is_abba,
get_all_substrings_from_section(section)))
def has_tls(line: str):
all_sections = produce_list_of_sections(line)
supernet = all_sections[::2]
hypernet = all_sections[1::2]
return all(map(lambda sec: not abba_exists_in_section(sec),
supernet)) and \
any(map(lambda sec: abba_exists_in_section(sec),
hypernet))
print(len(list(filter(lambda x: x is True,
map(has_tls, INPUT)))))
I knew there would be a better way to do the final print
instruction, and Phind gave me a hint.
print(sum(has_tls(x) for x in INPUT))
Much better, in fact, and the rest of the advice came for free:
Use generator expressions
(x for x in list)
instead of list comprehension when you don't need the list itself, but want to process each element in the list.
Second part
The second part was a little trickier, but I'm proud of my approach and the results. I had some trouble halfway,
def generate_all_aba(net: list):
substrings = list(map(get_all_substrings_from_section,
(section for section in net)))
return filter(lambda x: substring_is_aba(x),
substrings
)
(I was assigning variables so they could get printed for debugging). Here, the map
call was creating a two-dimensional array; the moment this got fixed, all of the rest came in place very quickly.
def produce_list_of_sections(
line: str
) -> list[str]:
"""given a line of the form ([a-z]+\[[a-z]+\])+[a-z]+,
returns a list of all strings outside and inside
the square brackets, in the pattern
[inside, outside,...,inside]"""
return re.split(r'\[|\]', line)
def get_all_substrings_from_section(
s: str
) -> list[str]:
return map(lambda i: s[i:i + 3],
range(0, len(s) - 2))
def substring_is_aba(s: str) -> bool:
return s[0] == s[2]
def generate_all_aba(net: list):
return (s for s in chain.from_iterable(get_all_substrings_from_section(section)
for section in net)
if substring_is_aba(s))
def match_aba_to_bab(aba: str, bab: str) -> bool:
return aba[0] == bab[1] and bab[0] == aba[1]
def has_ssl(line: str):
all_sections = produce_list_of_sections(line)
supernet = all_sections[::2]
hypernet = all_sections[1::2]
return any(map(lambda x: match_aba_to_bab(*x),
product(generate_all_aba(hypernet),
generate_all_aba(supernet))))
print(sum(has_ssl(x) for x in INPUT))
This was nice. Tools like Phind are really invaluable; I feel like I'm learning so much over this process. I start by reading the exercise, then sketching some approaches on paper – more often than not, this will already trigger some debugging because I realise there is a mismatch with two functions, or something of that kind – and then, I code. Phind comes in handy from time to time if I have any specific issue I'm not understanding. Programming is going to change a lot in the forthcoming years.