Checkerboard challenge - ruby-on-rails

I'm close to solving this challenge, however, I cannot figure out how to refactor my code to get the following spec to pass.
Spec to pass:
Test.assert_equals(checkered_board(2), "\u25A1 \u25A0\n\u25A0 \u25A1")
My error:
Expected: "\u25A1 \u25A0\n\u25A0 \u25A1", instead got: "\u25A1 \u25A0 \n\u25A0 \u25A1 "
My attempt (via visual on repl.it, everything appears correct)
def checkered_board(dimension)
return false unless dimension.integer? && dimension >= 2
checkboard = nil
checker_array = []
count = dimension
while dimension % 2 == 0 && count > 0 # set-up for even number passed in
if count % 2 == 0
checkboard = ("\u25A1 \u25A0 ") * (dimension / 2)
checkboard += "\n"
checker_array << checkboard
count -= 1
else
checkboard = ("\u25A0 \u25A1 ") * (dimension / 2)
if count == 1 #if count is the last number before 0, do not add \n to pass spec
checker_array << checkboard
else
checkboard += "\n"
checker_array << checkboard
end
count -= 1
end
end
while dimension % 2 == 1 && count > 0 # set-up for odd number passed in
if count % 2 == 1
checkboard = ("\u25A0 \u25A1 ") * (dimension / 2)
checkboard += "\u25A0"
if count == 1 #if count is the last number before 0, do not add \n to pass spec
checker_array << checkboard
else
checkboard += "\n"
checker_array << checkboard
end
count -= 1
else
checkboard = ("\u25A1 \u25A0 ") * (dimension / 2)
checkboard += "\u25A1"
checkboard += "\n"
checker_array << checkboard
count -= 1
end
end
checkboard = checker_array.join("")
checkboard
end
What should I add/do to pass how the \n is within the required passing spec?
Thanks

Figured out my own problem and solution is commented in code below
def checkered_board(dimension)
return false unless dimension.is_a?(Integer) && dimension >= 2
checkboard = nil
checker_array = []
spacer = " "
count = dimension
while dimension % 2 == 0 && count > 0 # set-up for even number passed in
if count % 2 == 0
checkboard = ("\u25A1#{spacer}\u25A0#{spacer}") * (dimension / 2)
checkboard = checkboard[0..-2] + "\n" #solution was here
checker_array << checkboard
count -= 1
else
checkboard = ("\u25A0#{spacer}\u25A1#{spacer}") * (dimension / 2)
if count == 1 #if count is the last number before 0, do not add \n to pass spec
checker_array << checkboard[0..-2] #solution was here
else
checkboard = checkboard[0..-2] + "\n" #solution was here
checker_array << checkboard
end
count -= 1
end
end
while dimension % 2 == 1 && count > 0 # set-up for odd number passed in
if count % 2 == 1
checkboard = ("\u25A0#{spacer}\u25A1#{spacer}") * (dimension / 2)
checkboard += "\u25A0"
if count == 1 #if count is the last number before 0, do not add \n to pass spec
checker_array << checkboard[0..-1] #solution was here
else
checkboard += "\n"
checker_array << checkboard
end
count -= 1
else
checkboard = ("\u25A1#{spacer}\u25A0#{spacer}") * (dimension / 2)
checkboard += "\u25A1"
checkboard = checkboard[0..-1] + "\n" #solution was here
checker_array << checkboard
count -= 1
end
end
checkboard = checker_array.join("")
checkboard
end

Related

RoR: Method to return a statement in a json format?

I'm looking for a method that will return this statement in a JSON format.
def statement
total = 0
bonus points = 0
result = 'Car rental for #{#name.to_s}\n'
for r in #rentals
this_amount = 0
case r.car.style
when Car::SUV
this_amount += r.days_rented * 30
when Car::HATCHBACK
this_amount += 15
if r.days_rented > 3
this_amount += (r.days_rented - 3) * 15
end
when Car::SALOON
this_amount += 20
if r.days_rented > 2
this_amount += (r.days_rented - 2) * 15
end
else
end
if this_amount < 0
bonus_points -= 10
end
bonus_points = bonus_points + 1
if r.car.style == Car::SUV && r.days_rented > 1
bonus_points = bonus_points + 1
end
result += r.car.title.to_s + "," + this_amount.to_s + "\n"
total += this_amount
end
result += "Amount owed is " + "#{total.to_s}" + "\n"
result +="Earned bonus points: " + bonus_points.to_s
result
end
What method would I need to add to my class to return this statement in a JSON format? Thank you.
Simplest way to do that
return {:result => result}
at the end of your method.
But if you are using controller to show data to user, I would prefer using .to_json in my controller method

How can I make query simpler?

I am creating a Rails 5 app.
In this app I got a method that gets values from child objects and adds them to an hash. The below method/code works perfectly fine but I how can I make it better in terms of speed and structure?
def generated_values(period, year, month, quarter)
count = 0
score = 0
actual = 0
goal = 0
red = 0
if stype == "measure"
measures.period(period, year, month, quarter).each do |measure|
count += 1
score += measure.score
actual += measure.value_actual
goal += measure.value_goal
red += measure.value_redflag
end
elsif stype == "objective"
children.each do |child|
child.measures.period(period, year, month, quarter).each do |measure|
count += 1
score += measure.score
actual += measure.value_actual
goal += measure.value_goal
red += measure.value_redflag
end
end
elsif stype == "scorecard"
children.each do |child|
child.children.each do |child2|
child2.measures.period(period, year, month, quarter).each do |measure|
count += 1
score += measure.score
actual += measure.value_actual
goal += measure.value_goal
red += measure.value_redflag
end
end
end
end
values = { :score => score == 0 ? 0 : (score / count).round, :actual => actual, :goal => goal, :red => red }
end
I think I would be tempted to do something like:
def generated_values(period, year, month, quarter)
case stype
when "measure"
selected_measures = measures
when "objective"
selected_measures = child_measures
when "scorecard"
selected_measures = grandchild_measures
end
count = selected_measures.count
{
score: count > 0 ? (selected_measures.sum(:score)/count).round : 0,
actual: selected_measures.sum(:value_actual)
goal: selected_measures.sum(:value_goal)
red: selected_measures.sum(:value_redflag)
}
end
Untested and off-the-cuff.
Naturally, the most interesting part is the selected_measures = bits. But, you haven't provided enough information to help with the proper formulation of those queries.

How can I write this conditional in fewer lines?

I wrote this code in my model:
percentage = 0
if self.date_of_birth.present?
percentage += 15
end
if self.gender.present?
percentage += 15
end
if self.relationship_status.present?
percentage += 10
end
if self.language.present?
percentage += 10
end
if self.qualification.present?
percentage += 10
end
if self.interests.present?
if self.interests.count >= 10
percentage += 10
else
percentage += self.interests.count * 5
end
end
But it does not look good. It is a lot of code for a small thing. I want to reduce the number of lines.
You can do it inline, like this:
percentage += 15 if self.date_of_birth.present?
Instead of this:
if self.interests.count >= 10
percentage += 10
else
percentage += self.interests.count*5
end
You can use a ternary operator:
percentage += self.interests.count >= 10 ? 10 : self.interests.count*5
percentage = [
(15 if date_of_birth.present?),
(15 if gender.present?),
(10 if relationship_status.present?),
(10 if language.present?),
(10 if qualification.present?),
((counts = interests.count.to_i) >= 10 ? 10 : (counts * 5)),
].compact.sum
You could use an instance method in your model:
#app/models/model.rb
class Model < ActiveRecord::Base
def percentage
value = 0
values = [[:date_of_birth, 15], [:gender, 15], [:relationship_status,10], [:language,10], [:qualification, 10]]
values.each do |attr,val|
value += val if self.send(attr).present?
end
value += self.interests.count >= 10 ? 10 : self.interests.count*5 if self.interests.present?
# Rails should return the value of the last line, which is the "value" var
end
end
This would allow you to use #user.percentage, where #user is your instance var for the model.
Personally, I don't think that "less lines" is a good idea, but if you want your code in less lines, you can write it like this:
percentage = 0; if date_of_birth.present? then percentage += 15 end; if gender.present? then percentage += 15 end; if relationship_status.present? then percentage += 10 end; if language.present? then percentage += 10 end; if qualification.present? then percentage += 10 end; if interests.present? then if interests.count >= 10 then percentage += 10 else percentage += interests.count*5 end end
In Ruby, you can (almost) always replace linebreaks with semicolons to make your code fit on less lines. In fact, every Ruby program can always be written on a single line.
inc_att = ["date_of_birth", "gender", "relationship_status" , "language", "qualification", "interests"]
inc_att.each do |s|
if self[s].present? && (s == "date_of_birth" || s == "gender")
percentage += 15
elsif self[s].present? && s == "interests" && self[s].count < 10
percentage += self[s].count * 5
else
percentage += 10 if self[s].present?
end
end
Have a look into it
inc_att = ["date_of_birth", "gender", "relationship_status" , "language", "qualification", "interests"]
inc_att.each do |s|
if self[s].present? && (s == "date_of_birth" || s == "gender")
percentage += 15
elsif self[s].present? && s == "interests" && self[s].count < 10
percentage += self[s].count * 5
else
percentage += 10 if self[s].present?
end
end
Inspired by #sawa's answer:
counts = interests.count.to_i
percentage = (counts >= 10 ? 10 : (counts * 5)) +
[
date_of_birth.present? && 15,
gender.present? && 15,
relationship_status.present? && 10,
language.present? && 10,
qualification.present? && 10,
].select(&:itself).sum

bad argument #1 to 'pairs' (table expected, got nil)

So I'm trying to program a space invaders like game in lua using löve2d, the first wave of enemies runs smoothly but once i try adding another wave to the levelctrl table i get this error: bad argument #1 to 'pairs' (table expected, got nil) on line 235 (even when i populate a second table)
function love.load()
hero = {}
hero.x = 400 - 16
hero.y = 450
hero.speedx = 300
hero.speedy = 50
hero.shots = {}
bgx = 0
bgs = .25
fleet1 = {}
f1x = 0
f1f = 0
lost = 0
score = 0
game = 0
menu = 1
startup = 1
--Images
bg = love.graphics.newImage("img/space.png")
bullet = love.graphics.newImage("img/shot.png")
player = love.graphics.newImage("img/player.png")
trail = love.graphics.newImage("img/trail.png")
ship1 = love.graphics.newImage("img/enemy1.png")
looser = love.graphics.newImage("img/looser.png")
boomp1 = love.graphics.newImage("img/boomp1.png")
boomp2 = love.graphics.newImage("img/boomp2.png")
boomp3 = love.graphics.newImage("img/boomp3.png")
boomp4 = love.graphics.newImage("img/boomp4.png")
boom1 = love.graphics.newImage("img/boom1.png")
boom2 = love.graphics.newImage("img/boom2.png")
boom3 = love.graphics.newImage("img/boom3.png")
boom4 = love.graphics.newImage("img/boom4.png")
winner = love.graphics.newImage("img/winner.png")
title = love.graphics.newImage("img/title.png")
message = love.graphics.newImage("img/message.png")
restart = love.graphics.newImage("img/restart.png")
icon = love.graphics.newImage("img/icon.gif")
boomtimer=0
isdead=0
boom={}
bgs = .25
bgx = 0
menublink = 0
menuf = 0
end
function love.update(dt)
if bgx < 600 then
bgx = bgx + bgs
else
bgx = -599
end
if bgs > .30 then
bgs = bgs - 2*dt
end
if menublink < 48 and menuf==0 then
menublink = menublink + 1
else
menuf=1
menublink = menublink -1
if menublink == 0 then
menuf=0
end
end
if menu == 1 then
hero = {}
hero.x = 400 - 16
hero.y = 450
hero.speedx = 300
hero.speedy = 50
hero.shots = {}
f1x = 0
f1f = 0
lost = 0
score = 0
boomtimer=0
isdead=0
won=0
boom={}
fleet1 = {}
for i=0,7 do
enemy = {}
enemy.width = 32
enemy.height = 16
enemy.img = ship1
enemy.x = i * (enemy.width + 64) + 48
if i<4 then
if i % 2 == 0 then
enemy.y = 0 - enemy.height
else
enemy.y = -16 - enemy.height
end
else
if i % 2 == 0 then
enemy.y = -16 - enemy.height
else
enemy.y = 0 - enemy.height
end
end
table.insert(fleet1, enemy)
end
boss = {}
level = 1
levelctrl = {}
table.insert(levelctrl, fleet1)
if love.keyboard.isDown(" ") then
menu = 0
game = 1
end
elseif game == 1 then
if isdead==0 then
if love.keyboard.isDown("left") then
if hero.x > 0 then
hero.x = hero.x - hero.speedx*dt
end
elseif love.keyboard.isDown("right") then
if hero.x < 800-32 then
hero.x = hero.x + hero.speedx*dt
end
end
if love.keyboard.isDown("up") then
if hero.y > 300 then
hero.y = hero.y - hero.speedy*dt
up=1
else
up=0
end
if bgs < 4 then
bgs = bgs + 5*dt
end
elseif love.keyboard.isDown("down") then
if hero.y < 600-64 then
hero.y = hero.y + hero.speedy*2*dt
end
else
up=0
end
end
for i,v in ipairs(hero.shots) do
v.y = v.y - dt * 300
end
if won == 0 then
local remShot = {}
local remEnemy = {}
for i,v in ipairs(hero.shots) do
if v.y < 0 then
table.insert(remShot, i)
end
for ii,vv in ipairs(levelctrl[1]) do
if CheckCollision(v.x,v.y,32,32,vv.x,vv.y,vv.width,vv.height) then
x = {}
x.x = vv.x
x.y = vv.y
x.t = 0
table.insert(boom, x)
table.insert(remEnemy, ii)
table.insert(remShot, i)
end
end
end
for i,v in ipairs(levelctrl[1]) do
if CheckCollision(hero.x,hero.y,32,64,v.x,v.y,v.width,v.height) then
lost = 1
end
end
for i,v in ipairs(remShot) do
table.remove(hero.shots, v)
end
for i,v in ipairs(remEnemy) do
table.remove(levelctrl[1], v)
score = score + 1
end
if level == 1 then
for i,v in ipairs(levelctrl[1]) do
v.y = v.y + dt * 12
if v.y > 512 then
lost = 1
end
if v.x>0 and v.x < 768 then
if f1f == 0 then
if f1x<3 then
f1x = f1x + 1 * dt
v.x = v.x + 1
else
f1f = 1
end
else
if f1x>-3 then
f1x = f1x - 1 * dt
v.x = v.x - 1
else
f1f = 0
end
end
end
end
end
end
if empty(levelctrl[1]) == true and won == 0 then
level = level + 1
table.remove(levelctrl, 1)
if remEnemy ~= nil then
for k in pairs (remEnemy) do
remEnemy[k] = nil
end
end
end
if won == 1 and love.keyboard.isDown("return") then
menu = 1
game = 0
elseif lost == 1 and love.keyboard.isDown("return") then
menu = 1
game = 0
end
end
end
function love.draw()
love.graphics.setIcon(icon)
love.graphics.setColor(255,255,255,255)
love.graphics.draw(bg, 0, bgx)
love.graphics.draw(bg, 0, bgx-1199)
if menu == 1 then
love.graphics.draw(title)
if menuf == 1 then
love.graphics.draw(message)
end
elseif game == 1 then
for i,v in ipairs(hero.shots) do
love.graphics.draw(bullet, v.x, v.y)
end
if up==1 then
love.graphics.draw(trail, hero.x, hero.y)
end
for i,v in ipairs(levelctrl[1]) do
love.graphics.draw(v.img, v.x, v.y)
end
if lost == 0 then
love.graphics.draw(player, hero.x, hero.y)
else
isdead=1
if boomtimer<8 then
love.graphics.draw(boomp1, hero.x-16, hero.y)
boomtimer = boomtimer + 1
elseif boomtimer<16 then
love.graphics.draw(boomp2, hero.x-16, hero.y)
boomtimer = boomtimer + 1
elseif boomtimer<24 then
love.graphics.draw(boomp3, hero.x-16, hero.y)
boomtimer = boomtimer + 1
elseif boomtimer<32 then
love.graphics.draw(boomp4, hero.x-16, hero.y)
boomtimer = boomtimer + 1
else
love.graphics.draw(looser)
if menuf == 1 then
love.graphics.draw(restart, 0, 20)
end
end
end
for i,v in ipairs(boom) do
if v.t<8 then
love.graphics.draw(boom1, v.x, v.y-8)
v.t = v.t + 1
elseif v.t<16 then
love.graphics.draw(boom2, v.x, v.y-8)
v.t = v.t + 1
elseif v.t<24 then
love.graphics.draw(boom3, v.x, v.y-8)
v.t = v.t + 1
elseif v.t<32 then
love.graphics.draw(boom4, v.x, v.y-8)
v.t = v.t + 1
else
table.remove(boom, i)
end
end
if won == 0 then
if empty(levelctrl) then
won = 1
end
end
if won == 1 then
love.graphics.draw(winner)
if menuf == 1 then
love.graphics.draw(restart, 0, 20)
end
end
end
end
function shoot()
local shot = {}
shot.x = hero.x
shot.y = hero.y - 4
table.insert(hero.shots, shot)
end
function CheckCollision(ax1,ay1,aw,ah, bx1,by1,bw,bh)
local ax2 = ax1 + aw
local ay2 = ay1 + ah
local bx2 = bx1 + bw
local by2 = by1 + bh
return ax1 < bx2 and ax2 > bx1 and ay1 < by2 and ay2 > by1
end
function love.keyreleased(key)
if isdead==0 then
if (key == " ") then
shoot()
end
end
end
function empty(self)
for _, _ in pairs(self) do
return false
end
return true
end
that's all the code and more specifically i get a problem with this:
for i,v in ipairs(levelctrl[1]) do
love.graphics.draw(v.img, v.x, v.y)
end
levelctrl initially has 2 tables inside so after i remove the first one to bump the second one down, I recieve the bad argument error
The problem seems to be around line 200:
if empty(levelctrl[1]) == true and won == 0 then
level = level + 1
table.remove(levelctrl, 1)
Here you remove the table from levelctrl when it's empty and increase the level. Since the table is empty already, can't you just re-use it instead of removing it (causing crashes on next rendering)? Removing the table.remove call should help.
I see
levelctrl = {}
table.insert(levelctrl, fleet1)
So levelctrl = {fleet1}
and some time later when you win,
if empty(levelctrl[1]) == true and won == 0 then
level = level + 1
table.remove(levelctrl, 1)
So levelctrl = {}
won is still 0, and game is still 1.
Then in your draw function
for i,v in ipairs(levelctrl[1]) do
love.graphics.draw(v.img, v.x, v.y)
end
At this point levelctrl = {} and levelctrl[1] = nil.
One solution would be to surround that bit of your draw function with
if not empty(levelctrl)
Also if you were trying to add fleet1 to levelctrl multiple times,
levelctrl = {}
table.insert(levelctrl, fleet1)
table.insert(levelctrl, fleet1)
table.insert(levelctrl, fleet1)
Then levelctrl would have three instances of the same table, and removing something from levelctrl[1] would remove it from fleet1 => all of the levels become empty at the same time.

How to monitor progress from ffmpeg?

I have this ffmpeg video encoder using Rails and I needed to report ffmpeg progress to the user.
How can that be done provided that I am using Linux?
This is the solution I reached:
def execute_ffmpeg(cmd, progress)
logger.debug "Running command #{cmd}"
command = "#{cmd} 2>&1"
progress = nil
frames = nil
fps = 25
ffmpeg = IO.popen(command)
ffmpeg.each("\r") do |line|
if frames.nil? && line =~ /Duration:(\s.?(\d*):(\d*):(\d*\.\d*))/
duration = $2.to_s + ":" + $3.to_s + ":" + $4.to_s
frames = ($4.to_i + ($3.to_i * 60) + ($2.to_i * 60 * 60)) * fps
end
if line =~ /frame=(\s.?(\d*))/
progress = $1.to_i / frames.to_i
print "Progress: #{progress}"
end
end
end
Thanks for the post, I modified it a little. I am using ffmpeg version 0.10.2. Here's my version:
def exec_ffmpeg src_path, dst_path
cmd = "ffmpeg -i \"%s\" -acodec libfaac -ac 2 -ab 128k -vcodec libx264 -threads 0 \"%s\" 2>&1" %
[src_path, dst_path]
puts "%s" % cmd.gsub(/2\>\&1$/,'')
puts "press 'q' to quit"
progress = nil
dur_secs = nil
frame_rate = nil
frames = 0
dur_str = '00:00:00.000'
ostr = ''
ffmpeg = IO.popen(cmd)
ffmpeg.each("\r") do |line|
if dur_secs == nil && line =~ /Duration:\s*(\d*):(\d*):(\d*\.\d*)/
dur_str = $1.to_s + ":" + $2.to_s + ":" + $3.to_s
dur_secs = ($3.to_f + ($2.to_i * 60).to_f + ($1.to_i * 360).to_f)
puts "Video Duration:" + dur_str
end
if frame_rate == nil && line =~ /Stream.+\, (\d+\.{0,1}\d{0,3}) fps\,/
frame_rate = $1.to_f
frames = dur_secs * frame_rate
puts "Total Frames: %i" % frames.to_i
puts "Frame Rate: %.3f fps" % frame_rate
end
if line =~ /frame=(\s.?(\d*))/
cframe = $1.to_i
csecs = 0
if line =~ /time=\s*(\d*):(\d*):(\d*\.\d*)/
csecs = ($3.to_f + ($2.to_i * 60).to_f + ($1.to_i * 360).to_f)
csecs_str = $1.to_s + ":" + $2.to_s + ":" + $3.to_s
elsif line =~ /time=\s*(\d*\.\d*)/
csecs $1.to_f
t = Time.at(csecs).gmtime
csecs_str = "%0.2i:%0.2i:%0.2i.%3i" % [t.hour, t.min, t.sec, t.nsec]
end
if line =~ /fps=\s*(\d*)/
cfps = $1.to_i
else
cfps = 0
end
if line =~ /bitrate=\s*(\d*\.\d*kbits)/
br = $1
else
br = "???"
end
ostr = " %3.2f%% ( %s ) #frame:%i fps:%i bitrate:%s" %
[((csecs/dur_secs)*100), csecs_str, cframe, cfps, br]
print ostr + ("\b" * (ostr.length + 4))
end
end
print "\n"
end

Resources