Teaser 3243: A break from tradition
From The Sunday Times, 17th November 2024 [link] [link]
Occasionally we used to play a very non-traditional variation of snooker. We tried to “pot” into the pockets five differently-coloured balls, which had points values of 7, 8, 9, 10, 13 in some order.
Our rules were that a red could only be followed by a blue or pink; a yellow by a red or green; a green by a yellow; a blue by a green; and a pink by a red or yellow. After a player had successfully potted a ball it was immediately returned to the table, and their turn continued, and this constituted a “break”.
One evening I scored breaks that totalled 33, 55, 24 and 57 points (none with more than five pots).
What were the values of red, yellow, green, blue and pink, in that order?
[teaser3243]


Jim Randell 7:05 am on 17 November 2024 Permalink |
This Python 3 program collects possible allowable sequences of up to 5 balls, and then looks for assignments of colour values that allow all the given scores to be made.
It runs in 75ms. (Internal runtime is 4.9ms).
from enigma import (flatten, subsets, map2str, printf) # allowed transitions adj = dict(R="BP", Y="RG", G="Y", B="G", P="RY") # generate break with up to <k> more balls def solve(k, bs): yield bs if k > 0: for x in adj[bs[-1]]: yield from solve(k - 1, bs + x) # collect possible breaks with up to 5 balls seqs = flatten(solve(4, k) for k in adj.keys()) # consider possible ball values for vs in subsets([7, 8, 9, 10, 13], size=len, select='P'): v = dict(zip("RYGBP", vs)) # score each of the possible sequences scores = set(sum(v[x] for x in bs) for bs in seqs) if scores.issuperset({33, 55, 24, 57}): # output solution printf("{v}", v=map2str(v, enc="", sort=0))Solution: The values of the balls are: red = 9, yellow = 10, green = 8, blue = 7, pink = 13.
The given breaks are:
LikeLike
ruudvanderham 3:22 pm on 17 November 2024 Permalink |
import itertools next_ball = dict(r="bp", y="rg", g="y", b="g", p="ry") targets = (33, 55, 24, 57) def shoot(balls, target, value): score = sum(value[ball] for ball in balls) if score <= target and len(balls) <= 5: if score == target: yield balls for try_ball in next_ball[balls[-1]] if balls else next_ball.keys(): yield from shoot(balls=balls + try_ball, target=target, value=value) for permutation in itertools.permutations((7, 8, 9, 10, 13)): ball_value = dict(zip(next_ball.keys(), permutation)) if all(any(shoot(balls="", target=target, value=ball_value)) for target in targets): print(" ".join(f"{ball}:{value}" for ball, value in ball_value.items())) for target in targets: print(f' {target} by playing {" or ".join(shoot(balls="",target=target,value=ball_value))}')LikeLike
Frits 3:44 pm on 17 November 2024 Permalink |
A lot of work for doing it a bit differently.
from enigma import SubstitutedExpression # dictionary of possible next colours d = {"b": "g", "g": "y", "p":"ry", "r": "bp", "y": "rg"} balls = sorted(d.keys()) points = [7, 8, 9, 10, 13] scores = [33, 55, 24, 57] # collect all possible breaks def solve(k, ss): yield ss if not k: return for x in d[ss[-1]]: yield from solve(k - 1, ss + x) breaks = set(''.join(sorted(x for x in s)) for k in d.keys() for s in solve(4, k)) # colours that may occur three times in a break balls3 = {k for k in balls if any(k in d[k1] for k1 in d[k])} # invalid digit / symbol assignments d2i = dict() for dgt in range(0, 10): vs = set() if dgt in {1, 2, 4, 5, 6}: vs.update('SCHQZ') if dgt > 1: vs.update('RBGPY') if dgt > 3: vs.update('ADFIJKLMNOTUVWXjqxyz') if dgt == 3: for i, k in enumerate(d.keys()): if k in balls3: continue # colours that may not occur three times in a break vs.update(['ADFIJKLMNOTUVWXjqxyz'[5 * j + i] for j in range(4)]) d2i[dgt] = vs # check if a colour combination is allowed def check(*s): # sorted used balls used = ''.join(balls[i] * x for i, x in enumerate(s) if x) return used in breaks # the alphametic puzzle p = SubstitutedExpression( [ # colors: b = BC, g = GH, p = PQ, r = RS, y = YZ "BC in {7, 8, 9, 10, 13}", "H != C", "GH in {7, 8, 9, 10, 13}", "Q not in {C, H}", "PQ in {7, 8, 9, 10, 13}", "S not in {C, H, Q}", "RS in {7, 8, 9, 10, 13}", "{7, 8, 9, 10, 13}.difference([BC, GH, PQ, RS]).pop() = YZ", #"A * BC + D * GH + F * PQ + I * RS + J * YZ == 57", "div(57 - (A * BC + D * GH + F * PQ + I * RS), YZ) = J", "A + D + F + I + J < 6", "check(A, D, F, I, J)", #"T * BC + U * GH + V * PQ + W * RS + X * YZ == 55", "div(55 - (T * BC + U * GH + V * PQ + W * RS), YZ) = X", "T + U + V + W + X < 6", "check(T, U, V, W, X)", #"K * BC + L * GH + M * PQ + N * RS + O * YZ == 24", "div(24 - (K * BC + L * GH + M * PQ + N * RS), YZ) = O", "K + L + M + N + O < 6", "check(K, L, M, N, O)", #"j * BC + q * GH + x * PQ + y * RS + z * YZ == 33", "div(33 - (j * BC + q * GH + x * PQ + y * RS), YZ) = z", "j + q + x + y + z < 6", "check(j, q, x, y, z)", ] , answer="(RS, YZ, GH, BC, PQ)", symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZjqxyz", d2i=d2i, distinct="", env=dict(check=check), denest=64, reorder=0, verbose=0, # use 256 to see the generated code ) # print answers for ans in p.answers(): print(f"answer: {ans}")LikeLike
Ruud 5:17 pm on 17 November 2024 Permalink |
Certainly a lot of work …
LikeLike