Horse racing

From Rosetta Code
Horse racing is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Background

In three recent races at Rosetta Park, the results have been:

Race 1

Pos Horse Weight Dist Sex
1 A 9.00 0.0 Colt
2 B 8.06 2.0 Filly
3 C 9.04 1.5 Colt

Time 1 minute 36.0 seconds


Race 2

Pos Horse Weight Dist Sex
1 D 8.10 0.0 Filly
2 E 9.03 1.0 Colt
3 F 9.07 3.0 Colt

(F was slowly away and lost 2 lengths at the finish as a result)

Time 1 minute 36.4 seconds


Race 3

Pos Horse Weight Dist Sex
1 G 8.04 0.0 Colt
2 H 8.10 1.5 Colt
3 I 9.05 0.5 Filly

Time 1 minute 35.8 seconds


It is assumed that:

1. Weights carried are expressed in stones and pounds (traditional in US and UK racing) where 1 stone equals 14 pounds.

2. The distances are the actual distances between horses at the finish of the race.

3. All races are run at 1 mile in similar going and weather conditions and the time shown is the time taken by the winner to run the race.

4. 2 pounds slows a horse down by 1 length at the finish.

5. 1 second is equivalent to 5 lengths at the finish.


Task

All 9 of these horses, plus a further horse J, are due to compete against each other in Race 4 at Rosetta Park again over 1 mile and in similar conditions to the earlier races. Colts will carry 9.00 and fillies 8.11.

Suppose that:

1. Horse B has been moving well on the galops and her trainer thinks she has improved 4 pounds since her last outing.

2. Horse C has had a setback in training and his trainer expects him to perform 4 pounds below his best.

3. Horse H is being ridden by the champion jockey which is expected to improve his performance by 3 pounds compared to what it would otherwise have been.

4. Horse J, a filly, has never run before but her performance on the galops suggests she can run 1 mile in 1 minute 35.8 seconds when carrying 8.11.


Other things being equal (which they seldom are in actual horse racing!) what would you expect the full result of Race 4 to be including the time of the winner?

Go

Translation of: Wren

<lang go>package main

import (

   "fmt"
   "sort"
   "strings"

)

func main() {

   // ratings on past form, assuming a rating of 100 for horse A
   a := 100
   b := a - 8 - 2*2 // carried 8 lbs less, finished 2 lengths behind
   c := a + 4 - 2*3.5
   d := a - 4 - 10*0.4 // based on relative weight and time
   e := d + 7 - 2*1
   f := d + 11 - 2*(4-2)
   g := a - 10 + 10*0.2
   h := g + 6 - 2*1.5
   i := g + 15 - 2*2
   // adjustments to ratings for current race
   b += 4
   c -= 4
   h += 3
   j := a - 3 + 10*0.2
   // filly's allowance to give weight adjusted weighting
   b += 3
   d += 3
   i += 3
   j += 3
   // create map of horse to its weight adjusted rating and whether colt (1 == yes, 0 == no)
   m := map[string][2]int{
       "A": {a, 1},
       "B": {b, 0},
       "C": {c, 1},
       "D": {d, 0},
       "E": {e, 1},
       "F": {f, 1},
       "G": {g, 1},
       "H": {h, 1},
       "I": {i, 0},
       "J": {j, 0},
   }
   type kv struct {
       key   string
       value [2]int
   }
   // convert to slice of kv
   l := make([]kv, len(m))
   x := 0
   for k, v := range m {
       l[x] = kv{k, v}
       x++
   }
   // sort in descending order of rating
   sort.Slice(l, func(i, j int) bool { return l[i].value[0] > l[j].value[0] })
   // show expected result of race
   fmt.Println("Race 4\n")
   fmt.Println("Pos Horse  Weight  Dist  Sex")
   pos := ""
   for x := 0; x < len(l); x++ {
       wt := "9.00"
       if l[x].value[1] == 0 {
           wt = "8.11"
       }
       dist := 0.0
       if x > 0 {
           dist = float64(l[x-1].value[0]-l[x].value[0]) * 0.5
       }
       if x == 0 || dist > 0.0 {
           pos = fmt.Sprintf("%d", x+1)
       } else if !strings.HasSuffix(pos, "=") {
           pos = fmt.Sprintf("%d=", x)
       }
       sx := "colt"
       if l[x].value[1] == 0 {
           sx = "filly"
       }
       fmt.Printf("%-2s  %s      %s    %3.1f   %s\n", pos, l[x].key, wt, dist, sx)
   }
   // weight adjusted rating of winner
   wr := float64(l[0].value[0])
   // expected time of winner (relative to A's time in Race 1)
   t := 96.0 - (wr-100)/10
   min := int(t / 60)
   sec := t - float64(min)*60
   fmt.Printf("\nTime %d minute %3.1f seconds\n", min, sec)

}</lang>

Output:
Race 4

Pos Horse  Weight  Dist  Sex
1   I      8.11    0.0   filly
2   J      8.11    2.0   filly
3   A      9.00    1.0   colt
4   F      9.00    0.5   colt
5   H      9.00    0.5   colt
6   E      9.00    0.5   colt
7   D      8.11    1.0   filly
7=  B      8.11    0.0   filly
9   C      9.00    1.0   colt
10  G      9.00    0.5   colt

Time 1 minute 35.4 seconds

Julia

Translation of: Go

<lang julia>function main()

   # ratings on past form, assuming a rating of 100 for horse A
   a = 100
   b = a - 8 - 2*2 # carried 8 lbs less, finished 2 lengths behind
   c = a + 4 - 2*3.5
   d = a - 4 - 10*0.4 # based on relative weight and time
   e = d + 7 - 2*1
   f = d + 11 - 2*(4-2)
   g = a - 10 + 10*0.2
   h = g + 6 - 2*1.5
   i = g + 15 - 2*2
   # adjustments to ratings for current race
   b += 4
   c -= 4
   h += 3
   j = a - 3 + 10*0.2
   # filly's allowance to give weight adjusted weighting
   b += 3
   d += 3
   i += 3
   j += 3
   # create vector of pairs of horse to pair of weight adjusted rating and whether colt (1 == yes, 0 == no)
   m = ["A" => a => 1,
        "B" => b => 0,
        "C" => c => 1,
        "D" => d => 0,
        "E" => e => 1,
        "F" => f => 1,
        "G" => g => 1,
        "H" => h => 1,
        "I" => i => 0,
        "J" => j => 0]
   # sort in descending order of rating
   sort!(m, lt = (x, y) -> first(last(x)) > first(last(y)))
   # show expected result of race
   println("Race 4\n")
   println("Pos Horse  Weight  Dist  Sex")
   pos = ""
   for x in eachindex(m)
       wt = last(last(m[x])) == 0 ? "8.11" : "9.00"
       dist = x == 1 ? 0.0 : (first(last(m[x - 1])) - first(last(m[x]))) * 0.5
       if x == 1 || dist > 0.0
           pos = "$x"
       elseif !occursin("=", pos)
           pos = "$(x - 1)="
       end
       sx = last(last(m[x])) == 0 ? "filly" : "colt"
       println(rpad(pos, 4), rpad(first(m[x]), 7), rpad(wt, 8), rpad(round(dist, digits=1), 6), sx)
   end
   # expected time of winner (relative to A's weight adjusted time rating in first race)
   t = 96.0 - (first(last(first(m))) - 100) / 10
   min, sec = divrem(t, 60.0)
   println("\nTime $min minutes and ", round(sec, digits=1), " seconds.")

end

main()

</lang>

Output:
Pos Horse  Weight  Dist  Sex
1   I      8.11    0.0   filly
2   J      8.11    2.0   filly
3   A      9.00    1.0   colt
4   F      9.00    0.5   colt
5   H      9.00    0.5   colt
6   E      9.00    0.5   colt
7   B      8.11    1.0   filly
7=  D      8.11    0.0   filly
9   C      9.00    1.0   colt
10  G      9.00    0.5   colt

Time 1.0 minutes and 35.4 seconds.


Nim

Translation of: Wren

<lang Nim>import algorithm, math, strformat, strutils

  1. Ratings on past form, assuming a rating of 100 for horse A.

var

 a = 100.0
 b = a - 8 - 2 * 2       # carried 8 lbs less, finished 2 lengths behind.
 c = a + 4 - 2 * 3.5
 d = a - 4 - 10 * 0.4    # based on relative weight and time.
 e = d + 7 - 2 * 1
 f = d + 11 - 2 * (4 - 2)
 g = a - 10  + 10 * 0.2
 h = g + 6 - 2 * 1.5
 i = g + 15 - 2 * 2
  1. Adjustments to ratings for current race.

b += 4 c -= 4 h += 3 var j = a - 3 + 10 * 0.2

  1. Filly's allowance to give weight adjusted weighting.

b += 3 d += 3 i += 3 j += 3

  1. Create table mapping horse to its weight adjusted rating and whether colt.

type Pair = tuple[key: char; value: tuple[rating: float; colt: bool]] let list: array[10, Pair] = {'A': (a, true), 'B': (b, false),

                            'C': (c, true), 'D': (d, false),
                            'E': (e, true), 'F': (f, true),
                            'G': (g, true), 'H': (h, true),
                            'I': (i, false), 'J': (j, false)}
  1. Sort in descending order of rating.

let slist = list.sortedByIt(-it.value.rating)

  1. Show expected result of race.

echo "Race 4\n" echo "Pos Horse Weight Dist Sex" var pos = "" for i, (key, value) in slist:

 let wt = if value.colt: "9.00" else: "8.11"
 var dist = 0.0
 if i > 0: dist = (slist[i-1].value.rating - value.rating) * 0.5
 pos = if i == 0 or dist > 0: $(i + 1)
       elif not pos.endsWith("="): $i & '='
       else: pos
 let sx = if value.colt: "colt" else: "filly"
 echo &"{pos:<2}  {key}      {wt}    {dist:3.1f}   {sx}"
  1. Weight adjusted rating of winner.

let wr = slist[0].value.rating

  1. Expected time of winner (relative to A's time in Race 1).

let t = 96 - (wr - 100) / 10 var min = int(t / 60) var sec = t mod 60 echo &"\nTime {min} minute {sec:.1f} seconds"</lang>

Output:
Race 4

Pos Horse  Weight  Dist  Sex
1   I      8.11    0.0   filly
2   J      8.11    2.0   filly
3   A      9.00    1.0   colt
4   F      9.00    0.5   colt
5   H      9.00    0.5   colt
6   E      9.00    0.5   colt
7   B      8.11    1.0   filly
7=  D      8.11    0.0   filly
9   C      9.00    1.0   colt
10  G      9.00    0.5   colt

Time 1 minute 35.4 seconds

Phix

Disclaimer: I know nothing about horse racing, all calculations were reverse-engineered from those in the Go entry.
Made table-based so you can plug in different races and adjustments, but any horse appearing in more than one race input would too in the output.

with javascript_semantics
requires ("1.0.1") // [else sort_columns() needs a deep_copy()]
enum Colt, Filly
constant cf = {"colt","filly"}
enum Runners, Time

constant races = {  // (with F adjusted, plus the prediction)
{{{"A", 9,00,   0.0,    Colt},
  {"B", 8,06,   2.0,    Filly},
  {"C", 9,04,   1.5,    Colt}},"1min 36s"},
{{{"D", 8,10,   0.0,    Filly},
  {"E", 9,03,   1.0,    Colt},
  {"F", 9,07,   3.0-2,  Colt}},"1min 36.4s"},
{{{"G", 8,04,   0.0,    Colt},
  {"H", 8,10,   1.5,    Colt},
  {"I", 9,05,   0.5,    Filly}},"1min 35.8s"},
{{{"J", 8,11,   0.0,    Filly}},"1min 35.8s"}}

constant adjusts = columnize({{"B",+4},
                              {"C",-4},
                              {"H",+3}})

function str2time(string t)
    sequence r = scanf(t,"%dmin %fs")[1]
    return r[1]*60+r[2]
end function

sequence horses = {}
string name, pos = ""
integer stone, pounds, sex
atom wt, dist, rating
for i=1 to length(races) do
    sequence runners = races[i][Runners]
    atom s = str2time(races[i][Time]), distt = 0
    for j=1 to length(runners) do
        {name, stone, pounds, dist, sex} = runners[j]
        distt += dist
        pounds += stone*14
        rating = pounds+934-10*s-distt*2    -- (make "A" a nominal 100)
        integer k = find(name,adjusts[1])
        if k then rating += adjusts[2][k] end if
        if sex=Filly then rating += 3 end if
        horses = append(horses,{name,sex,rating})
    end for
end for
horses = sort_columns(horses,{-3})
printf(1,"Race 4\n\nPos Horse  Weight  Dist  Sex\n")
for i=1 to length(horses) do
    {name,sex,rating} = horses[i]   
    wt = iff(sex=Colt?9.00:8.11)
    dist = iff(i=1?0:(horses[i-1][3]-rating)*0.5)
    if i=1 or dist>0 then
        pos = sprintf("%d",i)
    elsif not find('=',pos) then
        pos &= '='
    end if
    printf(1,"%-2s  %s      %.2f    %3.1f   %s\n", {pos, name, wt, dist, cf[sex]})
end for
// weight adjusted rating of winner
rating = horses[1][3]
// expected time of winner (relative to A's time in Race 1)
atom t = str2time(races[1][Time]) - (rating-100)/10
string m = elapsed(floor(t/60)*60),
       s = elapsed(remainder(t,60))
printf(1,"\nTime %s %s\n", {m,s})
Output:
Race 4

Pos Horse  Weight  Dist  Sex
1   I      8.11    0.0   filly
2   J      8.11    2.0   filly
3   A      9.00    1.0   colt
4   F      9.00    0.5   colt
5   H      9.00    0.5   colt
6   E      9.00    0.5   colt
7   B      8.11    1.0   filly
7=  D      8.11    0.0   filly
9   C      9.00    1.0   colt
10  G      9.00    0.5   colt

Time 1 minute 35.4s

Wren

Library: Wren-fmt

<lang ecmascript>import "/fmt" for Fmt

// ratings on past form, assuming a rating of 100 for horse A var a = 100 var b = a - 8 - 2 * 2 // carried 8 lbs less, finished 2 lengths behind var c = a + 4 - 2 * 3.5 var d = a - 4 - 10 * 0.4 // based on relative weight and time var e = d + 7 - 2 * 1 var f = d + 11 - 2 * (4 - 2) var g = a - 10 + 10 * 0.2 var h = g + 6 - 2 * 1.5 var i = g + 15 - 2 * 2

// adjustments to ratings for current race b = b + 4 c = c - 4 h = h + 3 var j = a - 3 + 10 * 0.2

// filly's allowance to give weight adjusted weighting b = b + 3 d = d + 3 i = i + 3 j = j + 3

// create map of horse to its weight adjusted rating and whether colt var m = {

   "A": [a, true], 
   "B": [b, false], 
   "C": [c, true],
   "D": [d, false],
   "E": [e, true],
   "F": [f, true],
   "G": [g, true],
   "H": [h, true],
   "I": [i, false],
   "J": [j, false]

} // convert to list of {key, value} map entries var l = m.toList

// sort in descending order of rating l.sort{ |i, j| i.value[0] >= j.value[0] }

// show expected result of race System.print("Race 4\n") System.print("Pos Horse Weight Dist Sex") var pos = "" for (x in 0...l.count) {

   var wt = l[x].value[1] ? "9.00" : "8.11"
   var dist = 0
   if (x > 0) dist = (l[x-1].value[0] - l[x].value[0]) * 0.5
   pos = (x == 0 || dist > 0) ? (x+1).toString : (!pos.endsWith("=") ? x.toString + "=" : pos)
   var sx = l[x].value[1] ? "colt" : "filly"
   Fmt.print("$-2s  $s      $s    $3.1f   $s", pos, l[x].key, wt, dist, sx)

}

// weight adjusted rating of winner var wr = l[0].value[0]

// expected time of winner (relative to A's time in Race 1) var t = 96 - (wr - 100) / 10 var min = (t/60).floor var sec = t % 60 System.print("\nTime %(min) minute %(sec) seconds")</lang>

Output:
Race 4

Pos Horse  Weight  Dist  Sex
1   I      8.11    0.0   filly
2   J      8.11    2.0   filly
3   A      9.00    1.0   colt
4   F      9.00    0.5   colt
5   H      9.00    0.5   colt
6   E      9.00    0.5   colt
7   B      8.11    1.0   filly
7=  D      8.11    0.0   filly
9   C      9.00    1.0   colt
10  G      9.00    0.5   colt

Time 1 minute 35.4 seconds