Enigmatic Code

Programming Enigma Puzzles

Enigma 814: Mixed and matched

From New Scientist #1969, 18th March 1995 [link] [link]

Enigma 814

I had a day at the health club recently. I planned to have one full session of squash, one full session of badminton and one full session in the sauna (but not necessarily in that order) with at least a one-hour break between each of the sessions. The club’s timetable of sessions is shown above.

By coincidence my colleagues Mark and Jenny also spent the day there with the same idea in mind (although none of us necessarily did any of the activities together).

Our boss (who knew all the above facts) tried to telephone me at one stage but was told I was busy. From this he was able to work out my exact day’s schedule.

An hour later he tried to telephone Mark but was told he was busy and he was told the activity which Mark was engaged in. From this my boss was able to work out Mark’s exact schedule.

Another hour later he tried to telephone Jenny but was told she was busy and he was told the activity which Jenny was engaged in. From this my boss was able to work out Jenny’s exact schedule.

Which is the correct order of the men’s, women’s, and mixed sessions in the sauna?

[enigma814]

Advertisement

One response to “Enigma 814: Mixed and matched

  1. Jim Randell 13 March 2023 at 11:30 am

    This Python program runs in 59ms. (Internal runtime is 1.3ms).

    Run: [ @replit ]

    from enigma import (
      irange, tuples, subsets, singleton, flatten, peek,
      update, join, sprintf, map2str, printf
    )
    
    # represent times in minutes
    T = lambda h, m: h * 60 + m
    
    sessions = {
      # squash sessions: 45m sessions starting at 10:15 - 16:15
      'sq': list(tuples(irange(T(10, 15), T(16, 15) + 45, step=45), 2)),
      # badminton sessions: 60m sessions, starting at 10:00 - 16:00
      'ba': list(tuples(irange(T(10, 00), T(16, 00) + 60, step=60), 2)),
      # sauna sessions, 120m sessions
      'sa': [ (T(10, 00), T(12, 00)), (T(12, 30), T(14, 30)), (T(15, 00), T(17, 00)) ],
    }
    
    # return activities in progress; key -> time
    def progress(t):
      d = dict()
      # select activities in progress at time t
      for (k, vs) in sessions.items():
        for (a, b) in vs:
          if a <= t < b:
            d[k] = (a, b)
      return d
    
    # complete a schedule that is busy at time t
    def schedule(d):
      # choose an unrepresented activity
      k = peek((k for k in sessions.keys() if k not in d), default=None)
      if k is None:
        yield d
      else:
        # look for times that don't interfere with existing activities
        for (x, y) in sessions[k]:
          if all(a >= y + 60 or x >= b + 60 for (a, b) in d.values()):
            yield from schedule(update(d, [(k, (x, y))]))
    
    # format times
    time = lambda *ts: join((sprintf("{h:02d}:{m:02d}", h=t // 60, m=t % 60) for t in ts), sep=" - ")
    
    # format a schedule
    fmt = lambda ss: map2str(((time(a, b), k) for (k, (a, b)) in ss.items()), arr=" = ", sep="; ")
    
    # consider times the call for josie could be made
    for t1 in irange(T(10, 15), T(14, 45), step=15):
      # times of subsequent calls
      (t2, t3) = (t1 + 60, t1 + 120)
      # collect possible activities in progress at time t
      d = progress(t1)
      # look for a time that gives a unique schedule
      ss1 = singleton(flatten((schedule({k: v}) for (k, v) in d.items()), fn=iter))
      if ss1 is None: continue
    
      # collect activities in progress for mark's call
      d = progress(t2)
      # look for an activity that gives a unique schedule
      for (k, v) in d.items():
        ss2 = singleton(schedule({k: v}))
        if ss2 is None: continue
    
        # collect activities in progress for jenny's call
        d = progress(t3)
        for (k, v) in d.items():
          ss3 = singleton(schedule({k: v}))
          if ss3 is None: continue
          # output schedules
          printf("call @ {t} -> josie = {ss}", t=time(t1), ss=fmt(ss1))
          printf("call @ {t} -> mark ({k}) = {ss}", t=time(t2), ss=fmt(ss2))
          printf("call @ {t} -> jenny ({k}) = {ss}", t=time(t3), ss=fmt(ss3))
          # consider Male, Female, miXed sauna sessions
          for (M, F, X) in subsets(sessions['sa'], size=3, select='P'):
            # no males in the Female session, and no females in the Male session
            if ss1['sa'] == M or ss2['sa'] == F or ss3['sa'] == M: continue
    
            printf("male = {M}; female = {F}; mixed = {X}", M=time(*M), F=time(*F), X=time(*X))
          printf()
    

    Solution: 10:00 – 12:00 = ladies only; 12:30 – 2:30 = men only; 3:00 – 5:00 = mixed.


    The setter (Josie, who I assume is female) has a schedule as follows:

    10:15 – 11:00 = squash
    11:00 – 12:00 = break
    12:00 – 13:00 = badminton
    13:00 – 15:00 = break
    15:00 – 17:00 = sauna

    Her phone call came in at 12:00, when she had just started her badminton session.

    Mike (male) has the following schedule:

    10:00 – 11:00 = badminton
    11:00 – 12:30 = break
    12:30 – 13:15 = squash
    13:15 – 15:00 = break
    15:00 – 17:00 = sauna

    His phone call came in at 13:00 when he was in the middle of a squash session.

    Josie and Mike are in the same sauna session, so it must be the mixed session.

    Jenny has two possible schedules:

    10:00 – 12:00 = sauna
    12:00 – 14:00 = break
    14:00 – 14:45 = squash
    14:45 – 16:00 = break
    16:00 – 17:00 = badminton

    10:00 – 12:00 = sauna
    12:00 – 14:00 = break
    14:00 – 15:00 = badminton
    15:00 – 16:15 = break
    16:15 – 17:00 = squash

    Her call comes in at 14:00, when she has just started either a squash or badminton session, and being told which of these she was doing enables her boss to identify which of these possibilities is her schedule (but we can’t).

    In either case Jenny is in the 10:00 – 12:00 sauna session, so that must be the ladies only session, and the 12:30 – 2:30 sauna session must be the men only session.

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 )

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: