Skip to content

Instantly share code, notes, and snippets.

@axiomsofchoice
Created February 1, 2016 14:04
Show Gist options
  • Save axiomsofchoice/7c7bc5b555173c873072 to your computer and use it in GitHub Desktop.
Save axiomsofchoice/7c7bc5b555173c873072 to your computer and use it in GitHub Desktop.
Partial Solution, "Registration" Stage 5 Director GCHQ's Christmas Puzzle 2015.
#!/usr/bin/env python
"""
Partial Solution, "Registration" Stage 5 Director GCHQ's Christmas Puzzle 2015.
===============================================================================
http://www.gchq.gov.uk/press_and_media/news_and_features/Pages/Director%27s-Christmas-puzzle-2015.aspx
Puzzle Crown Copyright.
SPOILER ALERT - If you're attempting the puzzle and you don't want to find out
any hints do not read ahead!
-----------
The MIT License (MIT)
Copyright (c) 2015-2016 Dan Hagon.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import itertools
import copy
# This is the list of surnames and numbers of days absent given in question.
register = [
# Boys
("Higgins", 6),
("Jones", 5),
("Jones", 3),
("Thomson", 4),
("Wright", 5),
("Yates", 2),
# Girls
("Dawson", 3),
("Franklin", 4),
("Hayes", 2),
("Higgins", 4)
]
# This is the grid that corresponds to the above entries.
# It started off as all *'s but gradually was built up using the numbered rules given below, following this convention:
# 0 - known present; 1 - known absent; * - don't know.
# At the end of each row we show which rules apply in each case.
# RulesApplied, DaysAbsent, "I"dential/"N"on-idential/*ther, LastName, FirstName
# Rules are also applied to columns
reg_grid = [
#['M', 'T', 'W', 'T', 'F', 'M', 'T', 'W', 'T', 'F'],
# Boys (since we're told boys comes before girls)
['1', '1', '1', '1', '0', '0', '0', '0', '1', '1'], # 5b) 6 N Higgins
['1', '1', '*', '0', '0', '0', '*', '0', '*', '*'], # 3) 5 I Jones
['1', '1', '*', '0', '0', '0', '*', '0', '*', '*'], # 3,5) 3 I Jones "Dennis"
['0', '1', '*', '*', '*', '0', '0', '0', '*', '1'], # 4b) 4 * Thompson "David"
['0', '0', '1', '1', '1', '0', '0', '0', '1', '1'], # 4e) 5 * Wright "Jon"
['0', '0', '0', '0', '0', '0', '0', '0', '1', '1'], # 2 * Yates
# Girls (Since the alphabetical order restarts)
['0', '0', '1', '*', '*', '0', '0', '0', '*', '1'], # 2 ) 3 * Dawson "Chris"
['0', '0', '0', '1', '1', '1', '0', '0', '0', '1'], # 5a) 4 * Franklin "Anita"
['1', '0', '0', '0', '0', '0', '0', '0', '0', '1'], # 3d) 2 * Hayes "Tina"
['1', '*', '*', '*', '*', '0', '0', '0', '*', '1'] # 4 N Higgins
# 3d) 5a) 6b) 6a) 7a) 1c)
]
# These lists were also helpful and were built up over time using the numbered rule as given below.
list_of_boys = [
"Dennis", # 1a)
"David", # 4a)
"Jon" # 4c)
]
list_of_girls = [
"Tina", # 3b)
"Antina", # 4f)
"Chris" # ??
]
#####################
# Helper functions. #
#####################
def rowsum(row):
"""Compute the sum of days absent for both weeks"""
count = 0
for i in row:
if i == '1':
count += 1
return count
def checkForValidRegister(r_grid, row_spec=None):
"""For each row check that the row sum equals the constraint for the question as set. Displays True or False at the end of each row."""
if row_spec is None:
for r in range(len(r_grid)):
if rowsum(r_grid[r]) != register[r][1]:
return False
return True
else:
for r in range(len(r_grid)):
if r in row_spec:
if rowsum(r_grid[r]) != register[r][1]:
return False
return True
##########################################
# Build combinations and print them out. #
##########################################
def buildBasicBoard():
"""
After constructing reg_grid by hand using the indicated rules the remaining possibilities are filled and tested in turn to eliminate in correction ones
"""
for i in [0,1]:
reg_grid[1][8] = '*'
reg_grid[2][8] = '*'
# 1c) Generate a 1 in each position for last Friday (2 combinations).
if i:
reg_grid[1][9] = '1'
reg_grid[2][9] = '0'
else:
reg_grid[1][9] = '0'
reg_grid[2][9] = '1'
for j in [0,1]:
# 6b) Generate each of the two possibilities for the one absentee for the last Tuesday (2 combinations).
if j:
reg_grid[1][6] = '1'
reg_grid[2][6] = '0'
else:
reg_grid[1][6] = '0'
reg_grid[2][6] = '1'
# For debug
#yield reg_grid
#continue
num_still_to_get = 1 # We already know about Jon Wright (and other)
locations_to_fill = [1,2,3,6,9]
for l in itertools.combinations(locations_to_fill, \
num_still_to_get):
# 7a )Generate a each of the four absentees for last Thursday (7 choose 2 = ?? combinations).
#print list(l)
for m in locations_to_fill:
#print m
if m in list(l):
reg_grid[m][8] = '1'
else:
reg_grid[m][8] = '0'
# For debug
#yield reg_grid
#continue
# Make sure nobody is absent on last Thursday was present on last Friday.
if (reg_grid[1][8] == '1' and reg_grid[1][9] == '0') or \
(reg_grid[2][8] == '1' and reg_grid[2][9] == '0'):
continue
# For debug
#yield reg_grid
#continue
# Fill in the remaining entry for the first Jones to make up the numbers.
if rowsum(reg_grid[1][5:]) == 2:
reg_grid[1][2] = '1'
elif rowsum(reg_grid[1][5:]) == 3:
reg_grid[1][2] = '0'
else:
continue
# For debug
#yield reg_grid
#continue
# Fill in the remaining entry for the second Jones to make up the numbers.
if rowsum(reg_grid[2][5:]) == 0:
reg_grid[2][2] = '1'
elif rowsum(reg_grid[2][5:]) == 1:
reg_grid[2][2] = '0'
else:
continue
# For debug
#yield reg_grid
#continue
# Both Joneses are only absent for two days at the same time.
if rowsum(reg_grid[1][:5]) == 2 and rowsum(reg_grid[2][:5]) == 2:
continue
yield reg_grid
#continue
# With all the rules applied we get 5 possible combinations with some unsure entries still present and by hand we then fill in (see below) possibilities and pick the one correct solution.
count = 0
for reg_grid in buildBasicBoard():
print
for r in range(len(reg_grid)):
print reg_grid[r], register[r][1], rowsum(reg_grid[r]) == register[r][1]
print "Is valid:", checkForValidRegister(reg_grid)
count += 1
print
print count
##############################################################################
# Rules #
# Each sentence from original question is numbered below (though not shown). #
# Best if read side-by-side with reg_grid above. #
##############################################################################
"""
Line 1)
"""
# Identical twins always have the same sex: http://scienceline.ucsb.edu/getkey.php?key=4016
# 1a) Dennis is one of the boys.
# 1b) Dennis is one of the identical twins.
# We infer that Jones are identical twins.
# 1c) One identical twin is present on the last Friday and everyone else is absent.
"""
Line 2)
"""
# 2 ) One pupil (different from the one in 1c)) was present for the whole of the first week.
"""
Line 3)
"""
# 3a) Tina is one of the girls.
# 3b) Tina is *not* one of the non-identical twins.
# 3c) Tina is *not* one of the identical twins.
# We infer Tina is one of Dawson or Hayes (not Franklin by 4g)).
# 3d) There are 5 pupils absent on the first Monday, 4 of them were twins and the other is one of Dawson, Franklin or Hayes.
# We infer that the three remaining boys (Thompson, Wright and Yates) are all present on the first Monday.
"""
Line 4)
"""
# 4a) David is one of the boys.
# 4a2) David is not one of the identical twins.
# 4a3) David is not one of the non-identical twins.
# 4b) One boy that was present on the first Monday is absent on the first Tuesday; that boy is called David.
# 4c) Jon is one of the boys.
# 4c2) Jon is not one of the identical twins.
# 4c3) Jon is not one of the non-identical twins.
# 4c4) Jon has the surname Wright.
# We infer David is one of Thompson or Yates.
# 4d) Chris is is too ambiguous to be a boy or a girl.
# 4e) A boys that was present on the first Monday is absent on the first Wednesday.
# 4e2) A pupil that was present on the first Monday is absent on the first Wednesday.
# 4f) Antia is one of the girls.
# 4g) One girl that was present on the first Monday is absent on the first Thursday; that girl is called Anita.
# Initially we skip 5) since later ones can reduce some of the possibilities.
"""
Line 5)
"""
# 5a) The girl that was present from the first Monday to the first Wednesday and absent from the first Thursday to the first Friday is also absent on the last Monday (but not the last Tuesday, presumably).
# From what we already know Anita and Jon are the two who are off for 5 days.
# We can infer that that first Jones cannot be off for 5 days. Since the first Monday is absent (and assuming male Higgins could not be off Thursday or Friday of the first) we can only have 2 or 3 days absent at the beginning of the week. Hence there need to be either 3 or 2 absences in columns 7, 9 or 10.
# 5b) Two pupils are ill for (consecutive) 5 days (two days of which can be over one of the weekends).
# It looks like Anita Franklin and the Male Higgins are the ones that were ill for five days straight - there is a strong suggestion that in the first week those that are off during the week are no off again until the next week, if at all.
# 5c) Six pupils are ill for (consecutive) 3 days (two days of which can be over one of the weekends).
# 5d) One pupil was ill for two days (possibly including part or all of the weekend).
# It seems from 2) that there could only be one pupil that was off for the whole of the first week.
# 5e) One pupil is not affected by the original bug (might be absent later for other reasons).
# By elimination this appears to be Hayes.
"""
Line 6)
"""
# 6a) All ten pupils are present in class on second Wednesday.
# 6b) An idential twin was absent on the second Tuesday, everyone else present.
"""
Line 7)
"""
# 7a) On the second Thursday there are 4 absent.
# 7a2) If a pupil was absent on the last Thursday they will also be absent on the last Friday (only applies to Jones twins).
# 7b) same as 1c).
# By consideration of the total number of absent days we can clearly see that Anita must have the surname Franklin.
# 4g) One of the girls (Anita); 5a) same girl was also ill on the Monday.
#['0', '0', '0', '1', '1', '1'
# This also allows us to fill all the remaining entried for the second Monday.
# By also considering absent days total we can fill in the remaining values for Jon Wright.
# From the fact that male Higgins was ill from the prior Sunday we infer that he would be absent for the first four days of the week only. That allows us to infer the rest of the entries for the male Higgins.
#############################################################################
# The possibility for the remaining unallocated first weeks are as follows. #
#############################################################################
# 2) and 5e) Any of those left (different from 1c)); could not be Thompson or Dawson due to total absent days consideration; so either Yates or Hayes.
#['0', '0', '0', '0', '0'
# 3d) One of the girls (Tina).
#['1', '*', '*', '*', '*'
# 4b) One of the boys (David).
#['0', '1', '*', '*', '*'
# 4e) Either a boy or a girl (Chris).
#['0', '0', '1', '*', '*'
# Yates could not be David as otherwise there would a single isolated day in the middle of the first week which does not appear to be the case from the description. That must mean that Yates was the pupil absent for the whole of the first week.
# By the same token Hayes must be Tina, meaning that Tina was off for 3 days, two of which were the weekend.
# That leaves David as the remaining boy
# And Chris as the remaining girl
# After running the above code the remaining solution is completed by hand below:
"""
# M Higgins was off for 5 days (by inclusion of sunday)
['1', '1', '1', '1', '0', '0', '0', '0', '1', '1'] 6 True
# first Jones was off for 3 days (not including 1st w/e)
['1', '1', '1', '0', '0', '0', '1', '0', '0', '1'] 5 True
# second Jones was off for 3 days (not including 1st w/e)
['1', '1', '1', '0', '0', '0', '0', '0', '0', '0'] 3 True
# Thomson was off for 3 days
['0', '1', '1', '1', '0', '0', '0', '0', '0', '1'] 4 True
# Jon Wright was off for 3 days (not including 2nd w/e)
['0', '0', '1', '1', '1', '0', '0', '0', '1', '1'] 5 True
# Yates was not off
['0', '0', '0', '0', '0', '0', '0', '0', '1', '1'] 2 True
# Dawson was off for 2 days (came in on the third)
['0', '0', '1', '1', '0', '0', '0', '0', '0', '1'] 3 True
# Anita was off for 5 days (by inclusion of 2nd w/e)
['0', '0', '0', '1', '1', '1', '0', '0', '0', '1'] 4 True
# Tina was off for 3 days (by including 1st w/e)
['1', '0', '0', '0', '0', '0', '0', '0', '0', '1'] 2 True
# F Higgins was off for 3 days (by inclusion of sunday)
['1', '1', '0', '0', '0', '0', '0', '0', '1', '1'] 4 True
"""
########################
# Final complete grid. #
########################
# 0 - known present; 1 - known absent; * - don't know.
reg_grid = [
#['M', 'T', 'W', 'T', 'F', 'M', 'T', 'W', 'T', 'F'],
# Boys (since we're told boys comes before girls)
['1', '1', '1', '1', '_', '_', '_', '_', '1', '1'], # 5b) 6 N Higgins
['1', '1', '1', '_', '_', '_', '1', '_', '_', '1'], # 3) 5 I Jones
['1', '1', '1', '_', '_', '_', '_', '_', '_', '_'], # 3,5) 3 I Jones "Dennis"
['_', '1', '1', '1', '_', '_', '_', '_', '_', '1'], # 4b) 4 * Thomson "David"
['_', '_', '1', '1', '1', '_', '_', '_', '1', '1'], # 4e) 5 * Wright "Jon"
['_', '_', '_', '_', '_', '_', '_', '_', '1', '1'], # 2 * Yates
# Girls (Since the alphabetical order restarts)
['_', '_', '1', '1', '_', '_', '_', '_', '_', '1'], # 2 ) 3 * Dawson "Chris"
['_', '_', '_', '1', '1', '1', '_', '_', '_', '1'], # 5a) 4 * Franklin "Anita"
['1', '_', '_', '_', '_', '_', '_', '_', '_', '1'], # 3d) 2 * Hayes "Tina"
['1', '1', '_', '_', '_', '_', '_', '_', '1', '1'] # 4 N Higgins
# 3d) 5a) 6b) 6a) 7a) 1c)
]
# At this point I stumbled and could not figure out what the keyword should be.
"""
Had I drawn it like this I might have got a clue:
'....0000..'
'...000.00.'
'...0000000'
'0...00000.'
'00...000..'
'00000000..'
'00..00000.'
'000...000.'
'.00000000.'
'..000000..'
For further discussion see:
https://www.reddit.com/r/puzzles/comments/3w6ja9/gchqs_christmas_puzzler_thread_spoilers_tagged/cxue9gl
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment