Enigmatic Code

Programming Enigma Puzzles

Enigma 482: Hopscotch

From New Scientist #1633, 8th October 1988 [link]

Enigma 482

I remember playing a version of hopscotch when I was a child. We used a chalked outline like the one shown, and there were various games we could play on it. The simplest one was to start where shown and throw a pebble towards number 1 and then hop to the pebble: then throw it towards number 2 and hop to it, and so on, finally throwing the pebble towards number 9 and hopping to the pebble. You scored 1 if the pebble landed on the correct number, ½ if it missed by one, ⅓ if it missed it by two, and so on, making 9 the maximum possible total score. You were disqualified if the pebble didn’t land on a numbered square.

When I first tried the game I was pretty hopeless. When throwing at number 1 the pebble went past it. I hopped to the pebble, threw the pebble towards number 2, and continued in this way to complete the game. On no occasion was I standing on the square which I should be aiming at and, apart from when the pebble went past number 1 on my first throw, on only one other occasion did the pebble go too far and go past the square I was aiming at. I ended up with the pebble landing on all the squares from 1 to 9 (albeit in the wrong order) and my score was a whole number.

In what order did I visit the squares?

The issue date of New Scientist that this puzzle was published in falls on a Saturday, the issue date of previous magazines fell on a Thursday, so the date of this issue is 9 days after the date of the previous issue.

[enigma482]

3 responses to “Enigma 482: Hopscotch

  1. Jim Randell 7 January 2019 at 7:41 am

    This Python 3 program uses a recursive function to generate possible sequences of squares that satisfy the movements required. Only one of them comes out with an integer score. It runs in 79ms.

    Run: [ @repl.it ]

    from enigma import irange, mlcm, printf
    
    # ss - sequence of squares
    # n - target square (= go number)
    # s - sequence so far
    # k - remaining overshoots
    def solve(ss, n, s, k):
      # are we done?
      if n == 10:
        if k == 0:
          yield s
      else:
        # current position
        p = s[-1]
        # cannot be the target
        if p == n: return
        # choose an onward square
        for x in ss:
          # each square is only visited once
          if x in s: continue
          # aiming towards the current target
          if (p - x) * (p - n) < 0: continue
          # is it an overshoot?
          f = (x < n < p or x > n > p)
          if f and k < 1: continue
          # check the remaining squares
          yield from solve(ss, n + 1, s + [x], k - f)
    
    # possible squares
    squares = list(irange(1, 9))
    
    # common denominator for scores
    k = mlcm(*squares)
    
    # first square is an overshoot
    for x in irange(2, 9):
      # consider remaining squares
      for s in solve(squares, 2, [x], 1):
        # number of points for this game
        t = sum(k // (abs(x - i) + 1) for (i, x) in enumerate(s, start=1))
        # is a whole number
        (t, r) = divmod(t, k)
        if r != 0: continue
        printf("{s} = {t} points")
    

    Solution: The order the squares were visited was: 6, 4, 1, 2, 3, 5, 7, 8, 9.

    Which gives a score of 1/6 + 1/3 + 1/3 + 1/3 + 1/3 + 1/2 + 1 + 1 + 1 = 5.

    There are 32 other sequences that satisfy the conditions of the puzzle text, but give non-integer scores.

  2. Brian Gladman 7 January 2019 at 6:24 pm
    from math import gcd
    
    def solve(target, tf_cnt, visited):
      # if all bases have been visited
      if target == 10:
        # ... and exactly two throws go too far
        if tf_cnt == 2:
          # return the sequence in which the bases are visited
          yield visited
      else:
        pos = visited[-1]
        # the current position must not be the target
        if pos != target:
          # consider possible next positions
          for next_pos in set(range(1, 10)).difference(visited):
            # find the ratio of the distance to the next position
            # over the distance to the target
            tf = (next_pos - pos) / (target - pos)
            tfc = tf_cnt + (tf > 1.0)
            # the throw must be towards the target and there
            # cannot be more than two throws that go too far
            if tf > 0 and tfc < 3:
              yield from solve(target + 1, tfc, visited + (next_pos,))
    
    # compute the score for a game
    def score(visited):
      t, b = 0, 1
      for i, j in enumerate(visited, 1):
        x = abs(i - j) + 1
        t, b = t * x + b, b * x
      g = gcd(t, b)
      return t // g, b // g
    
    sol = 0
    # the first throw goes too far
    for first in range(2, 10):
      # find possible sequences in which the bases are visited
      for v in solve(2, 1, (first,)):
        # compute the score (t / b)
        t, b = score(v)
        print(f'sequence (score {t}/{b}) = {v}')
        # find the score as an integer and remainder
        q, r = divmod(*score(v))
        if not r:
          sol = (q, v)
    print(f'\rSolution sequence (score {sol[0]}) = {sol[1]}')
    
  3. Hugh Casement 7 January 2019 at 7:40 pm

    From this issue onward, New Scientist bore a Saturday date, previously Thursday.
    It seems to have reverted to Friday from May 2018
    (but the last Enigma appeared in 2013).

Leave a Comment

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

WordPress.com Logo

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

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s

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

%d bloggers like this: