Enigmatic Code

Programming Enigma Puzzles

Enigma 377: Cricket, lovely cricket

From New Scientist #1526, 18th September 1986 [link]

Enigma 377

Three runs were scored in each over and they were all scored in singles.

What was the number of runs scored at the fall of each wicket?

[enigma377]

Advertisements

4 responses to “Enigma 377: Cricket, lovely cricket

  1. Jim Randell 30 December 2016 at 8:05 am

    I wasn’t sufficiently familiar with the rules of cricket to solve this one without doing a bit of research [ https://en.wikipedia.org/wiki/Cricket#Format_of_the_game ], but I eventually managed to get enough clarification to write this Python 3 program, which runs in 58ms.

    from enigma import update, join, printf
    
    # the score sheet
    scores = {
      # batsman -> (total, bowler)
      'A': (7, 'R'),
      'B': (2, 'R'),
      'C': (3, 'T'),
      'D': (9, 'R'),
      'E': (1, 'T'),
      'F': (1, 'T'),
      'G': (1, 'S'),
      'H': (2, 'S'),
      'I': (4, 'S'),
      'J': (4, '#'), # not out
      'K': (2, 'R')
    }
    
    # play the cricket match
    # n = over number
    # over = interesting events in the current over
    # batsman = current batsman
    # runs = runs for current batsman
    # other_batsman = the other batsman
    # other_runs = runs for the other batsman
    # bowlers = map over number to bowler
    # remaining_batsmen = list of remaining batsmen
    # overs = previous overs
    def play(n, over, batsman, runs, other_batsman, other_runs, bowlers, remaining_batsmen, overs):
    
      # count the number of runs in the over
      r = over.count('1')
    
      # can we start a new over?
      if r == 3:
        # the other batsman faces the first ball of the new over
        yield from play(n + 1, '', other_batsman, other_runs, batsman, runs, bowlers, remaining_batsmen, overs + [over])
    
      # can we do more in this over?
      if len(over) == 6: return
    
      # extract the total and bowler when the current batsman is dismissed
      (total, bowler) = scores[batsman]
        
      # can the batsman score a run?
      if runs < total and r < 3:
        # the batsman scores a single, and the other batsman faces the next ball
        yield from play(n, over + '1', other_batsman, other_runs, batsman, runs + 1, bowlers, remaining_batsmen, overs)
    
      # can the batsman be dismissed?
      if runs == total and bowler != '#':
        # check the bowler hasn't changed in the over
        if bowlers.get(n, bowler) != bowler: return
        # check the bowler hasn't bowled two consecutive overs
        if n > 1 and bowlers.get(n - 1, '') == bowler: return
        # if there are remaining batsmen...
        if remaining_batsmen:
          # ... the new batsman faces the next ball
          yield from play(n, over + 'd', remaining_batsmen[0], 0, other_batsman, other_runs, update(bowlers, [(n, bowler)]), remaining_batsmen[1:], overs)
        # ... else check the final batsman has the correct not-out score
        # and that three runs have been scored in the final over 
        elif scores[other_batsman] == (other_runs, '#') and r == 3:
          yield overs + [over + 'd']
    
    # start the match at over 1 with batsmen A and B
    for overs in play(1, '', 'A', 0, 'B', 0, dict(), 'CDEFGHIJK', []):
      # output the overs
      overs = join(overs, sep=' ')
      printf("overs = {overs}")
      # and output the total score at the dismissal of each batsman
      (t, n) = (0, 0)
      for x in overs:
        if x == '1':
          # a single is scored
          t += 1
        elif x == 'd':
          # the batsman is dismissed
          n += 1
          printf("{t} for {n}")
      printf()
    

    Solution: The total runs scored at the fall of each wicket are: 6 for 1, 11 for 2, 15 for 3, 19 for 4, 21 for 5, 24 for 6, 24 for 7, 30 for 8, 31 for 9, 36 for 10.

  2. Hugh Casement 30 December 2016 at 8:32 am

    I failed to work out the order in which the batsmen were out (except that, obviously, Johnson was left at the end).  And can we (must we?) assume that the order in which they went in was alphabetic?

  3. Brian Gladman 1 January 2017 at 10:18 pm
    # the runs made by each player
    p_sc = (7, 2, 3, 9, 1, 1, 1, 2, 4, 4, 2)
    # the bowler that dismisses each player
    p_bw = 'RRTRTTSSS_R'
    
    # p1   - first batsman (facing the bowler)
    # p2   - second batman (at the other end)
    # pr   - the batsmen still to bat
    # s1   - runs so far for batsman 1
    # s2   - runs so far for batsman 2
    # rns  - total runs so far
    # rio  - runs so far in the current over
    # nov  - over number
    # res  - list of (player, score, over) values at each dismissal
    # v    - record of the runs and dismissals in overs
    def play(p1, p2, pr, s1=0, s2=0, rns=0, rio=0, nov=0, res=[], v='|'):
      
      # are we at the end of an over?
      if rio == 3:
        # can the current batsman be dismissed here?
        if s1 == p_sc[p1]:
          # check for solutions in which this happens
          t, u = res + [(p1, rns, nov)], v + chr(ord('a') + p1)
          # if there are batsmen yet to bat  
          if pr:
            yield from play(pr[0], p2, pr[1:], 0, s2, rns, rio, nov, t, u)
          # is the end of the game valid?
          elif p2 == 9 and rns == 36:
            yield t, u + '|'
          else:
            return
        # start a new over with a change of ends for the bowlers
        nov, rio, v, p1, p2, s1, s2 = nov + 1, 0, v + '|', p2, p1, s2, s1
    
      if s1 < p_sc[p1]:
        # the current batman is not out - score a run and continue
        yield from play(p2, p1, pr, s2, s1 + 1, rns + 1, rio + 1, nov, res, v + '1')
      else:
        # he is out and is replaced by the next batsman
        t, u = res + [(p1, rns, nov)], v + chr(ord('a') + p1)
        if pr:
          yield from play(pr[0], p2, pr[1:], 0, s2, rns, rio, nov, t, u)
    
    for s, v in play(0, 1, range(2, 11)):
      for (p1, r1, o1), (p2, r2, o2) in zip(s, s[1:]):
        # bowlers can't change in an over or bowl consecutive overs 
        if (o1 == o2 and p_bw[p1] != p_bw[p2] 
              or p_bw[p1] == p_bw[p2] and o2 - o1 == 1):
          break
      else:
        print('{}\n{}'.format(tuple(x[1] for x in s), v))
    

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: