Snooker

March 30, 2018

Here is our solution:

(define colors '(black pink blue brown green yellow red))

(define (points color) (length (member color colors)))

(define (scores xs) (cdr (scan + 0 (map points xs))))

(define perfect
(append (copies 15 '(red black))
'(yellow green brown blue pink black)))

> (scores perfect)
(1 8 9 16 17 24 25 32 33 40 41 48 49 56 57 64 65 72 73 80 81
88 89 96 97 104 105 112 113 120 122 125 129 134 140 147)

You can run the program at https://ideone.com/AQtuyB.

Advertisement

Pages: 1 2

7 Responses to “Snooker”

  1. Couldn’t resist perl golf
    [sourcecode language="perl"]
    say”@{[map{$t+=$_}(map{(1,7)}1..15),2..7]}”
    [sourcecode]

  2. chaw said

    I interpreted the question to require mapping a sequence of potted
    balls (including fouls) to scores, so this code handles such cases.
    However, my knowledge of snooker rules is limited to what’s mentioned
    in the question, so only those violations are caught.

    (import (scheme base)
            (scheme write)
            (srfi 2))
    
    ;; State of the game: config[i] = # of balls of value i on table, for
    ;; 0 < i < 8.  config[0], if nonzero, is the value of the last potted
    ;; ball.
    (define (initial-config)
      (vector 0 15 1 1 1 1 1 1))
    
    ;; Colors of balls
    (define ball-color
      '#(#f "red" "yellow" "green" "brown" "blue" "pink" "black"))
    
    ;; A break is represented as a sequence of values of potted balls.
    ;; Here's a perfect game, which should score to 147 points:
    (define perfect-game (append (apply append
                                        (make-list 15 '(1 7)))
                                 '(2 3 4 5 6 7)))
    
    (define (vector-decrement! vec idx)
      (vector-set! vec idx (- (vector-ref vec idx) 1)))
    
    ;; Update game configuration cfg (in place) to record ball being
    ;; potted. Return the points scored, or #f if ball is not on table.
    (define (pot-ball! cfg ball)
      (and (positive? (vector-ref cfg ball)) ; else impossible => #f
           ;; Check for scratch (config remains as is and no points awarded)
           (if (and (positive? (vector-ref cfg 1))
                    (not (= 1 ball))
                    (not (= 1 (vector-ref cfg 0))))
               0
               (begin
                 (when (or (= ball 1)
                           (and (zero? (vector-ref cfg 1))
                                (not (= 1 (vector-ref cfg 0)))))
                   (vector-decrement! cfg ball))
                 (vector-set! cfg 0 ball)
                 ball))))
    
    (define (game-score-sequence balls)
      (let ((cfg (initial-config)))
        (let f ((balls balls)
                (scores '(0)))
          (if (null? balls)
              (cdr (reverse scores))
              (and-let* ((points (pot-ball! cfg (car balls))))
                (f (cdr balls)
                   (cons (+ (car scores) points)
                         scores)))))))
    
    (display (game-score-sequence perfect-game))
    (newline)
    

  3. Daniel said

    Here’s a solution in Python 3.

    from itertools import accumulate
    l = list(accumulate([1,7]*15 + list(range(2,8))))
    print(l)
    

    Output:

    [1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97, 104, 105, 112, 113, 120, 122, 125, 129, 134, 140, 147]
    
  4. Daniel said

    Here’s a solution in C.

    [sourecode lang=”c”]
    /* snooker.c */

    #include <stdio.h>
    #include <stdlib.h>

    #define NUM_POTS 36

    void print_array(int* array, size_t n) {
    printf(“[“);
    for (size_t i = 0; i < n; ++i) {
    if (i > 0) printf(“, “);
    printf(“%d”, array[i]);
    }
    printf(“]\n”);
    }

    void accumulate(int* array, size_t n) {
    for (size_t i = 1; i < n; ++i) {
    array[i] += array[i-1];
    }
    }

    void calc_perfect_game(int* scores) {
    for (size_t i = 0; i < 15; ++i) {
    scores[i2] = 1;
    scores[i
    2 + 1] = 7;
    }
    for (size_t i = 0; i < 6; ++i) {
    scores[i+30] = i + 2;
    }
    accumulate(scores, NUM_POTS);
    }

    int main(void) {
    int scores[NUM_POTS];
    calc_perfect_game(scores);
    print_array(scores, NUM_POTS);
    return EXIT_SUCCESS;
    }
    [/sourcecode]

    Output:

    [1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97, 104, 105, 112, 113, 120, 122, 125, 129, 134, 140, 147]
    
  5. Daniel said

    I spelled “sourcecode” wrong. Here it is again with the correct spelling (and formatting).

    /* snooker.c */

    #include <stdio.h>
    #include <stdlib.h>

    #define NUM_POTS 36

    void print_array(int* array, size_t n) {
    printf("[");
    for (size_t i = 0; i < n; ++i) {
    if (i > 0) printf(", ");
    printf("%d", array[i]);
    }
    printf("]\n");
    }

    void accumulate(int* array, size_t n) {
    for (size_t i = 1; i < n; ++i) {
    array[i] += array[i-1];
    }
    }

    void calc_perfect_game(int* scores) {
    for (size_t i = 0; i < 15; ++i) {
    scores[i*2] = 1;
    scores[i*2 + 1] = 7;
    }
    for (size_t i = 0; i < 6; ++i) {
    scores[i+30] = i + 2;
    }
    accumulate(scores, NUM_POTS);
    }

    int main(void) {
    int scores[NUM_POTS];
    calc_perfect_game(scores);
    print_array(scores, NUM_POTS);
    return EXIT_SUCCESS;
    }

    Output:

    [1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97, 104, 105, 112, 113, 120, 122, 125, 129, 134, 140, 147]
    
  6. Daniel said

    Last try…

    /* snooker.c */
    
    #include <stdio.h>
    #include <stdlib.h>
    
    #define NUM_POTS 36
    
    void print_array(int* array, size_t n) {
      printf("[");
      for (size_t i = 0; i < n; ++i) {
        if (i > 0) printf(", ");
        printf("%d", array[i]);
      }
      printf("]\n");
    }
    
    void accumulate(int* array, size_t n) {
      for (size_t i = 1; i < n; ++i) {
        array[i] += array[i-1];
      }
    }
    
    void calc_perfect_game(int* scores) {
      for (size_t i = 0; i < 15; ++i) {
        scores[i*2] = 1;
        scores[i*2 + 1] = 7;
      }
      for (size_t i = 0; i < 6; ++i) {
        scores[i+30] = i + 2;
      }
      accumulate(scores, NUM_POTS);
    }
    
    int main(void) {
      int scores[NUM_POTS];
      calc_perfect_game(scores);
      print_array(scores, NUM_POTS);
      return EXIT_SUCCESS;
    }
    

    Output:

    [1, 8, 9, 16, 17, 24, 25, 32, 33, 40, 41, 48, 49, 56, 57, 64, 65, 72, 73, 80, 81, 88, 89, 96, 97, 104, 105, 112, 113, 120, 122, 125, 129, 134, 140, 147]
    
  7. Globules said

    Here’s a Haskell version. For fun we generate a random snooker game.

    import Prelude hiding ((++))
    import Control.Monad.Random
    import Data.Vector (Vector, (++))
    import qualified Data.Vector as V
    import qualified Data.Vector.Mutable as M
    import Text.Printf (printf)
    
    data Ball = Red | Yellow | Green | Brown | Blue | Pink | Black
      deriving (Enum)
    
    -- The number of points awarded for potting a ball.
    points :: Ball -> Int
    points = (+1) . fromEnum
    
    -- The 15 red balls.
    reds :: Vector Ball
    reds = V.replicate 15 Red
    
    -- The remaining coloured (non-red) balls.
    coloureds :: Vector Ball
    coloureds = V.enumFromTo Yellow Black
    
    -- A series of balls corresponding to a random game in which one player pots one
    -- ball per shot.
    --
    -- We construct a game by generating the sequence of shots:
    --
    --   r, c, r, c, ..., r, c, c, c, c, c, c, c
    --   +-------------------+  +--------------+
    --      15 (r,c) pairs         final 6 cs
    -- 
    -- which consists of 15 pairs of a red ball, and a coloured ball chosen randomly
    -- (with replacement), followed by all the coloured balls in increasing order of
    -- points.
    randomGame :: Rand StdGen (Vector Ball)
    randomGame = do
      cols <- randomSelection (V.length reds) coloureds
      return $ unpair (V.zip reds cols) ++ coloureds
    
    -- Show the accumulation of points for a sequence of potted balls.
    score :: Vector Ball -> String
    score = unwords . map show . V.toList . V.postscanl (+) 0 . V.map points
    
    -- The sequence of balls potted in a perfect game.
    perfect :: Vector Ball
    perfect = let blacks = V.map (const Black) reds
              in unpair (V.zip reds blacks) ++ coloureds
    
    main :: IO ()
    main = do
      rand <- getStdGen
      printf "perfect: %s\n" $ score perfect
      printf " random: %s\n" $ score $ evalRand randomGame rand
    
    
    --
    --                          Utility functions
    --
    
    -- Given [(a, b), (c, d), ...] return [a, b, c, d, ...].
    unpair :: Vector (a, a) -> Vector a
    unpair xs = V.create $ do
      ys <- M.new (2 * V.length xs)
      V.imapM_ (\i (x1, x2) -> M.write ys (2*i) x1 >> M.write ys (2*i+1) x2) xs
      return ys
    
    -- A random selection of size n from v, with replacement.
    randomSelection :: MonadRandom m => Int -> Vector a -> m (Vector a)
    randomSelection n xs =
      V.backpermute xs <$> V.replicateM n (getRandomR (0, V.length xs - 1))
    
    $ ./snooker
    perfect: 1 8 9 16 17 24 25 32 33 40 41 48 49 56 57 64 65 72 73 80 81 88 89 96 97 104 105 112 113 120 122 125 129 134 140 147
     random: 1 4 5 10 11 17 18 25 26 29 30 33 34 41 42 46 47 52 53 56 57 59 60 62 63 66 67 72 73 77 79 82 86 91 97 104
    

Leave a Reply

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 )

Facebook photo

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

Connecting to %s

%d bloggers like this: