Tagged: by: E R Emmet Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 10:03 am on 24 September 2024 Permalink | Reply
    Tags: by: E R Emmet   

    Brain-Teaser 231: Holidays abroad 

    From The Sunday Times, 26th September 1965 [link]

    My old friends Alf, Bert, Charlie, Duggie and Ernie went with their wives for holidays abroad last year, to Andorra, Boulogne, Calais, Dunkirk and Ethiopia. I knew that the names of the five wives were Agnes, Beatrice, Clarissa, Daphne and Ethel, but the only information I had about who was married to whom was that for each pair the names of the husband, the wife and last year’s holiday destination all began with different letters.

    In conversation with the ladies, Beatrice told me that she was not married to Alf, and that she had heard from Ernie that Charlie went to Dunkirk last year.

    Daphne, however, firmly informed me that Charlie went to Ethiopia and that Beatrice went to Dunkirk. “Unlike some people I could mention”, she added darkly, “Alf always tells the truth”.

    Clarissa said that when her husband was asked whether Ethel was married to Charlie, he replied: “No”. She went on to tell me that Duggie went to Boulogne.

    Of each of these married couples one member always told the truth and the other never did.

    Name each man’s wife and holiday resort.

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser231]

     
    • Jim Randell's avatar

      Jim Randell 10:03 am on 24 September 2024 Permalink | Reply

      I used the [[ SubstitutedExpression ]] solver from the enigma.py library to solve this puzzle.

      I assigned 5 slots (1 – 5), each of which contains one wife, husband, destination and trait (for the wife).

      The following run file fills out the slots. It runs in 77ms. (Internal runtime of the generated code is 631µs).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # let A = 1, B = 2, C = 3, D = 4, E = 5
      #
      #  slot       : 1 2 3 4 5
      #  wife       : A B C D E
      #  husband    : F G H I J  {Alf, Bert, Charlie, Duggie, Ernie}
      #  destination: K L M N P  {Andorra, Boulogne, Calais, Dunkirk, Ethiopia}
      #  wife trait : V W X Y Z  {0 = false or 1 = true}
      --base=6
      --distinct="FGHIJ,KLMNP,FK,GL,HM,IN,JP"
      --invalid="0,FGHIJKLMNP"
      --invalid="2|3|4|5,VWXYZ"
      --invalid="1,FK"
      --invalid="2,GL"
      --invalid="3,HM"
      --invalid="4,IN"
      --invalid="5,JP"
      
      --macro="@hs = (F, G, H, I, J)"  # husbands
      --macro="@ds = (K, L, M, N, P)"  # destinations
      --macro="@ts = (V, W, X, Y, Z)"  # traits
      
      # find a slot with value v
      --code="slot = lambda vs, v: vs.index(v)"
      
      # check statements truth value "x" says "y"
      --code="check = lambda x, y: bool(x) == bool(y)"
      
      # Beatrice says (Beatrice is not married to Alf)
      "check(W, G != 1)"
      
      # Beatrice says (Ernie says Charlie went to Dunkirk)
      "check(W, check(@ts[slot(@hs, 5)] ^ 1, @ds[slot(@hs, 3)] == 4))"
      
      # Daphne says (Charlie went to Ethiopia)
      "check(Y, @ds[slot(@hs, 3)] == 5)"
      
      # Daphne says (Beatrice went to Dunkirk)
      "check(Y, L == 4)"
      
      # Daphne says (Alf tells the truth)
      "check(Y, @ts[slot(@hs, 1)] == 0)"
      
      # Clarissa says (her husband says (Ethel is not married to Charlie))
      "check(X, check(X ^ 1, J != 3))"
      
      # Clarissa says (Duggie went to Boulogne)
      "check(X, @ds[slot(@hs, 4)] == 2)"
      
      --template="(F G H I J) (K L M N P) (V W X Y Z)"
      --solution=""
      

      And this following Python program formats the output using the appropriate labels:

      from enigma import (SubstitutedExpression, printf)
      
      p = SubstitutedExpression.from_file("teaser231.run",
        # return (<husbands>, <destinations>, <traits>)
        ["--answer=(@hs, @ds, @ts)"],
      )
      
      make_map = lambda vs: dict(enumerate(vs.split(), start=1))
      husbands = make_map("Alf Bert Charlie Duggie Ernie")
      wives = make_map("Agnes Beatrix Clarissa Daphne Ethel")
      destinations = make_map("Andorra Boulogne Calais Dunkirk Ethiopia")
      traits = { 0: 'False', 1: 'True' }
      
      for ans in p.answers(verbose=0):
        # collect the slots [wife, husband, destination, trait]
        d = dict((k, [v]) for (k, v) in wives.items())
        for (vs, labels) in zip(ans, [husbands, destinations, traits]):
          for (k, v) in enumerate(vs, start=1):
            d[k].append(labels[v])
        # output the slots
        for k in sorted(d.keys(), key=(lambda k: d[k][1])):
          (W, H, D, T) = d[k]
          printf("{H} and {W} ({T}) -> {D}")
        printf()
      

      Solution: Alf and Clarissa → Dunkirk; Bert and Daphne → Ethiopia; Charlie and Ethel → Andorra; Duggie and Agnes → Boulogne; Ernie and Beatrix → Calais

      We know:

      Alf and Clarissa → Dunkirk; (Alf = F; Clarissa = T)
      Bert and Daphne → Ethiopia; (Bert = T, Daphne = F)
      Charlie and Ethel → Andorra
      Duggie and Agnes → Boulogne
      Ernie and Beatrix → Calais (Ernie = F, Beatrix = T)

      We cannot determine the traits for Charlie and Ethel or for Duggie and Agnes.

      Like

      • Frits's avatar

        Frits 11:02 am on 24 September 2024 Permalink | Reply

        @Jim,

        I have a different naming convention for storing Python programs.
        One idea might be to use something like (assuming the filename doesn’t contain periods):

        import os
        
        runfile = os.path.basename(__file__).split(".")[0] + ".run"
        p = SubstitutedExpression.from_file(runfile, [
          # return (<husbands>, <destinations>, <traits>)
          "--answer=(@hs, @ds, @ts)",
        ])
        

        Like

        • Jim Randell's avatar

          Jim Randell 3:28 pm on 26 September 2024 Permalink | Reply

          I’ve added a [[ parsepath() ]] function to enigma.py that can be used to extract the following components of a path:

          {path} = the full path name = "{dir}/{file}"
          {stem} = path without extension = "{dir}/{name}"
          {dir}  = directory containing the file
          {file} = the filename = "{name}{ext}"
          {name} = the filename without extension
          {ext}  = the extension
          

          For example:

          {path} = "/users/jim/puzzles/enigma/enigma123.py"
          {stem} = "/users/jim/puzzles/enigma/enigma123"
          {dir}  = "/users/jim/puzzles/enigma"
          {file} = "enigma123.py"
          {name} = "enigma123"
          {ext}  = ".py"
          

          You can then call this to make a new path name from an existing one.

          For example to make a path in the same directory:

          path = parsepath("{dir}/enigma123.run", __file__)
          

          Or to just replace the extension:

          path = parsepath("{dir}/{name}.run", __file__)
          
          path = parsepath("{stem}.run", __file__)
          

          As an added bonus [[ SubstitutedExpression.from_file() ]] will automatically pass the arguments to [[ parsepath() ]] if a non-string is provided as a path, to save you the bother:

          p = SubstitutedExpression.from_file(["{stem}.run", __file__])
          

          Like

  • Unknown's avatar

    Jim Randell 9:55 am on 23 October 2022 Permalink | Reply
    Tags: by: E R Emmet   

    Brain-Teaser 177: The pay roll rules 

    From The Sunday Times, 30th August 1964 [link]

    I am the Managing Director of a factory and I have under me five employees. Their names are: Alf, Bert, Charlie, Duggie and Ernie. And their jobs are, not necessarily respectively: Doorkeeper, Doorknob Polisher, Bottle Washer, Welfare Officer and Worker.

    There has been some dissatisfaction recently about wages which, in the past, I am bound to admit, have sometimes been rather haphazard. It is clearly very difficult to arrange things in such a way that merit is appropriately rewarded, but it seemed to me important that everybody’s position should at least be clear. After much thought, therefore, I put up the following notice:

    Wages:

    1. Alf is to get more than Duggie.

    2. Ernie is to get 12 per cent more than the Bottle Washer will when he receives the 10 percent rise that he will be getting next month.

    3. The Doorknob Polisher is to get 30 per cent more than he used to.

    4. Charlie is to get £12 a year less than 20 per cent more than the Welfare Officer.

    5. No one is to get less than £200 or more than £600 a year.

    6. The Doorkeeper is to get 5 per cent more than he would if he got 10 per cent less than Bert.

    Everyone always has received in my factory, receives now, and as long as I am in charge will always receive an exact number of £s per year.

    What are the various jobs of my employees, and what yearly wage is each of them to get?

    This puzzle is included in the book Sunday Times Brain Teasers (1974). The puzzle text above is taken from the book.

    [teaser177]

     
    • Jim Randell's avatar

      Jim Randell 9:56 am on 23 October 2022 Permalink | Reply

      (See also: Teaser 811).

      I think the wording in this puzzle was slightly confusing.

      I am assuming that an unqualified wage referred to on the notice are the new wage that each employee is to receive immediately.

      However there is also a future wage referred to (the Bottle Washer will receive a 10% rise next month), and a previous wage (the Doorknob polisher receives 30% more than he used to).

      This time I used constraint satisfaction solver, so here is a MiniZinc model to solve the puzzle. It uses my minizinc.py library to provide a shortcut for allowing multiple variables with the same domain to be declared in one statement.

      %#! python3 -m minizinc use_embed=1
      
      include "globals.mzn";
      
      % wages by name: A, B, C, D, E
      {var("200..600", "ABCDE")};
      
      % wages by job: DK, DP, BW, WO, WK
      {var("200..600", ["DK", "DP", "BW", "WO", "WK"])};
      
      % the sets of values are the same
      constraint sort([A, B, C, D, E]) = sort([DK, DP, BW, WO, WK]);
      
      % 1. A gets more than D
      constraint A > D;
      
      % 2. In the future BW is to get a 10% increase ...
      constraint (BW * 110) mod 100 = 0;
      % ... and E (now) gets 12% more than this future amount
      constraint (BW * 112 * 110) mod (100 * 100) = 0;
      constraint E = (BW * 112 * 110) div (100 * 100);
      
      % 3. DP gets a 30% increase
      constraint (DP * 100) mod 130 = 0;
      
      % 4. C is to get 12 less than 20% more than WO
      constraint (WO * 120) mod 100 = 0;
      constraint C = (WO * 120) div 100 - 12;
      
      % 6. DK is to get 5% more than 10% less than B
      constraint (B * 90 * 105) mod (100 * 100) = 0;
      constraint DK = (B * 90 * 105) div (100 * 100);
      
      solve satisfy;
      

      And you can then run it like this:

      % python3 minizinc.py use_embed=1 teaser177.mzn
      A=378 B=400 C=468 D=250 E=308 DK=378 DP=468 BW=250 WO=400 WK=308
      

      Solution: The jobs and wages are as follows:

      Alf: Doorkeeper; £378
      Bert: Welfare Officer; £400
      Charlie: Doorknob Polisher; £468 (was £360)
      Duggie: Bottle Washer; £250 (future £275)
      Ernie: Worker; £308

      The originally published puzzle was pre-decimalisation, and gave the salaries in shillings per week, rather than pounds per year, but used the same values.

      Like

    • Hugh Casement's avatar

      Hugh Casement 11:08 am on 23 October 2022 Permalink | Reply

      I have yet to come across a good puzzle set by Eric Emmet.

      I think we have to assume that what the doorknob polisher “used to get” is what he currently gets, and that all the new wages come into effect next month.

      Like

      • Jim Randell's avatar

        Jim Randell 12:52 pm on 23 October 2022 Permalink | Reply

        @Hugh: I thought originally that everyone had a “before” and “after” value. But unless you have a “future” value for D (that comes after “after”), then you don’t get the published solution.

        Like

  • Unknown's avatar

    Jim Randell 2:25 pm on 8 August 2019 Permalink | Reply
    Tags: by: E R Emmet   

    Brain-Teaser 492: Light on the matches 

    From The Sunday Times, 1st November 1970 [link]

    Six football sides, A, B, C, D, E and F are all to play each other once, with 2 points for a win, 1 point for a draw.

    After some of the matches have been played a battered piece of paper is discovered on which has obviously been written particulars of the results so far.

    All that can be read is shown below:

    What were the details (opponents and scores) of E’s matches? (Example: EvP = 4-3, EvQ = 0-2, etc. E’s score first).

    This puzzle is included in the book Sunday Times Brain Teasers (1974).

    [teaser492]

     
    • Jim Randell's avatar

      Jim Randell 2:26 pm on 8 August 2019 Permalink | Reply

      F has 7 points and has played 5 games, but they only have 2 goals for, so cannot have won more than 2 games. Their 7 points must be made by 2w + 3d, which must be 1-0, 1-0, 0-0, 0-0, 0-0, so their goals against is 0. Hence the total number of goals scored is 25, and between them C and E have scored 17 goals.

      This Python program chooses values for C and E’s “goals for” values and then uses the [[ Football() ]] helper class from the enigma.py library to determine the outcomes and scorelines of each match.

      It runs in 550ms.

      Run: [ @repl.it ]

      from enigma import Football, digit_map, irange, int2base, sprintf
      
      # scoring system
      football = Football(points=dict(w=2, d=1))
      
      # labels for the teams
      (A, B, C, D, E, F) = (0, 1, 2, 3, 4, 5)
      
      # digits stand for themselves, and could go up to 17
      M = 17
      d = digit_map(0, M)
      
      # columns of the table (without goals against)
      (table, gf, ga) = (dict(played="24?3?5", w="1????2", l="??1??0", d='?????3', points="?373?7"), "41{C}1{E}2", "247570")
      
      # find possible match outcomes from the table
      for (ms, _) in football.substituted_table(table, teams=[F, A, B, C, D, E], d=d):
      
        # consider possible goals for C and E
        for (fC, fE) in zip(irange(0, M), irange(M, 0, step=-1)):
          gf1 = sprintf(gf, C=int2base(fC, M + 1), E=int2base(fE, M + 1))
      
          # find possible scorelines
          for ss in football.substituted_table_goals(gf1, ga, ms, d=d):
      
            # output solution
            football.output_matches(ms, ss, teams="ABCDEF")
      

      Solution: The outcomes of E’s matches are: E vs A = not yet played; E vs B = 0-1; E vs C = 3-5; E vs D = not yet played; E vs F = 0-1.

      There is only one scenario that works, which is when C has 14 goals for, and E has 3.

      The full scores are:

      A vs B = —
      A vs C = 4-1
      A vs D = —
      A vs E = —
      A vs F = 0-1
      B vs C = 0-3
      B vs D = 0-1
      B vs E = 1-0
      B vs F = 0-0
      C vs D = 5-0
      C vs E = 5-3
      C vs F = 0-0
      D vs E = —
      D vs F = 0-0
      E vs F = 0-1

      Like

      • John Crabtree's avatar

        John Crabtree 6:24 pm on 8 August 2019 Permalink | Reply

        This teaser has a logical solution which leads directly to the answer, with no other paths.

        Like

        • John Crabtree's avatar

          John Crabtree 2:36 pm on 13 August 2019 Permalink | Reply

          F has 7 points, but only scores 2 goals, and so F’s line reads 5 2 0 3 2 0 7
          C has 7 points, but loses a game, and so C’s line reads 5 3 1 1 ? 7 7
          As A plays 2 games, E must play 3 games, not 5.
          The total of the Played column = 22 = the total of the Points column.
          A has a minimum of 2 points and so the minimum Points total = 22
          And so A has 2 points and so A’s line reads 2 1 1 0 4 2 2
          And so E has 0 points and so B’s line reads 3 0 3 0 ? 7 0
          B can only draw one game not three, and so B’s line reads 4 1 2 1 1 4 3
          D can only draw one game,not three, and so D’s line reads 3 1 1 1 1 5 3

          By inspection A v F 0-1, B v F 0-0, C v F 0-0, D v F 0-0, E v F 0-1.
          Then A beats C. C beats B, D and E. B loses to D and beats E.
          From A’s GF and GA, A v C 4-1.
          From D”s GF = 1, , B v D 0-1
          From D’s GF and GA, C v D 5-0
          From B’s GF = 1, B v E 1-0
          From B’s GF and GA, C v B 3-0
          From C’s GA and E’s GA, C v E 5 -3

          And so E played 3 matches: E v B 0-1, E v C 3-5 and E v F 0-1

          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