Updates from February, 2025 Toggle Comment Threads | Keyboard Shortcuts

  • Unknown's avatar

    Jim Randell 5:07 pm on 26 February 2025 Permalink | Reply
    Tags:   

    Teaser 2548: Planetary line 

    From The Sunday Times, 24th July 2011 [link] [link]

    George and Martha are studying a far-off galaxy consisting of 10 planets circling a sun clockwise, all in the same plane. The planets are Unimus (taking one year to circle the sun), Dubious (two years), Tertius (three years), and so on up to Decimus (10 years).

    At one instant today the sun and all 10 planets were in one straight line. Martha said it will be ages before that happens again. “Don’t let’s be greedy,” said George. “How long must we wait until at least nine planets and the sun are in a straight line?”

    How long must they wait?

    In honour of the current planetary parade.

    [teaser2548]

     
    • Jim Randell's avatar

      Jim Randell 5:08 pm on 26 February 2025 Permalink | Reply

      Assuming the planets are in circular orbits, and they can be aligned on opposite sides of the sun.

      If we have a faster planet that makes a half orbit in period x, and a slower planet that makes a half orbit in period y, then, if they are initially in alignment, the time t taken for the fast planet to get one half orbit ahead of the slow planet is given by:

      t/x = t/y + 1

      t = x.y / |x − y|

      For for any collection of planets we can choose one of the planets, calculate the time taken for it to align with each of the other planets separately, and then calculate the LCM of these values to find the earliest time when they all align.

      This Python program runs in 68ms. (Internal runtime is 211µs).

      from enigma import (Rational, Accumulator, mlcm, mgcd, subsets, call, arg, printf)
      
      Q = Rational()
      
      # orbital periods of the planets
      orbits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      
      k = arg(9, 0, int)
      
      # find the earliest (half-orbit) alignment of the planets <ss>
      def align(ss):
        x = ss[0]
        qs = list(Q(x * y, 2 * abs(x - y)) for y in ss[1:])
        # calculate the LCM of the fractions
        n = call(mlcm, (q.numerator for q in qs))
        d = call(mgcd, (q.denominator for q in qs))
        return Q(n, d)
      
      # find minimal times
      r = Accumulator(fn=min, collect=1)
      
      # consider k sized subsets
      for ss in subsets(orbits, size=k):
        # calculate the alignment times for this set
        t = align(ss)
        printf("[{ss} -> {t} ({f})]", f=float(t))
        r.accumulate_data(t, ss)
      
      # output solution
      printf("min time = {r.value} ({f}) years", f=float(r.value))
      for ss in r.data:
        printf("-> {ss}")
      

      Solution: 9 planets and the sun will align after 180 years.

      If all the planets start at an angle of 0°, then in 180 years we have the following alignment:

      Planet 1: 0°
      Planet 2: 0°
      Planet 3: 0°
      Planet 4: 0°
      Planet 5: 0°
      Planet 6: 0°
      Planet 7: 257.14°
      Planet 8: 180°
      Planet 9: 0°
      Planet 10: 0°

      We see that planets 1, 2, 3, 4, 5, 6, 9, 10 are in their original positions, and planet 8 is on the opposite side of the sun (but still in a straight line with the other planets). Planet 7 is the only one that is off the line.

      After 1260 years all the planets will have completed a whole number of half-orbits (all except 8 have returned to their original positions, but 8 is on the opposite side of the sun), and after 2520 years (= LCM of 1 .. 10) all the planets have returned to their original positions and the cycle will start again. The cycle of alignments is as follows:

      0, 2520 years = all planets at 0°
      180, 540, 900, 1620, 1980, 2340 years = planets 1, 2, 3, 4, 5, 6, 9, 10 at 0°; planet 8 at 180°; planet 7 out of alignment
      360, 720, 1080, 1440, 1800, 2160 years = planets 1, 2, 3, 4, 5, 6, 8, 9, 10 at 0°; planet 7 out of alignment
      420, 2100 years = planets 1, 2, 3, 4, 5, 6, 7, 10 at 0°; planet 8 at 180°; planet 9 out of alignment
      630, 1890 years = planets 1, 2, 3, 5, 6, 7, 9, 10 at 0°; planet 4 at 180°; planet 8 out of alignment
      840, 1680 years = planets 1, 2, 3, 4, 5, 6, 7, 8, 10 at 0°; planet 9 out of alignment

      But we can have alignments other than at 0°/180°:

      For example the earliest alignment of the 5 planets 1, 3, 5, 7, 9 is after 78.75 years:

      They are aligned along 90°/270°.

      Like

  • Unknown's avatar

    Jim Randell 12:09 pm on 21 February 2025 Permalink | Reply
    Tags:   

    Teaser 2585: Easter Teaser 

    From The Sunday Times, 8th April 2012 [link] [link]

    I took the letters A, E, E, R, S and T and wrote down the long list of all possible different combinations of them. So, in alphabetical order, my list was AEERST, AERTS, …, EASTER, …, TEASER, …, TSREEA. I then assigned each of the letters a different odd digit, turning my list into a list of numbers. Surprisingly, the grand total of my list of numbers contained no odd digit.

    What is E+A+S+T+E+R?

    This completes the archive of Teaser puzzles from 2012, so there is now a complete archive of puzzles (and solutions) from Teaser 2569 (December 2011) to the most recent puzzle Teaser 3256 (February 2025), along with many earlier puzzles. There are another 21 puzzles to post from 2011 before all puzzles from the 2020 book are posted.

    Between S2T2 and Enigmatic Code there are now 3577 puzzles available.

    [teaser2585]

     
    • Jim Randell's avatar

      Jim Randell 12:09 pm on 21 February 2025 Permalink | Reply

      The following Python runs in 59ms. (Internal runtime is 367µs).

      from enigma import (irange, multiset, repdigit, subsets, nsplit, map2str, printf)
      
      word = 'EASTER'
      m = multiset.from_seq(word)
      
      # calculate multiplier for sum of letters
      n = m.size()
      p = repdigit(n) * m.multinomial() // n
      
      # even/odd digits
      evens = set(irange(0, 9, step=2))
      odds = irange(1, 9, step=2)
      
      # now assign odd digits to each letter
      rs = set()
      ks = sorted(m.keys())
      for vs in subsets(odds, size=len(ks), select='P'):
        d = dict(zip(ks, vs))
        # calculate the sum of the letters in the word
        s = sum(d[x] for x in word)
        # calculate the sum of all the permutations
        t = s * p
        # check the total contains only even digits
        if not evens.issuperset(nsplit(t)): continue
        # answer is sum of the letters in word
        printf("[sum = {s}; {d} -> {t}]", d=map2str(d))
        rs.add(s)
      
      printf("answer = {rs}", r=sorted(rs))
      

      Solution: E+A+S+T+E+R = 34.

      E is 9, and A, R, S, T are 1, 3, 5, 7 in some order.

      And the sum of all possible arrangements is 226666440.


      For EASTER there are 360 different arrangements of the letters (there are 2 E‘s that are not distinguishable).

      There are 6 possible positions each letter can appear in. And each of the 6 letters (including repeats) appears in each position 60 times.

      So each letter contributes 60 × 111111 = 6666660 times its own value to the total sum.

      With the sum of the letters in EASTER = 1 + 3 + 5 + 7 + 2×9 = 34 we arrive at a total sum of:

      total = 34 × 6666660 = 226666440

      which consists entirely of even digits.

      And we see that reordering the values of the non-repeated letters does not change the total.

      Using two copies of any odd digit other than 9 gives a total with at least one odd digit.

      Like

  • Unknown's avatar

    Jim Randell 8:30 am on 18 February 2025 Permalink | Reply
    Tags: by: Neil MacKinnon   

    Teaser 2567: A close thing 

    From The Sunday Times, 4th December 2011 [link] [link]

    Our football league consists of five teams, each playing each other once, earning three points for a win, one for a draw. Last season was close. At the end, all five teams had tied on points and on “goal difference”.

    So the championship was decided on “goals scored”, which, for the five teams, consisted of five consecutive numbers. The scores in the games were all different, including a no-score draw, and no team scored more than five goals in any game.

    How many goals did the league champions score in total?

    [teaser2567]

     
    • Jim Randell's avatar

      Jim Randell 8:30 am on 18 February 2025 Permalink | Reply

      I started doing some analysis of the puzzle, in order to write a program, but the solution fell out:

      There are 5 teams and each team plays 4 matches and has the same number of points. So the number of points must be a multiple of 5.

      There are 10 matches in total, and 2 or 3 points are awarded in each match, so the total number of points is between 20 (all draws) and 30 (all wins).

      So the total number of points must be 20, 25, or 30. However we are told there is a 0-0 draw, so the total cannot be 30, and there are only 6 possible different scores in drawn matches, so the total cannot be 20. Hence the total number of points awarded must be 25, and each team must have gained 5 points in their 4 matches. So each team must win 1 match, draw 2 matches, and lose the remaining match.

      So there are 5 matches that are won (each by one of the teams), and 5 matches that are drawn.

      The sum of the goal differences must be 0 (each goal for one team is against some other team), and so if the goal differences are all the same, they must all be 0.

      Hence the winning margin for each team in their won game must be balanced by the losing margin in their lost game, and so all the won games are won by the same margin. And to have 5 different scores the winning margin must be 1 goal (i.e. the games are 1-0, 2-1, 3-2, 4-3, 5-4). And these account for 25 goals scored.

      The remaining goals scored are accounted for by the 5 drawn matches, which are 0-0 and 4 of {1-1, 2-2, 3-3, 4-4, 5-5}.

      But the numbers of goals scored by each team are 5 consecutive numbers, say (x, x + 1, x + 2, x + 3, x + 4), which means the total number of goals scored is 5x + 10, i.e. a multiple of 5.

      And so the missing draw must be 5-5, leaving 20 goals scored in the drawn matches.

      So the total number of goals scored is 25 + 20 = 45 = 5x + 10.

      Hence x = 7, and the total goals scored by each team are (7, 8, 9, 10, 11).

      Solution: The league champions scored 11 goals in total.


      But I wanted to find a possible arrangement of matches, and so here is a program that does that:

      from enigma import (multiset, compare, subsets, diff, rev, unzip, flatten, printf)
      
      # won matches
      wins = [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4)]
      # drawn matches
      draws = [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
      
      # choose matches from <wins>, <draws> that along with existing matches <ms>
      # give 1 win, 1 loss, 2 draws, and <g> goals scored (and conceded)
      def choose(g, wins, draws, ms=[]):
        # count how many wins/draws/losses we already have
        m = multiset.from_seq(compare(x, y) for (x, y) in ms)
        (w, d, l) = (m.count(x) for x in [1, 0, -1])
        if w > 1 or l > 1 or d > 2: return
        # choose wins, losses, draws to make up the missing goals
        for ws in subsets(wins, size=1 - w):
          for ws_ in subsets(diff(wins, ws), size=1 - l):
            ls = tuple(rev(x) for x in ws_)
            for ds in subsets(draws, size=2 - d):
              # calculate goals for/against
              rs = flatten([ws, ls, ds])
              (f, a) = (sum(xs) for xs in unzip(flatten([rs, ms])))
              if not (f == a == g): continue
              # return (<chosen matches>, <remaining wins>, <remaining draws>)
              yield (rs, diff(wins, ws, ws_), diff(draws, ds))
      
      # find matches for (A, B, C, D, E) that give goals scored of (gA, gB, gC, gD, gE)
      def solve(gA, gB, gC, gD, gE):
        # choose a set of matches for A
        for (msA, wsA, dsA) in choose(gA, wins, draws):
          # and an order the matches
          for (AB, AC, AD, AE) in subsets(msA, size=len, select='P'):
      
            # choose the remaining matches for B
            for (msB, wsB, dsB) in choose(gB, wsA, dsA, [rev(AB)]):
              # and an order for the matches
              for (BC, BD, BE) in subsets(msB, size=len, select='P'):
      
                # choose the remaining matches for C
                for (msC, wsC, dsC) in choose(gC, wsB, dsB, [rev(AC), rev(BC)]):
                  # and an order for the matches
                  for (CD, CE) in subsets(msC, size=len, select='P'):
      
                    # the remaining match is DE
                    for ((DE,), wsD, dsD) in choose(gD, wsC, dsC, [rev(AD), rev(BD), rev(CD)]):
      
                      # check goals for/against E
                      for _ in choose(gE, wsD, dsD, [rev(AE), rev(BE), rev(CE), rev(DE)]):
      
                        # return the matches
                        yield (AB, AC, AD, AE, BC, BD, BE, CD, CE, DE)
      
      # find solutions
      for (AB, AC, AD, AE, BC, BD, BE, CD, CE, DE) in solve(11, 10, 9, 8, 7):
        printf("AB={AB} AC={AC} AD={AD} AE={AE} BC={BC} BD={BD} BE={BE} CD={CD} CE={CE} DE={DE}")
        break  # we only need one solution
      

      Here is an example set of matches:

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

      In total the program finds 200 ways to assign the matches so that the total goals scored for (A, B, C, D, E) are (11, 10, 9, 8, 7).

      The program finds an example set of matches in 3.3ms, and takes 209ms to find all 200 sets.

      Like

    • Frits's avatar

      Frits 10:33 am on 22 February 2025 Permalink | Reply

      Only using the fact that each team has 1 win, 1 loss and 2 draws.
      The program prints all 200 possibilities. It runs for 50 seconds.

      from itertools import permutations
      
      pts = lambda s: sum([3 if x > y else 0 if x < y else 1 for x, y in s])
      gdiff = lambda s: sum([x - y for x, y in s])
      gscored = lambda s: sum([x for x, _ in s])
      rev = lambda s: s[::-1]
      output = lambda s: '   '.join(', '.join(f"{x}-{y}" for x, y in z) for z in s)
      
      # determine 4 matches for each of <k> teams out of remaining games <gs>
      def solve(k, gs, gf=0, ss=[]):
        if k == 0:
          # draw 0-0 must exist (more likely at the end of <ss>)
          if any((0, 0) in ss[i] for i in range(3, -1, -1)): 
            yield ss, gf
        else:  
          # pick <k - 1> games from remaining games <gs>
          for p in permutations(gs, k - 1):
            gs_ = gs.difference(p)
            gs_ -= {rev(x) for x in p} 
            gd = gdiff(p)
            
            if not ss:
              # reverse score may not be played by the same player
              if any((y, x) in p for x, y in p if x != y): continue
            
              # we need at least one draw out of the 3 games
              if sum([x == y for x, y in p]) == 0: continue
              
              # process goals "for" for the 4th game
              for g4_f in range(6): 
                # goal difference must be zero
                if (g4 := (g4_f, gd + g4_f)) not in gs_: continue
                if pts(g := p + (g4, )) != 5: continue
                yield from solve(k - 1, gs_ - {g4, rev(g4)}, gscored(g) - 1, [g])
            else:   
              # already known games for this player
              known = tuple(rev(x[len(ss) - 1]) for x in ss)
              n3 = known + p
              
              # we need at least one draw out of the 3 games
              if sum([x == y for x, y in n3]) == 0: continue
              
              gs_ = gs_.difference(known)
              gs_ -= {rev(x) for x in known} 
              
              # goals "for" for the 4th game
              g4_f = gf - gscored(n3)     
              # goal difference must be zero
              if (g4 := (g4_f, gdiff(n3) + g4_f)) not in gs_: continue
              
              if pts(g := n3 + (g4, )) != 5: continue
              # reverse score may not be played by the same player
              if any((y, x) in g for x, y in g if x != y): continue
              
              yield from solve(k - 1, gs_ - {g4, rev(g4)}, gf - 1, ss + [g])
      
      games = {(i, j) for i in range(6) for j in range(6)}  
      sols = set()
      cnt = 0 
      # determine matches of teams A, B, C and D (decreasing scored goals)
      for (a, b, c, d), gf in solve(4, games):
        # check matches of team E that scored four goals less than A
        e = (rev(a[3]), rev(b[3]), rev(c[3]), rev(d[3]))
        if gscored(e) != gf: continue 
        if gdiff(e) != 0: continue
        if pts(e) != 5: continue  
        
        # integrity check
        abcde = a + b + c + d + e
        if any(abcde.count((x, y)) > 1 + (x == y) for x, y in abcde): continue
              
        cnt += 1
        print(f"{cnt:>3} ", output([a, b, c, d, e]))
        sols.add(str(gf + 4))
        
      print("answer:", ' or '.join(sols))  
      

      Like

    • Frits's avatar

      Frits 3:02 am on 23 February 2025 Permalink | Reply

      A similar approach. It takes 9 seconds to run.

      from enigma import SubstitutedExpression
       
      '''
      A vs B = A-B
      A vs C = C-D
      A vs D = E-F
      A vs E = G-H
      B vs C = I-J
      B vs D = K-L
      B vs E = M-N
      C vs D = O-P
      C vs E = Q-R
      D vs E = S-T
      '''
      
      pts = lambda s: sum([3 if x > y else 0 if x < y else 1 for x, y in s])
      gdiff = lambda s: sum([x - y for x, y in s])
      gscored = lambda s: sum([x for x, _ in s])
      # return sorted tuple
      stup = lambda x, y: (x, y) if x < y else (y, x)
      alldiff = lambda *s: len(set(p := [stup(x, y) for x, y in s])) == len(p)
      output = lambda s: '   '.join(', '.join(f"{x}-{y}" for x, y in z) for z in s)
      
      
      # check if both known games for C, D and E have zero goal difference 
      # (if win and loss)     
      def check(ss):
        chk = (1, 2, 3)  # check known games in C,D and E 
        # get known games for C, D and E (no need to reverse them)
        games_per_team = [[x[i] for x in ss] for i in chk]
        for gs in games_per_team:
          gd = [x - y for x, y in gs if x != y]
          # two non-draws?
          if len(gd) == 2 and sum(gd):
            return False
              
        return True     
      
      # the alphametic puzzle
      p = SubstitutedExpression(
        
        [ # all goal differences must be zero and 
          # we assume 1 win, 1 loss and 2 draws
          "gdiff([(A, B), (C, D), (E, F)]) + G = H",
          "(points := pts(gsa := [(A, B), (C, D), (E, F), (G, H)])) == 5",
          "alldiff(*gsa)",
          
          "(goalsa := gscored(gsa)) - B - I - K - 1 = M",
          "alldiff(*gsa, (I, J))",
          "gdiff([(B, A), (I, J), (K, L)]) + M = N",
          "pts(gsb := [(B, A), (I, J), (K, L), (M, N)]) == points",
          "alldiff(*gsa, *gsb[1:])",
          # check the already known games of C, D and E
          "check([gsa, gsb])",
          
          "goalsa - D - J - O - 2 = Q",
          "gdiff([(D, C), (J, I), (O, P)]) + Q = R",
          "pts(gsc := [(D, C), (J, I), (O, P), (Q, R)]) == points",
          "alldiff(*gsa, *gsb[1:], *gsc[2:])",
          
          "goalsa - F - L - P - 3 = S",
          "goalsa - H - N - R - 4 = T",
          "pts(gsd := [(F, E), (L, K), (P, O), (S, T)]) == points",
          "gdiff(gsd) == 0",  
          
          "pts(gse := [(H, G), (N, M), (R, Q), (T, S)]) == points",
          "gdiff(gse) == 0",  
          
          "(0, 0) in (*gsa, *gsb[1:], *gsc[2:], *gsd[3:])",
          "alldiff(*gsa, *gsb[1:], *gsc[2:], *gsd[3:])",
        ],
        answer="(gsa, gsb, gsc, gsd, gse), goalsa",
        base=6,
        distinct="",
        env=dict(pts=pts, alldiff=alldiff,gdiff=gdiff,gscored=gscored, check=check),
        reorder=0,
        verbose=0,    # use 256 to see the generated code
      ) 
      
      sols = set()
      # print answers
      for i, (gs, ga) in enumerate(p.answers(), 1):
        print(f"--{i:>4}-- {output(gs)}")
        sols.add(str(ga))
      
      print("answer:", ' or '.join(sols))  
      

      Like

  • Unknown's avatar

    Jim Randell 9:21 am on 11 February 2025 Permalink | Reply
    Tags: by: Roger Webster   

    Teaser 2562: Chez nous 

    From The Sunday Times, 30th October 2011 [link] [link]

    A sign-writer stocks signs of house numbers written in words, from “one” up to my own house number. The signs are kept in boxes according to the number of letters used. (e.g., all copies of “seven” and “fifty” are in the same box). Each box contains at least two different signs. Boxes are labelled showing the number of letters used, (e.g. the box holding “fifty” is labelled “five”).

    Naturally, the sign-writer has used signs from his own stock for the labels. To label all the boxes he needed to take signs from half of the boxes.

    What is my house number?

    [teaser2562]

     
    • Jim Randell's avatar

      Jim Randell 9:21 am on 11 February 2025 Permalink | Reply

      I used the [[ int2words() ]] routine from the enigma.py library to turn the numbers into English text.

      The following Python program runs in 61ms. (Internal runtime is 406µs).

      from enigma import (defaultdict, irange, inf, int2words, cache, printf)
      
      # integer -> number of letters
      i2k = cache(lambda n: sum(x.isalpha() for x in int2words(n)))
      
      # group numbers by letter count
      g = defaultdict(list)
      
      # consider increasing numbers
      for n in irange(1, inf):
        # translate the number to words, and count the letters
        k = i2k(n)
        g[k].append(n)
        if n < 50: continue
      
        # each box contains at least two different signs
        if any(len(vs) < 2 for vs in g.values()): continue
      
        # to label the boxes requires signs from exactly half the boxes
        ks = list(g.keys())
        ss = set(i2k(n) for n in ks)
        if not (2 * len(ss) == len(ks) and ss.issubset(ks)): continue
      
        # output solution
        printf("n = {n}; {x} boxes", x=len(ks))
        for k in sorted(ks):
          printf("{k} -> {vs}", vs=g[k])
        printf("labels from {ss}; {x} boxes", ss=sorted(ss), x=len(ss))
        printf()
        break
      

      Solution: Your house number is 112.

      The 14 boxes are labelled (with the length of the label indicated in parentheses):

      three (5)
      four (4)
      five (4)
      six (3)
      seven (5)
      eight (5)
      nine (4)
      ten (3)
      eleven (6)
      twelve (6)
      sixteen (7)
      seventeen (9)
      eighteen (8)
      nineteen (8)

      And to label these boxes you need to use labels from the following 7 boxes:

      3, 4, 5, 6, 7, 8, 9

      Like

  • Unknown's avatar

    Jim Randell 8:16 am on 7 February 2025 Permalink | Reply
    Tags:   

    Teaser 2575: More teams 

    From The Sunday Times, 29th January 2012 [link] [link]

    In our local pub soccer league each team plays each of the others at home and away during the season. Two seasons ago we had a two-digit number of teams. The number increased the next year, and again this year, but this time the increase was one less than the previous season. This season the number of games played will be over three times the number played two seasons ago. Also, this season’s increase in games played actually equals the number played two seasons ago.

    How many teams are there now in the league?

    [teaser2575]

     
    • Jim Randell's avatar

      Jim Randell 8:16 am on 7 February 2025 Permalink | Reply

      The number of teams goes:

      n → n + (k + 1) → n + (2k + 1)

      where n is a 2-digit number.

      So the current season has an odd number of teams (≥ 3) more than the number of teams two seasons ago.

      Each pair of teams plays twice, so for n teams the number of matches in a season is:

      M(n) = 2 C(n, 2) = n (n − 1)

      and if the numbers of matches are A → B → C we have:

      C > 3A
      C − B = A; or: A + B = C

      This Python program looks for the first possible solution.

      It runs in 66ms. (Internal runtime is just 38µs).

      from enigma import (irange, inf, printf)
      
      # number of matches for n teams = 2 * C(n, 2)
      M = lambda n: n * (n - 1)
      
      def solve():
        # consider a league with c teams (current season)
        for c in irange(13, inf):
          C = M(c)
          # consider an odd number of teams less than this (two seasons ago)
          for a in irange(c - 3, 10, step=-2):
            A = M(a)
            if 3 * A >= C or a > 99: continue
            b = (a + c + 1) // 2
            B = M(b)
            if A + B == C:
              yield ((a, b, c), (A, B, C))
      
      # find the first solution
      for ((a, b, c), (A, B, C)) in solve():
        printf("{a} teams, {A} matches -> {b} teams, {B} matches -> {c} teams, {C} matches")
        break
      

      Solution: There are now 17 teams in the league.

      And so there are 272 matches in the current season.

      Two years ago there were 10 teams in the league, and the season consisted of 90 matches. (The current season has more than 3× this number of matches).

      One year ago there were 14 teams in the league, and the season consisted of 182 matches, and 182 + 90 = 272.

      Like

      • Frits's avatar

        Frits 4:07 pm on 7 February 2025 Permalink | Reply

        Further analysis (solving a quadratic equation) leads to :

        c = (5a + 1) / 3

        and C greater than 3 * A means a^2 -11a + 1 less than 0.
        a =10 is still valid but already at a = 11 the condition is not met.

        Like

        • John Crabtree's avatar

          John Crabtree 4:58 am on 14 February 2025 Permalink | Reply

          Frits, nicely done. It is useful to know the intermediate result, ie n = 3k – 2

          Like

  • Unknown's avatar

    Jim Randell 9:15 am on 4 February 2025 Permalink | Reply
    Tags:   

    Teaser 2571: Dog show 

    From The Sunday Times, 1st January 2012 [link] [link]

    Five dogs were entered by owners Alan, Brian, Colin, David and Edward, whose surnames are Allen, Bryan, Collins, Davis and Edwards. No owner had the same first and second initials, no two had the same pair of initials and none shared an initial with their breed.

    Colin was in position 1, David Allen’s dog was in position 4, Brian’s corgi was not next to the collie; the chow and doberman were at opposite ends. The other breed was a dachshund.

    In the voting, dogs eliminated in order were Mr Bryan’s, the corgi, David’s and the doberman.

    Who won, and what breed was their dog?

    [teaser2571]

     
    • Jim Randell's avatar

      Jim Randell 9:17 am on 4 February 2025 Permalink | Reply

      Initially, I found the wording of this puzzle a bit confusing.

      I think the situation is that the five competitors are lined up (I assigned them numbers 1 – 5 in the line), they are then inspected (in order) by the judges, who then deliberate, eliminating one competitor at a time until there is only the winner remaining. Although we are given the order of the elimination, this has nothing to do with the order of the line-up, but serves to tell us that the descriptions of the four eliminations identify four different competitors.

      I used the [[ SubstitutedExpression() ]] solver from the enigma.py library to check most of the required constraints. (I found it was easier to check the “no competitors shared the same pair of initials” condition after the slots in the line-up had been filled out).

      The following Python program runs in 73ms. (Internal runtime is 4.9ms).

      from enigma import (SubstitutedExpression, irange, seq_all_different, ordered, printf)
      
      # we allocate the competitor numbers: 1 - 5 to:
      #
      #  owner:   A B C D E  [A = Alan; B = Brian; C = Colin; D = David; E = Edward]
      #  surname: F G H I J  [F = Allen; G = Bryan; H = Collins; I = Davis; J = Edwards]
      #  breed:   K L M N P  [K = corgi; L = collie; M = chow; N = doberman; P = dachshund]
      
      # the distinct groups:
      distinct = [
        # each group is a permutation of the digits 1-5
        'ABCDE', 'FGHIJ', 'KLMNP',
        # no owner has the same first/last initial
        # no breed shares an initial with either of its owners names
        'AF', 'BG', 'CHKLM', 'DINP', 'EJ',
        # elimination order is: Bryan (G), corgi (K), David (D), doberman (N)
        # so these must all be in different positions
        'GKDN',
      ]
      
      # assignments we are given:
      #   Colin (C) is competitor 1
      #   David Allen (D F) is competitor 4
      s2d = dict(C=1, D=4, F=4)
      
      # invalid assignments we are given:
      #   the chow (M) and the doberman (N) were at opposite ends (i.e. 1 and 5)
      d2i = dict.fromkeys([2, 3, 4], "MN")
      
      # additional constraints
      exprs = [
        # Brian (B)'s corgi (K) was not next to the collie (L)
        "B = K",
        "abs(K - L) > 1",
      ]
      
      # construct the puzzle
      p = SubstitutedExpression(
        exprs,
        base=6, digits=irange(1, 5),
        distinct=distinct, s2d=s2d, d2i=d2i,
        answer="((A, B, C, D, E), (F, G, H, I, J), (K, L, M, N, P))",
      )
      
      # labels for owners / surnames / breeds
      owners = "Alan Brian Colin David Edward".split()
      surnames = "Allen Bryan Collins Davis Edwards".split()
      dogs = "corgi collie chow doberman dachshund".split()
      
      # solve the puzzle and fill out the slots in the line-up
      for ss in p.answers(verbose=0):
        slots = dict((k, [None] * 3) for k in irange(1, 5))
        for (i, (s, labels)) in enumerate(zip(ss, [owners, surnames, dogs])):
          for (k, v) in zip(s, labels):
            slots[k][i] = v
        # check: no two owners share the same pair of initials
        if not seq_all_different(ordered(fn[0], sn[0]) for (fn, sn, br) in slots.values()): continue
      
        # output the competitors, and identify the winner
        for k in sorted(slots.keys()):
          (fn, sn, br) = slots[k]
          eliminated = (sn == 'Bryan' or br == 'corgi' or fn == 'David' or br == 'doberman')
          printf("({k}) {fn} {sn}, {br}{w}", w=('' if eliminated else ' [WINNER]'))
        printf()
      

      Solution: The winner was the dachshund, owned by Alan Collins.

      The line-up of the competitors was:

      (1) Colin Edwards, doberman
      (2) Brian Davis, corgi
      (3) Alan Collins, dachshund [WINNER]
      (4) David Allen, collie
      (5) Edward Bryan, chow

      Like

  • Unknown's avatar

    Jim Randell 10:39 am on 31 January 2025 Permalink | Reply
    Tags: ,   

    Teaser 2581: Resplendency 

    From The Sunday Times, 11th March 2012 [link] [link]

    Our local Hibernian band will lead the St Patrick’s Day parade next Saturday, resplendent in their new tunics and finery. The total cost of this was one thousand pounds and it was generously paid for by the parish council. Each tunic cost the same two-figure number of pounds (and that number happens to be double the number of girls in the band). In addition, each girl in the band has some extra finery, and the cost of this for each girl was the same as the cost of the tunic but with the two digits in reverse order.

    How many boys and how many girls are there in the band?

    [teaser2581]

     
    • Jim Randell's avatar

      Jim Randell 10:40 am on 31 January 2025 Permalink | Reply

      If:

      b = number of boys
      g = number of girls
      t = cost of tunic (pounds)
      f = cost of finery (pounds) = reverse of t

      we have:

      t = 2 × g
      t × b + (t + f) × g = 1000

      Hence b, g, f can all be expressed in terms of t, which is a 2-digit even number.

      This Python program runs in 66ms. (Internal runtime is 57µs).

      from enigma import (irange, cproduct, div, printf)
      
      # the cost of each tunic is XY, a 2-digit even number
      # the cost of each finery is YX, a 2 digit number
      for (X, Y) in cproduct([irange(1, 9), [2, 4, 6, 8]]):
        (t, f) = (10 * X + Y, 10 * Y + X)
      
        # calculate the number of girls and boys
        g = t // 2
        b = div(1000 - (t + f) * g, t)
        if b is None or b < 0: continue
      
        # output solution
        printf("b={b} g={g}; t={t} f={f}")
      

      Solution: There are 24 boys and 8 girls.

      The tunics cost £ 16, and the finery costs £ 61.

      £16 × 24 + £(16 + 61) × 8 = £1000

      Like

      • Ruud's avatar

        Ruud 3:58 pm on 2 February 2025 Permalink | Reply

        Very straightforward:

        import peek
        import istr
        
        for n_girls in istr(range(5, 50)):
            tunic_cost = 2 * n_girls
            finery_cost = tunic_cost.reversed()
            girls_total = n_girls * (tunic_cost + finery_cost)
            boys_total = 1000 - girls_total
            if finery_cost[0] != 0 and boys_total.is_divisible_by(tunic_cost):
                n_boys = boys_total / tunic_cost
                peek(girls_total, boys_total, n_girls, n_boys, tunic_cost, finery_cost)
        

        Like

    • GeoffR's avatar

      GeoffR 9:03 am on 1 February 2025 Permalink | Reply

      # the cost of each tunic is XY, a 2-digit even number
      # the cost of each finery is YX, a 2 digit number
      
      for X in range(1, 9):
        for Y in range(2, 10, 2):
          XY, YX = 10*X + Y, 10*Y + X
          for girls in range(1, 100):
            # cost of a tunic is double the number of girls
            if XY != 2 * girls:continue
            for boys in range(1, 100):
              tunics = (boys + girls) * XY
              finery = girls * YX
              # total cost = £1,000
              if tunics + finery == 1000:
                print(f"Band = {boys} boys and {girls} girls.")
      
      # Band = 24 boys and 8 girls.
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:41 am on 28 January 2025 Permalink | Reply
    Tags:   

    Teaser 2582: Anyone for tennis? 

    From The Sunday Times, 18th March 2012 [link] [link]

    The girls of St Trinian’s choose two games from the four on offer. In Felicity’s gang of four, no game was chosen by both Daphne and Erica; Chloe and Miss Brown chose lacrosse; Miss Smith chose netball; and only Miss Jones excluded hockey. In Harriet’s gang of four, Clara, Debbie and Ellen all chose netball but only one of the four chose hockey. To avoid having two girls with the same first name initial in any one game, one of the girls in the second group (but not Debbie) moved from one game to another. This meant that there was an odd number of these [eight] girls playing each game.

    Which of the eight girls played tennis?

    [teaser2582]

     
    • Jim Randell's avatar

      Jim Randell 10:42 am on 28 January 2025 Permalink | Reply

      We want each of the sports to end up with an odd number of participants after the swap.

      So before the swap there must be two sports that have an even number of participants (and the other two sports have an odd number of participants). And the swap must occur from one of the sports with an even number of participants to the other sport with an even number of participants. Leaving all sports with an odd number.

      Furthermore, the girl who swaps (who is in the second group, and shares and initial with a member of the first group, and is not Debbie – so must be Clara or Ellen), must have chosen one of the even sports (that was also chosen by her counterpart with the same initial), and not chosen the other even sport (that was also not chosen by her counterpart with the same initial, otherwise the swap would not remedy the situation). And this must be the only situation where a sport has been chosen by two girls with the same initial.

      This Python program uses the [[ SubstitutedExpression() ]] solver from the enigma.py library to determine possible choices for each of the groups separately, and then combines possibilities to consider possible choices for the 8 girls, and then checks the remaining conditions for the combined choices.

      It runs in 82ms. (Internal runtime is 11.5ms).

      from enigma import (
        SubstitutedExpression, cproduct, chain, filter2, multiset,
        singleton, diff, join, printf
      )
      
      # possible choices are:
      #
      #   k   L N H T
      #   0 = - - x x (HT)
      #   5 = x x - - (LN)
      #   1 = - x - x (NT)
      #   4 = x - x - (LH)
      #   2 = - x x - (NH)
      #   3 = x - - x (LT)
      #
      # such that for a choice k the unchosen options are (5 - k)
      
      # map choice numbers to sports (0 - 5)
      choice = ['HT', 'NT', 'NH', 'LT', 'LH', 'LN']
      
      # sets for the four sports
      macros = {
        'lacrosse': "{3, 4, 5}",
        'netball': "{1, 2, 5}",
        'hockey': "{0, 2, 4}",
        'tennis': "{0, 1, 3}",
      }
      
      # group 1:
      # for sport choices allocate: C, D, E, F to k-values (0-5)
      # for surnames allocate: B, S, J to values 0-3 (= C - F) [distinct]
      exprs1 = [
        # "no game was chosen by both D and E"
        "D + E == 5",
        # "C chose lacrosse"
        "C in @lacrosse",
        # "Miss Brown (who is not C) also chose lacrosse"
        "B != 0",
        "[C, D, E, F][B] in @lacrosse",
        # "Miss Smith chose netball"
        "[C, D, E, F][S] in @netball",
        # only Miss Jones excluded hockey
        "all((x in @hockey) == (i != J) for (i, x) in enumerate([C, D, E, F]))",
      ]
      
      names1 = "Chloe Daphne Erica Felicity".split()
      surnames1 = "Brown Smith Jones".split()
      
      def solve1():
        # find the first group
        p1 = SubstitutedExpression(
          exprs1, base=6, macro=macros,
          distinct="BSJ", d2i={ 4: "BSJ", 5: "BSJ" },
        )
        for s in p1.solve(verbose=0):
          # map names to choices
          d1 = dict((x, choice[s[x[0]]]) for x in names1)
          # map names to surnames
          sn = dict((names1[s[x[0]]], x) for x in surnames1)
          yield (d1, sn)
      
      
      # group 2:
      # for sport choices allocate: C, D, E, H to k-values (0-5)
      exprs2 = [
        # "C, D, E all chose netball"
        "{C, D, E}.issubset(@netball)",
        # "only one of C, D, E, H chose hockey"
        "sum(x in @hockey for x in [C, D, E, H]) == 1",
      ]
      
      names2 = "Clara Debbie Ellen Harriet".split()
      
      def solve2():
        # find the second group
        p2 = SubstitutedExpression(exprs2, base=6, macro=macros, distinct="")
        for s in p2.solve(verbose=0):
          # map names to choices
          d2 = dict((x, choice[s[x[0]]]) for x in names2)
          yield d2
      
      
      # output a group
      def output(g, sn=None):
        for k in sorted(g.keys()):
          s = (sn.get(k) if sn else None)
          name = (k + " " + s if s else k)
          printf("{name} -> {v}", v=join(g[k], sep=' + '))
        printf()
      
      # choose solutions for each group
      for ((g1, sn), g2) in cproduct([solve1(), solve2()]):
        # make the combined solution
        g = dict(chain(g1.items(), g2.items()))
        # map each sport to a list of girls
        s = dict((k, list()) for k in 'LNHT')
        for (k, vs) in g.items():
          for v in vs:
            s[v].append(k)
      
        # find sports with even and odd numbers of participants
        (even, odd) = filter2((lambda k: len(s[k]) % 2 == 0), s.keys())
        # there must be 2 even sports
        if len(even) != 2: continue
      
        # find sports that have more than one participant with a given initial
        ms = list()
        for (k, vs) in s.items():
          ds = list(x for (x, n) in multiset.from_seq(v[0] for v in vs).items() if n > 1)
          if ds: ms.append((k, ds))
        # there must be just one sport with just one duplicated initial
        if not (len(ms) == 1 and len(ms[0][1]) == 1): continue
        (x, i) = (ms[0][0], ms[0][1][0])
        # the duplicate sport must have an even number of participants
        # and we must swap to the other sport with an even number of participants
        y = singleton(diff(even, {x}))
        if y is None: continue
        # the duplicate initial must be C or E
        swap = other = None
        if i == 'C':
          # Clara needs to swap from x to y
          (swap, other) = ('Clara', 'Chloe')
        elif i == 'E':
          # Ellen needs to swap from x to y
          (swap, other) = ('Ellen', 'Erica')
        # check the swap fixes the issue
        if swap is None or not (x in g[swap] and y not in g[swap] and y not in g[other]): continue
      
        # final list for tennis
        ts = list(s['T'])
        if x == 'T': ts.remove(swap)
        if y == 'T': ts.append(swap)
      
        # output solution:
        # first output the choices for each group
        output(g1, sn)
        output(g2)
        # output the swap, and the final list for tennis
        printf("{swap} swaps {x} -> {y}")
        printf("-> tennis = {ts}", ts=join(ts, sep=", ", sort=1))
        printf()
      

      Solution: The girls playing tennis are: Clara, Debbie, Erica.

      The initial choices made are:

      Chloe = lacrosse + hockey
      Daphne Brown = lacrosse + hockey
      Erica Jones = netball + tennis
      Felicity Smith = netball + hockey

      Clara = netball + tennis
      Debbie = netball + tennis
      Ellen = lacrosse + netball
      Harriet = netball + hockey

      But both Erica and Ellen have chosen netball, so there would be 2 girls with the same initial in the netball group.

      To fix this Ellen swaps from netball to hockey, giving the following assignments:

      lacrosse = Chloe, Daphne, Ellen (CDE, 3)
      netball = Clara, Debbie, Erica, Felicity, Harriet (CDEFH, 5)
      hockey = Chloe, Daphne, Ellen, Felicity, Harriet (CDEFH, 5)
      tennis = Clara, Debbie, Erica (CDE, 3)

      Like

  • Unknown's avatar

    Jim Randell 7:58 am on 24 January 2025 Permalink | Reply
    Tags:   

    Teaser 2583: Playing cards 

    From The Sunday Times, 25th March 2012 [link] [link]

    Oliver wrote down some numbers and then used a letter-for-digit code to make them into words. In this way his numbers became JACK, QUEEN, KING and ACE. In fact JACK and KING were perfect squares, QUEEN was odd, and ACE was a prime. Furthermore, appropriately enough, the sum of the four was divisible by 52.

    What number was represented by QUEEN?

    [teaser2583]

     
    • Jim Randell's avatar

      Jim Randell 7:59 am on 24 January 2025 Permalink | Reply

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

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

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # JACK and KING are perfect squares
      "is_square(JACK)"
      "is_square(KING)"
      
      # QUEEN is odd
      "N % 2 = 1"
      
      # ACE is prime
      "is_prime(ACE)"
      
      # total sum is divisible by 52
      "(JACK + QUEEN + KING + ACE) % 52 = 0"
      
      --answer="QUEEN"
      

      Solution: QUEEN = 89115.

      We have:

      JACK = 2704 = 52^2
      KING = 4356 = 66^2
      QUEEN = 89115 (odd)
      ACE = 701 (prime)
      sum = 97056 = 52 × 1863

      Like

    • GeoffR's avatar

      GeoffR 3:52 pm on 24 January 2025 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:J; var 1..9:A; var 0..9:C; var 1..9:K;
      var 1..9:Q; var 0..9:U; var 0..9:E; var 0..9:N;
      var 0..9:I; var 0..9:G; 
      
      var 1000..9999:JACK == 1000*J + 100*A + 10*C + K;
      var 10000..99999:QUEEN == 10000*Q + 1000*U + 110*E + N;
      var 1000..9999:KING == 1000*K + 100*I + 10*N + G;
      var 100..999:ACE == 100*A + 10*C + E;
      
      constraint all_different([J,A,C,K,Q,U,E,N,I,G]);
      
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) ((i < x) -> (x mod i > 0));
          
      predicate is_sq(var int: c) =
         let {
            var 0..ceil(pow(int2float(ub(c)),(1/2))): z
         } in z*z = c;
         
      constraint is_sq(JACK) /\ is_sq(KING);
      constraint QUEEN mod 2 == 1;
      constraint is_prime(ACE);
      constraint (JACK + QUEEN + KING + ACE) mod 52 == 0;
       
      solve satisfy;
      
      output["[JACK, QUEEN, KING, ACE ] =" ++ show ( [JACK, QUEEN, KING, ACE] ) ];
      
      % [JACK, QUEEN, KING, ACE ] =[2704, 89115, 4356, 701]
      % ----------
      % ==========
       
      
      

      Like

    • Ruud's avatar

      Ruud 5:57 pm on 24 January 2025 Permalink | Reply

      import istr
      
      for j, a, c, k, q, u, n, i, g, e in istr.permutations(istr.digits()):
          if (
              (j | a | c | k).is_square()
              and (k | i | n | g).is_square()
              and (q | u | e | e | n).is_odd()
              and (a | c | e).is_prime()
              and ((j | a | c | k) + (k | i | n | g) + (q | u | e | e | n) + (a | c | e)).is_divisible_by(52)
          ):
              print(f"queen={q | u | e | e | n}")
      

      Like

    • GeoffR's avatar

      GeoffR 2:17 pm on 25 January 2025 Permalink | Reply

      from itertools import permutations
      from enigma import is_prime
      sq4 = [x * x for x in range(32,100)]
      digits = set(range(10))
      
      # First stage permutation
      for p1 in permutations(digits, 4):
        k, i, n, g = p1
        if k == 0:continue
        king = 1000*k + 100*i + 10*n + g
        if king not in sq4:continue
        
        # Second stage permutation
        q1 = digits.difference(p1)
        for p2 in permutations(q1, 4):
          j, a, c, e = p2
          if 0 in (j, a): continue
          jack = 1000*j + 100*a + 10*c + k
          if jack not in sq4:continue
          ace = 100*a + 10*c + e
          if not is_prime(ace):continue
          
          # Third stage permutation
          q2 = q1.difference(p2) 
          for p3 in permutations(q2):
            q, u = p3
            if q == 0:continue
            queen = 10000*q + 1000*u + 110*e + n
            if queen % 2 == 1:
              if (jack + king + ace + queen) % 52 == 0:
                print(f"QUEEN = {queen}")
      
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 7:42 am on 17 January 2025 Permalink | Reply
    Tags: ,   

    Teaser 2588: On-line 

    From The Sunday Times, 29th April 2012 [link] [link]

    Linesmen Alf and Bert were checking the railway line from Timesborough to Teaserton. Alf started at Timesborough, Bert at Teaserton, and they walked towards each other at their own steady speeds. A train from Timesborough, travelling at constant speed, reached Alf at noon and passed him ten seconds later. At 12:06 pm the train reached Bert and passed him eight seconds later.

    At what time did the two men meet?

    [teaser2588]

     
    • Jim Randell's avatar

      Jim Randell 7:42 am on 17 January 2025 Permalink | Reply

      This puzzle is solved with a straightforward bit of analysis.

      Suppose A is walking with a speed of a (units/s) and B with speed of b, and the train travels with a speed of v.

      The train takes 10s to pass A, so in that 10s A walks a distance 10a, and the front of the train travels a distance 10v (in the same direction), so the length of the train t is:

      t = 10(v − a)

      The train takes 8s to pass B, and in that 8s B walks a distance 8a, and the front of the train travels a distance 8v (in the opposite direction), and so the length of the train can also be expressed as:

      t = 8(v + b)

      So:

      10(v − a) = 8(v + b)
      v = 5a + 4b

      The front of the train is level with A at noon, and at 12:06pm (i.e. 360 seconds later) it is level with B.

      So the train has travelled a distance of 360v in that time, and A has travelled a distance of 360a, so at 12:06 pm the distance between A and B is:

      d = 360(v − a)

      And the time taken for A and B to reduce this distance to zero is:

      360(v − a) / (a + b)
      = 360 (4a + 4b) / (a + b)
      = 360 × 4

      So it takes another 4× 6 minutes = 24 minutes after 12:06 pm for A and B to meet.

      Solution: Alf and Bert met at 12:30 pm.

      Like

  • Unknown's avatar

    Jim Randell 10:07 am on 14 January 2025 Permalink | Reply
    Tags: by: Graham Smith   

    Teaser 2590: Moving downwards 

    From The Sunday Times, 13th May 2012 [link] [link]

    A three-by-three array of the nine digits 1 – 9 is said to be “downward moving” if each digit is less than the digit to the east of it and to the south of it — see, for example, the keypad on a mobile phone.

    Megan has produced a downward moving array that is different from that on a mobile phone. If she were to tell you the sum of the digits in its left-hand column and whether or not the central digit was even, then you should be able to work out Megan’s array.

    What is Megan’s array?

    [teaser2590]

     
    • Jim Randell's avatar

      Jim Randell 10:09 am on 14 January 2025 Permalink | Reply

      This Python program uses the [[ SubstitutedExpression ]] solver from the enigma.py library to generate all possible “downward moving” arrays. And then uses the [[ filter_unique() ]] function to find solutions that are unique by the required criteria.

      It runs in 74ms. (Internal runtime is 9ms).

      from enigma import (SubstitutedExpression, irange, filter_unique, unpack, chunk, printf)
      
      # for a "downward moving" grid the rows and columns are in ascending order
      # consider the grid:
      #   A B C
      #   D E F
      #   G H I
      exprs = [
        "A < B < C", "D < E < F", "G < H < I",
        "A < D < G", "B < E < H", "C < F < I",
      ]
      
      # make a solver to find downward moving grids
      p = SubstitutedExpression(
        exprs,
        digits=irange(1, 9),
        answer="(A, B, C, D, E, F, G, H, I)",
      )
      
      # look for a non-standard layout, unique by <f>
      non_standard = lambda x: x != (1, 2, 3, 4, 5, 6, 7, 8, 9)
      # f = (sum of digits in LH column, central digit parity)
      f = unpack(lambda A, B, C, D, E, F, G, H, I: (A + D + G, E % 2))
      rs = filter_unique(p.answers(verbose=0), f, st=non_standard).unique
      
      # output solution(s)
      for r in rs:
        printf("{r}", r=list(chunk(r, 3)))
      

      Solution: Megan’s downward moving array looks like this:

      Of the 42 possible downward moving arrays, this is the only one that is unique by LH column sum (11) and parity of the central digit (even).

      (Of course moving from left-to-right and top-to-bottom the numbers are increasing, so maybe this should be considered an “upward moving” array).

      Liked by 1 person

    • Ruud's avatar

      Ruud 8:27 am on 16 January 2025 Permalink | Reply

      import itertools
      import collections
      
      
      collect = collections.defaultdict(list)
      for a in itertools.permutations(range(1, 10)):
          if (
              all(a[row * 3] < a[row * 3 + 1] < a[row * 3 + 2] for row in range(3))
              and all(a[col] < a[col + 3] < a[col + 6] for col in range(3))
              and a != tuple(range(1, 10))
          ):
              collect[a[0] + a[3] + a[6], a[4] % 2].append(a)
      
      for ident, solutions in collect.items():
          if len(solutions) == 1:
              for row in range(3):
                  for col in range(3):
                      print(solutions[0][row * 3 + col], end="")
                  print()
      

      Like

    • GeoffR's avatar

      GeoffR 9:28 am on 18 January 2025 Permalink | Reply

      #   A B C
      #   D E F
      #   G H I
      
      from itertools import permutations
      from collections import defaultdict
      
      MEG = defaultdict(list)
      digits = set(range(1, 10))
      
      for p1 in permutations(digits, 6):
        A, B, C, D, E, F = p1
        if A < B < C and D < E < F and \
          A < D and B < E and C < F:
          q1 = digits.difference(p1)
          for p2 in permutations(q1):
            G, H, I = p2
            if G < H < I:
              if A < D < G and B < E < H and C < F < I:
                MEG[(A + D + G, E % 2)].append([A, B, C, D, E, F, G, H, I])
                
      # check E is even for Meghans array as 5th element(E)
      # ..of standard telephone array is odd i.e. 5
      for k, v in MEG.items():
        if len(v) == 1 and v[0][4] % 2 == 0:
          print("Meghans array: ")
          print(v[0][0], v[0][1], v[0][2])
          print(v[0][3], v[0][4], v[0][5])
          print(v[0][6], v[0][7], v[0][8])
      
      '''
      Meghans array:  
      1 2 5
      3 4 6
      7 8 9
      '''
      
      

      Like

  • Unknown's avatar

    Jim Randell 8:29 am on 10 January 2025 Permalink | Reply
    Tags:   

    Teaser 2594: Just enough 

    From The Sunday Times, 10th June 2012 [link] [link]

    I have two identical bags of balls, each containing two reds, two yellows and two blues. Blindfolded throughout, I remove just enough balls from the first to be sure that the removed balls include at least two colours. I place these balls in the second bag. Then I remove some balls from the second bag and put them in the first: I move just enough to be sure that the first bag will now contain at least one ball of each colour.

    What (as a percentage) is the probability that all the balls left in the second bag are the same colour?

    [teaser2594]

     
    • Jim Randell's avatar

      Jim Randell 8:29 am on 10 January 2025 Permalink | Reply

      This Python program determines the minimal number of balls moved in each of the two steps, to meet the required conditions.

      It then generates all possible (equally likely) outcomes using those numbers to determine the proportion that leave all balls the same colour in the second bag.

      from enigma import (Accumulator, irange, multiset, subsets, fdiv, printf)
      
      # initial state of the bags
      bag0 = multiset(R=2, Y=2, B=2)
      
      # move balls <ss> from <X> to <Y>
      def move(X, Y, ss): return (X.difference(ss), Y.combine(ss))
      
      # find minimum subset size to transfer from <X> to <Y>
      # that ensures condition <fn> (on the modified <X>, <Y>)
      def solve(X, Y, fn):
        for k in irange(1, X.size()):
          fail = 0
          for ss in X.subsets(size=k):
            # move ss from X to Y
            (X_, Y_) = move(X, Y, ss)
            if not fn(X_, Y_):
              fail = 1
              break
          if fail == 0: return k
      
      # initially move <k1> balls, such that at least 2 colours are moved
      k1 = solve(bag0, bag0, (lambda X, Y: sum(v > 2 for v in Y.values()) >= 2))
      printf("[k1 = {k1}]")
      
      # find the number of balls required to move back
      r = Accumulator(fn=max)
      # consider all bags with <k1> balls moved
      for ss in bag0.subsets(size=k1):
        (bag1, bag2) = move(bag0, bag0, ss)
        # move balls from bag2 to bag1, such that bag1 has at least one of each colour
        r.accumulate(solve(bag2, bag1, (lambda X, Y: Y.distinct_size() >= 3)))
      k2 = r.value
      printf("[k2 = {k2}]")
      
      # count all possible outcomes (t), and those where the second bag only
      # contains balls of one colour (n)
      t = n = 0
      bag1_0 = bag2_0 = bag0
      
      # step (1) move k1 balls from bag1 to bag2
      for ss1 in subsets(bag1_0, size=k1):
        (bag1_1, bag2_1) = move(bag1_0, bag2_0, ss1)
      
        # step (2) move k2 balls from bag2 to bag1
        for ss2 in subsets(bag2_1, size=k2):
          (bag2_2, bag1_2) = move(bag2_1, bag1_1, ss2)
      
          t += 1
          # does bag2 only contain balls of one colour?
          if bag2_2.distinct_size() == 1: n += 1
      
      # output solution
      printf("P = {n}/{t} = {f:.2%}", f=fdiv(n, t))
      

      Solution: The probability that the balls remaining in the second bag are all the same colour is 5%.

      It is easy to see that the first step requires 3 balls to be moved: if we moved only 2 balls we might choose 2 of the same colour, but with 3 balls they cannot all be of the same colour (as there are only 2 of each colour).

      The second step requires 6 balls to be moved to ensure that there is at least one ball of each colour in the first bag. For example: if the first step moves RRY to bag 2, then bag 1 contains YBB and bag 2 contains RRRRYYYBB, and we need to move at least 6 balls (as if we only choose 5 we might choose YYYBB, which would leave bag 1 with only blue and yellow balls).


      Manually:

      For the first step, moving 3 balls from bag 1 to bag 2, we might move 3 different coloured balls, with a probability of 5/5 × 4/5 × 2/4 = 2/5. And the remaining 3/5 of the time we would remove 2 balls of one colour and 1 ball of a different colour.

      So we have two possible patterns of balls in the bags:

      (2/5) XYZ + XXXYYYZZZ
      (3/5) YZZ + XXXXYYYZZ

      We can then look at moving 6 balls back to the first bag. Which is the same as choosing 3 balls to leave behind, and we want to look at the probability that these balls are the same colour.

      In the first case the probability this happening is: 9/9 × 2/8 × 1/7 = 1/28.

      In the second case we can choose to leave 3 X’s or 3 Y’s to leave behind. The probabilities are:

      P(XXX) = 4/9 × 3/8 × 2/7 = 1/21
      P(YYY) = 3/9 × 2/8 × 1/7 = 1/84

      Giving a final probability of:

      P = (2/5) × (1/28) + (3/5) × (1/21 + 1/84) = 1/20

      Like

  • Unknown's avatar

    Jim Randell 8:07 am on 8 January 2025 Permalink | Reply
    Tags:   

    Teaser 2595: Stylish fences 

    From The Sunday Times, 17th June 2012 [link] [link]

    Farmer Giles has a hexagonal field bounded by six straight fences. The six corners lie on a circle of diameter somewhere around 250 metres. At three alternate corners there are stiles. The angles of the hexagon at the corners without stiles are all the same. All six fences are different whole numbers of metres long. I have just walked in a straight line from one of the stiles to another and the distance walked was a whole number of metres.

    How many?

    [teaser2595]

     
    • Jim Randell's avatar

      Jim Randell 8:07 am on 8 January 2025 Permalink | Reply

      Suppose the field is:

      If the stiles are at A, C, E, then the angles at B, D, F are all equal, which means they are all 120°, and so the lines AC, AE, CE (= paths between stiles) are all equal length and so ACE is an equilateral triangle. (See: [ @wikipedia ]).

      If the diameter of the circle is d, then the length of the paths, p, is given by:

      p = d √(3/4)

      And d is around 250, let’s say in the range [245, 255], so the path is in the range [212.176, 220.836], so we can consider path lengths of 212 .. 221.

      For a given path length p (= AC) we can look for integer fence lengths (x, y) (= (AB, BC)) that complete the triangle ABC. By the cosine rule in ABC:

      p^2 = x^2 + y^2 + xy

      y^2 + xy + (x^2 − p^2) = 0

      which is a quadratic equation in y.

      So we can consider values for x and use the equation to find possible corresponding y values.

      And we need to find (at least) 3 different (x, y) pairs to complete the hexagon, so that all 6 fences are different lengths.

      The following Python program runs in 76ms. (Internal runtime is 7.5ms).

      from enigma import (irange, quadratic, sq, sqrt, printf)
      
      r43 = sqrt(4, 3)
      
      # consider possible path lengths
      for p in irange(212, 221):
        # collect possible (x, y) pairs
        ss = list()
        for x in irange(1, p - 1):
          for y in quadratic(1, x, sq(x) - sq(p), domain='Z'):
            if x < y < p:
              ss.append((x, y))
      
        # we need (at least) 3 different pairs
        if len(ss) < 3: continue
        # output solution
        printf("p={p} d={d:.3f} -> {ss}", d=p * r43)
      

      Solution: The path is 217m long.

      There are actually 4 possible (x, y) pairs, and any 3 of them can be used to construct the hexagon:

      p=217 d=250.570 → [(17, 208), (77, 168), (87, 160), (93, 155)]

      If the puzzle had specified that the diameter of the circle was “around 400 m”, then we would have been able to give the lengths of the 6 fences:

      p=343 d=396.062 → [(37, 323), (112, 273), (147, 245)]

      Like

  • Unknown's avatar

    Jim Randell 10:22 pm on 3 January 2025 Permalink | Reply
    Tags:   

    Teaser 2589: A certain age 

    From The Sunday Times, 6th May 2012 [link] [link]

    My cousin and I share a birthday. At the moment my age, in years, is the same as hers but with the order of the two digits reversed. On one of our past birthdays I was five times as old as her but, even if I live to a world record “ripe old age”, there is only one birthday in the future on which my age will be a multiple of hers.

    How old am I?

    [teaser2589]

     
    • Jim Randell's avatar

      Jim Randell 10:22 pm on 3 January 2025 Permalink | Reply

      Suppose my age is XY and my cousin’s age is YX, then we have X > Y.

      This Python program runs in 63ms. (Internal runtime is 306µs).

      from enigma import (irange, subsets, printf)
      
      # ages are the reverse of each other
      for (Y, X) in subsets(irange(1, 9), size=2):
        (XY, YX) = (10 * X + Y, 10 * Y + X)
      
        # look for an age in the past where I was five times the age of the cousin
        past = ((XY - n, YX - n) for n in irange(1, YX - 1))
        x1 = list((x, y) for (x, y) in past if x == 5 * y)
        if not x1: continue
      
        # look for ages in the future where my age is a multiple of hers
        future = ((XY + n, YX + n) for n in irange(1, 130 - XY))
        x2 = list((x, y) for (x, y) in future if x % y == 0)
        if len(x2) != 1: continue
      
        # output solution
        printf("XY={XY} YX={YX} [past={x1} future={x2}]")
      

      Solution: Your age is 62.

      And the cousin’s age is 26.

      17 years ago the ages were 45 and 9 (and 45 = 5 × 9).

      In 10 years time the ages will be 72 and 36 (and 72 = 2 × 36).

      Like

    • Hugo's avatar

      Hugo 7:23 am on 5 January 2025 Permalink | Reply

      The difference between the ages is 9(X – Y).
      That remains the same each year, and at one time it was 4 times the cousin’s age.
      So (X – Y) is either 4 or 8.
      The current ages could be 15, 51; 26, 62; 37, 73; 48, 84; or 59, 95.
      In all cases the difference is 36.
      Ages when one age is an integer multiple of the other are 1, 37; 2, 38; 4, 40; 9, 45; 18, 54; 36, 72.
      Only one of those is allowed to be in the future.

      Alternatively, the current ages could be 19, 91, with difference 72.
      Then the ‘multiple’ ages are 1, 73; 2, 74; 4, 76; 8, 80; 9, 81; 18, 90; 36, 108; 72, 144.
      But two of those are in the future.
      The only valid solution appears to be 26, 62.

      Like

    • Hugo's avatar

      Hugo 7:32 am on 6 January 2025 Permalink | Reply

      I forgot some pairs of ages where one is an integer multiple of the other.
      With a difference 36: 3, 39; 6, 42; 12, 48.
      Similarly with a difference 72, but we’ve eliminated that as a potential solution.

      Like

  • Unknown's avatar

    Jim Randell 4:43 pm on 31 December 2024 Permalink | Reply
    Tags:   

    Teaser 2596: Unusual factors 

    From The Sunday Times, 24th June 2012 [link] [link]

    There are no neat tests for divisibility by 7 or 13, so it’s unusual for these factors to feature in Teasers. Today we put that right.

    If you start with any three different non-zero digits, then you can make six different three-digit numbers using precisely those digits. For example, with 2, 5 and 7 you get 257, 275, 527, 572, 725 and 752. Here, one of their differences (572 − 257) is divisible by 7, and another (725
    − 257) is divisible by 13.

    Your task today is to find three different digits so that none of the six possible three-digit numbers and none of the differences between any pair of them is a multiple of either 7 or 13.

    What are the three digits?

    My review of puzzles posted in 2024 is at Enigmatic Code: 2024 in review.

    [teaser2596]

     
    • Jim Randell's avatar

      Jim Randell 4:44 pm on 31 December 2024 Permalink | Reply

      This Python program looks at possible sets of three digits, and then uses the [[ generate() ]] function to generate possible arrangements and differences. This means as soon as we find a number that fails the divisibility tests we can move on to the next set of digits.

      It runs in 65ms. (Internal runtime is 491µs).

      from enigma import (irange, subsets, nconcat, printf)
      
      # generate numbers to test from digits <ds>
      def generate(ds):
        seen = list()
        # consider all arrangements of the digits
        for n in subsets(ds, size=len, select='P', fn=nconcat):
          # return the arrangement
          yield n
          # return the differences
          for x in seen:
            yield abs(n - x)
          seen.append(n)
      
      # choose 3 digits
      for ds in subsets(irange(1, 9), size=3):
        # check numbers are not divisible by 7 or 13
        if any(n % 7 == 0 or n % 13 == 0 for n in generate(ds)): continue
        # output solution
        printf("digits = {ds}")
      

      Solution: The three digits are: 1, 4, 9.

      See also: A video by Matt Parker on divisibility tests [@youtube].

      Like

    • GeoffR's avatar

      GeoffR 7:34 pm on 1 January 2025 Permalink | Reply

      from itertools import permutations
      from enigma import nsplit
      
      N = []
      
      for a, b, c in permutations(range(1, 10), 3): 
          n1, n2 = 100*a + 10*b + c, 100*a + 10*c + b
          n3, n4 = 100*b + 10*a + c, 100*b + 10*c + a
          n5, n6 = 100*c + 10*a + b, 100*c + 10*b + a
          # Check three-digit numbers are not divisible by 7 or 13
          if any(x % 7 == 0 for x in (n1 ,n2, n3, n4, n5, n6)):
              continue
          if any(x % 13 == 0 for x in (n1, n2, n3, n4, n5, n6)):
              continue
          N.append(n1)
      
      # check differences for each number in the list
      # of potential candidates
      for m in N:
          x, y, z = nsplit(m)
          m1, m2 = m, 100*x + 10*z + y
          m3, m4 = 100*y + 10*x + z, 100*y + 10*z + x
          m5, m6 = 100*z + 10*x + y, 100*z + 10*y + x
          # a manual check of the 15 differences of each 6-digit number
          if abs(m1-m2) % 7 == 0 or abs(m1-m2) % 13 == 0: continue
          if abs(m1-m3) % 7 == 0 or abs(m1-m3) % 13 == 0: continue
          if abs(m1-m4) % 7 == 0 or abs(m1-m4) % 13 == 0: continue
          if abs(m1-m6) % 7 == 0 or abs(m1-m5) % 13 == 0: continue
          if abs(m1-m6) % 7 == 0 or abs(m1-m6) % 13 == 0: continue
          #
          if abs(m2-m3) % 7 == 0 or abs(m2-m3) % 13 == 0: continue
          if abs(m2-m4) % 7 == 0 or abs(m2-m4) % 13 == 0: continue
          if abs(m2-m5) % 7 == 0 or abs(m2-m5) % 13 == 0: continue
          if abs(m2-m6) % 7 == 0 or abs(m2-m6) % 13 == 0: continue
          #
          if abs(m3-m4) % 7 == 0 or abs(m3-m4) % 13 == 0: continue
          if abs(m3-m5) % 7 == 0 or abs(m3-m5) % 13 == 0: continue
          if abs(m3-m6) % 7 == 0 or abs(m3-m6) % 13 == 0: continue
          #
          if abs(m4-m5) % 7 == 0 or abs(m4-m5) % 13 == 0: continue
          if abs(m4-m6) % 7 == 0 or abs(m4-m6) % 13 == 0: continue
          if abs(m5-m6) % 7 == 0 or abs(m5-m6) % 13 == 0: continue
          if x < y < z:
              print(f"The three digit numbers are {x}, {y} and {z}.")
      
      # The three digit numbers are 1, 4 and 9.
      

      I resorted to a manual check of the differences of the 6 numbers arising from each 3 digit candidate, as it was not coming out easily in code.

      Like

  • Unknown's avatar

    Jim Randell 1:31 pm on 25 December 2024 Permalink | Reply
    Tags:   

    Teaser 2569: Christmas gift 

    From The Sunday Times, 18th December 2011 [link] [link]

    I sent Hank, an American friend, a Times subscription for a Christmas present. He is pleased and has responded with this numerical addition sum with a prime total in which he has consistently replaced digits by letters, with different letters for different digits.

    What is TIMES?

    [teaser2569]

     
    • Jim Randell's avatar

      Jim Randell 1:31 pm on 25 December 2024 Permalink | Reply

      A quick one for Christmas Day.

      Here is a solution using the [[ SubstitutedExpression.split_sum ]] solver from the enigma.py library.

      It runs in 87ms. (Internal runtime of the generated code is 12.5ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression.split_sum
      
      "GEE + A + XMAS + TREE + GIFT = TIMES"
      
      --extra="is_prime(TIMES)"
      --answer="TIMES"
      

      Solution: TIMES = 12589.

      Like

    • GeoffR's avatar

      GeoffR 9:00 pm on 25 December 2024 Permalink | Reply

      
      % A Solution in MiniZinc
      include "globals.mzn";
      
      var 1..9:G; var 1..9:A; var 1..9:X; var 1..9:T;
      var 0..9:E; var 0..9:M; var 0..9:S; var 0..9:R;
      var 0..9:I; var 0..9:F;
      
      constraint all_different([G, E, A, X, T, M, S, R, I, F]);
        
      predicate is_prime(var int: x) = 
      x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) ((i < x) -> (x mod i > 0));
        
      var 100..999:GEE == 100*G + 11*E;
      var 1000..9999 : XMAS == 1000*X + 100*M + 10*A + S;
      var 1000..9999 : TREE == 1000*T + 100*R + 11*E;
      var 1000..9999 : GIFT == 1000*G + 100*I + 10*F + T;
      var 10000..99999: TIMES == 10000*T + 1000*I + 100*M + 10*E + S;
      
      constraint GEE + A + XMAS + TREE  + GIFT == TIMES;
      
      constraint is_prime(TIMES);
      
      solve satisfy;
      output ["TIMES = " ++ show(TIMES) ];
      
      % TIMES = 12589
      % ----------
      % ==========  
      
      
      
      
      

      Like

    • GeoffR's avatar

      GeoffR 7:25 pm on 26 December 2024 Permalink | Reply

      from itertools import permutations
      from enigma import is_prime
      
      # Adding columns from the right hand end being column col1
      # with a carry of c1 to col2
      for p1 in permutations(range(10), 4):
          E, A, S, T = p1
          if 0 in (A, T): continue
          c1, col1 = divmod((2*E + A + S + T), 10)
          if col1 != S: continue
      
          # Column 2
          q1 = set(range(10)).difference({E, A, S, T})
          for p2 in permutations(q1, 1):
              F = p2[0]
              c2, col2 = divmod((2*E + A + F + c1), 10)
              if col2 != E: continue
      
              # Column 3
              q2 = q1.difference({F})
              for p3 in permutations(q2, 4):
                  G, M, R, I = p3
                  if G == 0: continue
                  c3, col3 = divmod((G + M + R + I + c2), 10)
                  if col3 != M: continue
      
                  # Column 4
                  q3 = q1.difference({F, G, M, R, I})
                  X = next(iter(q3))
                  c4, col4 = divmod((X + T + G + c3), 10)
                  if col4 != I: continue
                  if c4 != T: continue
                  
                  TIMES = 10000*T + 1000*I + 100*M + 10*E + S
                  if not is_prime(TIMES): continue
                  print(f"TIMES = ", TIMES)
      
      

      Like

  • Unknown's avatar

    Jim Randell 12:08 pm on 19 December 2024 Permalink | Reply
    Tags:   

    Teaser 2601: Olympic economics 

    From The Sunday Times, 29th July 2012 [link] [link]

    George and Martha are organising a large sports meeting. Usually a gold medal (costing £36) is given to each winner, silver (£17) to each runner-up, and bronze (£11) to each third. In a minority of events, such as tennis and boxing, both losing semi-finalists get a bronze medal. However, George and Martha are going to economise by having third place play-offs in such events, thus reducing the medals needed by an odd number. George noticed that the total cost of the medals is now a four-figure palindrome, and Martha commented that the same would have been true under the previous system.

    How many events are there?

    [teaser2601]

     
    • Jim Randell's avatar

      Jim Randell 12:09 pm on 19 December 2024 Permalink | Reply

      If there are n events, under the new system each has a 1 gold, 1 silver and 1 bronze medal (assuming each event is for individual competitors). The cost of medals for each event is £36 + £17 + £11 = £64. So the total cost of medals is 64n, and we are told this is a 4-digit palindrome.

      This Python program looks for possible multiples of 64 that give a 4-digit palindrome, and then looks at adding an odd number of extra bronze medals (for less than half of the events), that also gives a 4-digit palindrome for the total cost.

      It runs in 67ms. (Internal runtime is 156µs).

      from enigma import (irange, inf, is_npalindrome, printf)
      
      # consider the number of events
      for n in irange(1, inf):
        # total cost of medals is a 4-digit palindrome
        t = 64 * n  #  (gold + silver + bronze) per event
        if t < 1000: continue
        if t > 9999: break
        if not is_npalindrome(t): continue
      
        # consider an odd number of extra bronze medals
        for x in irange(1, (n - 1) // 2, step=2):
          tx = t + 11 * x
          if tx > 9999: break
          if not is_npalindrome(tx): continue
      
          # output solution
          printf("{n} events -> cost = {t}; +{x} bronze -> cost = {tx}")
      

      Solution: There are 132 events.

      So the total cost of medals under the new system is £64×132 = £8448.

      There are two situations where the cost of additional bronze medals also gives a 4-digit palindromic total cost:

      If there are 51 extra bronze medals required under the old system, then the additional cost is £11×51 = £561, and the total cost would have been £9009.

      If there are 61 extra bronze medals required, then the additional cost is £11×61 = £671, and the total cost would have been £9119.

      Like

  • Unknown's avatar

    Jim Randell 10:02 am on 17 December 2024 Permalink | Reply
    Tags:   

    Teaser 2598: Die hard 

    From The Sunday Times, 8th July 2012 [link] [link]

    I have placed three identical standard dice in a row on a table-top, leaving just eleven faces and an odd number of spots visible. Regarding each face as a digit (with, naturally enough, the five-spotted face being the digit 5, etc.) from the front I can read a three-digit number along the vertical faces and another three-digit number along the top. Similarly, if I go behind the row I can read a three-digit number along the vertical faces and another three-digit number along the top. Of these four three-digit numbers, two are primes and two are different perfect squares.

    What are the two squares?

    [teaser2598]

     
    • Jim Randell's avatar

      Jim Randell 10:02 am on 17 December 2024 Permalink | Reply

      See also: Teaser 3130.

      Here is a solution using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      It runs in 80ms. (Internal runtime of the generated code is 2.3ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      # layout (viewed from above):
      #
      #       I H G
      #     +-------+
      #   X | A B C | Y
      #     +-------+
      #       D E F
      
      --digits="1-6"
      --distinct="ADIX,BEH,CFGY"
      
      # check for valid numbers
      --code="check = lambda x: is_prime(x) or is_square_p(x)"
      
      # the top can be read both ways
      "check(ABC)"
      "check(CBA)"
      
      # the front and back
      "D + I = 7" "E + H = 7" "F + G = 7"
      "check(DEF)"
      "check(GHI)"
      
      # total number of spots is odd
      "sum([A, B, C, D, E, F, G, H, I, X, Y]) % 2 = 1"
      
      # two of the 3-digit numbers are primes, and two are (different) squares
      --macro="@numbers = (ABC, CBA, DEF, GHI)"
      "len(list(filter(is_prime, @numbers))) = 2"
      "len(set(filter(is_square_p, @numbers))) = 2"
      
      # possible corner configurations (right-handed dice)
      --code="all_corners = lambda x, y, z, k=7: union([(x, y, z), (k - x, k - z, k - y)] for (y, z) in tuples([y, z, k - y, k - z, y], 2))"
      --code="corners = union(repeat(rotate, v, 2) for v in all_corners(3, 2, 1))"  # or [[ (1, 2, 3) ]] for LH
      "(A, D, X) in corners"
      "(A, X, I) in corners"
      "(C, Y, F) in corners"
      "(C, G, Y) in corners"
      
      --code="ans = lambda ns: call(ordered, filter(is_square_p, ns))"
      --answer="ans(@numbers)"
      --template="@numbers (X, Y)"
      --solution=""
      

      Solution: The squares are 361 and 625.

      (Or this arrangement can be viewed from the back).

      From the front the top reads 361 (= 19^2) and the front reads 625 (= 25^2).

      From the back the top reads 163 (= prime) and the back reads 251 (= prime).

      With standard (right-handed) dice the left and right sides are 2 and 4, giving a total visible spot count of 37.

      The same numbers work for mirrored (left-handed) dice, but the left and right sides are 5 and 3, giving a total visible spot count of 39.

      Like

    • Frits's avatar

      Frits 2:12 pm on 17 December 2024 Permalink | Reply

      # as we have 2 primes and 2 squares and primes and squares are mutually
      # exclusive every 3-digit number must either be square or prime
      
      # layout is:
      #
      #       I H G
      #     +-------+
      #   X | A B C | Y
      #     +-------+
      #       D E F
      
      # given two dice faces anti-clockwise at a vertex, find the third
      # face at this vertex (using a western die if 'same' is True)
      def die_third_face(f, s, same=True):
        if s in {f, 7 - f}:
          raise ValueError
        oth = [i for i in range(1, 7) if i not in {f, s, 7 - f, 7 - s}]
        return oth[((f < s) == ((s - f) % 2)) == (same == (s + f > 7))]
      
      dgts = "123456" 
      # opposite side
      opp = {str(i): str(7 - i) for i in range(1, 7)}  
      
      # 3-digit squares
      sqs = {str(i * i) for i in range(10, 32) if all(x in dgts for x in str(i * i))}
         
      # determine valid 3-digit primes
      prms = {3, 5, 7}
      prms |= {x for x in range(11, 100, 2) if all(x % p for p in prms)}
      prms = {str(i) for i in range(101, 700, 2) if all(i % p for p in prms) and
              all(x in dgts for x in str(i))}
      
      # candidates for numbers along the top
      top = sqs | prms
      
      # front and back: DEF and IHG
      fb = [(DEF, GHI[::-1]) for DEF in top if 
            (GHI := (''.join(opp[x] for x in DEF))[::-1]) in top]
              
      # combine 3-digit squares which reverse are square or prime with
      #         3-digit primes which reverse are square or prime
      top = ({s for s in sqs if s[::-1] in top} |
             {p for p in prms if p[::-1] in top})
      
      sols = set()
      for f, b in fb:
        # mix front and back with possible top 
        for t in [t for t in top if all(t[i] not in f[i] + b[i] for i in range(3))]:
          # two are different perfect squares
          used_sqs = tuple(x for x in (f, b[::-1], t, t[::-1]) if x in sqs)
          if len(used_sqs) != 2 or len(set(used_sqs)) == 1: continue
          
          # assume a western die (counterclockwise)
          left  = str(die_third_face(int(f[0]), int(t[0])))
          right = str(die_third_face(int(t[2]), int(f[2])))
          # an odd number of spots visible
          if sum(int(x) for x in f + b + t + left + right) % 2 == 0: continue
          sols.add(used_sqs)
          
      for sq1, sq2 in sols:
        print(f"answer: {sq1} and {sq2}")
      

      Like

  • Unknown's avatar

    Jim Randell 10:50 am on 12 December 2024 Permalink | Reply
    Tags:   

    Teaser 2622: Christmas message 

    From The Sunday Times, 23rd December 2012 [link] [link]

    In the message below the words represent numbers where the digits have consistently been replaced by letters, with different letters used for different digits:

    A
    MERRY
    XMAS
    2012
    TO
    YOU

    In fact the largest of these six numbers is the sum of the other five.

    What number is represented by MERRY?

    [teaser2622]

     
    • Jim Randell's avatar

      Jim Randell 10:51 am on 12 December 2024 Permalink | Reply

      We can solve this in a straightforward manner using the [[ SubstitutedExpression ]] solver from the enigma.py library.

      The following run file executes in 555ms. (The internal runtime of the generated code is 506ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression
      
      "A + XMAS + 2012 + TO + YOU = MERRY"
      
      --answer="MERRY"
      

      Solution: MERRY = 10552.

      There are 4 possible sums, but the value of MERRY is the same in each of them:

      A + XMAS + 2012 + TO + YOU = MERRY
      6 + 8163 + 2012 + 97 + 274 = 10552
      6 + 8164 + 2012 + 97 + 273 = 10552
      7 + 8173 + 2012 + 96 + 264 = 10552
      7 + 8174 + 2012 + 96 + 263 = 10552
      

      A and O are 6 and 7 (in some order), and S and U are 3 and 4 (in some order). This gives rise to the 4 solutions above.


      For a faster run time we can use the [[ SubstitutedExpression.split_sum ]] solver, which forms separate sums from the columns of the larger sum.

      The following run file executes in 90ms. (And the internal runtime of the generated code is just 1.6ms).

      #! python3 -m enigma -rr
      
      SubstitutedExpression.split_sum
      
      --symbols="AEMORSTUXY012"
      --literal="012"
      --distinct="AEMORSTUXY"
      
      "A + XMAS + 2012 + TO + YOU = MERRY"
      
      --answer="MERRY"
      --solution="AEMORSTUXY"
      

      Like

    • GeoffR's avatar

      GeoffR 1:28 pm on 12 December 2024 Permalink | Reply

      
      from itertools import permutations
      
      for p1 in permutations('0123456789', 5):
          a, m, e, r, y, = p1
          if '0' in (a, m, y): continue
          A, MERRY = int(a), int(m + e + r + r + y)
          q1 = set('0123456789').difference({a,m,e,r,y})
          for p2 in permutations(q1):
              x, s, t, o, u = p2
              if '0' in (x, t):continue
              XMAS, TO, YOU = int(x + m + a + s), int(t + o), int(y + o + u)
              if MERRY == A + XMAS + TO + YOU + 2012:
                  print(f"MERRY = {MERRY}")
          
      # MERRY = 10552
      

      Like

    • Ruud's avatar

      Ruud 5:18 am on 13 December 2024 Permalink | Reply

      import istr
      
      istr.repr_mode("str")
      for a, m, e, r, y, x, s, t, o, u in istr.permutations(istr.digits(), 10):
          if not istr("0") in (a, m, x, t, y) and a + (x | m | a | s) + 2012 + (t | o) + (y | o | u) == (m | e | r | r | y):
              print(f"{a=} {x|m|a|s=} {2012=} {t|o=} {y|o|u=} {m|e|r|r|y=}")
      print("done")
      

      Like

    • GeoffR's avatar

      GeoffR 2:59 pm on 13 December 2024 Permalink | Reply

      % A Solution in MiniZinc
      include "globals.mzn";
      
      int: M == 1; % MERRY is the only 5-digit number
      % .. and there are two 4-digit numbers
      
      var 1..9:A; var 0..9:E; var 0..9:R; var 1..9:Y; var 1..9:X;
      var 0..9:S; var 1..9:T ; var 0..9:O; var 0..9:U;
      
      constraint all_different ([A, E, R, Y, X ,M, S, T, O, U]);
      
      var 10000..99999:MERRY = 10000*M + 1000*E + 110*R + Y;
      var 1000..9999:XMAS = 1000*X + 100*M + 10*A + S;
      var 100..999:YOU = 100*Y + 10*O + U;
      var 10..99:TO = 10*T + O;
      
      constraint MERRY == A + XMAS + TO + YOU + 2012;
      
      solve satisfy;
      output ["MERRY = " ++ show(MERRY)];
      
      % MERRY = 10552
      % ----------
      % ==========
      
      
      

      Like

  • Unknown's avatar

    Jim Randell 10:26 am on 10 December 2024 Permalink | Reply
    Tags: ,   

    Teaser 2560: Three trees 

    From The Sunday Times, 16th October 2011 [link] [link]

    Our front garden is circular, with a diameter less than 100 metres. Three trees grow on the perimeter: an ash, a beech and a cherry, each a different whole number of metres from each of the other two. A straight trellis 84 metres long joins the ash to the beech, and a honeysuckle grows at the point on the trellis nearest to the cherry tree. The distance from the cherry to the ash plus the distance from the ash to the honeysuckle equals the distance from the cherry to the beech.

    What is the diameter of the garden?

    [teaser2560]

     
    • Jim Randell's avatar

      Jim Randell 10:26 am on 10 December 2024 Permalink | Reply

      We represent the trees by points A, B, C on the perimeter of the circle.

      If the sides of the triangle (opposite A, B, C) are a, b, c, then the diameter of the circumcircle d is given by:

      d = (a . b . c) / 2T

      where T is the area of the triangle.

      In this case we have:

      T = c . h / 2

      d = a . b / h

      and also:

      a^2 = h^2 + c2^2
      b^2 = h^2 + c1^2
      a = b + c1

      equating values for h^2 allows us to calculate a (and b) knowing c1 and c2:

      a^2 − c2^2 = (a − c1)^2 − c1^2
      c2^2 = 2a . c1

      a = c2^2 / (2 . c1)

      This Python program considers possible splits of c into integers c1, c2, and then checks that the resulting a, b values form a viable triangle.

      It runs in 66ms. (Internal runtime is 84µs).

      from enigma import (decompose, div, sq, rcs, fdiv, printf)
      
      # split c into 2 integers
      c = 84
      for (c1, c2) in decompose(c, 2):
      
        # calculate a and b, and check (a, b, c) form a viable triangle
        a = div(sq(c2), 2 * c1)
        if a is None or not (a < 100): continue
        b = a - c1
        if not (b > 0 and a + b > c): continue
      
        # output solution
        h = rcs(a, -c2)
        d = fdiv(a * b, h)
        printf("c1={c1} c2={c2} -> a={a} b={b} c={c} -> h={h} d={d}")
      

      Solution: The diameter of the garden is 85 metres.

      The 84m trellis (AB) is split into 60m and 24m lengths by the honeysuckle. And the distances AC and BC are 51m and 75m.

      And it turns out the height of the triangle is also an integer (h = 45), so we have two Pythagorean triples that share a non-hypotenuse length:

      (24, 45, 51) = 3× (8, 15, 17)
      (45, 60, 75) = 15× (3, 4, 5)

      But the next smallest solution (which is eliminated by the d < 100 requirement) does not have an integer height (or diameter):

      a = 121, b = 103, c1 = 18, c2 = 66

      h = 11√85 ≈ 101.41, d = 1133/√85 ≈ 122.89

      Like

    • Frits's avatar

      Frits 12:18 pm on 11 December 2024 Permalink | Reply

      Trellis length 84 is cleverly chosen.
      For D=100 we only have to investigate c1 values 22, 24 and 26.

      The following program shows solutions with a integer diameter less than 1000 meters.

      from math import ceil
      D = 1000  #  with a diameter less than D metres
      
      # a = c2**2 / (2 * c1) < D, (c - c1)**2 < 2 * D * c1, c1**2 - 2 * (D + c1) * c1 + c**2 < 0  
      # (c1 - (D + c))**2 < (D + c)**2 - c**2
      # c1 > D + c - ((D + c)**2 - c**2)**.5  
      # c1 > 20.29
      
      # a + b > 84 or c2**2 / c1 - c1 > 84 or (84 - c1)**2 / c1 - c1 > 84
      # (84 - c1)**2 > c1**2 + 84 * c1 or 252 * c1 < 84**2
      # c1 < 28
      
      prt = 0
      
      hdr = " (c1,  c2)   c2^2 2*c1 (        a,         b)     a + b          h          d   "
      sols = []
      for c in range(3, D):
        if prt: print(hdr) 
        min_c1 = int(D + c - ((D + c)**2 - c**2)**.5) + 1
        # c2 must be even if a is an integer
        #for c1 in range(1 + (c % 2 == 0), ceil(c / 3), 2): 
        for c1 in range(min_c1 + ((c - min_c1) % 2), ceil(c / 3), 2): 
          c2 = c - c1
          a = round(c2**2 / (2 * c1), 2)
          b = round(a - c1, 2)
          h = round((b**2 - c1**2)**.5 if b >= c1 else -1, 2)
          d = round(a * b / h if h > 0 else -1, 2)
          OK = (' OK' if a + b > c and int(a) == a and int(d) == d and 
                        all (x < D for x in [a, b, d]) else '')
          txt = (f"({c1:>3}, {c2:>3}) " + 
                f"{c2**2:>6} " + 
                f"{2 * c1:>3}  ({a:>9}, {b:>9}) " + 
                f"{round(a + b, 2):>9} " +
                f"{h:>10} " +
                f"{d:>10} " +
                f"{round(D + c - ((D + c)**2 - c**2)**.5, 2) :>8} < c1 < " + 
                f"{round(c / 3, 2):>6} " + 
                f"{OK}")
          if prt: print(txt)      
          if OK: 
            sols.append(txt)
          if h == -1: break   
          
      print("solutions with a integer diameter:\n")  
      print(hdr) 
      for s in sols: 
        print(s)  
      
      '''
      solutions with a integer diameter:
      
       (c1,  c2)   c2^2 2*c1 (        a,         b)     a + b          h          d
      (  1,  16)    256   2  (    128.0,     127.0)     255.0      127.0      128.0     0.14 < c1 <   5.67  OK
      (  1,  18)    324   2  (    162.0,     161.0)     323.0      161.0      162.0     0.18 < c1 <   6.33  OK
      (  1,  20)    400   2  (    200.0,     199.0)     399.0      199.0      200.0     0.22 < c1 <    7.0  OK
      (  1,  22)    484   2  (    242.0,     241.0)     483.0      241.0      242.0     0.26 < c1 <   7.67  OK
      (  1,  24)    576   2  (    288.0,     287.0)     575.0      287.0      288.0      0.3 < c1 <   8.33  OK
      (  1,  26)    676   2  (    338.0,     337.0)     675.0      337.0      338.0     0.35 < c1 <    9.0  OK
      (  1,  28)    784   2  (    392.0,     391.0)     783.0      391.0      392.0     0.41 < c1 <   9.67  OK
      (  1,  30)    900   2  (    450.0,     449.0)     899.0      449.0      450.0     0.47 < c1 <  10.33  OK
      (  1,  32)   1024   2  (    512.0,     511.0)    1023.0      511.0      512.0     0.53 < c1 <   11.0  OK
      (  1,  34)   1156   2  (    578.0,     577.0)    1155.0      577.0      578.0     0.59 < c1 <  11.67  OK
      (  1,  36)   1296   2  (    648.0,     647.0)    1295.0      647.0      648.0     0.66 < c1 <  12.33  OK
      (  1,  38)   1444   2  (    722.0,     721.0)    1443.0      721.0      722.0     0.73 < c1 <   13.0  OK
      (  1,  40)   1600   2  (    800.0,     799.0)    1599.0      799.0      800.0     0.81 < c1 <  13.67  OK
      (  1,  42)   1764   2  (    882.0,     881.0)    1763.0      881.0      882.0     0.89 < c1 <  14.33  OK
      (  2,  42)   1764   4  (    441.0,     439.0)     880.0      439.0      441.0     0.93 < c1 <  14.67  OK
      (  1,  44)   1936   2  (    968.0,     967.0)    1935.0      967.0      968.0     0.97 < c1 <   15.0  OK
      (  2,  44)   1936   4  (    484.0,     482.0)     966.0      482.0      484.0     1.01 < c1 <  15.33  OK
      (  2,  46)   2116   4  (    529.0,     527.0)    1056.0      527.0      529.0      1.1 < c1 <   16.0  OK
      (  2,  48)   2304   4  (    576.0,     574.0)    1150.0      574.0      576.0     1.19 < c1 <  16.67  OK
      (  2,  50)   2500   4  (    625.0,     623.0)    1248.0      623.0      625.0     1.29 < c1 <  17.33  OK
      (  2,  52)   2704   4  (    676.0,     674.0)    1350.0      674.0      676.0     1.38 < c1 <   18.0  OK
      (  2,  54)   2916   4  (    729.0,     727.0)    1456.0      727.0      729.0     1.49 < c1 <  18.67  OK
      (  2,  56)   3136   4  (    784.0,     782.0)    1566.0      782.0      784.0     1.59 < c1 <  19.33  OK
      (  2,  58)   3364   4  (    841.0,     839.0)    1680.0      839.0      841.0      1.7 < c1 <   20.0  OK
      (  2,  60)   3600   4  (    900.0,     898.0)    1798.0      898.0      900.0     1.81 < c1 <  20.67  OK
      (  2,  62)   3844   4  (    961.0,     959.0)    1920.0      959.0      961.0     1.93 < c1 <  21.33  OK
      ( 24,  60)   3600  48  (     75.0,      51.0)     126.0       45.0       85.0     3.26 < c1 <   28.0  OK
      ( 36, 120)  14400  72  (    200.0,     164.0)     364.0      160.0      205.0    10.57 < c1 <   52.0  OK
      ( 48, 120)  14400  96  (    150.0,     102.0)     252.0       90.0      170.0    12.15 < c1 <   56.0  OK
      ( 46, 138)  19044  92  (    207.0,     161.0)     368.0     154.29      216.0    14.38 < c1 <  61.33  OK
      ( 32, 192)  36864  64  (    576.0,     544.0)    1120.0     543.06      577.0    20.67 < c1 <  74.67  OK
      ( 42, 210)  44100  84  (    525.0,     483.0)    1008.0     481.17      527.0    25.62 < c1 <   84.0  OK
      ( 72, 180)  32400 144  (    225.0,     153.0)     378.0      135.0      255.0    25.62 < c1 <   84.0  OK
      ( 81, 180)  32400 162  (    200.0,     119.0)     319.0      87.18      273.0    27.31 < c1 <   87.0  OK
      ( 36, 228)  51984  72  (    722.0,     686.0)    1408.0     685.05      723.0    27.88 < c1 <   88.0  OK
      ( 72, 240)  57600 144  (    400.0,     328.0)     728.0      320.0      410.0    37.64 < c1 <  104.0  OK
      ( 96, 240)  57600 192  (    300.0,     204.0)     504.0      180.0      340.0    42.94 < c1 <  112.0  OK
      ( 92, 276)  76176 184  (    414.0,     322.0)     736.0     308.58      432.0    50.43 < c1 < 122.67  OK
      (120, 300)  90000 240  (    375.0,     255.0)     630.0      225.0      425.0    63.53 < c1 <  140.0  OK
      (108, 360) 129600 216  (    600.0,     492.0)    1092.0      480.0      615.0     76.6 < c1 <  156.0  OK
      (144, 360) 129600 288  (    450.0,     306.0)     756.0      270.0      510.0    86.96 < c1 <  168.0  OK
      (162, 360) 129600 324  (    400.0,     238.0)     638.0     174.36      546.0    92.31 < c1 <  174.0  OK
      (168, 420) 176400 336  (    525.0,     357.0)     882.0      315.0      595.0   112.87 < c1 <  196.0  OK
      (144, 480) 230400 288  (    800.0,     656.0)    1456.0      640.0      820.0   124.67 < c1 <  208.0  OK
      (192, 480) 230400 384  (    600.0,     408.0)    1008.0      360.0      680.0   140.99 < c1 <  224.0  OK
      (198, 528) 278784 396  (    704.0,     506.0)    1210.0     465.65      765.0   160.11 < c1 <  242.0  OK
      (216, 540) 291600 432  (    675.0,     459.0)    1134.0      405.0      765.0   171.07 < c1 <  252.0  OK
      (240, 600) 360000 480  (    750.0,     510.0)    1260.0      450.0      850.0   202.93 < c1 <  280.0  OK
      (264, 660) 435600 528  (    825.0,     561.0)    1386.0      495.0      935.0    236.4 < c1 <  308.0  OK
      '''
      

      Like

      • Jim Randell's avatar

        Jim Randell 9:30 am on 12 December 2024 Permalink | Reply

        @Frits: Your code gives solutions that are close to a whole number of metres, rather than exactly a whole number of metres.

        For example, the first configuration you give:

        a = 128; b = 127; c1 = 1; c2 = 16

        h ≈ 126.996; d ≈ 128.004

        Here is some code that looks for pairs of Pythagorean triples that share a non-hypotenuse side to find solutions where h and d are (exact) integers:

        from enigma import (defaultdict, pythagorean_triples, subsets, div, arg, printf)
        
        D = arg(100, 0, int)
        printf("[d < {D}]")
        
        # collect pythagorean triples by non-hypotenuse sides
        d = defaultdict(list)
        for (x, y, z) in pythagorean_triples(D - 1):
          d[x].append((y, z))
          d[y].append((x, z))
        
        # look for pairs of triangles
        for (h, ts) in d.items():
          if len(ts) < 2: continue
        
          for ((c1, b), (c2, a)) in subsets(sorted(ts), size=2):
            c = c1 + c2
            if not (a == b + c1 and c < D): continue
            d = div(a * b, h)
            if d is None: continue
        
            printf("a={a} b={b} c={c} -> c1={c1} c2={c2} h={h} d={d}")
        

        Note that if h is an integer, then d will be rational (but not necessarily an integer).

        Like

        • Frits's avatar

          Frits 11:26 am on 12 December 2024 Permalink | Reply

          @Jim, you are right.

          I get the same output as your program if I only do rounding during printing (which I should have done).

          Like

    • Brian Gladman's avatar

      Brian Gladman 6:56 pm on 13 December 2024 Permalink | Reply

      Finding all solutions:

      # (a - b).(a + b + 2.c) =  c^2 = p.q with p < q
      # 
      # a - b = p        )  a = (p + q) / 2 - c
      # a + b + 2.c = q  )  b = a - p
      
      from enigma import divisor_pairs
      from fractions import Fraction as RF
      
      c = 84
      print('   a    b              d^2')
      for p, q in divisor_pairs(c * c):
        t, r = divmod(p + q, 2)
        a = t - c
        b = a - p
        if not r and a > 0 and 2 * b > a:
          d2 = RF(a * b * b, b + b - a)
          print(f"{a:4} {b:4} {d2:16}")
      

      Like

    • GeoffR's avatar

      GeoffR 10:59 am on 14 December 2024 Permalink | Reply

      
      % A Solution in MiniZinc - using Jim's variable notations
      include "globals.mzn";
      
      int: c == 84; % given distance between ash and beech trees
      
      var 1..100:a; var 1..100:b; var 1..100:c1; 
      var 1..100:c2; var 1..100:h; 
      var 1..5000: T; % UB = 100 * 100/2
      var 1..99:d; % circle diameter < 100
      
      constraint all_different ([a, b, c]);
      constraint c == c1 + c2;
      constraint 2 * T * d == a * b * c;
      constraint a == b + c1;
      constraint c1 * c1 + h * h == b * b;
      constraint c2 * c2 + h * h == a * a;
      constraint 2 * T == c * h;
      
      % Triangle formation constraints
      constraint a + b > c /\ b + c > a /\ a + c > b;
      
      solve satisfy;
      
      output ["[a, b, c] = " ++ show([a, b, c]) ++
      "\n" ++ "[c1, c2, h] = " ++ show( [c1, c2, h]) ++
      "\n" ++ "Circle diameter = " ++ show(d) ++ " metres"];
      
      % [a, b, c] = [75, 51, 84]
      % [c1, c2, h] = [24, 60, 45]
      % Circle diameter = 85 metres
      % ----------
      % ==========
      
      
      
      

      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