Tagged: by: Eric Emmet Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 6:57 am on 27 August 2023 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 930: All scores different 

    From The Sunday Times, 18th May 1980 [link]

    The great thing about the new football method is that goals count whoever wins the match. With luck, this will create more excitement, and reward skills better than at present, for even a side losing heavily will have some incentive to attack.

    In this new method 10 points are given for a win, five points to each side for a draw, and one point for each goal scored.

    In a recent competition between four teams — A, B, C and D — who played each other once, the points were as follows:

    A = 25
    B = 34
    C = 6
    D = 20

    Each side scored at least one goal in each match, and — rather interestingly — the score was different in every match.

    Find the score in each match.

    This puzzle is included in the book The Sunday Times Book of Brainteasers (1994). The puzzle text above is taken from the book.

    The setter is given as “the late Eric Emmet”, and it is noted in the 1994 book that Eric Emmet had died by the time this puzzle was published.

    Eric Emmet also contributed the Puzzle series of puzzles to New Scientist, as well as several Enigma puzzles (including puzzles published as late as 1991).

    [teaser930]

     
    • Jim Randell's avatar

      Jim Randell 6:57 am on 27 August 2023 Permalink | Reply

      See also: Enigma 599.

      The total number of points awarded are: 25 + 34 + 6 + 20 = 85.

      There are 6 matches in the tournament, and 10 points are awarded depending on the match outcomes, accounting for 60 points in total.

      So there are 25 goals scored. Each side scored at least one goal in each match, so there are only 13 goals unaccounted for.

      In order to get different scores in each match we need to allow at least 4, but no more than 6 goals to be scored (per team) in any match.

      And we find a solution with up to 4 goals scored (per team) in a match.

      The following run file executes in 79ms. (Internal runtime of the generated program is 19ms).

      Run: [ @replit ]

      #! python3 -m enigma -rr
      
      # matches are:
      #
      #  A vs B = E - F
      #  A vs C = G - H
      #  A vs D = I - J
      #  B vs C = K - L
      #  B vs D = M - N
      #  C vs D = P - Q
      
      SubstitutedExpression
      
      --distinct=""
      --digits="1-4"
      
      # points for X in match X vs Y, where the score is x - y
      --code="pts = lambda x, y: compare(x, y, (0, 5, 10)) + x"
      
      # points for A, B, C, D
      "pts(E, F) + pts(G, H) + pts(I, J) == 25"
      "pts(F, E) + pts(K, L) + pts(M, N) == 34"
      "pts(H, G) + pts(L, K) + pts(P, Q) ==  6"
      "pts(J, I) + pts(N, M) + pts(Q, P) == 20"
      
      # all matches have different scores
      "seq_all_different(ordered(x, y) for (x, y) in [(E, F), (G, H), (I, J), (K, L), (M, N), (P, Q)])"
      
      # output match scores
      --header=""
      --template="AvB = {E}-{F}; AvC = {G}-{H}; AvD = {I}-{J}; BvC = {K}-{L}; BvD = {M}-{N}; CvD = {P}-{Q}"
      --solution=""
      

      Solution: The scores are: A vs B = 2-2; A vs C = 2-1; A vs D = 1-1; B vs C = 4-3; B vs D = 3-1; C vs D = 2-3.

      Like

      • Frits's avatar

        Frits 1:01 pm on 28 August 2023 Permalink | Reply

        @Jim, please elaborate on “And we find a solution with up to 4 goals scored in a match”.

        Did you add it because –digits=”1-6″ was rather slow?

        Like

        • Jim Randell's avatar

          Jim Randell 7:51 am on 29 August 2023 Permalink | Reply

          Limiting the digits to a maximum of 4 is enough to find a solution, and it runs quickly.

          For an exhaustive search we can increase the maximum digit to 6. The runtime is increased, but it is still less than 1s.

          But we can limit the total number of goals in any match (it cannot be more than 7), to reduce the runtime again. It complicates the recipe a little, but gives a complete solution that still runs quickly.

          Like

    • Frits's avatar

      Frits 11:45 am on 30 August 2023 Permalink | Reply

      Based on Jim’s setup.

       
      # points for X in match X vs Y, where the score is x - y
      pts = lambda x, y: x + 1 if x < y else x + 6 if x == y else x + 11
      
      # decompose <t> into <k> numbers from <ns> so that sum(<k> numbers) equals <t>
      def solve(t, ns, k=12, s=[]):
        if k == 1:
          if t in ns:
            yield s + [t]
        else: # perform early checks
        
          # H, L, P are already known
          if k == 9 and sum(s) != 3: return  # C must have had three losses
          
          # H, L, P, F, K, M are already known
          if k == 6:                 
            if sum(s) != 9: return   # B didn't loose and had one draw
            if s[4] <= s[1]: return  # C lost from B
          
          # H, L, P, F, K, M, E, N are already known  
          if k == 4:                  
            # B: 2 wins and a draw so (F > E and M == N) or (F == E and M > N)
            if not((s[3] > s[6] and s[5] == s[7]) or 
                   (s[3] == s[6] and s[5] > s[7])): return
            # G > 0 and Q > 0  (and G + Q > 2)
            if sum(s) > 10: return
         
          # G > H and Q > P (and G + Q > 2)
          if k == 3 and s[-1] <= s[0]:  return 
          if k == 2 and (s[-1] <= s[2] or s[-2] + s[-1] <= 2):  return 
             
          for n in ns:
            if n > t: break
            yield from solve(t - n, ns, k - 1, s + [n])
            
      # there are 25 goals scored and only 13 goals unaccounted for   
      # B must have had two wins and one draw as otherwise one win and two draws
      # leads to two "1 - 2" losses for C (who had definitely had three losses)
      
      # matches are:
      #
      #  A vs B = 1 + E  -  1 + F
      #  A vs C = 1 + G  -  1 + H
      #  A vs D = 1 + I  -  1 + J
      #  B vs C = 1 + K  -  1 + L
      #  B vs D = 1 + M  -  1 + N
      #  C vs D = 1 + P  -  1 + Q
      
      # first unaccounted goals for C followed by unaccounted goals for B
      for H, L, P, F, K, M, E, N, G, Q, I, J in solve(13, range(6)):
        games = [(E, F), (G, H), (I, J), (K, L), (M, N), (P, Q)]
        # all matches have different scores
        if len(set(seq := [tuple(sorted(x)) for x in games])) != len(seq): continue
        
        # check number of points
        if pts(E, F) + pts(G, H) + pts(I, J) != 25: continue
        if pts(J, I) + pts(N, M) + pts(Q, P) != 20: continue
        if pts(J, I) + pts(N, M) + pts(Q, P) != 20: continue
        if pts(H, G) + pts(L, K) + pts(P, Q) !=  6: continue
        
        desc = "AvB AvC AvD BvC BvD CvD".split()
        # add 1 to scores
        scores = [desc[i] + " = " + str(games[i][0] + 1) + "-" + 
                  str(games[i][1] + 1) for i in range(6)]
        print(', '.join(scores))  
      

      Like

  • Unknown's avatar

    Jim Randell 2:09 pm on 24 August 2023 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 922: Additional letters 

    From The Sunday Times, 23rd March 1980 [link]

    It is true, of course, that there are a lot of letters in this puzzle, but in spite of that I thought that for once Uncle Bungle was going to write it out correctly.

    In fact there was no mistake until the third and last line across but, in that line one of the letters, I’m afraid, was incorrect.

    This is another addition sum with letters substituted for digits. Each letter consistently stands for the same digit whenever it appears, and different letters stand for different digits — or at least the should do — and they do, but for the mistake in the last line across:

    What is the correct numerical 10-figure answer to the addition sum?

    This puzzle is included in the book The Sunday Times Book of Brainteasers (1994). The puzzle text above is taken from the book.

    [teaser922]

     
    • Jim Randell's avatar

      Jim Randell 2:09 pm on 24 August 2023 Permalink | Reply

      We can use [[ bungled_sum() ]] solver (as seen in Puzzle 56 etc.) for this puzzle.

      The following Python command line executes in 146ms.

      Run: [ @replit ]

      >>> from bungled_sum import bungled_sum
      >>> bungled_sum(['YTBBEDMKD', 'YHDBTYYDD', 'EDYTERTPTY'], [2])
      
                                      T
      YTBBEDMKD + YHDBTYYDD = EDYTERTPHY / @[2,8] T -> H
      695513243 + 673596633 = 1369109876 / B=5 D=3 E=1 H=7 K=4 M=2 P=8 R=0 T=9 Y=6
      

      Solution: The answer to the addition sum is 1369109876.

      The complete sum is:

      695513243 + 673596633 = 1369109876

      which would have been correctly posed as:

      YTBBEDMKD + YHDBTYYDD = EDYTERTPHY

      i.e. the penultimate (tens) digit of the result should have been H (not T).

      Like

  • Unknown's avatar

    Jim Randell 11:16 am on 21 June 2022 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 719: Wotta-Woppa! 

    From The Sunday Times, 27th April 1975 [link]

    On the Island of Imperfection there are three tribes; the Pukkas who always tell the truth, 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 is about three inhabitants; Ugly, Stupid and Toothless, whose names give some indication of the nature of their imperfections.

    The island currency is a Hope. The weekly wage of a Pukka is between 10 and 19 Hopes, of a Shilli-Shalla between 20 and 29, and of a Wotta-Woppa between 30 and 39 (in each case a whole number of Hopes). They make three statements each, anonymously:

    A says: (1) B is a Wotta-Woppa. (2) My wages are 25% less than 20% more than one of the others. (3) I get 10 hopes more than B.

    B says: (1) The wages of one of us is different from the sum of those of the other two. (2) We all belong to the same tribe. (3) More of C‘s statements are true than mine.

    C says: (1) Ugly earns more than Toothless. (2) The wages of one of us are 15% less than the wages of another. (3) Stupid is a Shilli-Shalla.

    Find C‘s name, tribe and wages.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 2 (1981). The puzzle text above is taken from the book.

    [teaser718]

     
    • Jim Randell's avatar

      Jim Randell 11:17 am on 21 June 2022 Permalink | Reply

      This is similar to Puzzle 15 (and Puzzle 66).

      Some obvious initial analysis:

      Statement B1 (“The wages of one of us is different from the sum of those of the other two”) must always be true, as it it not possible for each of the three to have wages that are equal to the sum of the other two. (Who would have the largest value?)

      So B cannot be a Wotta-Woppa. (And so A1 is false).

      Which means B3 must be also be true. So C must make more true statements than B, so B cannot be a Pukka. Hence B is a Shilli-Shalla, and C is a Pukka. (And B2 is false).

      And so C3 must be true, and “Stupid” is a Shilli-Shalla.

      And as A1 is false, then A cannot be a Pukka, and A3 must also be false.

      I incorporated these findings into a generic solution to get a program that runs in 71ms.

      Run: [ @replit ]

      from enigma import (subsets, tuples, irange, product, div, intersect, printf)
      
      # Pukka - always tell 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))
      
      # wages, by tribe
      wage = { P: irange(10, 19), S: irange(20, 29), W: irange(30, 39) }
      
      # assign tribes to the participants
      for ts in ((W, S, P), (S, S, P)):
        (A, B, C) = ts
      
        # B's statements
        Bs = [True, False, True]
        assert B(Bs)
      
        # assign names to the participants
        for ns in subsets("STU", size=3, select="P"):
          (nA, nB, nC) = ns
          if nC == 'S': continue  # not possible
          (iS, iT, iU) = (ns.index(x) for x in "STU")
      
          # assign wages to the participants
          for ws in product(wage[A], wage[B], wage[C]):
            (wA, wB, wC) = ws
      
            # check A's statements
            As = [False, div(10 * wA, 9) in {wB, wC}, wA == wB + 10]
            if not A(As): continue
      
            # check C's statements
            Cs = [
              ws[iU] > ws[iT],
              ts[iS] is S,
              bool(intersect([{ div(85 * x, 100) for x in ws }, ws])),
            ]
            if not C(Cs): continue
      
            # check B's 3rd statement
            assert Cs.count(True) > Bs.count(True)
      
            # output solution
            f = lambda x: x.__name__
            printf("A={A} {nA} {wA}; B={B} {nB} {wB}; C={C} {nC} {wC} [{As}; {Bs}; {Cs}]", A=f(A), B=f(B), C=f(C))
      

      Solution: C is Toothless, he is a Pukka, his wage is 17 Hopes.

      A is Ugly, he is a Wotta-Woppa, his wage is 31 – 39 Hopes.

      B is Stupid, he is a Shilli-Shalla, his wage is 20 Hopes.

      C‘s wage (17) is 15% less than that of B (20).

      Like

      • Frits's avatar

        Frits 3:04 pm on 21 June 2022 Permalink | Reply

        @Jim, You swapped the ranges of Wotta-Woppa and Shilli-Shalla.

        Like

      • Jim Randell's avatar

        Jim Randell 12:25 pm on 27 June 2022 Permalink | Reply

        Inspired by Frits’ solution (below) I wrote a solution that uses the [[ SubstitutedExpression ]] solver from the enigma.py library, and is just a restatement of the puzzle text with no analysis.

        The values assigned for the tribes make it easy to express the wages alphametically.

        This Python program runs in 73ms.

        Run: [ @replit ]

        from enigma import (SubstitutedExpression, irange, tuples, div, printf)
        
        #    statements  tribe  name  wage
        # A: a d g       p      x     ps
        # B: b e h       q      y     qt
        # C: c f i       r      z     ru
        #
        # statements: 0 (false), 1 (true)
        # tribe: 1 (P), 2 (SS), 3 (WW)
        # name: 1 (S), 2 (T), 3 (U)
        # wage (units): 0 - 9
        
        # check statements for a tribe
        def tribe(t, ss):
          if t == 1: return all(ss)
          if t == 2: return all(a != b for (a, b) in tuples(ss, 2))
          if t == 3: return not any(ss)
        
        # B1: one of the wages is different from the sum of the other two
        def B1(*ws):
          t = sum(ws)
          return any(w != t - w for w in ws)
        
        # C1: Ugly (3) earns more than Toothless (2)
        def C1(ns, ws):
          d = dict(zip(ns, ws))
          return d[3] > d[2]
        
        # C2: the wages of one of us is 85% the wages of another
        def C2(*ws):
          for w in ws:
            x = div(85 * w, 100)
            if x in ws: return True
          return False
        
        # expressions to evaluate
        exprs = [
          # tribes and statements
          "tribe({p}, [{a}, {d}, {g}])",
          "tribe({q}, [{b}, {e}, {h}])",
          "tribe({r}, [{c}, {f}, {i}])",
        
          # A1: "B is a Wotta-Woppa (3)" = {a}
          "int({q} == 3) = {a}",
          # A2: "My wages are [0.9] one of the others" = {d}
          "int(div({ps} * 10, 9) in { {qt}, {ru} }) = {d}",
          # A3: "I get 10 Hopes more than B" = {g}
          "int({ps} == {qt} + 10) = {g}",
          # B1: "The wages of one of us is different from the sum of the wages
          # of the other two" = {b}
          "int(B1({ps}, {qt}, {ru})) = {b}",
          # B2: "We all belong the same tribe" = {e}
          "int({p} == {q} == {r}) = {e}",
          # B3: "More of C's statements are true than mine" = {h}
          "int({c} + {f} + {i} > {b} + {e} + {h}) = {h}",
          # C1: "Ugly earns more than Toothless" = {c}
          "int(C1([{x}, {y}, {z}], [{ps}, {qt}, {ru}])) = {c}",
          # C2: "The wages of one of us are 0.85 the wages of another" = {f}
          "int(C2({ps}, {qt}, {ru})) == {f}",
          # C3: "Stupid (1) is a Shilli-Shalla (2)" = {i}
          "int({ {x}: {p}, {y}: {q}, {z}: {r} }[1] == 2) = {i}",
        ]
        
        # invalid digit / symbol assignments
        d2i = dict()
        for d in irange(0, 9):
          vs = set()
          if d > 1: vs.update('abcdefghi')
          if d < 1 or d > 3: vs.update('pqrxyz')
          d2i[d] = vs
        
        # create the puzzle
        p = SubstitutedExpression(
          exprs,
          distinct="xyz",
          d2i=d2i,
          env=dict(tribe=tribe, B1=B1, C1=C1, C2=C2),
          verbose=0,
        )
        
        # solve the puzzle and output solutions
        Ts = { 1: "Pukka", 2: "Shilli-Shalla", 3: "Wotta-Woppa" }
        Ns = { 1: "Stupid", 2: "Toothless", 3: "Ugly" }
        for s in p.solve():
          (a, b, c, d, e, f, g, h, i, p, q, r, s, t, u, x, y, z) = (s[k] for k in 'abcdefghipqrstuxyz')
          printf("A: {N:9s}  {T:13s}  {p}{s}  [{a} {d} {g}]", N=Ns[x], T=Ts[p])
          printf("B: {N:9s}  {T:13s}  {q}{t}  [{b} {e} {h}]", N=Ns[y], T=Ts[q])
          printf("C: {N:9s}  {T:13s}  {r}{u}  [{c} {f} {i}]", N=Ns[z], T=Ts[r])
          printf()
        

        Like

        • Frits's avatar

          Frits 1:31 pm on 29 June 2022 Permalink | Reply

          Nice idea to let tribes go from 1 to 3 so you can use the tribe value as a tens unit of the wage.

          Like

    • Frits's avatar

      Frits 3:30 pm on 21 June 2022 Permalink | Reply

      For all tribes the veracity of the first and third statement must be the same.

         
      from enigma import SubstitutedExpression
      
      # tribes 0 = Wotta-Woppa, 1 = Shilli-Shilla, 2 = Pukka 
      # names: 0 = Stupid, 1 = Toothless, 2 = Ugly
      
      #     stmnts  tribe  wages   name
      #      0/1    0/1/2         0/1/2
      # A:  D E D     P     UV      K
      # B:  F G F     Q     WX      L
      # C:  H I H     R     YZ      M
      # the alphametic puzzle
      p = SubstitutedExpression(
        [
          # if Wotta-Woppa all statements are false
          "P != 0 or D == E == 0",
          "Q != 0 or F == G == 0",
          "R != 0 or H == I == 0",
          
          # if Shilli-Shalla all statements are alternating true/false
          "P != 1 or D + E == 1",
          "Q != 1 or F + G == 1",
          "R != 1 or H + I == 1",
          
          # if Pukka all statements are true
          "P != 2 or D == E == 1",
          "Q != 2 or F == G == 1",
          "R != 2 or H == I == 1",
          
          # ranges: Pukka 10-19, Shilli-Shalla 20-29 and Wotta-Woppa 30-39
          "(P != 0 or U == 3) and (P != 1 or U == 2) and (P != 2 or U == 1)",
          "(Q != 0 or W == 3) and (Q != 1 or W == 2) and (Q != 2 or W == 1)",
          "(R != 0 or Y == 3) and (R != 1 or Y == 2) and (R != 2 or Y == 1)",
          
          # A1: B is a Wotta-Woppa
          "(F == G == 0) == D",
          # A2: My wages are 25% less than 20% more than one of the others
          "(UV in {0.9 * WX, 0.9 * YZ}) == E",
          # A3: I get 10 hopes more than B
          "(UV == WX + 10) == D",
          
          # B1: The wages of one of us is different from the sum of those 
          # of the other two
          "(UV != WX + YZ or UV + WX != YZ or UV + YZ != WX) == F",
          # B2: We all belong to the same tribe
          "(P == Q == R) == G",
          # B3: More of C's statements are true than mine
          "(2* H + I >  2 * F + G) == F",
          
          
          # C1: Ugly earns more than Toothless
          "((UV if K == 2 else WX if L == 2 else YZ) > \
            (UV if K == 1 else WX if L == 1 else YZ)) == H",
          # C2: The wages of one of us are 15% less than the wages of another
          "((20 * UV in {17 * YZ, 17 * WX}) or \
            (20 * WX in {17 * YZ, 17 * UV}) or \
            (20 * YZ in {17 * UV, 17 * WX})) == I",
          # C3: Stupid is a Shilli-Shalla.
          "((K == 0 and P == 1) or \
            (L == 0 and Q == 1) or \
            (M == 0 and R == 1)) == H",
        ],
        answer="(D, E, F, G, H, I), (P, Q, R), (UV, WX, YZ), (K, L, M)",
        d2i=dict([(0, "UWY")] +
                 [(2, "DEFGHI")] +
                 [(3, "DEFGHIKLMPQR")] +
                 [(k, "DEFGHIKLMPQRUWY") for k in range(4, 10)]),
        distinct="KLM",
        verbose=0,
      )
      
      # print answers
      for (_, ans) in p.solve():
        print(f"{ans}")
      

      Like

  • Unknown's avatar

    Jim Randell 10:04 am on 30 September 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 869: New type football 

    From The Sunday Times, 19th March 1978 [link]

    In this puzzle four football teams are going to play each other once during the course of the season. The incomplete table below shows the situation when some of the matches have been played (2 points for a win, 1 for a draw as usual).

    The digits have been replaced by letters and each letter stands for the same digit wherever it appears and different letters stand for different digits.

    The table looks like this:

    List the matches played and the score in each match.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser869]

     
    • Jim Randell's avatar

      Jim Randell 10:04 am on 30 September 2021 Permalink | Reply

      We know the goals for/against values for A and B, so we can calculate scores for matches involving A or B, which only leaves the score in the C vs D match to be decided, and as the overall goals for C is t, we can calculate potential scores for C in the match. If the match is a draw, then D’s score is the same as C’s. If it is a win for C, then D scored fewer goals. And if it is a win for D, then D scored more goals. I put an upper limit on the number of goals in this last case, but it is not a situation that is reached.

      This Python program uses the [[ Football() ]] helper class from the enigma.py library. It runs in 54ms.

      Run: [ @replit ]

      from enigma import Football, irange
      
      # scoring system
      football = Football(points=dict(w=2, d=1))
      
      # the table
      table = dict(played='x?pk', w='?hx?', l='??h?', d='k?k?', points='??m?')
      (gf, ga) = ('hmt?', 'pm??')
      
      # label the teams
      (A, B, C, D) = (0, 1, 2, 3)
      
      # find match outcomes
      for (ms, d) in football.substituted_table(table):
      
        # calculate scores for games involving A and B
        for ss in football.substituted_table_goals(gf, ga, ms, d=d, teams=[A, B]):
      
          # only the C vs D match is undecided
          sCD = list()
          m = ms[(C, D)]
          if m == 'x':
            sCD = [None]
          else:
            # calculate known goals for/against C
            (f, a) = football.goals([ss[(A, C)], ss[(B, C)]], [1, 1])
            # so the score in the C vs D match is (x, y)
            for x in irange(0, 9 - f):
              t = f + x
              if t in d.values(): continue
              if m == 'd':
                sCD.append((x, x))
              elif m == 'w':
                sCD.extend((x, y) for y in irange(0, x - 1))
              elif m == 'l':
                sCD.extend((x, y) for y in irange(x + 1, 10))  # upper limit on goals scored
      
          for ss[(C, D)] in sCD:
            # output solution
            football.output_matches(ms, ss, teams="ABCD", d=d)
      

      Solution: The played matches are: A vs B = 0-0; A vs C = 0-3; B vs C = 5-5; C vs D = 1-0.

      The A vs D, B vs D matches are not yet played.

      Like

  • Unknown's avatar

    Jim Randell 10:08 am on 20 July 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 811: When rules were rules … 

    From The Sunday Times, 6th February 1977 [link]

    No organisation can be efficient without clear-cut rules setting out exactly the rewards and the responsibilities of those who have the honour to be members of the team.

    I came across the other day a copy of the rules which, as the Managing Director of Our Factory, I had put on the Society’s Notice Board for all to see and understand. But this was many years ago when the pound was worth something, and when Rules were obeyed.

    There were five employees in the Factory then, and their names were Alf, Bert, Charlie, Duggie and Ernie. Their jobs, not necessarily respectively, were those of Door-Opener, Door-Shutter, Door-Knob-Polisher, Bottle-Washer and Welfare Officer. The notice which I put up read as follows:

    RULES:
    1. Charlie is to get 10% more than the worst paid of you all.
    2. Alf is to be paid more than Duggie.
    3. The Bottle-Washer is to get 5% more than 10% less than Bert.
    4. Duggie is to get either £1 more or £1 less than Ernie.
    5. The Door-Opener’s wages are to be an odd multiple of 10p.
    6. Ernie is to get 20% more than £1 less than the Door-Knob-Polisher.
    7. The Door-Shutter is to be the best paid of you all.
    8. Your wages are all to be different and each one is to be a multiple of 10p.
    9. No one is to get more than £20 or less than £10 per week.

    What were their weekly wages?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser811]

     
    • Jim Randell's avatar

      Jim Randell 10:09 am on 20 July 2021 Permalink | Reply

      Working in units of 10p means the wages are in the range 100 – 200.

      This Python program runs in 54ms.

      Run: [ @replit ]

      from enigma import irange, div, subsets, int2base, printf
      
      # possible values
      values = irange(100, 200)
      
      # find possible (x, f(x)) pairs
      def pairs(f):
        for x in values:
          y = f(x)
          if y is not None and y in values:
            yield (x, y)
      
      # consider BW/B pairs (BW = 1.05 * 0.9 * B)
      for (BW, B) in pairs(lambda x: div(200 * x, 21 * 9)):
      
        # consider DKP/E pairs (E = 1.2 * (DKP - 10))
        for (DKP, E) in pairs(lambda x: div(6 * (x - 10), 5)):
      
          # consider D values
          for D in (E - 10, E + 10):
            if D not in values: continue
      
            # consider A values (A > D)
            for A in values:
              if not(A > D): continue
              C = div(11 * min(B, D, E), 10)
              if C is None or C not in values: continue
              # the set of wages
              ws = set([A, B, C, D, E])
              if len(ws) != 5: continue
              # remaining jobs
              js = ws.difference([BW, DKP])
              if len(js) != 3: continue
              for (DS, DO, WO) in subsets(js, size=len, select="P"):
                # check DS, DO values
                if not(DO % 2 == 1 and DS == max([A, B, C, D, E])): continue
                # jobs: map: wage -> job
                jobs = dict(zip([BW, DKP, DS, DO, WO], "BW DKP DS DO WO".split()))
      
                # output solution
                fmt = lambda x: int2base(x * 10, group=2, sep='.')
                printf("A={A} ({j})", A=fmt(A), j=jobs[A])
                printf("B={B} ({j})", B=fmt(B), j=jobs[B])
                printf("C={C} ({j})", C=fmt(C), j=jobs[C])
                printf("D={D} ({j})", D=fmt(D), j=jobs[D])
                printf("E={E} ({j})", E=fmt(E), j=jobs[E])
                printf()
      

      Solution: The wages per week are: Alf = £18.90; Bert = £20.00; Charlie = £12.10; Duggie = £11.00; Ernie = £12.00.

      And their jobs:

      Alf = Bottle-Washer
      Bert = Door-Shutter
      Charlie = Door-Opener
      Duggie = Door-Knob-Polisher
      Ernie = Welfare Officer

      Like

    • Frits's avatar

      Frits 4:00 pm on 21 July 2021 Permalink | Reply

      Manual solution.

      # Consider amounts in 10p.
      
      # Ernie = 1.2 * (Door-Knob-Polisher - 10)                              (rule 6)
      # Ernie = 108 + 6 * YZ,     YZ  less than 16  
      # Ernie must be even so also Duggie must be even                       (rule 4)
      
      # Bottle-Washer = 1.05 * 0.9 * Bert, 189 * Bert = 200 * Bottle-Washer  (rule 3)
      # 189 and 200 are coprime so Bottle-Washer = 189, Bert = 200
      # Bottle-Washer is odd so not Bert, Duggie or Ernie
      # Charlie has to be a multiple of 11                                  (rule 11)
      # so --- Bottle-Washer must be Alf ---, Alf = 189
      # so --- Door-Shutter must be Bert ---                                 (rule 7)
      
      # Door-Opener's wages are to be an odd multiple of 10p                 (rule 5)
      # Alf and Bert have diffeent jobs, Duggie and Ernie are even
      # so --- Charlie is the Door-Opener ---
      
      # Ernie is to get 20% more than £1 less than the Door-Knob-Polisher    (rule 6)
      # three jobs already have been assigned
      # so --- Duggie is the Door-Knob-Polisher ---
      # so --- Ernie is the Welfare Officer ---
      # Ernie is paid more than Door-Knob-Polisher
      # so worst paid has to be Duggie 
      # so Ernie = Duggie + 10                                               (rule 4)
      # Door-Knob-Polisher = Ernie / 1.2 + 10 = 5 / 6 * (108 + 6 * YZ) + 10 =
      # 5 * YZ + 100 so YZ must be even (as Duggie is even)
      
      # Charlie = 1.1 * Duggie = 1.1 * (Ernie - 10)                    (rule 1 and 4)
      # so Ernie must be a multiple of 10
      # so YZ must be 2 or 12    (Ernie = 108 + 6 * YZ and YZ is even)
      # so (Charlie, Duggie, Ernie) must be resp. (121, 110, 120) or (187, 170, 180)
      # only in first case rule 6 is obeyed
      
      # so Alf = £18.90, Bert = £20, Charlie = £12.10, Duggie = £11 and Ernie = £12
      

      Like

  • Unknown's avatar

    Jim Randell 8:48 am on 1 June 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 771: Doctor Bungle bungles 

    From The Sunday Times, 25th April 1976 [link]

    The doctors in the country town of Keepwell are keen soccer fans and with the assistance of their staff they have formed themselves into 3 teams who are all to play each other once.

    I asked my friend, Dr Bungle, who is secretary of one of the teams, if he could let me know the current situation and he produced the following table:

    I knew that not more than 3 goals had been scored in any match and it did not take me long to see that there was something wrong with the table. I discovered subsequently that the doctor had been taking a non-truth drug of his own prescription. The drug was not 100% effective and so all but one of the figures were incorrect, and the remaining figure was correct.

    What were the scores in the matches played so far?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser771]

     
    • Jim Randell's avatar

      Jim Randell 8:49 am on 1 June 2021 Permalink | Reply

      This Python program uses the [[ Football() ]] helper class from the enigma.py library.

      It runs in 49ms.

      Run: [ @replit ]

      from itertools import product
      from enigma import Football, printf
      
      # possible scores (not more than 3 goals scored)
      score = dict()
      score['w'] = { (1, 0), (2, 0), (3, 0), (2, 1) }
      score['d'] = { (0, 0), (1, 1) }
      score['l'] = set((y, x) for (x, y) in score['w'])
      score['x'] = [None]
      
      def check(xs, ys):
        return sum(x == y for (x, y) in zip(xs, ys))
      
      # scoring system (matches may not be played)
      football = Football(games='wdlx')
      
      # there are 3 matches
      for (AB, AC, BC) in football.games(repeat=3):
        # table rows for A, B, C
        A = football.table([AB, AC], [0, 0])
        B = football.table([AB, BC], [1, 0])
        C = football.table([AC, BC], [1, 1])
      
        # check for correct digits (the can be at most one)
        d1 = check(
          [A.played, A.w, A.l, A.d, B.played, B.w, B.l, B.d, C.played, C.w, C.l, C.d],
          [1, 0, 1, 0, 2, 2, 0, 0, 1, 0, 2, 1],
        )
        if d1 > 1: continue
      
        # consider possible scores
        for (sAB, sAC, sBC) in product(score[AB], score[AC], score[BC]):
          # goals for/against
          (fA, aA) = football.goals([sAB, sAC], [0, 0])
          (fB, aB) = football.goals([sAB, sBC], [1, 0])
          (fC, aC) = football.goals([sAC, sBC], [1, 1])
      
          # check for correct digits
          d2 = check([fA, aA, fB, aB, fC, aC], [2, 0, 1, 2, 0, 3])
          if d1 + d2 != 1: continue
      
          printf("AB={AB}:{sAB} AC={AC}:{sAC} BC={BC}:{sBC}; d1={d1} d2={d2}")
          printf("A={A}; f={fA} a={aA}")
          printf("B={B}; f={fB} a={aB}")
          printf("C={C}; f={fC} a={aC}")
          printf()
      

      Solution: The scores were: A vs B = 1-1; A vs C = 3-0; B vs C = 1-2.

      The only correct figure is B’s played value (= 2).

      Like

  • Unknown's avatar

    Jim Randell 9:05 am on 27 April 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 744: Job prediction 

    From The Sunday Times, 19th October 1975 [link]

    Professor Knowall has recently become very interested in dreams.

    A friend of mine has been doing some research about the extent to which dreams can be used foretell the future. He persuaded his five employees (Alf, Bert, Charlie, Duggie and Ernie) to help him in his enquiries. They liked the idea of being in the forefront of anything new and their beds seemed a nice cosy place for research.

    They met a year ago, and predicted their jobs now. Thus:

    Alf: “Ernie will not be the Door-Opener.”
    Bert: “Duggie will not be the Bottle-Washer.”
    Charlie: “Alf will not be the Welfare Officer.”
    Duggie: “Ernie will be the Bottle-Washer.”
    Ernie: “Bert’s prediction will be true.”

    Their jobs now are those of Bottle-Washer, Welfare Officer, Door-Shutter, Door-Opener and Worker.

    The Professor was most interested in this

    “But, my dear Sergeant Bungle”, he said, “how many of these predictions were correct and who made them?”

    In fact only two of the predictions were correct and they were made by the men who became the Welfare Officer and the Worker.

    What are all their jobs now?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser744]

     
    • Jim Randell's avatar

      Jim Randell 9:05 am on 27 April 2021 Permalink | Reply

      This Python program runs in 49ms.

      Run: [ @replit ]

      from enigma import subsets, printf
      
      jobs = ("BW", "WO", "DS", "DO", "W")
      
      # consider assignments of jobs
      for (A, B, C, D, E) in subsets(jobs, size=len, select="P"):
      
        # evaluate the statements:
        # A: "E will not be DO"
        sA = (E != "DO")
        # B: "D will not be BW"
        sB = (D != "BW")
        # C: "A will not be WO"
        sC = (A != "WO")
        # D: "E will be BW"
        sD = (E == "BW")
        # E: "B's statement is true"
        sE = (sB == True)
      
        # only 2 of the statements are true, and they were made by WO and W
        ts = set(j for (s, j) in zip([sA, sB, sC, sD, sE], [A, B, C, D, E]) if s)
        if ts != {"WO", "W"}: continue
      
        # output solution
        printf("A={A} B={B} C={C} D={D} E={E}")
      

      Solution: Alf is the Worker. Bert is the Door-Opener. Charlie is the Welfare Officer. Duggie is the Bottle-Washer. Ernie is the Door-Shutter.

      Like

    • Frits's avatar

      Frits 10:18 am on 28 April 2021 Permalink | Reply

      from enigma import SubstitutedExpression
      
      # BW = 1, WO = 2, DS = 3, DO = 4, W = 5
      jobs = ("n/a", "BW", "WO", "DS", "DO", "W")
      
      # the alphametic puzzle
      p = SubstitutedExpression( [
        # A: "E will not be DO"
        "(E != 4) = V",
        # B: "D will not be BW"
        "(D != 1) = W",
        # C: "A will not be WO"
        "(A != 2) = X",
        # D: "E will be BW"
        "(E == 1) = Y",
        # E: "B's statement is true"
        "(W == 1) = Z",
        
        # only 2 of the statements are true, and they were made by WO and W
        "sorted([V * A, W * B, X * C, Y * D, Z * E]) == [0, 0, 0, 2, 5]",
        ],
        answer="A, B, C, D, E",
        # let values range from 1-5 so the sorted formula is enough 
        # to check that only 2 of the statements are true
        digits=range(0, 6),
        d2i=dict([(0, "ABCDE")]),
        distinct="ABCDE",
        verbose=256
      )
      
      # print answers
      for (_, ans) in p.solve():
        for i, a in enumerate(ans):
          print(f"{'ABCDE'[i]}={jobs[a]}", end=" ")
        print()  
      
      # A=W B=DO C=WO D=BW E=DS  
      

      Like

      • Frits's avatar

        Frits 10:48 am on 28 April 2021 Permalink | Reply

         
        If E is true:
          B is true
            A is false
              E = DO
              DO talks the truth -- > incorrect
        Else:
          E is false 
            B is false
            D = BW
              D is false
                A is true
                C is true
                  A = W
                  C = WO
                  E = DS
                  B = DO
        

        Like

        • John Crabtree's avatar

          John Crabtree 4:30 pm on 29 April 2021 Permalink | Reply

          One does not need to know D’s prediction to solve the teaser. It must be false, which in fact it is.

          Like

  • Unknown's avatar

    Jim Randell 8:28 am on 18 March 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 702: That’s torn it … again 

    From The Sunday Times, 29th December 1974 [link]

    Last time it was vertical. But no one could accuse Uncle Bungle of being consistent and this time it was horizontal. The way, I mean, in which he tore the piece of paper on which were written the details of the matches between 4 local football teams, ABC, and D, who are to play each other once.

    All that was left was:

    It is not known whether all the matches have been played. And not more than 7 goals were scored in any game.

    With the information that it is possible to discover the score in each match, you should be able to discover them.

    What was the score in each match?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser702]

     
    • Jim Randell's avatar

      Jim Randell 8:29 am on 18 March 2021 Permalink | Reply

      I wasn’t entirely happy about this puzzle, but after some thought I was able to convince myself that there is a way to arrive at a single solution.

      We are told that there is enough information to determine the scores in each match (even though on the face of it it seems that we have not), so that fact it is possible to determine the scores means that there must be some ambiguous situations that cannot arise.

      We have no information at all about C and D, so any solution that we find will be equally applicable if C and D’s labels are swapped over. But as we are told we can determine the scores in the matches, this must mean swapping the labels of C and D would have no effect on the scores, so C and D must have performed identically.

      From this we can see that the C vs D match cannot be won by either of the teams, so it must be a draw, or not yet played. If it were a draw, then it could be 0-0, 1-1, 2-2, 3-3 and we don’t know which. So the C vs D match must be not yet played.

      We know A and B have both played each of the other teams (as they have played 3 matches each), so all 6 matches involving A and B must have been played. And so the A vs B and A vs C matches must have identical scores, as must the B vs C and B vs D matches.

      So we only need to calculate three scorelines: A vs B (win), A vs (C and D) (draw), B vs (C and D) (win).

      This Python program runs in 48ms.

      Run: [ @replit ]

      from itertools import product
      from enigma import Football, printf
      
      # possible scores (no more than 7 goals scored per match)
      win = [
        (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0),
        (2, 1), (3, 1), (4, 1), (5, 1), (6, 1),
        (3, 2), (4, 2), (5, 2),
        (4, 3),
      ]
      draw = [(0, 0), (1, 1), (2, 2), (3, 3)]
      
      # for computing goals for/against
      football = Football()
      
      # CD is not yet played
      CD = None
      
      # A has 2 draws (which must be AC = AD) and a win (which must be AB)
      # B has 2 wins (which must be BC = BD)
      for (AB, AX, BX) in product(win, draw, win):
        # check goals for/against
        if not(football.goals([AB, AX, AX], [0, 0, 0]) == (7, 5)): continue
        if not(football.goals([AB, BX, BX], [1, 0, 0]) == (5, 5)): continue
        # output solution
        printf("AB={AB} AC={AX} AD={AX} BC={BX} BD={BX} CD={CD}")
      

      Solution: A vs B = 3-1; A vs C = 2-2; A vs D = 2-2; B vs C = 2-1; B vs D = 2-1; C vs D = not yet played.

      Like

    • John Crabtree's avatar

      John Crabtree 7:33 pm on 18 March 2021 Permalink | Reply

      At the point where you can say “So we only need to calculate three scorelines”, you are almost home.
      A must beat B by 2 goals, and score an odd number of goals. That fixes the score in A vs B, and the remaining scores follow.

      Like

  • Unknown's avatar

    Jim Randell 10:43 am on 25 February 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 846: The Rajah’s jewels 

    From The Sunday Times, 2nd October 1977 [link]

    It was a frightened, breathless, but very charming young lady who knocked on my office door late one night.

    “They have gone”, she said: “The Rajah’s rubies are no longer in their ancestral home”.

    I visited the scene of the crime and discovered a tattered piece of paper on which was written:

    Where were the jewels hidden?

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser846]

     
    • Jim Randell's avatar

      Jim Randell 10:44 am on 25 February 2021 Permalink | Reply

      We can use the [[ SubstitutedDivision() ]] solver from the enigma.py library to solve the alphametic long division sum, and then we can create a map of digits to letters, and use that to translate to final string of digits back into letters.

      This Python program runs in 58ms.

      Run: [ @repl.it ]

      from enigma import (SubstitutedDivision, translate, printf)
      
      # the alphametic division puzzle
      p = SubstitutedDivision(
        "?DEEF? / NU = O?SU",
        [ "?DE - NU = UR", "UR? - DTE = SH", "SH? - UT? = DF", "DFD - DT? = T" ],
      )
      
      # solve the puzzle
      for s in p.solve(verbose=0):
        # map digits -> symbols
        d = dict((str(v), k) for (k, v) in s.d.items())
        # translate the string of digits
        r = translate("4936270681427669705719691270187069477266", d)
        # output solution
        printf("{r} [{s.a} / {s.b} = {s.c} (rem {s.r})]")
      

      Solution: The message reads: “UNDER THE FOURTEENTH STONE NORTH OF THE NUT TREE”.

      The division sum is: 136683 ÷ 94 = 1454 (remainder 7).

      Which gives us a mapping between digits and letters.

      The string of digits then translates as:

      UNDERTHEFOURTEENTHSTONENORTHOFTHENUTTREE

      which, with appropriate breaks reads:

      UNDER THE FOURTEENTH STONE NORTH OF THE NUT TREE

      Like

  • Unknown's avatar

    Jim Randell 9:01 am on 11 February 2021 Permalink | Reply
    Tags: by: Eric Emmet   

    Brain-Teaser 691: That’s torn it … 

    From The Sunday Times, 13th October 1974 [link]

    It was typical of Uncle Bungle that he should have torn up the sheet of paper which gave particulars of the numbers of matches played, won, lost, drawn, etc. of four local football teams who were eventually going to play each other once. The only piece left was as shown (as usual there are 2 points for a win and 1 for a draw):

    It will not surprise those who know my Uncle to hear that one of the figures was wrong, but fortunately it was only one out (i.e. one more or less than the correct figure).

    Each side had played at least one game, and not more than seven goals were scored in any match.

    Calling the teams A, B, C and D (in that order), find the score in each match.

    This puzzle is included in the book The Sunday Times Book of Brain-Teasers: Book 1 (1980). The puzzle text above is taken from the book.

    [teaser691]

     
    • Jim Randell's avatar

      Jim Randell 9:02 am on 11 February 2021 Permalink | Reply

      (See also: Puzzle 81).

      This Python program uses the [[ Football ]] helper class from the enigma.py library. It runs in 198ms.

      from enigma import (Football, subsets, cproduct, printf)
      
      # scoring system
      football = Football(points=dict(w=2, d=1))
      
      # possible scores in the matches (no match has more than 7 goals scored)
      scores = {
        'w': [
          (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0),
          (2, 1), (3, 1), (4, 1), (5, 1), (6, 1),
          (3, 2), (4, 2), (5, 2),
          (4, 3),
        ],
        'd': [(0, 0), (1, 1), (2, 2), (3, 3)],
        'x': [None],
      }
      scores['l'] = list((b, a) for (a, b) in scores['w'])
      
      # choose outcomes for each of the matches
      for (AB, AC, AD, BC, BD, CD) in subsets('wdlx', size=6, select="M"):
        # calculate the points
        A = football.table([AB, AC, AD], [0, 0, 0])
        B = football.table([AB, BC, BD], [1, 0, 0])
        C = football.table([AC, BC, CD], [1, 1, 0])
        D = football.table([AD, BD, CD], [1, 1, 1])
      
        # each team has played at least one game
        if not all(x.played > 0 for x in [A, B, C, D]): continue
      
        # count the discrepancies in the points
        dp = abs(A.points - 3) + abs(B.points - 5) + C.points
        if dp > 1: continue
      
        # choose the scores in C's games
        for (sAC, sBC, sCD) in cproduct([scores[AC], scores[BC], scores[CD]]):
          (fC, aC) = football.goals([sAC, sBC, sCD], [1, 1, 0])
          dC = aC
          if dp + dC > 1: continue
      
          # choose the scores in A's games
          for (sAB, sAD) in cproduct([scores[AB], scores[AD]]):
            (fA, aA) = football.goals([sAB, sAC, sAD], [0, 0, 0])
            dA = abs(aA - 5)
            if dp + dA + dC > 1: continue
      
            # remaining game
            for sBD in scores[BD]:
              (fB, aB) = football.goals([sAB, sBC, sBD], [1, 0, 0])
              dB = abs(aB - 6)
              if dp + dA + dB + dC > 1: continue
      
              (fD, aD) = football.goals([sAD, sBD, sCD], [1, 1, 1])
              dD = abs(aD - 7)
              if dp + dA + dB + dC + dD != 1: continue
      
              printf("A=({aA} {A.points}) B=({aB} {B.points}) C=({aC} {C.points}) D=({aD} {D.points}) / \\")
              printf("AB={AB}:{sAB} AC={AC}:{sAC} AD={AD}:{sAD} BC={BC}:{sBC} BD={BD}:{sBD} CD={CD}:{sCD}")
      

      Solution: The scores in the played matches are: A vs B = 3-3; A vs D = 3-2; B vs C = 1-0; B vs D = 4-3.

      The A vs C and C vs D matches are not yet played.

      The incorrect value is C’s “goals against”. It should be 1, not 0.

      It is clear that the incorrect value must be one of C’s, as if they have played at least one game they cannot have both 0 points and 0 goals against.

      Like

c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel
Design a site like this with WordPress.com
Get started