Enigmatic Code

Programming Enigma Puzzles

Enigma 1694: The town clock prank

From New Scientist #2861, 21st April 2012 [link]

Our town clock has a standard 12-hour face, divided into 60 minute marks. The hands do not move smoothly: the minute hand moves sharply on to the next minute mark at the end of each minute, and the hour hand moves at the end of each 12 minutes.

The other day, a prankster swapped the hands (an operation that only took a few seconds between movements of the mechanism). Immediately after the swap, the clock showed the wrong time, but it was a valid time; in other words, the relative positions of the hands made sense. Within the next 10 minutes, the clock had shown the right time for exactly 2 minutes.

What time did the clock show immediately before the hands were swapped?

[enigma1694]

Advertisements

4 responses to “Enigma 1694: The town clock prank

  1. Jim Randell 18 April 2012 at 7:24 pm

    The following Python code runs in 51ms.

    from enigma import printf
    
    # the clock face has 60 divisions
    # make a list of valid times for 12 hours
    valid = list((i // 12, i % 60) for i in range(0, 12 * 60))
    
    # convert division hour hand is pointing to to the actual hour
    def hour(t):
      h = t // 5
      return 12 if h == 0 else h
    
    # now find elements which are also a valid time when swapped
    swapped = []
    for i, p in enumerate(valid):
      s = tuple(reversed(p))
      if s not in valid: continue
      swapped.append(i)
    
    # and find a run of 3 valid swapped elements with 10 minutes of each other
    n = len(swapped)
    for i in range(n):
      s = list(swapped[j % n] for j in range(i, i + 3))
      if s[2] - s[0] > 10: continue
      # swapped time can only be right if the hands are on top of each other
      # but this can't happen at the time of the swap
      ts = list(valid[j] for j in s)
      if list(t[0] == t[1] for t in ts) != [False, True, True]: continue
    
      # show the actual times and the displayed times
      for t in ts:
        printf("time = {h0}:{t[1]:02d} -> {h1}:{t[0]:02d}", h0=hour(t[0]), h1=hour(t[1]))
      print()
    

    Solution: The clock was showing 11:54 when the hands were switched.

    • Jim Randell 18 April 2012 at 9:58 pm

      And a slightly more efficient variation (although it’s not noticeably faster).

      from enigma import printf
      
      # the clock face has 60 divisions
      # make a list of valid times for 12 hours
      valid = list((i // 12, i % 60) for i in range(0, 12 * 60))
      
      # convert division hour hand is pointing to to the actual hour
      def hour(t):
        h = t // 5
        return 12 if h == 0 else h
      
      # now find elements which are also a valid time when swapped
      swapped = list(i for i, p in enumerate(valid) if tuple(reversed(p)) in valid)
      
      # and elements where the hands are superimposed
      same = set(i for i, p in enumerate(valid) if p[0] == p[1])
      
      # and find a run of 3 valid swapped elements with 10 minutes of each other
      n = len(swapped)
      for i in range(n):
        s = list(swapped[j % n] for j in range(i, i + 3))
        if s[2] - s[0] > 10: continue
        # the swap changes to a different time
        if s[0] in same: continue
        # and the other two times should be the same
        if not(s[1] in same and s[2] in same): continue
      
        # show the actual times and the displayed times
        ts = list(valid[j] for j in s)
        for t in ts:
          printf("time = {h0}:{t[1]:02d} -> {h1}:{t[0]:02d}", h0=hour(t[0]), h1=hour(t[1]))
        print()
      
      • Jim Randell 18 April 2012 at 11:02 pm

        Here’s a version that finds the two times that are correct first, and then goes back to find when the hands were swapped. It runs in 34ms.

        from enigma import irange, sprintf, printf
        
        # the clock face has 60 divisions
        # make a list of valid times for 12 hours
        valid = list((i // 12, i % 60) for i in range(0, 12 * 60))
        
        # convert divisions to standard times
        def time(t):
          (h, m) = (t[0] // 5, t[1])
          if h == 0: h = 12
          return sprintf("{h:02d}:{m:02d}")
        
        # find elements where the hands are superimposed
        same = list(i for (i, p) in enumerate(valid) if p[0] == p[1])
        n = len(same)
        
        # find two times when the hands are the same that are within 10 (in fact 9) minutes of each other
        for (i, s1) in enumerate(same):
          s2 = same[(i + 1) % n]
          d = (s2 - s1) % 720
          if d > 9: continue
          # and then look for a time within 10 minutes of the second time
          # where the hands are valid when swapped (but not the same)
          for j in irange(1, 10 - d):
            t0 = valid[s1 - j]
            if s1 - j in same: continue
            if (t0[1], t0[0]) not in valid: continue
        
            # show the times and the displayed times
            for t in (t0, valid[s1], valid[s2]):
              printf("time = {t} -> {s}", t=time(t), s=time((t[1], t[0])))
            print()
        
  2. Brian Gladman 19 April 2012 at 9:05 am

    Here is my version:

    
    full_day = 12 * 60
    # create dictionaries to/from time and hand positions
    # (hand positions in 6 degree units)
    time_to_clock = dict()
    clock_to_time = dict()
    for time in range(full_day):
      time_to_clock[time] = (time // 12, time % 60)
      clock_to_time[(time // 12, time % 60)] = time
    
    for time in range(0, full_day):
      # get hour and minute hand positions
      h_pos, m_pos = time_to_clock[time]
      # check time shown is wrong but valid
      if h_pos != m_pos and (m_pos, h_pos) in clock_to_time:
        # find times in next 10 minutes when clock is right
        times = []
        for t in range(time, time + 10):
          # get hour and minute hand positions
          h_p, m_p = time_to_clock[t % 720]
          # get time shown when hands are swapped
          try:
            clock_time = clock_to_time[(m_p, h_p)]
          except KeyError:
            continue
          # count how many times the clock is correct
          if clock_time == t % 720:
            times += [(t // 60, t % 60)]
        if len(times) == 2:
          print('{0:02d}:{1:02d} ('.format(time // 60, time % 60), end='')
          print('{0[0]:02d}:{0[1]:02d}, '.format(times[0]), end='')
          print('{0[0]:02d}:{0[1]:02d})'.format(times[1]))
    

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: