# Print a Quinella Table

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:

(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.

• 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? – Οurous Sep 7 '18 at 7:55
• @Οurous I think yes. I'll modify to clarify this. – Shieru Asakoto Sep 7 '18 at 8:11
• 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)] – Οurous Sep 7 '18 at 10:37
• @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...) – Shieru Asakoto Sep 7 '18 at 10:52

# 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:|+


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
)                       //
.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
) | k                         // end of map(); return k

• Holy ternary Batman! o.Ô – Kevin Cruijssen Sep 7 '18 at 11:38
• @KevinCruijssen I actually gave up detailing all these ternary operators. :) – Arnauld Sep 7 '18 at 11:50

# Python 2, 500448 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!

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

# 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