In [1]:
import forge
from puzzle.puzzlepedia import puzzlepedia

puzzle = puzzlepedia.parse("""
import itertools
plot in {A, B, C, D, E, F, G, H, I, J, K, L, M, N}
vegetable in {
  asparagus, broccoli, cabbage, carrots, cucumbers, green_beans, lettuce,
  peas, peppers, potatoes, radishes, squash, tomatoes, turnips,
}
name in {Beth, Charles, David, Frank, Jessica, Karen, Taylor}

neighbor_map = {
  A: [B, D, E],
  B: [A, C, E, F],
  C: [B, F],
  D: [A, E, G, J],
  E: [A, B, D, F, G, H],
  F: [B, C, E, H, I],
  G: [D, E, H, J, K, L],
  H: [E, F, G, L, M, I],
  I: [F, H, M, N],
  J: [D, G, K],
  K: [J, G, L],
  L: [G, H, K, M, N],
  M: [H, I, L, N],
  N: [I, L, M],
}
valid_pairs = []
pairs_with_neighbor_pairs = []

SUSPECTED_ASPARAGUS = H

with init:
  valid_pairs_seen = set()
  for center, neighbors in neighbor_map.items():
    for neighbor in neighbors:
      key = ''.join(sorted([str(center), str(neighbor)]))
      if key in valid_pairs_seen:
        continue
      valid_pairs_seen.add(key)
      valid_pairs.append([center, neighbor])
  for (pa, pa_neighbors), (pb, pb_neighbors) in itertools.combinations(neighbor_map.items(), 2):
    if pb.dimension_constraints() == SUSPECTED_ASPARAGUS.dimension_constraints():
      pa, pb = pb, pa
    if pa.dimension_constraints() != SUSPECTED_ASPARAGUS.dimension_constraints():
      continue
    shared_neighbors = set(pa_neighbors).intersection(set(pb_neighbors))
    for n1, n2 in itertools.combinations(shared_neighbors, 2):
      pairs_with_neighbor_pairs.append(((pa, pb), (n1, n2)))

def pair_border_plots(v1, v2, n1, n2):
  return sum([
    (pa[v1] and pb[v2] and (na[n1] or na[n2]) and (nb[n1] or nb[n2]))
    for (pa, pb), (na, nb) in pairs_with_neighbor_pairs
  ]) == 1

def pair_border_names(v1, v2, n1, n2):
  return sum(nx[v1] or nx[v2] or nx[n1] or nx[n2] for nx in name) == 4

def pair_border(v1, v2, n1, n2):
  return pair_border_names(v1, v2, n1, n2) and pair_border_plots(v1, v2, n1, n2)


#1: 2 plots pp.
# NB: No longer needed. Plots have been solved with only Charles and Frank free.
#for nx in [Charles, Frank]:
#  sum(nx[p1] and nx[p2] for p1, p2 in valid_pairs) == 1

#2, #3, #4: Establish valid plot->name assignments.
# There are only two valid solutions:
# plot |    name  plot |    name
#    A | Charles     A |   Frank
(A == Charles and E == Charles) or (A == Frank and E == Frank)
#    B |   Karen     B |   Karen
B == Karen
#    C |   Karen     C |   Karen
C == Karen
#    D |  Taylor     D |  Taylor
D == Taylor
#    E | Charles     E |   Frank
# NB: A and E constrained at same time above.
#    F | Jessica     F | Jessica
F == Jessica
#    G |  Taylor     G |  Taylor
G == Taylor
#    H |   Frank     H | Charles
(H == Frank and M == Frank) or (H == Charles and M == Charles)
#    I | Jessica     I | Jessica
I == Jessica
#    J |   David     J |   David
J == David
#    K |   David     K |   David
K == David
#    L |    Beth     L |    Beth
L == Beth
#    M |   Frank     M | Charles
# NB: H and M constrained at same time above.
#    N |    Beth     N |    Beth
N == Beth

#5: Corner plots.
for px in [A, C, J, N]:
  sum(px[vx] for vx in [broccoli, carrots, green_beans, radishes]) == 1

#6:
pair_border(asparagus, lettuce, squash, tomatoes)

#7:
pair_border(asparagus, peas, cucumbers, turnips)

#8:
pair_border(asparagus, squash, tomatoes, turnips)

#9:
pair_border(asparagus, turnips, cucumbers, squash)

#10:
for center, neighbors in neighbor_map.items():
  if center == peppers:
    sum(nx[green_beans] + nx[potatoes] for nx in neighbors) == 2
    all(nx != Frank for nx in neighbors)

#11:
# Jessica is in I and F.
((sum(nx[potatoes] + nx[radishes] for nx in neighbor_map[I]) == 2) or
 (sum(nx[potatoes] + nx[radishes] for nx in neighbor_map[F]) == 2))

#12:
# David is in J and K.
David != broccoli
David != lettuce
sum(nx[squash] + nx[tomatoes] for nx in [D, J, L]) == 0
""")

In [6]:
# Plot -> Name assignments.
# There appear to be two solutions (used above).
import forge
from puzzle.puzzlepedia import puzzlepedia

puzzle = puzzlepedia.parse("""
import itertools
plot in {A, B, C, D, E, F, G, H, I, J, K, L, M, N}
name in {Beth, Charles, David, Frank, Jessica, Karen, Taylor}

neighbor_map = {
  A: [B, D, E],
  B: [A, C, E, F],
  C: [B, F],
  D: [A, E, G, J],
  E: [A, B, D, F, G, H],
  F: [B, C, E, H, I],
  G: [D, E, H, J, K, L],
  H: [E, F, G, L, M, I],
  I: [F, H, M, N],
  J: [D, G, K],
  K: [J, G, L],
  L: [G, H, K, M, N],
  M: [H, I, L, N],
  N: [I, L, M],
}
valid_pairs = []
pairs_with_neighbor_pairs = []
with init:
  valid_pairs_seen = set()
  for center, neighbors in neighbor_map.items():
    for neighbor in neighbors:
      key = ''.join(sorted([str(center), str(neighbor)]))
      if key in valid_pairs_seen:
        continue
      valid_pairs_seen.add(key)
      valid_pairs.append([center, neighbor])
  for (pa, pa_neighbors), (pb, pb_neighbors) in itertools.combinations(neighbor_map.items(), 2):
    shared_neighbors = set(pa_neighbors).intersection(set(pb_neighbors))
    for n1, n2 in itertools.combinations(shared_neighbors, 2):
      pairs_with_neighbor_pairs.append(((pa, pb), (n1, n2)))

def pair_border(v1, v2, n1, n2):
  return sum(nx[v1] or nx[v2] or nx[n1] or nx[n2] for nx in name) == 4



#1: 2 plots pp.
for nx in name:
  sum(nx[p1] and nx[p2] for p1, p2 in valid_pairs) == 1

#2: E's borders.
sum(Taylor[px] for px in neighbor_map[E]) == 2
for other in [Frank, Karen, Jessica, Charles]:
  sum(other[px] for px in neighbor_map[E]) == 1

#3: G's borders.
sum(David[px] for px in neighbor_map[G]) == 2
for other in [Beth, Charles, Frank, Taylor]:
  sum(other[px] for px in neighbor_map[G]) == 1

#4: H's borders.
sum(Jessica[px] for px in neighbor_map[H]) == 2
for other in [Beth, Charles, Frank, Taylor]:
  sum(other[px] for px in neighbor_map[H]) == 1
""")


Widget Javascript not detected.  It may not be installed properly. Did you enable the widgetsnbextension? If not, then run "jupyter nbextension enable --py --sys-prefix widgetsnbextension"

In [ ]: