Merge pull request #371 from AlaaSarhan/main

13. Bounce in Ruby
This commit is contained in:
Jeff Atwood
2022-01-03 19:09:15 -08:00
committed by GitHub

184
13_Bounce/ruby/bounce.rb Normal file
View File

@@ -0,0 +1,184 @@
## Global constants
# Gravity accelaration (F/S^2) ~= 32
G = 32
# Used to indent the plotting of ball positions
# so that the height digits don't affect
# where we start plotting ball positions
BALL_PLOT_INDENT = "\t"
# The deviation between current plotted height and the actual
# height of the ball that we will accept to plot the ball in
# that plotted height
BALL_PLOT_DEVIATION = 0.25
# The step we will take as we move down vertically while
# plotting ball positions
BALL_PLOT_HEIGHT_STEP = 0.5
## Helper functions
# Calculates the bounce speed (up) of the ball for a given
# bounce number and coefficient
def calc_velocity_for_bounce(v0, bounce, coefficient)
v = v0 * coefficient**bounce
end
# Check https://physics.stackexchange.com/a/333436 for nice explanation
def calc_bounce_total_time(v0, bounce, coefficient)
v = calc_velocity_for_bounce(v0, bounce, coefficient)
t = 2 * v / G
end
# Check https://physics.stackexchange.com/a/333436 for nice explanation
def calc_ball_height(v0, bounce, coefficient, t)
v = calc_velocity_for_bounce(v0, bounce, coefficient)
h = v * t - 0.5 * G * t**2
end
def heighest_position_in_next_bounce(time_in_bounce, v0, i, c)
time_in_next_bounce = time_in_bounce[i+1]
return -1 if time_in_next_bounce.nil?
return calc_ball_height(v0, i, c, time_in_next_bounce / 2) unless time_in_next_bounce.nil?
end
def intro
puts <<~INSTRUCTIONS
BOUNCE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY
OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF
ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION
COEFFICIENCY (LESS THAN 1).
YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN
'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).
INSTRUCTIONS
end
## Plottin functions
def plot_header
puts
puts "FEET"
end
def plot_bouncing_ball(strobbing_time, v0, c)
## Initializing helper values
# How many bounces we want to plot
# original BASIC version is 70 / (V / (16 * S2))
# 70 is assumed to be an arbitrary number higher than 2G and 16 is 1/2G
bounces_to_plot = (G**2 / (v0 / strobbing_time)).to_i
# Holds the total time the ball spends in the air in every bounce
time_in_bounce = bounces_to_plot.times.map { |i| calc_bounce_total_time v0, i, c }
plot_width = 0
# Calculate the highest position for the ball after the very first bounce
plotted_height = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i
## Plotting bouncing ball
while plotted_height >= 0 do
# We will print only whole-number heights
print plotted_height.to_i if plotted_height.to_i === plotted_height
print BALL_PLOT_INDENT
bounces_to_plot.times { |i|
(0..time_in_bounce[i]).step(strobbing_time) { |t|
ball_pos = calc_ball_height v0, i, c, t
# If the ball is within the acceptable deviation
# from the current height, we will plot it
if (plotted_height - ball_pos).abs <= BALL_PLOT_DEVIATION then
print "0"
else
print " "
end
# Increment the plot width when we are plotting height = 0
# which will definitely be the longest since it never gets
# skipped by line 98
plot_width += 1 if plotted_height == 0
}
if heighest_position_in_next_bounce(time_in_bounce, v0, i, c) < plotted_height then
# If we got no more ball positions at or above current height in the next bounce,
# we can skip the rest of the bounces and move down to the next height to plot
puts
break
end
}
plotted_height -= BALL_PLOT_HEIGHT_STEP
end
# Return plot_width to be used by the plot_footer
plot_width
end
def plot_footer (plot_width, strobbing_time)
# Dotted separator line
puts
print BALL_PLOT_INDENT
(plot_width).times { |_| print "." }
puts
# Time values line
print BALL_PLOT_INDENT
points_in_sec = (1 / strobbing_time).to_i
plot_width.times { |i|
if i % points_in_sec == 0 then
print (i / points_in_sec).to_i
else
print " "
end
}
puts
# Time unit line
print BALL_PLOT_INDENT
(plot_width / 2 - 4).to_i.times { |_| print " " }
puts "SECONDS"
puts
end
def game_loop
# Read strobing, velocity and coefficient parameters from user input
puts "TIME INCREMENT (SEC)"
strobbing_time = gets.to_f
puts "VELOCITY (FPS)"
v0 = gets.to_f
puts "COEFFICIENT"
c = gets.to_f
# Plotting
plot_header
plot_width = plot_bouncing_ball strobbing_time, v0, c
plot_footer plot_width, strobbing_time
end
## Entry point
begin
intro
loop do
game_loop
end
rescue SystemExit, Interrupt
exit
rescue => exception
p exception
end