In [39]:
import forge
from puzzle.puzzlepedia import puzzlepedia
puzzle = puzzlepedia.parse("""
model.disable_constraints()
model.disable_inference()
woman in {wA, wB, wC, wD, wE, wF, wG}
concealer in {bigger, burning, bye, enthusiastic, makeup, married, shapely}
(kinds, colors) in ({eye, lip}, {CA, ER, EU, FI, GR, HE, ID, LE, SH, SP, SE, TY, TH, VA})
eyes = [eyeCA, eyeER, eyeEU, eyeFI, eyeGR, eyeHE, eyeID, eyeLE, eyeSH, eyeSP, eyeSE, eyeVA]
lips = [lipCA, lipER, lipEU, lipFI, lipGR, lipHE, lipID, lipLE, lipSH, lipSP, lipSE, lipVA, lipTY, lipTH]
# Each woman has one of each.
for w in woman:
sum(w[c] for c in concealer) == 1
sum(w[e] for e in eyes) == 1
sum(w[l] for l in lips) == 1
# Each concealer is used once.
for c in concealer:
sum(c[w] for w in woman) == 1
# Each color is used once.
for a, b in zip(eyes, lips):
sum(w[a] + w[b] for w in woman) == 1
# 5: Someone has matching lipstick and eyeshadow first letter
# 6: Number of S lipsticks == number of T lipsticks
#12: None of the eyeshadows start with same letter
# E E S S S T T
# Lip: E E? S S T T
# Eye: E? S
# Conclusion: Lip has 2 Ss, 2 Ts.
sum(any(w[c] for w in woman) for c in [lipSH, lipSP, lipSE]) == 2
sum(any(w[c] for w in woman) for c in [lipTY, lipTH]) == 2
for w in woman:
w != eyeTY
w != eyeTH
#11: FI + ER combo exists
# Conclusion: Matching letter is an S, not E.
def color(dim, c):
return dim['eye' + c] | dim['lip' + c]
def color_vector(c):
return sum(color(w, c) * i for i, w in zip(range(8), woman))
def matched_vector(dim):
return sum(w[dim] * i for i, w in zip(range(8), woman))
def matching_color(w):
return (
(color(w, 'ER') + color(w, 'EU') == 2) +
((color(w, 'SH') + color(w, 'SP') + color(w, 'SE')) == 2) +
(color(w, 'TY') + color(w, 'TH') == 2)
)
# NB: Matching color must be an S. See conclusion above.
def matching_color(w):
return (color(w, 'SH') + color(w, 'SP') + color(w, 'SE')) == 2
def concealer_vector(c):
return sum(w[c] * i for i, w in zip(range(8), woman))
#1: LGTM.
wC.lipEU or wC.lipSH
#2: LGTM.
wC != married
wG != married
#3: LGTM.
#(color(wE, 'TY') and wE.enthusiastic) or (color(wF, 'TY') and wF.enthusiastic)
# NB: Ts are lip colors; see above.
(wE.lipTY and wE.enthusiastic) or (wF.lipTY and wF.enthusiastic)
#4: LGTM.
any(bigger[w] and (eyeSE[w] or eyeGR[w]) for w in woman)
#5: LGTM.
# Someone (not wC) wore first-letter-matching lip & eye
#not matching_color(wC)
matching_women = [wA, wB, wD, wE, wF, wG]
# Duplicates: ER, EU; SH, SP, SE; TY, TH
any(matching_color(w) for w in matching_women)
#6: LGTM.
# NB: Implemented via conclusions above.
#7: LGTM.
if any(lipSP[w] for w in woman):
color(wD, 'CA')
#8: LGTM.
#8a: Find bbq eyeshadow, shapely lipstick and assign GR xor ID.
# Eligible determined via GR | ID eligibility.
# B excluded by definition.
# C excluded because #1 says lip is either EU or SH (not GR).
# G excluded due to #15.
shapely_eligible = [wA, wD, wE, wF]
shapely_ineligible = [wB, wC, wG]
for w in shapely_eligible:
if w.shapely: (w.lipGR and wB.eyeID) or (w.lipID and wb.eyeGR)
#8b: (These two are different women.)
for w in shapely_ineligible:
w != shapely
#9: LGTM.
#9: TH first letter > EU first letter
matched_vector(lipTH) > color_vector('EU')
#10: TODO.
# CA woman and VA woman have matching concealers.
# Matching concealers: bigger, burning, bye; makeup, married
b_concealers = [bigger, burning, bye]
ca_is_b = any(color(w, 'CA') and any(w[c] for c in b_concealers) for w in woman)
va_is_b = any(color(w, 'VA') and any(w[c] for c in b_concealers) for w in woman)
ca_is_b == va_is_b
m_concealers = [makeup, married]
ca_is_m = any(color(w, 'CA') and any(w[c] for c in m_concealers) for w in woman)
va_is_m = any(color(w, 'VA') and any(w[c] for c in m_concealers) for w in woman)
ca_is_m == va_is_m
#11: LGTM.
#11: FI/ER woman exists
any((w.eyeFI and w.lipER) or (w.eyeER and w.lipFI) for w in woman)
#12: LGTM.
#12: None of the eyeshadows start with the same letter.
eye_letters = [
[eyeER, eyeEU],
[eyeSH, eyeSP, eyeSE],
]
# None of the eyeshadow colors use T; see deduction above.
# [eyeTY, eyeTH],
# Duplicates: eyeER, eyeEU; eyeSH, eyeSP, eyeSE; eyeTY, eyeTH
for eye_group in eye_letters:
sum(any(e[w] for e in eye_group) for w in woman) <= 1
#13: Exactly two women used eyeshadow, concealer, or lipstick that started with same name.
# wA: None
# wB: bigger, burning, bye
# wC: eyeCA, lipCA
# wD: None
# wE: enthusiastic, eyeER, lipER, eyeEU, lipEU
# wF: eyeFI, lipFI
# wG: eyeGR, lipGR
sum([
any([wB.bigger, wB.burning, wB.bye]),
any([wC.eyeCA, wC.lipCA]),
any([wE.enthusiastic, wE.eyeER, wE.lipER, wE.eyeEU, wE.lipEU]),
any([wF.eyeFI, wF.lipFI]),
any([wG.eyeGR, wF.lipGR]),
]) == 2
#14: LGTM.
wF != eyeHE
wF != lipHE
wF != eyeER
wF != lipER
#15: LGTM.
wG == lipLE
#16: LGTM.
# wX.makeup's name is 2+ before burning
matched_vector(burning) - matched_vector(makeup) > 1
model.grid()
#with init:
# debug(str(model))
""")
In [16]:
colors = 'CA, ER, EU, FI, GR, HE, ID, LE, SH, SP, SE, TY, TH, VA'.split(', ')
print(', lip'.join(colors))
In [ ]: