Jump to content

Horse racing

From Rosetta Code
Task
Horse racing
You are encouraged to solve this task according to the task description, using any language you may know.
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?

C++

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <unordered_map>
#include <string>
#include <vector>

enum Gender { COLT, FILLY };

std::string gender_to_string(const Gender& gender) {
	if ( gender == Gender::COLT ) { return "Colt"; }
	return "Filly";
}

struct Info {
	Gender gender;
	double rating;
};

int main() {
	// Horses with their rating after the first 3 races
	std::unordered_map<std::string, Info> horses = {
		{ "A", { Gender::COLT, 100.0 } },
		{ "B", { Gender::FILLY, 100.0 - 8 - 2 * 2 } },
		{ "C", { Gender::COLT, 100.0 + 4 - 2 * 3.5 } },
		{ "D", { Gender::FILLY, 100.0 - 4 - 10 * 0.4 } },
		{ "E", { Gender::COLT, ( 100.0 - 4 - 10 * 0.4 ) + 7 - 2 * 1 } },
		{ "F", { Gender::COLT, ( 100.0 - 4 - 10 * 0.4 ) + 11 - 2 * ( 4 - 2 ) } },
		{ "G", { Gender::COLT, 100.0 - 10 + 10 * 0.2 } },
		{ "H", { Gender::COLT, ( 100.0 - 10 + 10 * 0.2 ) + 6 - 2 * 1.5 } },
		{ "I", { Gender::FILLY, ( 100.0 - 10 + 10 * 0.2 ) + 15 - 2 * 2 } },
		{ "J", { Gender::FILLY, 0.0 } }
	};

	// Adjustments to ratings for Race 4
	horses["B"].rating += 4.0;
	horses["C"].rating -= 4.0;
	horses["H"].rating += 3.0;
	horses["J"].rating += 100.0 - 3.0 + 10 * 0.2;

	// Filly's weight allowance adjustment
	for ( std::pair<std::string, Info> pair : horses ) {
		if ( pair.second.gender == Gender::FILLY ) {
			horses[pair.first] = Info(pair.second.gender, pair.second.rating + 3.0);
		}
	}

	// Sort in descending order of rating
	std::vector<std::pair<std::string, Info>> sorted_horses{ std::make_move_iterator(horses.begin()),
        													 std::make_move_iterator(horses.end()) };
	std::sort(sorted_horses.begin(), sorted_horses.end(),
		[](const auto& p1, const auto& p2) { return p1.second.rating > p2.second.rating; });

	// Display the expected result of Race 4
	std::cout << "Race 4" << std::endl << std::endl;
	std::cout << "Position  Horse  Weight  Distance  Gender" << std::endl;
	for ( uint32_t i = 0; i < sorted_horses.size(); ++i ) {
		std::pair<std::string, Info> entry = sorted_horses[i];
		const double weight = ( entry.second.gender == Gender::COLT ) ? 9.00 : 8.11;
		const double distance = ( i > 0 ) ?
			( sorted_horses[i - 1].second.rating - entry.second.rating ) * 0.5 : 0.0;
		const std::string position = ( i == 0 || distance > 0 ) ? std::to_string(i + 1) : std::to_string(i) + "=";
		std::cout << "   " << std::left << std::setw(9) << position << std::setw(6) << entry.first
				  << std::setw(9) << std::fixed << std::setprecision(2) << weight
				  << std::setw(9) << std::setprecision(1) << distance
				  << gender_to_string(entry.second.gender) << std::endl;
	}

	// Weight adjusted rating of the winning horse
	const double rating = sorted_horses[0].second.rating;

	// Expected time of the winning horse, calculated by comparison to horse A's time in Race 1
	const double time = 96 - ( rating - 100 ) / 10;
	std::cout << "\n" << "Time " << std::fixed << std::setprecision(0) << std::floor(time / 60)
		      << " minute " << std::setprecision(1) << std::fmod(time, 60) << " seconds" << std::endl;
}
Output:
Race 4

Position  Horse  Weight  Distance  Gender
   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

FreeBASIC

Translation of: Nim
' Ratings on past form, assuming a rating of 100 for horse A.
Dim As Double a = 100.0
Dim As Double b = a - 8 - 2 * 2       ' carried 8 lbs less, finished 2 lengths behind.
Dim As Double c = a + 4 - 2 * 3.5
Dim As Double d = a - 4 - 10 * 0.4    ' based on relative weight and time.
Dim As Double e = d + 7 - 2 * 1
Dim As Double f = d + 11 - 2 * (4 - 2)
Dim As Double g = a - 10  + 10 * 0.2
Dim As Double h = g + 6 - 2 * 1.5
Dim As Double i = g + 15 - 2 * 2

' Adjustments to ratings for current race.
b += 4
c -= 4
h += 3
Dim As Double j = a - 3 + 10 * 0.2

' Filly's allowance to give weight adjusted weighting.
b += 3
d += 3
i += 3
j += 3

' Create table mapping horse to its weight adjusted rating and whether colt.
Type Pair
    key As String*1
    rating As Double
    colt As Boolean
End Type

Dim As Pair list(9)
list(0).key = "A": list(0).rating = a: list(0).colt = True
list(1).key = "B": list(1).rating = b: list(1).colt = False
list(2).key = "C": list(2).rating = c: list(2).colt = True
list(3).key = "D": list(3).rating = d: list(3).colt = False
list(4).key = "E": list(4).rating = e: list(4).colt = True
list(5).key = "F": list(5).rating = f: list(5).colt = True
list(6).key = "G": list(6).rating = g: list(6).colt = True
list(7).key = "H": list(7).rating = h: list(7).colt = True
list(8).key = "I": list(8).rating = i: list(8).colt = False
list(9).key = "J": list(9).rating = j: list(9).colt = False

' Sort in descending order of rating.
Dim As Integer n = Ubound(list)
For x As Integer = 0 To n - 1
    For y As Integer = 0 To n - x - 1
        If list(y).rating < list(y + 1).rating Then Swap list(y), list(y + 1)
    Next y
Next x

' Show expected result of race.
Print !"Race 4\n"
Print "Pos Horse  Weight  Dist  Sex"
Dim As String posic = ""
For x As Integer = Lbound(list) To Ubound(list)
    Dim As Double wt = Iif(list(x).colt, 9.00, 8.11)
    Dim As Double dist = 0.0
    If x > 0 Then dist = (list(x-1).rating - list(x).rating) * 0.5
    posic = Iif(x = 0 Or dist > 0, Str(x + 1), Iif(Right(posic, 1) <> "=", Str(x) & "=", posic))
    Dim As String sx = Iif(list(x).colt, "colt", "filly")
    Print Using "\\   !     ##.##   #.#   \       \"; posic; list(x).key; wt; dist; sx
Next x

' Weight adjusted rating of winner.
Dim As Double wr = list(0).rating

' Expected time of winner (relative to A's time in Race 1).
Dim As Double t = 96 - (wr - 100) / 10
Dim As Integer min = Int(t / 60)
Dim As Integer sec = t Mod 60
Print
Print Using "Time ## minute ##.# seconds"; min; sec

Sleep
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.0 seconds

Go

Translation of: Wren
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)
}
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

Java

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class HorseRacing {

	public static void main(String[] args) {
		// Horses with their rating after the first 3 races
		Map<String, Info> horses = new HashMap<String, Info>(Map.ofEntries(
			Map.entry("A", new Info(Gender.COLT, 100.0) ),
			Map.entry("B", new Info(Gender.FILLY, 100.0 - 8 - 2 * 2)),
			Map.entry("C", new Info(Gender.COLT, 100.0 + 4 - 2 * 3.5)),
			Map.entry("D", new Info(Gender.FILLY, 100.0 - 4 - 10 * 0.4)),
			Map.entry("E", new Info(Gender.COLT, ( 100.0 - 4 - 10 * 0.4 ) + 7 - 2 * 1)),
			Map.entry("F", new Info(Gender.COLT, ( 100.0 - 4 - 10 * 0.4 ) + 11 - 2 * ( 4 - 2 ))),
			Map.entry("G", new Info(Gender.COLT, 100.0 - 10 + 10 * 0.2)),
			Map.entry("H", new Info(Gender.COLT, ( 100.0 - 10 + 10 * 0.2 ) + 6 - 2 * 1.5)),
			Map.entry("I", new Info(Gender.FILLY, ( 100.0 - 10 + 10 * 0.2 ) + 15 - 2 * 2)),
			Map.entry("J", new Info(Gender.FILLY, null))
		));		

		// Adjustments to ratings for Race 4
		horses.computeIfPresent("B", (k, v) -> new Info(v.gender, v.rating + 4));
		horses.computeIfPresent("C", (k, v) -> new Info(v.gender, v.rating - 4));
		horses.computeIfPresent("H", (k, v) -> new Info(v.gender, v.rating + 3));
		horses.computeIfPresent("J", (k, v) -> new Info(v.gender, 100 - 3 + 10 * 0.2));

		// Filly's weight allowance adjustment
		for ( Map.Entry<String, Info> entry : horses.entrySet() ) {
			if ( entry.getValue().gender == Gender.FILLY ) {
				entry.setValue( new Info(entry.getValue().gender, entry.getValue().rating + 3) );
			}	
		}

		// Sort in descending order of rating
		List<Map.Entry<String, Info>> sortedHorses = horses.entrySet().stream()
            .sorted(Map.Entry.<String, Info>comparingByValue(infoComparator)).toList();
		
		// Display the expected result of Race 4
		System.out.println("Race 4" + System.lineSeparator());
		System.out.println("Position  Horse  Weight  Distance  Gender");
		for ( int i = 0; i < sortedHorses.size(); i++ ) {
			Map.Entry<String, Info> entry = sortedHorses.get(i);
			final double weight = ( entry.getValue().gender == Gender.COLT ) ? 9.00 : 8.11; 
			final double distance = ( i > 0 ) ?
				( sortedHorses.get(i - 1).getValue().rating - entry.getValue().rating ) * 0.5 : 0.0;
			final String position = ( i == 0 || distance > 0 ) ? String.valueOf(i + 1) : String.valueOf(i) + "=";
			System.out.println(String.format("   %-4s%6s%9.2f%8.1f%10s",
				position, entry.getKey(), weight, distance, entry.getValue().gender));
		}
		
		// Weight adjusted rating of the winning horse
		final double rating = sortedHorses.getFirst().getValue().rating;

		// Expected time of the winning horse, calculated by comparison to horse A's time in Race 1
		final double time = 96 - ( rating - 100 ) / 10;
		System.out.println(String.format("%n%s%.0f%s%.1f%s",
			"Time ", Math.floor(time / 60), " minute ", time % 60, " seconds"));
	}
	
	private static Comparator<Info> infoComparator = (i1, i2) -> Double.compare(i2.rating, i1.rating);
	
	private enum Gender { COLT, FILLY }
	
	private static record Info(Gender gender, Double rating) {}

}
Output:
Race 4

Position  Horse  Weight  Distance  Gender
   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
   8        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

Julia

Translation of: Go
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()
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.0 minutes and 35.4 seconds.

Nim

Translation of: Wren
import algorithm, math, strformat, strutils

# 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

# Adjustments to ratings for current race.
b += 4
c -= 4
h += 3
var j = a - 3 + 10 * 0.2

# Filly's allowance to give weight adjusted weighting.
b += 3
d += 3
i += 3
j += 3

# 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)}

# Sort in descending order of rating.
let slist = list.sortedByIt(-it.value.rating)

# 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}"

# Weight adjusted rating of winner.
let wr = slist[0].value.rating

# 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"
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

Perl

Translation of: Raku
use strict;
use warnings;

my %card;
$card{a} = { name => 'Alberta Clipper',       sex => 'M', rating => 100 };
$card{b} = { name => 'Beetlebaum',            sex => 'F', rating => $card{a}{rating} -  8 -  2*2     };
$card{c} = { name => 'Canyonero',             sex => 'M', rating => $card{a}{rating} +  4 -  2*3.5   };
$card{d} = { name => 'Donnatello',            sex => 'F', rating => $card{a}{rating} -  4 - 10*0.4   };
$card{e} = { name => 'Exterminator',          sex => 'M', rating => $card{d}{rating} +  7 -  2*1     };
$card{f} = { name => 'Frequent Flyer',        sex => 'M', rating => $card{d}{rating} + 11 -  2*(4-2) };
$card{g} = { name => 'Grindstone',            sex => 'M', rating => $card{a}{rating} - 10 + 10*0.2   };
$card{h} = { name => 'His Honor',             sex => 'M', rating => $card{g}{rating} +  6 -  2*1.5   };
$card{i} = { name => 'Iphigenia in Brooklyn', sex => 'F', rating => $card{g}{rating} + 15 -  2*2     };
$card{j} = { name => 'Josephine',             sex => 'F' };

# adjustments to ratings for current race
$card{b}{rating} += 4;
$card{c}{rating} -= 4;
$card{h}{rating} += 3;
$card{j}{rating} = $card{a}{rating} - 3 + 10*0.2;

# initialize carry weights
for (keys %card) {
    $card{$_}{rating} += 3 if $card{$_}{sex} eq 'F';
    $card{$_}{weight} = $card{$_}{sex} eq 'M' ? 9.00 : 8.11
}

print "Pos Horse         Name           Weight   Back    Sex    Time\n";

my($previous, $position, $leader, @predictions) = 0;
for (sort { $card{$b}{rating} <=> $card{$a}{rating} } keys %card) {
    $leader = $_ unless $leader;
    ++$position if $previous != $card{$_}{rating};
    $previous = $card{$_}{rating};
    $card{$_}{back} = ($card{$leader}{rating} - $card{$_}{rating}) / 2;
    $card{$_}{time} = 96 - ($card{$_}{rating} - 100) / 10;
    push @predictions, sprintf "%2d    %s   %-22s  %.2f    %.1f    %-6s %s\n", $position, $_, $card{$_}{name}, $card{$_}{weight},
      $card{$_}{back}, $card{$_}{sex} eq 'M' ? 'colt' : 'filly', sprintf '%1d:%.1f', int($card{$_}{time}/60), $card{$_}{time}-60;
}

print for sort @predictions;
Output:
 1    i   Iphigenia in Brooklyn   8.11    0.0    filly  1:35.4
 2    j   Josephine               8.11    2.0    filly  1:35.8
 3    a   Alberta Clipper         9.00    3.0    colt   1:36.0
 4    f   Frequent Flyer          9.00    3.5    colt   1:36.1
 5    h   His Honor               9.00    4.0    colt   1:36.2
 6    e   Exterminator            9.00    4.5    colt   1:36.3
 7    b   Beetlebaum              8.11    5.5    filly  1:36.5
 7    d   Donnatello              8.11    5.5    filly  1:36.5
 8    c   Canyonero               9.00    6.5    colt   1:36.7
 9    g   Grindstone              9.00    7.0    colt   1:36.8

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 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, sex
integer stone, pounds
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, 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

Raku

I know nothing about horse racing and/or handicapping. This is mostly a straightforward translation of the logic from Go though I did take certain liberties with formatting and layout.

my %card;
%card<a> = { name => 'Alberta Clipper',       :sex<M>, rating => 100 };
%card<b> = { name => 'Beetlebaum',            :sex<F>, rating => %card<a><rating> - 8 - 2*2 };
%card<c> = { name => 'Canyonero',             :sex<M>, rating => %card<a><rating> + 4 - 2*3.5 };
%card<d> = { name => 'Donnatello',            :sex<F>, rating => %card<a><rating> - 4 - 10*0.4 };
%card<e> = { name => 'Exterminator',          :sex<M>, rating => %card<d><rating> + 7 - 2*1 };
%card<f> = { name => 'Frequent Flyer',        :sex<M>, rating => %card<d><rating> + 11 - 2*(4-2) };
%card<g> = { name => 'Grindstone',            :sex<M>, rating => %card<a><rating> - 10 + 10*0.2 };
%card<h> = { name => 'His Honor',             :sex<M>, rating => %card<g><rating> + 6 - 2*1.5 };
%card<i> = { name => 'Iphigenia in Brooklyn', :sex<F>, rating => %card<g><rating> + 15 - 2*2 };
%card<j> = { name => 'Josephine',             :sex<F>, rating => Nil };

# adjustments to ratings for current race
%card<b><rating> += 4;
%card<c><rating> -= 4;
%card<h><rating> += 3;
%card<j><rating> = %card<a><rating> - 3 + 10*0.2;

# adjust filly's allowance
# initialize carry weights
for %card {
    .value<rating> += 3 if .value<sex> eq 'F';
    .value<weight> = (.value<sex> eq 'M') ?? 9.00 !! 8.11
}

my ($previous, $position, $leader) = 0;
say "Pos Horse         Name           Weight   Back    Sex    Time";

for %card.sort( -*.value.<rating> ) {
    FIRST $leader = $_;
    ++$position if $previous != .value.<rating>;
    $previous = .value.<rating>;
    .value<back> = ($leader.value<rating> - .value<rating>) / 2;
    .value<time> = 96 - (.value.<rating> - 100) / 10;
    printf "%2d    %s   %-22s  %.2f    %.1f    %-6s %s\n", $position, .key, .value<name>, .value<weight>,
      .value<back>, .value<sex> eq 'M' ?? 'colt' !! 'filly', .value.<time>.polymod(60 xx*).reverse.join: ':';
}
Output:
Pos Horse         Name           Weight   Back    Sex    Time
 1    i   Iphigenia in Brooklyn   8.11    0.0    filly  1:35.4
 2    j   Josephine               8.11    2.0    filly  1:35.8
 3    a   Alberta Clipper         9.00    3.0    colt   1:36
 4    f   Frequent Flyer          9.00    3.5    colt   1:36.1
 5    h   His Honor               9.00    4.0    colt   1:36.2
 6    e   Exterminator            9.00    4.5    colt   1:36.3
 7    d   Donnatello              8.11    5.5    filly  1:36.5
 7    b   Beetlebaum              8.11    5.5    filly  1:36.5
 8    c   Canyonero               9.00    6.5    colt   1:36.7
 9    g   Grindstone              9.00    7.0    colt   1:36.8

V (Vlang)

Translation of: go
struct Kv {
    key   string
    value []f64
}
fn main() {
    // ratings on past form, assuming a rating of 100 for horse A
    a := 100.0
    mut b := a - 8 - 2*2 // carried 8 lbs less, finished 2 lengths behind
    mut c := a + 4 - 2*3.5
    mut 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
    mut h := g + 6 - 2*1.5
    mut i := g + 15 - 2*2
 
    // adjustments to ratings for current race
    b += 4
    c -= 4
    h += 3
    mut 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 := {
        "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],
    }
 
    // convert to slice of Kv
    mut l := []Kv{ len: m.len}
    mut x := 0
    for k, v in 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] })
    l.sort_with_compare(fn(a &Kv, b &Kv) int {
        if a.value[0] < b.value[0] {
            return 1
        }else if a.value[0] > b.value[0] {
            return -1
        }
        return 0
    })
    // show expected result of race
    println("Race 4\n")
    println("Pos Horse  Weight  Dist  Sex")
    mut pos := ""
    for v in 0..l.len {
        mut wt := "9.00"
        if l[v].value[1] == 0 {
            wt = "8.11"
        }
        mut dist := 0.0
        if v > 0 {
            dist = (l[v-1].value[0]-l[v].value[0]) * 0.5
        }
        if v == 0 || dist > 0.0 {
            pos = "${v+1}"
        } else {
            if pos.index('=') or {-1} < 0{
                pos = "${v}="
            }
        }
        mut sx := "colt"
        if l[v].value[1] == 0 {
            sx = "filly"
        }
        println("${pos:-2}  ${l[v].key}      $wt    ${dist:3.1f}   $sx")
    }
 
    // weight adjusted rating of winner
    wr := 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 - f64(min)*60
    println("\nTime $min minute ${sec:3.1f} seconds")
}
Output:
Same as go entry

Wren

Library: Wren-fmt
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")
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
Cookies help us deliver our services. By using our services, you agree to our use of cookies.