Football Teasers
I don’t like football teasers (or, indeed, anything to do with football) but I do make an effort to solve those that are published by the Sunday Times. Until now I have tended to use an ad-hoc program to solve them but I have also played with a general purpose solver along the lines of that provided by Jim Randell in his enigma.py library. However I have now put more effort into a generic solver that I am making available here as football.py . This is very much “work in progress” so if you do try it, be aware that it is likely to have bugs in it. The documentation is very limited so it is most easily understood by looking at examples of its use that I will reference on this page as and when I produce them. To get started, here is my use of it to solve New Scientist Enigma 492 that Jim Randell has just added on his Enigmatic Code site:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
from football import Football f = Football('ABCD', outcomes='uldw', sep='') # The B row is wrong since B cannot have won 3 matches with # only two more 'goals for' than 'goals against'. Consider # which of B's entries is wrong. entry = ('p', 'w', 'gf', 'ga') for bad in entry: # find outcomes (unplayed, lost, drawn, won) # for A, B, C and D that are consistent with # the known correct table entries for d1 in f.games(dict(), 'A'): At = f.table(d1, 'A') if At.p != 2 or At.d != 2: continue for d2 in f.games(d1, 'B'): Bt = f.table(d2, 'B') if (not (bad == 'p' or Bt.p == 3) or not (bad == 'w' or Bt.w == 3)): continue for d3 in f.games(d2, 'C'): Ct = f.table(d3, 'C') if Ct.p != 2: continue for d4 in f.games(d3, 'D'): Dt = f.table(d4, 'D') if Dt.w != 1 or Dt.d: continue # now find match results for A, B, C and D that # are consistent with the known correct entries for d5 in f.matches(d4, 'A', 4, 4): for d6 in f.matches(d5, 'D', 3, 1): b_gf = 5 if bad != 'gf' else (0, 10) b_ga = 3 if bad != 'ga' else (0, 10) for d7 in f.matches(d6, 'B', b_gf, b_ga): Ct = f.table(d7, 'C') c_gf = (Ct.gf, Ct.gf + 5) c_ga = (Ct.ga, Ct.ga + 5) for d8 in f.matches(d7, 'C', c_gf, c_ga): s = ('played', 'won', 'goals for', 'goals_against') print(f"\nB's '{s[entry.index(bad)]}' value is wrong\n") res = f.results(d8)[:2] print(', '.join(f"{n}:{r}" for n, r in zip(*res))) for t in f.teams: print(' ', t, f.table(d8, t)) print() |
I am not at all at home with the intricacies of football so I would like to thank John Crabtree who has been an enormous help while I have been building this solver. He has unerringly put me on the right track whenever I have come up with ‘solutions’ to my test cases that should not be there.
Teaser 2942
Teaser 2893
Teaser 2845
Teaser 2721
Teaser 2567
Teaser 2528
Teaser 2516