# Enigmatic Code

Programming Enigma Puzzles

## Enigma 1148: Four-way fairway tie

From New Scientist #2304, 18th August 2001 [link]

After they had each played four rounds in the golf tournament Bernhard, Colin, Darren and Ernie all ended up with the same total score even though the scores for the 16 individual rounds that they played were all different. Each player’s score for each round was in the 60s or 70s (that is to say between 60 and 79 inclusive). Bernhard’s score in each of his four rounds was a prime number, Colin’s score in each of his four rounds was a semi-prime (the product of two prime numbers), Darren’s and Ernie’s scores in each of their four rounds were numbers that are neither prime nor semi-prime. Darren’s best (lowest) round was better than Ernie’s best round, and Darren’s worst round was better than Ernie’s worst round.

List in ascending order Ernie’s scores for the four rounds.

[enigma1148]

### 3 responses to “Enigma 1148: Four-way fairway tie”

1. Jim Randell 26 September 2016 at 8:10 am

This Python program runs in 38ms.

```from collections import defaultdict
from itertools import combinations, product
from enigma import irange, factor, printf

# categorise the numbers from 60 to 79 into exactly one of:
# primes, semiprimes, others
(primes, semiprimes, others) = (set(), set(), set())
for i in irange(60, 79):
n = len(factor(i))
if n == 1:
elif n == 2:
else:

# record sets of k numbers by their sum
def scores(ns, k=4):
r = defaultdict(set)
for s in combinations(ns, k):
return r

# B's scores are 4 primes
Bs = scores(primes)

# C's scores are 4 semi-primes
Cs = scores(semiprimes)

# D's and E's are 4 others
DEs = scores(others)

# consider possible common total scores
for t in set(Bs.keys()).intersection(Cs.keys()).intersection(DEs.keys()):

# there must be two candidates for DE
for DE in combinations(DEs[t], 2):
# D's lowest score is lower than E's lowest score
(D, E) = sorted(DE)
if not(D[0] < E[0]): continue
# D's highest score is also lower than E's highest score
if not(D[-1] < E[-1]): continue
# D and E have no scores in common
if set(D).intersection(E): continue

# B and C also have the same total score
for (B, C) in product(Bs[t], Cs[t]):
printf("t={t}, B={B} C={C} D={D} E={E}")
```

Solution: Ernie’s scores were: 64, 66, 70, 78.

The full results (lowest score to highest score) are:

Bernhard: 61, 67, 71, 79;
Colin: 62, 65, 74, 77;
Darren: 63, 68, 72, 75;
Ernie: 64, 66, 70, 78.

Each total score is 278.

By the time we reach line 46 we already know the solution to the problem (E’s score), and that there are scores for B and C (which must all be distinct from the other scores by definition).

2. geoffrounce 27 September 2016 at 8:52 am

The narrow range of maximum scores (60 – 79) makes it fairly easy to itemise
the numbers into primes, semi-primes and others, as follows:

Primes = 61, 67, 71, 73 and 79

Semi-Primes = 62 (2*31), 65 (5*13), 69 (3*23), 74 (2*37), and 77 (7*11)

Remaining numbers = 60, 63, 64, 66, 68, 70, 72, 75, 76 and 78

These sets of numbers are used in my programme

I did not find a general semi-prime constraint in MiniZinc.

Any ideas ?.

```% A Solution in MiniZinc
include "globals.mzn";

var 240..280: tot_score;

% Arrays of 4 rounds of 4 players
array[1..4] of var 60..79: B;   % Bernhard
array[1..4] of var 60..79: C;   % Colin
array[1..4] of var 60..79: D;   % Darren
array[1..4] of var 60..79: E;   % Ernie

% Scores for the 16 individual rounds that they played were all different
constraint all_different ([ B[1], B[2], B[3], B[4],  C[1], C[2], C[3], C[4],
D[1], D[2], D[3], D[4],  E[1], E[2], E[3], E[4] ]) ;

% Bernhard, Colin, Darren and Ernie all ended up with the same total score
constraint tot_score == B[1] + B[2] + B[3] + B[4] /\ tot_score == C[1] + C[2] + C[3] + C[4]
/\ tot_score == D[1] + D[2] + D[3] + D[4] /\ tot_score == E[1] + E[2] + E[3] + E[4];

% Bernhard’s score in each of his four rounds was a prime number (between 60 and 79)
constraint forall (i in 1..4) (B[i] in {61, 67, 71, 73, 79});

% Colin’s score in each of his four rounds was a semi-prime (between 60 and 79)
constraint forall (i in 1..4) (C[i] in {62, 65, 69, 74, 77});

% Darren’s and Ernie’s scores in each of their four rounds were numbers that
% are neither prime nor semi-prime (between 60 and 79)
constraint forall (i in 1..4) (D[i] in {60, 63, 64, 66, 68, 70, 72, 75, 76, 78});

constraint forall (i in 1..4) (E[i] in {60, 63, 64, 66, 68, 70, 72, 75, 76, 78});

% Darren’s best (lowest) round was better than Ernie’s best round
% and Darren’s worst(highest) round was better than Ernie’s worst round
constraint increasing( [D[1], D[2], D[3], D[4]])
/\ increasing( [E[1], E[2], E[3], E[4]])
/\ D[1] < E[1] /\ D[4] < E[4];

solve satisfy;

output [ "B: " ++ show(sort( (B))) ++ "\n" ++
"C: " ++ show(sort((C))) ++ "\n" ++
"D: " ++ show( sort((D))) ++ "\n" ++
"E: " ++ show(sort((E))) ++ "\n" ++
"Total score = " ++ show(tot_score) ];

% B: [61, 67, 71, 79]
% C: [62, 65, 74, 77]
% D: [63, 68, 72, 75]
% E: [64, 66, 70, 78]  <<< the answer
% Total score = 278
% Finished in 89msec
```
3. geoffrounce 27 September 2016 at 8:34 pm

Brian Gladman has found a neat solution to the semi-prime constraint in MiniZinc, as shown in the sample test code below

```% semi primes test

include "globals.mzn";

int: max_val = 100;

% the primes up to max_val
set of int: primes = {2} union (2..max_val diff { i | i in 2..max_val,
j in 2..ceil(sqrt(i)) where i mod j = 0});  % ex Hakan
% the semi-primes up to max_val
set of int: semi_primes = {i * j | i in primes, j in primes where i * j <= max_val};

solve satisfy;

output[ "Semi Primes = "  ++ show(semi_primes) ];

% Semi Primes = {4,6,9,10,14,15,21,22,25,26,33,34,35,38,39,46,49,51,55,57,58,62,65,69,
% 74,77,82,85,86,87,91,93,94,95}
```