# 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;
```

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