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

J

Jenny Purcell

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), 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 Purcell 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, horse_name, horse_info,
owners_initials, best_result, race_style, best_time,
pre_points, roll_1, roll_2, roll_3, roll_4, roll_5,
score])
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.
 
J

James Britt

Jenny said:
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 Britt

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
 
J

Jenny Purcell

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.

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
 
H

Harry Kakueki

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
 
J

Jenny Purcell

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

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
 
H

Harry Kakueki

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
 
J

James Britt

Jenny said:
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.

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 Britt

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

Sharon Phillips

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 Purcell based off starter code
# provided by Robbert Haarman

class Horse
attr_reader :horse_name, :horse_info, :eek:wners_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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top