Method to return elements of an array in numeric 'rank'?

I’m working on a Ruby program to simulate horse races for a fantasy
horse racing league. It works off a csv file I put together by hand
with the information necessary for each race. Included in this is a
set of “pre-points” which is a sort of quality rating for each horse
which shows how it’s expected to race. Then, the program rolls 5d6,
adds the pre-points and random points together, and comes up with a
total result for each horse.

The below program does just that. However, now I get to the next step
of what I’d like it to do, and I’m stuck.

What I’d like is to get a variable which shows each horse’s final
position (did they finish 1 for 1st, 2 for 2nd, etc). I have the
“score” (score[i]), and the horse with the largest score number is the
winner, but, I don’t know how to count/sort the array in such a way
that I return that the horse #7 is 1, horse #4 is 2, etc.

I would expect there would be an existing method that works off of the
array class that would do this, and I’d like to check on that before
(painfully) writing my own nested loops that would do the same thing.

Jenny

#! /usr/bin/env ruby

require ‘csv’

Written by Jenny P. based off starter code

provided by Robbert Haarman

assign counter to keep track of number of horses

count=-1

create new arrays

horse_name=Array.new(20)
horse_info=Array.new(20)
owners_initials=Array.new(20)
best_result=Array.new(20)
race_style=Array.new(20)
best_time=Array.new(20)
pre_points=Array.new(20)
roll_1=Array.new(20)
roll_2=Array.new(20)
roll_3=Array.new(20)
roll_4=Array.new(20)
roll_5=Array.new(20)
score=Array.new(20)

For each record in the CSV file…

CSV::Reader.parse(File.open(‘horses.csv’)) do |record|

Now we assign the appropriate fields of the record to

variables named after the fields in the CSV file.

Note that we need to call .to_i on the 6th field

to convert it from strings to integers.

count = count + 1 # count the number of horses in a particular race
horse_name[count] = record[0]
horse_info[count] = record[1]
owners_initials[count] = record[2]
best_result[count] = record[3]
race_style[count] = record[4]
best_time[count] = record[5]
pre_points[count] = record[6].to_i

Roll the dice.

This generates an array containing five die rolls,

each die roll ranging from 1 to 6

dice = (1…5).map { rand(6) + 1 }

Calculate score.

The score is the sum pre_points, and each of the die rolls

temp_score = pre_points[count]
dice.each { |n| temp_score = temp_score + n }
score[count]=temp_score
roll_1[count]=dice[0]
roll_2[count]=dice[1]
roll_3[count]=dice[2]
roll_4[count]=dice[3]
roll_5[count]=dice[4]

Display each horse’s name, owner, score, pre_points, and other

info

#end of the “do” statement for reading in CSV files
end

Randomizing Post Position

i is a counter to count up to the number of horses

pole = Array.new(count + 1) { |m| m + 1 }
pole_new=pole.sort_by { rand }
i=0
while i <= count do
puts CSV.generate_line([pole_new[i], horse_name[i], horse_info[i],
owners_initials[i], best_result[i], race_style[i], best_time[i],
pre_points[i], roll_1[i], roll_2[i], roll_3[i], roll_4[i], roll_5[i],
score[i]])
i = i + 1
end

# Features to add
# Break ties.
# Have it calculate race times
# Have it calculate margins
# Have it share out pre_points based on running style (adapt to
# numbered "legs")
# Have it export to basic HTML file (for archive/email results)
# all sixes = possible NTR?
# Two consecutive ones is a break in pace.

Jenny P. wrote:

I would expect there would be an existing method that works off of the
array class that would do this, and I’d like to check on that before
(painfully) writing my own nested loops that would do the same thing.

See http://www.ruby-doc.org/core/classes/Enumerable.html#M003151

Look at sort_by

Also, in your code, consider representing objects of interest (such as
horses and races) by defining suitable classes.

I think you code would be easier to follow if, for example, it managed a
collection of Horse objects.


James B.

http://web2.0validator.com - We’re the Dot in Web 2.0
http://www.rubyaz.org - Hacking in the Desert
http://www.jamesbritt.com - Playing with Better Toys

On Sun, 22 Jul 2007 11:44:40 +0900, James B.
[email protected] wrote:

Also, in your code, consider representing objects of interest (such as
horses and races) by defining suitable classes.

I think you code would be easier to follow if, for example, it managed a
collection of Horse objects.

I realize it would be easier for people in the fgroup to follow if I
was programming with objects. However, I just don’t understand OO
programming, my skills (such as they are) are procedural.

Ruby was pointed out to me as a language that would allow me to write
procedural programs and so far it is working out very nicely in that
regard.

Your suggestion to try .sort_by didn’t work for me. However, I did
read up on .each and that in fact DID work for me.

The bit where I multiply my c variable by 100 and then add 20 (and
later subtract 20) is a simple way for me to break ties.

Determining each horse’s finishing position

temp_score = Array.new(count + 1) {|c| 100 * score[c] + 20 -
pole_new[c]}
sort_score = temp_score.sort
sort_score.reverse!
number.each {|a| position[a - 1] = number.detect {|b| sort_score[b - 1] == temp_score[a - 1]}}

Jenny

On 7/22/07, Jenny P. [email protected] wrote:

I realize it would be easier for people in the fgroup to follow if I
was programming with objects. However, I just don’t understand OO
programming, my skills (such as they are) are procedural.

Ruby was pointed out to me as a language that would allow me to write
procedural programs and so far it is working out very nicely in that
regard.

Would something like this work?

arr = [[1,7],[3,2],[4,9],[2,3]] # [[horse,score],[horse,score] etc.]
arr.sort! {|x,y| y[1] <=> x[1]}
p arr

Harry

On Sun, 22 Jul 2007 13:52:15 +0900, “Harry K.”
[email protected] wrote:

Would something like this work?

arr = [[1,7],[3,2],[4,9],[2,3]] # [[horse,score],[horse,score] etc.]
arr.sort! {|x,y| y[1] <=> x[1]}
p arr

It looks like you’re suggesting a two dimensional array, is that
correct? I think that would also work, although I’m not quite good
enough with Ruby syntax (or arrays) to follow it 100%.

Jenny

Jenny P. wrote:

Look at sort_by
programming, my skills (such as they are) are procedural.
Skill to do comes of doing. And you can ask for help on ruby-talk.

Ruby was pointed out to me as a language that would allow me to write
procedural programs and so far it is working out very nicely in that
regard.

That will not carry very far.

While it is technically true that one can use Ruby to write procedural
code that does not make it a good idea.

It’s a fair bet that at some point maintainability will approach zero
unless you employ some higher abstractions Classes and objects are one
approach to this.

When you have procedural code that works (and you are using Ruby’s
unit testing libraries, right? :)) consider refactoring it into
something more OO. It might make life simpler all around.

Just a suggestion.


James B.

“In physics the truth is rarely perfectly clear, and that is certainly
universally the case in human affairs. Hence, what is not surrounded
by
uncertainty cannot be the truth.”

  • R. Feynman

On 7/22/07, Jenny P. [email protected] wrote:

It looks like you’re suggesting a two dimensional array, is that
correct? I think that would also work, although I’m not quite good
enough with Ruby syntax (or arrays) to follow it 100%.

Jenny

What I’m thinking is that you could set up a 2D array and sort it on
the scores (element [1] of each sub-array).
Then you can extract the data you want.
You can do it using only arrays, which I think you said you wanted to
do.
This is not a total solution, just an idea.

arr = [[1,7],[3,2],[4,9],[2,3]] # [horse,score],[horse,score] etc.]
arr.sort! {|x,y| y[1] <=> x[1]}
p arr #> [[4,9],[1,7],[2,3],[3,2]]
puts
p arr[0] #>[4,9]
p arr[1] #>[1,7]
p arr[2] #>[2,3]
puts
p arr[0][0] #> 4
p arr[0][1] #> 9
p arr[1][0] #> 1
p arr[1][1] #> 7

Harry

On 22/07/2007, at 1:59 PM, Jenny P. wrote:

However, I just don’t understand OO
programming, my skills (such as they are) are procedural.

Ruby is an excellent place to get started with OO as it requires
much less repetitious coding than, say, Java.

I’ve taken the liberty of changing your code slightly to use a Horse
class and keep your horses in a collection ‘horses’. I’ve tried to
stay as true to the original code as possible, just changing what’s
required.
Basically, I’ve removed all the arrays into a Horse class which we
can use to create new horses by calling its ‘new’ method with the
line from the csv file (Horse.new calls Horse::initialize. I have not
idea why the different names, just something to be aware know)

Cheers,
Dave

#! /usr/bin/env ruby

require ‘csv’

Written by Jenny P. based off starter code

provided by Robbert Haarman

class Horse
attr_reader :horse_name, :horse_info, :owners_initials, :best_result,
:race_style, :best_time, :pre_points, :rolls, :score
attr_accessor :new_pole

def initialize(record)
# Now we assign the appropriate fields of the record to
# variables named after the fields in the CSV file.
# Note that we need to call .to_i on the 6th field
# to convert it from strings to integers.
@horse_name = record[0]
@horse_info = record[1]
@owners_initials = record[2]
@best_result = record[3]
@race_style = record[4]
@best_time = record[5]
@pre_points = record[6].to_i

 # Roll the dice.
 # This generates an array containing five die rolls,
 # each die roll ranging from 1 to 6
 @rolls= (1..5).map { rand(6) + 1 }

 # Calculate score.
 # The score is the sum pre_points, and each of the die rolls
 @score= @rolls.inject(pre_points){|sum, i| sum+ i}

end # initialize
end # Horse

create an array to keep all our horses in

horses=[]

For each record in the CSV file…

CSV::Reader.parse(File.open(‘horses.csv’)) do |record|
horses << Horse.new(record)
end # record

Randomizing Post Position

Array.new(horses.size){ |m| m + 1 }.sort_by{ rand }.each_with_index
do |pole, index|
horses[index].new_pole= pole
end # pole, index

horses.each do |horse|
puts CSV.generate_line([
horse.pole_new,
horse.horse_name,
horse.horse_info,
horse.owners_initials,
horse.best_result,
horse.race_style,
horse.best_time,
horse.pre_points]+
horse.rolls+
[horse.score])
i = i + 1
end # horse

 # Features to add
 # Break ties.
 # Have it calculate race times
 # Have it calculate margins
 # Have it share out pre_points based on running style (adapt to
 # numbered "legs")
 # Have it export to basic HTML file (for archive/email results)
 # all sixes = possible NTR?
 # Two consecutive ones is a break in pace.