Enigmatic Code

Programming Enigma Puzzles

Singapore School Logic Puzzle

This puzzle has been getting a lot of attention recently, for example on the Guardian and BBC websites. It is quite similar to some of the Enigma puzzles published here.

Here it is slightly paraphrased to improve readability:

Albert and Bernard have just become friends with Cheryl, and they want to know when her birthday is.

Cheryl gives them a list of 10 possible dates, one of which is her birthday:

May 15, May 16, May 19,
June 17, June 18,
July 14, July 16,
August 14, August 15, August 17.

Cheryl then says she is going to tell the month of her birthday to Albert (but not to Bernard), and the day of her birthday to Bernard (but not to Albert). She does this.

The following conversation then took place:

Albert: I don’t know when Cheryl’s birthday is, but I know that Bernard does not know either.
Bernard: I didn’t know when Cheryl’s birthday is, but now I do.
Albert: Then I also know when Cheryl’s birthday is.

So, assuming they are all telling the truth and are all perfect logicians and have no further information, when is Cheryl’s birthday.

4 responses to “Singapore School Logic Puzzle

  1. Jim Randell 14 April 2015 at 5:33 pm

    The first statement by Albert is the most complicated, but the rest can be processed using the [[ filter_unique() ]] function in enigma.py (originally written for Enigma 265).

    Here’s a Python solution:

    from collections import namedtuple, defaultdict
    from enigma import filter_unique, printf
    
    # a type for representing dates
    Date = namedtuple('Date', 'month day')
    
    # possible dates (by month)
    dates = {
      'May': (15, 16, 19),
      'Jun': (17, 18),
      'Jul': (14, 16),
      'Aug': (14, 15, 17),
    }
    
    # map months to dates and dates to months
    (m2d, d2m) = (defaultdict(list), defaultdict(list))
    for (m, ds) in dates.items():
      for d in ds:
        m2d[m].append(d)
        d2m[d].append(m)
    
    # find candidate dates
    # A does not know the date (obviously, there are multiple choices for each month)
    # but also says B cannot know, so whatever month A has each day must be ambiguous
    dates = list()
    for (m, ds) in m2d.items():
      if all(len(d2m[d]) > 1 for d in ds):
        dates.extend((Date(m, d) for d in ds))
    
    # B now knows the full date, so it must be unique by day
    (dates, _) = filter_unique(dates, (lambda x: x.day))
    
    # A now knows the full date, so it must be unique by month
    (dates, _) = filter_unique(dates, (lambda x: x.month))
    
    # output possible dates
    for d in dates:
      printf("birthday = {d.month} {d.day}")
    

    Solution: Cheryl’s birthday is July 16.

  2. Jim Olson 14 April 2015 at 6:04 pm

    Thanks for the bonus puzzle

  3. arthurvause 16 April 2015 at 5:09 pm

    The puzzle seems to lend itself to an SQL solution:

    CREATE TABLE Birthdays ( 
        Month VARCHAR,
        Day   INTEGER 
    );
    
    insert into Birthdays 
    values ('May',15), ('May',16), ('May',19),
    ('June',17), ('June',18),
    ('July',14), ('July',16),
    ('August',14), ('August',15), ('August',17);
    
    create view Alberts_options as
    select * from Birthdays 
    where month not in 
    (select distinct b.Month from 
      (select Day from Birthdays
       group by Day
       having count(*)=1) a, Birthdays b 
       where b.Day=a.Day);
       
    with Bernards_options(Month,Day) as 
    (select Month, Day from Alberts_options 
    where Day not in (select Day from Alberts_options group by Day having count(*)>1))
    select * from Bernards_options
    where Month not in (select Month from Bernards_options group by Month having count(*)>1);
    
  4. arthurvause 27 May 2015 at 4:28 pm

    Here is an SQL solution to a follow-up variation of the puzzle, Denise’s Revenge

    CREATE TABLE DeniseBirthday ( 
        Day   INTEGER,
        Month VARCHAR( 3 ),
        Year  INTEGER 
    );
    
    insert into DeniseBirthday
    values
    (17,'Feb',2001),(16,'Mar',2002),(13,'Jan',2003),(19,'Jan',2004),
    (13,'Mar',2001),(15,'Apr',2002),(16,'Feb',2003),(18,'Feb',2004),
    (13,'Apr',2001),(14,'May',2002),(14,'Mar',2003),(19,'May',2004),
    (15,'May',2001),(12,'Jun',2002),(11,'Apr',2003),(14,'Jul',2004),
    (17,'Jun',2001),(16,'Aug',2002),(16,'Jul',2003),(18,'Aug',2004);
    
    create view Albert1 as
    select * from DeniseBirthday 
    where month not in 
    (select b.Month from 
      (select Day from DeniseBirthday
       group by Day having count(*)=1) a, DeniseBirthday b 
       where b.Day=a.Day);
       
    create view Bernard1a as
    select * from Albert1
    where Day not in
    (select Day from Albert1
     group by Day having count(*)==1);
    
    create view Bernard1b as
    select * from Bernard1a
    where Day not in 
    (select Day from Bernard1a
     where Year in 
    (select Year from Bernard1a
     group by year having count(*)==1)); 
    
    create view Cheryl1 as
    select * from Bernard1b
    where Year not in
    (select Year from Bernard1b
     where Month in
     (select Month from Bernard1b
      group by Month having count(*)=1));
    
    create view Albert2 as
    select * from Cheryl1
    where Month not in 
    (select Month from Cheryl1
    group by Month having count(*)>1);
      
    create view Bernard2 as
    select * from Albert2
    where Day not in 
    (select Day from Albert2
     group by Day having count(*)>1);
    
    select * from Bernard2;
    

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: