Enigmatic Code

Programming Enigma Puzzles

Puzzle 26: Some old men on The Island of Imperfection

From New Scientist #1077, 10th November 1977 [link]

There are three tribes on The Island of Imperfection, the Pukkas who always tell the trith, the Wotta-Woppas who never tell the truth, and the Shilli-Shallas who make statements which are alternately true and false or false and true.

This story deals with four men whom we shall call ABC, and D and there is at least one representative of each tribe among them.

They make statements in accordance with their tribal characteristics as follows:

A:
(1) My age is a multiple of six;
(2) My age is not the same as B‘s age;
(3) D belongs to a more truthful tribe than I do.

B:
(1) I am older than A;
(2) The ages of A and C are the same;
(3) D‘s age is a multiple of twelve.

C:
(1) I am older than A;
(2) D‘s age is even;
(3) B‘s second remark is true.

D:
(1) B is three times as old as C;
(2) My age is twelve more than A‘s age;
(3) C‘s age is a multiple of thirteen.

People tend to live for a long time on this wonderful island, but none of the four with whom this story deals is older than 105.

Find the tribes to which each man belongs, and their ages.

[puzzle26]

One response to “Puzzle 26: Some old men on The Island of Imperfection

  1. Jim Randell 27 March 2019 at 6:38 am

    I think there is an issue with ages. For example if X and Y were born on the same day, it is possible for X to say “I am older than Y“, and also for Z to say “X and Y are the same age”.

    Also if they were born a couple of days apart, on the day in between they would have different numerical ages, but Z could still reasonably say “X and Y are the same age”.

    So here is my variation (taking the description of the tribes as read):

    On the Island of Imperfection they always count their eggs before they are hatched. Indeed the number of unhatched eggs is so important everybody knows exactly how many everybody else has.

    Between them A, B, C, and D represent all three tribes.

    A:

    (1) My number of eggs is a multiple of 6;
    (2) B has a different number of eggs to me;
    (3) D belongs to a more truthful tribe than I do.

    B:

    (1) I have more eggs than A;
    (2) A and C have exactly the same number of eggs;
    (3) D’s number of eggs is a multiple of twelve.

    C:

    (1) I have more eggs than A;
    (2) D has an even number of eggs;
    (3) B’s second remark is true.

    D:

    (1) B has exactly three times as many eggs as C;
    (2) I have exactly 1 dozen more eggs than A;
    (3) C’s number of eggs is a multiple of thirteen.

    Everyone has at least 12 eggs, bit no-one has more than 105.

    Find the tribes to which each belongs, and how many eggs they have.

    We can then proceed to solve this variation.

    This Python program generates potential sets of numbers depending on who is telling the truth. It then examines the possible assignments of tribes, and chooses an appropriate set of numbers to consider. But it’s not quick, it runs in 16.7s (but there is a way to speed this up – see below).

    Run: [ @repl.it ]

    from itertools import product
    from enigma import tuples, irange, divc, Delay, printf
    
    # Pukka - always tells the truth
    def P(ss):
      return all(ss)
    
    # Wotta-Woppa - never tells the truth
    def W(ss):
      return not any(ss)
    
    # Shilli-Shalla - alternates between true and false
    def S(ss):
      return all(a ^ b for (a, b) in tuples(ss, 2))
    
    # truthfulness (how many true statements out of every 2)
    T = { P: 2, W: 0, S: 1 }
    
    # min, max number of eggs
    (m, M) = (14, 105)
    
    # generate numbers according to a truth-teller
    
    # if A is Pukka: A % 6 = 0, B != A
    def eggsA():
      for A in irange(6 * divc(m, 6), M, step=6):
        for B in irange(m, M):
          if B == A: continue
          for (C, D) in product(irange(m, M), repeat=2):
            yield (A, B, C, D)
    
    # if B is Pukka: B > A, C = A, D % 12 = 0
    def eggsB():
      for D in irange(12 * divc(m, 12), M, step=12):
        for A in irange(m, M):
          for B in irange(A + 1, M):
            yield (A, B, A, D)
    
    # if C is Pukka: C > A, D % 2 = 0, A = C
    # this is not possible
    eggsC = lambda: []
      
    # if D is Pukka: B = 3C, D = A + 12, C % 13 = 0
    def eggsD():
      for C in irange(13 * divc(m, 13), M, step=13):
        B = 3 * C
        if B > M: break
        for A in irange(m, M):
          D = A + 12
          if D > M: break
          yield (A, B, C, D)
    
    # collect the potential sets of eggs
    eggs = (
      Delay(lambda: list(eggsA())),
      Delay(lambda: list(eggsB())),
      Delay(lambda: list(eggsC())),
      Delay(lambda: list(eggsD())),
    )
    
    # consider the permutations of the tribes
    for ts in product((P, W, S), repeat=4):
    
      # there is 1 representative from each tribe
      if not(all(t in ts for t in (P, W, S))): continue
      (tA, tB, tC, tD) = ts
    
      # neither A nor C can be Pukka [this can be determined by analysis]
      if tA == P or tC == P: continue
    
      # choose the smallest set of eggs, defined by a Pukka
      eggsP = min((x.value for (x, t) in zip(eggs, ts) if t == P), key=len)
    
      # consider eggs
      for (nA, nB, nC, nD) in eggsP:
    
        # A's statements
        if not tA([
          # "A is a multiple of 6"
          nA % 6 == 0,
          # "A is different to B"
          nA != nB,
          # "D belongs to a more truthful tribe than I do"
          T[tD] > T[tA],
        ]): continue
    
        # B's statements
        if not tB([
          # "B > A"
          nB > nA,
          # "A = C"
          nA == nC,
          # "D is a multiple of 12"
          nD % 12 == 0,
        ]): continue
    
        # C's statements
        if not tC([
          # "C > A"
          nC > nA,
          # "D is even"
          nD % 2 == 0,
          # "B's second remark [A = C] is true"
          nA == nC,
        ]): continue
    
        # D's statements
        if not tD([
          # "B = 3C"
          nB == 3 * nC,
          # "D = A + 12"
          nD == nA + 12,
          # "C is a multiple of 13"
          nC % 13 == 0,
        ]): continue
    
        printf("A={tA.__name__}:{nA}, B={tB.__name__}:{nB}, C={tC.__name__}:{nC} D={tD.__name__}:{nD}")
    

    Solution: A is a Shilla-Shalla, with 78 eggs; B is a Wotta-Woppa, with 78 eggs; C is a Shilla-Shalla, with 26 eggs; D is a Pukka, with 90 eggs.

    (Using ages, with the issue described above we can get multiple solutions. For example:

    A is Shilli-Shalla, age 105; B is Shilli-Shalla, age 104; C is Pukka, age 105; D is Wotta-Woppa, age 104

    This would provide a viable solution if C born on the same day as A, but slightly earlier in the day).

    We can do a bit of analysis to speed the program up:

    We can see that C cannot be a Pukka (he says: (1) “C > A” and (3) “A = C”). Also A cannot be a Pukka (he says: (3) “D is more truthful than me”).

    If we remove these cases from consideration (see line 69) the run time is reduced to a more acceptable 772ms.

    The program is written to use delayed evaluation provided by the [[ Delay() ]] class in the enigma.py library (see my comment on Enigma 371), and it turns out that the [[ eggsA() ]] function is never called (it generates over 11 million potential sets of numbers, so we save a lot of time by not generating and considering them).

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: