7
\$\begingroup\$

ALERT: IN CASE YOU DOUBT, NO PUN ON "QUINE" IS INTENDED

In regulated horse-racing for gambling, bets called Quinella bets exist and in which a person bet on the first two places of a race. In racecourses, usually there is a display which shows the odds of a specific combination as below:

enter image description here

(This picture is a cropped picture from Hong Kong Jockey Club. QIN stands for Quinella)

Challenge

Write a program or function, receiving either lines of strings or an array, print or return an ASCII Quinella table with the following format:

 QUINELLA  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  | 10  | 11  | 12  | 13  | 14  |     
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
     | 8\1 | 162 | 7.2 | 11  | 109 | 4.5 | 6.0 | 11  | 999 | 11  |     |     |     |     |  1  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
  9  | 37  | 9\2 | 11  | 999 | 41  | 350 | 75  | 24  | 999 | 31  |     |     |     |     |  2  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 10  | 8.2 | 84  |10\3 | 75  | 999 | 85  | 48  | 8.1 | 138 | 191 |     |     |     |     |  3  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 11  |     |     |     |11\4 | 45  | 6.4 | 999 | 287 | 473 | 60  |     |     |     |     |  4  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 12  |     |     |     |     |12\5 | 15  | 8.9 | 40  | 175 | 378 |     |     |     |     |  5  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 13  |     |     |     |     |     |13\6 | 26  | 999 | 15  | 860 |     |     |     |     |  6  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 14  |     |     |     |     |     |     |14\7 | 727 | 100 | 37  |     |     |     |     |  7  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
     |  8  |  9  | 10  | 11  | 12  | 13  |     |  8  |  9  | 10  | 11  | 12  | 13  | 14  |   

In this challenge, the numbers in the middle are calculated from the input by a specific algorithm, which will be explained later.

Specification

Input: Your program or function must receive one of the two formats as below:

  • Lines of Strings: The first line will be <number of horses> <number of bets>, following by <number of bets> lines of <horse 1> <horse 2> <amount of bet>.

Example:

10 100
1 2 100
3 4 500
5 6 100
7 8 2000
9 10 10
(... 95 more lines)
  • Array: An array or tuple, with 3 elements, [<number of horses>, <number of bets>, <bets>], where <bets> is a 2d array consisting of <number of bets> elements, each element consists of 3 elements, [<horse 1>, <horse 2>, <amount of bet>]. As a special rule for functions, passing the tuple as 3 arguments is allowed.

Example:

[10, 100, [
 [1, 2, 100],
 [3, 4, 500],
 [5, 6, 100],
 [7, 8, 2000],
 [9, 10, 10],
 ... 95 more elements
]]
  • You may assume all inputs are valid.

  • You may assume 1 <= <horse 1> < <horse 2> <= <number of horses> in both cases.

  • You may assume 9 <= <number of horses> <= 14 in both cases.

Processing: The odds for each quinella pair <horse 1>, <horse 2> (unordered, i.e. 1,2 and 2,1 are considered identical) is calculated as follows:

Odds = <Total bet amount of all pairs> / <Total bet amount of the pair> / 2
Maximum Odds: 999, Minimum Odds: 1.1

In case you doubt, the odds will be 999 if <Total bet amount of the pair> == 0.

The result should be rounded to the nearest 0.1 if Odds < 9.95 (in this case, the .0 must be retained if necessary), and to the nearest integer if Odds >= 9.95.

Output: Replace each bold-and-italic number pair with the corresponding odds calculated in the previous step. Leave the cell blank if any of the number in the pair is larger than <number of horses>.

 QUINELLA  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  | 10  | 11  | 12  | 13  | 14  |     
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
     | 8\1 | 1,2 | 1,3 | 1,4 | 1,5 | 1,6 | 1,7 | 1,8 | 1,9 |1,10 |1,11 |1,12 |1,13 |1,14 |  1  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
  9  | 8,9 | 9\2 | 2,3 | 2,4 | 2,5 | 2,6 | 2,7 | 2,8 | 2,9 |2,10 |2,11 |2,12 |2,13 |2,14 |  2  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 10  |8,10 |9,10 |10\3 | 3,4 | 3,5 | 3,6 | 3,7 | 3,8 | 3,9 |3,10 |3,11 |3,12 |3,13 |3,14 |  3  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 11  |8,11 |9,11 |10,11|11\4 | 4,5 | 4,6 | 4,7 | 4,8 | 4,9 |4,10 |4,11 |4,12 |4,13 |4,14 |  4  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 12  |8,12 |9,12 |10,12|11,12|12\5 | 5,6 | 5,7 | 5,8 | 5,9 |5,10 |5,11 |5,12 |5,13 |5,14 |  5  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 13  |8,13 |9,13 |10,13|11,13|12,13|13\6 | 6,7 | 6,8 | 6,9 |6,10 |6,11 |6,12 |6,13 |6,14 |  6  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
 14  |8,14 |9,14 |10,14|11,14|12,14|13,14|14\7 | 7,8 | 7,9 |7,10 |7,11 |7,12 |7,13 |7,14 |  7  
-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
     |  8  |  9  | 10  | 11  | 12  | 13  |     |  8  |  9  | 10  | 11  | 12  | 13  | 14  |    
  • Each cell is 5 characters wide.
  • You must center the odds, and if you cannot balance the margins, make left margin 1 character less wider than right margin.

Test Cases

Here is a TIO driver which generates random test cases. No hard-coded test cases will be provided.

Winning Criteria

Shortest code in each language wins as per the rules of code-golf challenges. No standard loopholes allowed.

\$\endgroup\$
  • \$\begingroup\$ I take it that 9 <= number of horses <= 14 based on the test generator, and the output format? Additionally, is input in the form of a tuple (number of horses, number of bets, [array of bets]) acceptable? \$\endgroup\$ – Οurous Sep 7 '18 at 7:55
  • \$\begingroup\$ @Οurous I think yes. I'll modify to clarify this. \$\endgroup\$ – Shieru Asakoto Sep 7 '18 at 8:11
  • \$\begingroup\$ Can the three-element sub-arrays be taken as tuples? And may the bet values be taken as floats-with-integer-value instead of integers? Eg: [[Int]] becomes [(Int,Int,Real)] \$\endgroup\$ – Οurous Sep 7 '18 at 10:37
  • 1
    \$\begingroup\$ @Ourous of course yes (for both questions) (I'd say array and tuple are to be treated the same here but when I was building this challenge I was coding with JavaScript so...) \$\endgroup\$ – Shieru Asakoto Sep 7 '18 at 10:52
4
\$\begingroup\$

JavaScript (ES8), 321 bytes

Takes input as (number_of_horses)(bets). The number of bets is not taken.

n=>b=>' QUINELLA  '+(y=e='',g=(x,y)=>b.map(([X,Y,b])=>k+=X-x|Y-y?0:b,k=-(x>n|y>n))|k,F=x=>y>16?e:`|+
`[x?y&1:2]+(Y=y/2,s=y&1?'-----':(x%15|Y%8?x?Y>7?x>7?x:x<7?x+7:e:x-Y?y?x<15?~g(x>Y?Y:x+7,x>Y?x:Y+7)?(k=.5/k*g())<999?k.toFixed(k<9.95):999:e:Y:x:x+7+'\\'+x:y-2?Y+7:e:e)+e).padStart(s[2]?4:3).padEnd(5)+F(++x<16?x:!++y))(2)

Try it online!

How?

Main part

n => b =>                       // n = number of horses; b[] = bet array
  ' QUINELLA  ' + (             // append header
    y = e = '',                 // e is the empty string; start with y = '', coerced to 0
    F = x =>                    // F = main recursive function, taking x
      y > 16 ?                  //   if y is greater than 16:
        e                       //     return an empty string and stop recursion
      :                         //   else:
        `|+\n`[x ? y & 1 : 2]   //     append a linefeed if we are at the beginning of a row
        + (                     //     or either '|' or '+' depending on the parity of y
          Y = y / 2,            //     define Y = y / 2
          s =                   //     define s:
            y & 1 ?             //       if y is odd:
              '-----'           //         s = 5 hyphens
            :                   //       else:
              [cell content]    //         s is set to the content of the cell
        )                       //
        .padStart(s[2] ? 4 : 3) //   prepend leading spaces
        .padEnd(5) +            //   append trailing spaces
        F(++x < 16 ? x : !++y)  //   append the result of a recursive call
  )(2)                          // initial call to F() with x = 2

Cell content

The cell content is computed with a huge cascade of ternary operators, which is neither easy nor very interesting to detail here.

One key part in there is the formatting of a bet:

~g(                             // invoke g():
  x > Y ? Y : x + 7,            //   first horse
  x > Y ? x : Y + 7             //   second horse
) ?                             // if g() didn't return -1:
  (k = .5 / k * g())            //   compute amount_of_all_pairs / amount_of_this_pair / 2
  < 999 ?                       //   if the result is less than 999:
    k.toFixed(k < 9.95)         //     output it with the proper number of decimal places
  :                             //   else:
    999                         //     force result to 999
:                               // else:
  e                             //   output an empty cell

Bet amount processing

The helper function \$g\$ computes the total bet amount for a given pair of horses, or the total bet amount of all pairs when invoked with no argument. If at least one of the horses is not valid, it returns \$-1\$ instead.

g = (x, y) =>                   // x = 1st horse, y = 2nd horse
  b.map(([X, Y, b]) =>          // for each bet of amount b on horses (X, Y):
    k +=                        //   update k:
      X - x | Y - y ?           //     if (x, y) is not matching (X, Y):
        0                       //       add 0 to k
      :                         //     else:
        b,                      //       add b to k
    k = -(x > n | y > n)        //   start with k = 0 if both x and y are valid horses
                                //   otherwise, start with k = -1
  ) | k                         // end of map(); return k
\$\endgroup\$
  • 1
    \$\begingroup\$ Holy ternary Batman! o.Ô \$\endgroup\$ – Kevin Cruijssen Sep 7 '18 at 11:38
  • \$\begingroup\$ @KevinCruijssen I actually gave up detailing all these ternary operators. :) \$\endgroup\$ – Arnauld Sep 7 '18 at 11:50
3
\$\begingroup\$

Python 2, 500 448 444 bytes

R=range(1,15)
h,_,b=input()
Q=eval(`[['']*16]*9`)
Q[8][1:6]=R[7:13];Q[8][8:16]=R[7:]
for i in R:
 if i<8:Q[i][i]=`7+i`+'\\'+`i`;Q[i][::15]=[['',7+i][i>1],i]
 for j in R:v=sum(v*((A,B)==(i,j))for A,B,v in b);x=min(999,0**v*999or.5*sum(zip(*b)[2])/v);X,Y=i<j<=h and[i,j-7,j,i-7][i>7::2]or(0,0);Q[X][Y]=x<10and round(x,1)or int(x+.5)
Q[0]=[' QUINELLA  ']+R[1:]+['']
for l in Q:print('-----+'*15+'-----\n')*(l<[' '])+'|'.join(map('{:^5}'.format,l))

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ I cannot test right now, but would x+.5 work over round(x)? \$\endgroup\$ – Mr. Xcoder Sep 7 '18 at 10:59
  • \$\begingroup\$ @Mr.Xcoder Yeah, thanks :) \$\endgroup\$ – TFeld Sep 7 '18 at 12:13
1
\$\begingroup\$

Clean, 498 ... 483 bbytes

Doesn't take the number of bets.

import StdEnv,Text
q=""
j=join
$h a=replaceSubString" 0""  "(" QUINELLA"+(j("\n"+j"+"(repeatn 16"-----")+"\n")[j"|"[q<+cjustify 5[c\\c<-: ?h a u v]\\v<-[0..15]]\\u<-[0..8]])%(9,9^9))
?h a u v|u<1=q<+v rem 15|v>14=q<+u rem 8|u>7|v>7=q<+v=q<+(v+7)*sign(v rem 7)
|u==v=u+7<+"\\"<+v|v<1|u>1=q<+u+7=q
#p=sort[k+if(u>=v)7 0\\k<-[u,v]]
#r=sum[z\\(x,y,z)<-a|[x,y]==p]
|r>0.0#r=sum(map thd3 a)/r/2E0
|r<9.95=snd(sort[(abs(e-r),e<+".0")\\e<-[1.1,1.2..1E1]]!!0)%(0,2)=q<+toInt r
|p!!1<h="999"=q

Try it online!

Explained:

q = ""                  // define `q` as the empty string
j = join                // define `j` as the join function
$ h a                   // function $ of horses `h` and array `a`
 = replaceSubString     // replace all
  " 0"                  // zeroes with spaces before them
  "  "                  // with two spaces
  (                     // (in the string)
   " QUINELLA" +        // " QUINELLA" prepended to
   (                    // (the string created by)
    j                   // joining arg2 with arg1
     (                  // (the separator string)
      "\n" +            // newline prepended to
      j "+"             // joining arg2 with plusses
       (repeatn 16      // a list with 16 of
        "-----")        // string with 5 dashes
      + "\n"            // prepended to a newline
     )
     [                  // (a list made out of)
      j "|"             // joining arg2 with vertical pipes
       [                // (a list made out of)
        q <+            // stringifying
        cjustify 5 [    // center-aligning a list of
         c              // character `c`
         \\ c <-:       // for every `c` in the string
          ? h a u v     // (the thing that goes in the cell)
        ]
        \\ v <- [0..15] // for every `v` from 0 to 15
       ]
       \\ u <- [0..8]   // for every `u` from 0 to 8
     ]
   ) % (9, 9^9)         // drop 9 elements off the front
  )
? h a u v               // function ? of `h` `a` `u` and `v`
 | u < 1                // if `u` is zero
  = q <+ v rem 15       // stringify `v` modulo 15
 | v > 14               // if `v` is 15
  = q <+ u rem 8        // stringify `u` modulo 8
 | u > 7                // if `u` is 8
  | v > 7               // if `v` is more than 7
   = q <+ v             // stringify `v`
  = q <+                // otherwise stringify
  (v+7) *               // `v` plus 7 unless
  sign(v rem 7)         // `v` is 7 or zero
 | u == v               // if `u` equals `v`
  = u + 7 <+            // stringify `u` plus 7 and append
  "\\" <+ v             // a slash and the stringification of v
 | v < 1                // if `v` is zero
  | u > 1               // if `u` is more than 1
   = q <+ u + 7         // stringify `u` + 7
  = q                   // empty string
 # p =                  // define `p` as
  sort                  // the sorted
   [                    // (list of horse numbers)
    k +                 // k plus
    if(u >= v)
     7                  // if u >= v, 7
     0                  // else zero
    \\ k <- [u, v]      // for every `k` in u and v
   ]
 # r =                  // define `r` as
  sum                   // the sum of
   [                    // (the bets on those horses)
    z                   // the bet `z`
    \\ (x, y, z) <- a   // for every subarray
    | [x, y] == p       // where the horse pair matches `p`
   ]
 | r > 0.0              // if `r` is greater than zero
  # r =                 // redefine `r` as
   sum                  // the sum of
    (map thd3 a)        // every bet
   / r                  // divided by the previous `r`
   / 2E0                // divided by two
  | r < 9.95            // if r is less than 9.95
   = snd                // the second element of
    (                   // (the tuple with the closest matching odd)
     sort               // sort the
      [                 // (list of potential odds)
       (                // (tuple with)
        abs(e - r)      // the difference between `r` and the odd `e`
        , e <+ ".0"     // and the stringification of `e`
       )                // for every `e` in the list of odds
       \\ e <- [1.1, 1.2..1E1] 
      ] !! 0            // the first element
    ) % (0, 2)          // "round" the number
  = q <+ toInt r        // otherwise stringify and round `r`
 | p!!1 < h             // if the bet was actually zero
  = "999"               // the odds are 999
 = q                    // otherwise there isn't a horse
\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.