Santa needs help figuring out which strings in his text file are naughty or nice. There are different rules for part 1 and 2. So the main program just calls the function is_nice1 and is_nice2 for each line of the input:
nice_1=0
nice_2=0
with open(INPUTFILE) as inputfile:
for line in inputfile:
if is_nice_1(line.rstrip()): nice_1+=1
if is_nice_2(line.rstrip()): nice_2+=1
print("Number of nice lines (version 1): ", nice_1)
print("Number of nice lines (version 2): ", nice_2)
Nice rules
The easiest thing to check is rule 3 of part 1: It does not contain the strings ab, cd, pq, or xy, even if they are part of one of the other requirements. All we need is a list of naughty strings. We can then iterate over it and check the input string:
for pair in NAUGHTYPAIRS:
if pair in chstring:
return False
Then, there are regular expressions. Personally, I don't like to construct complicated regexps, they are prone to bugs. These are not so bad yet. All are precompiled and checked the same way:
if not rx1.search(chstring):
return False
and the regexps are:
rx1=re.compile(r"(\w)\1")
rx2=re.compile(r'(\w)\w\1')
rx3=re.compile(r'(\w\w)\w*\1')
Here's the meanining:
- rx1 - \w means any letter, in parentheses means remember it, \1 mean repeat the first parentheses, so combined it means a pair of letters
- rx2 - (\w) and \1 has the same meaning as in rx1 and there's another letter in between
- rx3 - (\w\w) is a 2-letter group, \1 is the same group, repeated and there's \w* in between, meaning any number of letters (including 0)
A slightly more interesting case is rule 1 of part 1: It contains at least three vowels (aeiou only). Interesting, because it can be solved with a useful Python class called Counter. It can count occurences of all characters in the string. We then need to check vowelcounter[letter] for letter in VOWELS (wow, Python code is exactly the same as English explanation!) and sum it:
VOWELS=("a", "e", "i", "o", "u")
vowelcounter=Counter(chstring)
vowelcount = sum(vowelcounter[letter] for letter in VOWELS)
if vowelcount<3:
return False
Full code below
#!/usr/bin/python3
from collections import Counter
import re
INPUTFILE="05-input.txt"
NAUGHTYPAIRS=("ab", "cd", "pq", "xy")
VOWELS=("a", "e", "i", "o", "u")
rx1=re.compile(r"(\w)\1")
rx2=re.compile(r'(\w)\w\1')
rx3=re.compile(r'(\w\w)\w*\1')
def is_nice_1(chstring: str) -> bool:
for pair in NAUGHTYPAIRS:
# condition: doesn't contain naughty pair
if pair in chstring:
#print ("naughty pair: ",chstring)
return False
# condition: at least 3 vowels
vowelcounter=Counter(chstring)
vowelcount = sum(vowelcounter[letter] for letter in VOWELS)
if vowelcount<3:
#print (f"less than 3 vowels ({vowelcount}): {chstring}")
return False
# condition: letter appears 2 times in a row
if not rx1.search(chstring):
#print ("no repeated character: ", chstring)
return False
#print("nice: ", chstring)
return True
def is_nice_2(chstring: str) ->bool:
# condition: contains a letter which repeats with another letter between (xyx)
if not rx2.search(chstring):
print ("no (xyx): ", chstring)
return False
if not rx3.search(chstring):
print ("no repeated 2-char group: ", chstring)
return False
print("nice: ", chstring)
return True
nice_1=0
nice_2=0
with open(INPUTFILE) as inputfile:
for line in inputfile:
if is_nice_1(line.rstrip()): nice_1+=1
if is_nice_2(line.rstrip()): nice_2+=1
print("Number of nice lines (version 1): ", nice_1)
print("Number of nice lines (version 2): ", nice_2)