Решение на Трета задача от Ангел Венчев

Обратно към всички решения

Към профила на Ангел Венчев

Резултати

  • 5 точки от тестове
  • 0 бонус точки
  • 5 точки общо
  • 62 успешни тест(а)
  • 7 неуспешни тест(а)

Код

module Graphics
class Canvas
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
@canvas = 1.upto(height).map { 1.upto(width).map { false } }
end
def set_pixel(x, y)
@canvas[y][x] = true
end
def pixel_at?(x, y)
@canvas[y][x]
end
def draw(shape)
shape.points.each { |point| set_pixel(point.x, point.y) }
end
def render_as(renderer)
renderer.render(self)
end
def render_image(pixels, delimiter)
@canvas.map do |pixel_row|
pixel_row.map do |pixel|
pixels[pixel]
end.join
end.join(delimiter)
end
end
class Point
include Comparable
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
alias_method :eq?, :==
def ==(other)
@x == other.x and @y == other.y
end
def <=>(other)
# @x == other.first.x ? @y <=> other.first.y : @x <=> other.first.x
comparison = self.x <=> other.x
if comparison == 0
return self.y <=> other.y
else
return comparison
end
end
def hash
@x.hash - @y.hash
end
def points
[self]
end
end
class Line
attr_reader :from, :to
def initialize(first_point, second_point)
@from, @to = [first_point,second_point].sort
end
alias_method :eq?, :==
def ==(other)
[@from, @to].sort == [other.from, other.to].sort
end
def hash
@from.hash + @to.hash
end
def points
line_points(*bresenham_points)
end
private
def bresenham_points()
from_x, from_y, to_x, to_y = @from.x, @from.y, @to.x, @to.y
if steep_line?
from_x, from_y, to_x, to_y = from_y, from_x, to_y, to_x
end
if from_x > to_x
from_x, to_x, from_y, to_y = to_x, from_x, to_y, from_y
end
[Point.new(from_x, from_y), Point.new(to_x, to_y)]
end
def line_points(from_point, to_point)
error = (delta(from_point.x, to_point.x) / 2).to_i
y = from_point.y
ordinate_step = from_point.y < to_point.y ? 1 : -1
generate_points(error, y, ordinate_step, from_point, to_point)
end
def generate_points(error, y, ordinate_step, from_point, to_point)
(from_point.x..to_point.x).each_with_object([]) do |x, points|
points << (steep_line? ? Point.new(y, x) : Point.new(x, y))
error -= delta(from_point.y, to_point.y)
if error < 0
y += ordinate_step
error += delta(from_point.x, to_point.x)
end
end
end
def steep_line?
(@to.y-@from.y).abs > (@to.x-@from.x).abs
end
def delta(from_coordinate, to_coordinate)
(from_coordinate - to_coordinate).abs
end
end
class Rectangle
attr_reader :left, :right, :top_left, :top_right, :bottom_left, :bottom_right
def initialize(first, second)
@left = Point.new([first.x,second.x].min,[first.y,second.y].min)
@right = Point.new([first.x,second.x].max,[first.y,second.y].max)
@top_left, @bottom_right = @left, @right
@bottom_left = Point.new(@left.x,@right.y)
@top_right = Point.new(@right.x,@left.y)
end
alias_method :eq?, :==
def ==(other)
@left == other.left and @right == other.right
end
def hash
@left.hash + @right.hash
end
def points
[
[@top_left,@top_right],
[@top_right,@bottom_right],
[@bottom_right,@bottom_left],
[@bottom_left,@top_left]
].map { |a, b| Line.new(a,b).points }.flatten
end
end
module Renderers
class Ascii
def self.render(canvas)
hash = {true => '@', false => '-'}
canvas.render_image(hash, "\n")
end
end
class Html
LAYOUT_HEADER = %(<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title>
<style type="text/css"> .canvas {font-size: 1px;line-height: 1px;}
.canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;}
.canvas i {background-color: #eee;}
.canvas b {background-color: #333;}
</style> </head> <body> <div class="canvas">)
LAYOUT_FOOTER = %(</div> </body> </html>)
def self.render(canvas)
hash = {true => "<b></b>", false => "<i></i>"}
self.new.add_layout canvas.render_image(hash, "<br />")
end
def add_layout(drawing)
LAYOUT_HEADER + drawing + LAYOUT_FOOTER
end
end
end
end

Лог от изпълнението

..........F..............FF.......F..............F......F....F.......

Failures:

  1) Graphics Canvas drawing of shapes and rasterization of points works for multiple ones
     Failure/Error: canvas.set_pixel 4, 4
     NoMethodError:
       undefined method `[]=' for nil:NilClass
     # /tmp/d20131223-4637-12dk80m/solution.rb:12:in `set_pixel'
     # /tmp/d20131223-4637-12dk80m/spec.rb:57:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  2) Graphics Renderers Html renders a grid of the size of the canvas
     Failure/Error: lines.size.should eq 3
       
       expected: 3
            got: 1
       
       (compared using ==)
     # /tmp/d20131223-4637-12dk80m/spec.rb:283:in `block (4 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  3) Graphics Renderers Html renders simple canvases
     Failure/Error: html_rendering_of(canvas).should eq [
       
       expected: "<i></i><i></i><i></i><i></i><br><i></i><b></b><i></i><i></i><br><i></i><b></b><i></i><i></i>"
            got: "<i></i><i></i><i></i><i></i><br/><i></i><b></b><i></i><i></i><br/><i></i><b></b><i></i><i></i>"
       
       (compared using ==)
     # /tmp/d20131223-4637-12dk80m/spec.rb:291:in `block (4 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  4) Graphics shapes Point comparison for equality works for eql? as well
     Failure/Error: a1.should eql a2
       
       expected: #<Graphics::Point:0xb887b2a8 @x=4, @y=5>
            got: #<Graphics::Point:0xb887b2e4 @x=4, @y=5>
       
       (compared using eql?)
       
       Diff:
       @@ -1,2 +1,2 @@
       -#<Graphics::Point:0xb887b2a8 @x=4, @y=5>
       +#<Graphics::Point:0xb887b2e4 @x=4, @y=5>
     # /tmp/d20131223-4637-12dk80m/spec.rb:356:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  5) Graphics shapes Line comparison for equality works with eql? as well
     Failure/Error: a.should eql b
       
       expected: #<Graphics::Line:0xb884b724 @from=#<Graphics::Point:0xb884b7c4 @x=1, @y=1>, @to=#<Graphics::Point:0xb884b74c @x=10, @y=14>>
            got: #<Graphics::Line:0xb884b88c @from=#<Graphics::Point:0xb884b8f0 @x=1, @y=1>, @to=#<Graphics::Point:0xb884b8b4 @x=10, @y=14>>
       
       (compared using eql?)
       
       Diff:
       @@ -1,4 +1,4 @@
       -#<Graphics::Line:0xb884b724
       - @from=#<Graphics::Point:0xb884b7c4 @x=1, @y=1>,
       - @to=#<Graphics::Point:0xb884b74c @x=10, @y=14>>
       +#<Graphics::Line:0xb884b88c
       + @from=#<Graphics::Point:0xb884b8f0 @x=1, @y=1>,
       + @to=#<Graphics::Point:0xb884b8b4 @x=10, @y=14>>
     # /tmp/d20131223-4637-12dk80m/spec.rb:457:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  6) Graphics shapes Rectangle initialization puts the leftmost point in its left field
     Failure/Error: rect.left.y.should eq 1
       
       expected: 1
            got: 0
       
       (compared using ==)
     # /tmp/d20131223-4637-12dk80m/spec.rb:508:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

  7) Graphics shapes Rectangle comparison for equality works with eql? as well
     Failure/Error: a.should eql b
       
       expected: #<Graphics::Rectangle:0xb88028bc @left=#<Graphics::Point:0xb8802830 @x=1, @y=1>, @right=#<Graphics::Point:0xb88026a0 @x=10, @y=14>, @bottom_right=#<Graphics::Point:0xb88026a0 @x=10, @y=14>, @top_left=#<Graphics::Point:0xb8802830 @x=1, @y=1>, @bottom_left=#<Graphics::Point:0xb880268c @x=1, @y=14>, @top_right=#<Graphics::Point:0xb8802650 @x=10, @y=1>>
            got: #<Graphics::Rectangle:0xb8802c68 @left=#<Graphics::Point:0xb8802b14 @x=1, @y=1>, @right=#<Graphics::Point:0xb880295c @x=10, @y=14>, @bottom_right=#<Graphics::Point:0xb880295c @x=10, @y=14>, @top_left=#<Graphics::Point:0xb8802b14 @x=1, @y=1>, @bottom_left=#<Graphics::Point:0xb8802948 @x=1, @y=14>, @top_right=#<Graphics::Point:0xb8802934 @x=10, @y=1>>
       
       (compared using eql?)
       
       Diff:
       @@ -1,8 +1,8 @@
       -#<Graphics::Rectangle:0xb88028bc
       - @bottom_left=#<Graphics::Point:0xb880268c @x=1, @y=14>,
       - @bottom_right=#<Graphics::Point:0xb88026a0 @x=10, @y=14>,
       - @left=#<Graphics::Point:0xb8802830 @x=1, @y=1>,
       - @right=#<Graphics::Point:0xb88026a0 @x=10, @y=14>,
       - @top_left=#<Graphics::Point:0xb8802830 @x=1, @y=1>,
       - @top_right=#<Graphics::Point:0xb8802650 @x=10, @y=1>>
       +#<Graphics::Rectangle:0xb8802c68
       + @bottom_left=#<Graphics::Point:0xb8802948 @x=1, @y=14>,
       + @bottom_right=#<Graphics::Point:0xb880295c @x=10, @y=14>,
       + @left=#<Graphics::Point:0xb8802b14 @x=1, @y=1>,
       + @right=#<Graphics::Point:0xb880295c @x=10, @y=14>,
       + @top_left=#<Graphics::Point:0xb8802b14 @x=1, @y=1>,
       + @top_right=#<Graphics::Point:0xb8802934 @x=10, @y=1>>
     # /tmp/d20131223-4637-12dk80m/spec.rb:548:in `block (5 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (3 levels) in <top (required)>'
     # ./lib/language/ruby/run_with_timeout.rb:5:in `block (2 levels) in <top (required)>'

Finished in 0.09333 seconds
69 examples, 7 failures

Failed examples:

rspec /tmp/d20131223-4637-12dk80m/spec.rb:51 # Graphics Canvas drawing of shapes and rasterization of points works for multiple ones
rspec /tmp/d20131223-4637-12dk80m/spec.rb:280 # Graphics Renderers Html renders a grid of the size of the canvas
rspec /tmp/d20131223-4637-12dk80m/spec.rb:287 # Graphics Renderers Html renders simple canvases
rspec /tmp/d20131223-4637-12dk80m/spec.rb:355 # Graphics shapes Point comparison for equality works for eql? as well
rspec /tmp/d20131223-4637-12dk80m/spec.rb:452 # Graphics shapes Line comparison for equality works with eql? as well
rspec /tmp/d20131223-4637-12dk80m/spec.rb:504 # Graphics shapes Rectangle initialization puts the leftmost point in its left field
rspec /tmp/d20131223-4637-12dk80m/spec.rb:543 # Graphics shapes Rectangle comparison for equality works with eql? as well

История (4 версии и 3 коментара)

Ангел обнови решението на 20.12.2013 18:02 (преди почти 11 години)

+module Graphics
+ class Canvas
+ attr_reader :width,:height
+
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @canvas = 1.upto(height).map { |_| 1.upto(width).map { |_| false } }
+ end
+
+ def set_pixel(x, y)
+ @canvas[y][x] = true
+ end
+
+ def pixel_at?(x, y)
+ @canvas[y][x]
+ end
+
+ def draw(shape)
+ shape.points.each { |point| set_pixel(point.x,point.y) }
+ end
+
+ def render_as(renderer)
+ if renderer == Graphics::Renderers::Ascii
+ render_image({true => '@', false => '-'},"\n")
+ else renderer == Graphics::Renderers::Html
+ add_html render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
+ end
+ end
+
+ private
+
+ def render_image(pixels, delimiter)
+ @canvas.each_with_object("") do |row, rendered|
+ row.each { |element| rendered << pixels[element] }
+ rendered << delimiter
+ end.chomp
+ end
+
+ def add_html(drawing)
+ "<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title> " +
+ "<style type=\"text/css\"> .canvas {font-size: 1px;line-height: 1px;} "+
+ ".canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;} " +
+ ".canvas i {background-color: #eee;} " +
+ ".canvas b {background-color: #333;} " +
+ "</style> </head> <body> <div class=\"canvas\">#{drawing}</div> </body> </html>"
+ end
+ end
+
+ class Point
+ include Comparable
+
+ attr_reader :x,:y
+
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ x == other.x and y == other.y
+ end
+
+ def <=>(other)
+ x == other.x ? y <=> other.y : x <=> other.x
+ end
+
+ def points
+ [self]
+ end
+ end
+
+ class Line
+ attr_reader :from,:to
+
+ def initialize(first_point, second_point)
+ @from, @to = [first_point,second_point].sort
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ [@from,@to].sort == [other.from,other.to].sort
+ end
+
+ def points
+ bresenham_points = prepare_coordinates()
+ get_line_points(*bresenham_points)
+ end
+
+ private
+
+ def prepare_coordinates()
+ from_x,from_y,to_x,to_y = @from.x,@from.y,@to.x,@to.y
+ if steep_line?
+ from_x,from_y,to_x,to_y = from_y,from_x,to_y,to_x
+ end
+ if from_x > to_x
+ from_x,to_x,from_y,to_y = to_x,from_x,to_y,from_y
+ end
+ [Point.new(from_x,from_y),Point.new(to_x,to_y)]
+ end
+
+ def get_line_points(from_point, to_point)
+ error = (delta(from_point.x,to_point.x) / 2).to_i
+ y = from_point.y
+ ordinate_step = from_point.y < to_point.y ? 1 : -1
+ generate_points(error, y, ordinate_step,from_point, to_point)
+ end
+
+ def generate_points(error, y, ordinate_step, from_point, to_point)
+ (from_point.x..to_point.x).each_with_object([]) do |x,points|
+ points << (steep_line? ? Point.new(y,x) : Point.new(x,y))
+ error -= delta(from_point.y,to_point.y)
+ if error < 0
+ y += ordinate_step
+ error += delta(from_point.x,to_point.x)
+ end
+ end
+ end
+
+ def steep_line?
+ ((@to.y-@from.y).abs) > ((@to.x-@from.x).abs)
+ end
+
+ def delta(from_coordinate, to_coordinate)
+ (from_coordinate - to_coordinate).abs
+ end
+ end
+
+ class Rectangle
+ attr_reader :left,:right,:top_left,:top_right,:bottom_left,:bottom_right
+
+ def initialize(first_point, second_point)
+ @left, @right = [first_point,second_point].sort
+ @top_left, @bottom_right = @left, @right
+ @bottom_left = Point.new(@left.x,@right.y)
+ @top_right = Point.new(@right.x,@left.y)
+ end
+
+ alias_method :==, :eql?
+ def ==(other)
+ @left == other.left and @right == other.right
+ end
+
+ def points
+ [Line.new(@top_left,@top_right).points,
+ Line.new(@top_right,@bottom_right).points,
+ Line.new(@bottom_right,@bottom_left).points,
+ Line.new(@bottom_left,@top_left).points].flatten
+ end
+ end
+
+ module Renderers
+ class Ascii
+
+ end
+
+ class Html
+
+ end
+ end
+end
+
+module Graphics
+ canvas = Canvas.new 30, 30
+
+ # Door frame and window
+ canvas.draw Rectangle.new(Point.new(3, 3), Point.new(18, 12))
+ canvas.draw Rectangle.new(Point.new(1, 1), Point.new(20, 28))
+
+ # Door knob
+ canvas.draw Line.new(Point.new(4, 15), Point.new(7, 15))
+ canvas.draw Point.new(4, 16)
+
+ # Big "R"
+ canvas.draw Line.new(Point.new(8, 5), Point.new(8, 10))
+ canvas.draw Line.new(Point.new(9, 5), Point.new(12, 5))
+ canvas.draw Line.new(Point.new(9, 7), Point.new(12, 7))
+ canvas.draw Point.new(13, 6)
+ canvas.draw Line.new(Point.new(12, 8), Point.new(13, 10))
+
+ puts canvas.render_as(Renderers::Ascii)
+end

Ангел обнови решението на 20.12.2013 18:06 (преди почти 11 години)

module Graphics
class Canvas
attr_reader :width,:height
def initialize(width, height)
@width = width
@height = height
@canvas = 1.upto(height).map { |_| 1.upto(width).map { |_| false } }
end
def set_pixel(x, y)
@canvas[y][x] = true
end
def pixel_at?(x, y)
@canvas[y][x]
end
def draw(shape)
shape.points.each { |point| set_pixel(point.x,point.y) }
end
def render_as(renderer)
- if renderer == Graphics::Renderers::Ascii
- render_image({true => '@', false => '-'},"\n")
- else renderer == Graphics::Renderers::Html
- add_html render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
- end
+ renderer.render(self)
end
- private
-
def render_image(pixels, delimiter)
@canvas.each_with_object("") do |row, rendered|
row.each { |element| rendered << pixels[element] }
rendered << delimiter
end.chomp
end
- def add_html(drawing)
- "<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title> " +
- "<style type=\"text/css\"> .canvas {font-size: 1px;line-height: 1px;} "+
- ".canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;} " +
- ".canvas i {background-color: #eee;} " +
- ".canvas b {background-color: #333;} " +
- "</style> </head> <body> <div class=\"canvas\">#{drawing}</div> </body> </html>"
- end
end
class Point
include Comparable
attr_reader :x,:y
def initialize(x, y)
@x = x
@y = y
end
alias_method :==, :eql?
def ==(other)
x == other.x and y == other.y
end
def <=>(other)
x == other.x ? y <=> other.y : x <=> other.x
end
def points
[self]
end
end
class Line
attr_reader :from,:to
def initialize(first_point, second_point)
@from, @to = [first_point,second_point].sort
end
alias_method :==, :eql?
def ==(other)
[@from,@to].sort == [other.from,other.to].sort
end
def points
bresenham_points = prepare_coordinates()
get_line_points(*bresenham_points)
end
private
def prepare_coordinates()
from_x,from_y,to_x,to_y = @from.x,@from.y,@to.x,@to.y
if steep_line?
from_x,from_y,to_x,to_y = from_y,from_x,to_y,to_x
end
if from_x > to_x
from_x,to_x,from_y,to_y = to_x,from_x,to_y,from_y
end
[Point.new(from_x,from_y),Point.new(to_x,to_y)]
end
def get_line_points(from_point, to_point)
error = (delta(from_point.x,to_point.x) / 2).to_i
y = from_point.y
ordinate_step = from_point.y < to_point.y ? 1 : -1
generate_points(error, y, ordinate_step,from_point, to_point)
end
def generate_points(error, y, ordinate_step, from_point, to_point)
(from_point.x..to_point.x).each_with_object([]) do |x,points|
points << (steep_line? ? Point.new(y,x) : Point.new(x,y))
error -= delta(from_point.y,to_point.y)
if error < 0
y += ordinate_step
error += delta(from_point.x,to_point.x)
end
end
end
def steep_line?
((@to.y-@from.y).abs) > ((@to.x-@from.x).abs)
end
def delta(from_coordinate, to_coordinate)
(from_coordinate - to_coordinate).abs
end
end
class Rectangle
attr_reader :left,:right,:top_left,:top_right,:bottom_left,:bottom_right
def initialize(first_point, second_point)
@left, @right = [first_point,second_point].sort
@top_left, @bottom_right = @left, @right
@bottom_left = Point.new(@left.x,@right.y)
@top_right = Point.new(@right.x,@left.y)
end
alias_method :==, :eql?
def ==(other)
@left == other.left and @right == other.right
end
def points
[Line.new(@top_left,@top_right).points,
Line.new(@top_right,@bottom_right).points,
Line.new(@bottom_right,@bottom_left).points,
Line.new(@bottom_left,@top_left).points].flatten
end
end
module Renderers
class Ascii
-
+ def self.render(canvas)
+ canvas.render_image({true => '@', false => '-'},"\n")
+ end
end
class Html
+ def self.render(canvas)
+ add_html canvas.render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
+ end
+ end
+ def add_html(drawing)
+ "<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title> " +
+ "<style type=\"text/css\"> .canvas {font-size: 1px;line-height: 1px;} "+
+ ".canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;} " +
+ ".canvas i {background-color: #eee;} " +
+ ".canvas b {background-color: #333;} " +
+ "</style> </head> <body> <div class=\"canvas\">#{drawing}</div> </body> </html>"
end
end
end
module Graphics
canvas = Canvas.new 30, 30
# Door frame and window
canvas.draw Rectangle.new(Point.new(3, 3), Point.new(18, 12))
canvas.draw Rectangle.new(Point.new(1, 1), Point.new(20, 28))
# Door knob
canvas.draw Line.new(Point.new(4, 15), Point.new(7, 15))
canvas.draw Point.new(4, 16)
# Big "R"
canvas.draw Line.new(Point.new(8, 5), Point.new(8, 10))
canvas.draw Line.new(Point.new(9, 5), Point.new(12, 5))
canvas.draw Line.new(Point.new(9, 7), Point.new(12, 7))
canvas.draw Point.new(13, 6)
canvas.draw Line.new(Point.new(12, 8), Point.new(13, 10))
puts canvas.render_as(Renderers::Ascii)
end

Ангел обнови решението на 20.12.2013 18:06 (преди почти 11 години)

module Graphics
class Canvas
attr_reader :width,:height
def initialize(width, height)
@width = width
@height = height
@canvas = 1.upto(height).map { |_| 1.upto(width).map { |_| false } }
end
def set_pixel(x, y)
@canvas[y][x] = true
end
def pixel_at?(x, y)
@canvas[y][x]
end
def draw(shape)
shape.points.each { |point| set_pixel(point.x,point.y) }
end
def render_as(renderer)
renderer.render(self)
end
def render_image(pixels, delimiter)
@canvas.each_with_object("") do |row, rendered|
row.each { |element| rendered << pixels[element] }
rendered << delimiter
end.chomp
end
end
class Point
include Comparable
attr_reader :x,:y
def initialize(x, y)
@x = x
@y = y
end
alias_method :==, :eql?
def ==(other)
x == other.x and y == other.y
end
def <=>(other)
x == other.x ? y <=> other.y : x <=> other.x
end
def points
[self]
end
end
class Line
attr_reader :from,:to
def initialize(first_point, second_point)
@from, @to = [first_point,second_point].sort
end
alias_method :==, :eql?
def ==(other)
[@from,@to].sort == [other.from,other.to].sort
end
def points
bresenham_points = prepare_coordinates()
get_line_points(*bresenham_points)
end
private
def prepare_coordinates()
from_x,from_y,to_x,to_y = @from.x,@from.y,@to.x,@to.y
if steep_line?
from_x,from_y,to_x,to_y = from_y,from_x,to_y,to_x
end
if from_x > to_x
from_x,to_x,from_y,to_y = to_x,from_x,to_y,from_y
end
[Point.new(from_x,from_y),Point.new(to_x,to_y)]
end
def get_line_points(from_point, to_point)
error = (delta(from_point.x,to_point.x) / 2).to_i
y = from_point.y
ordinate_step = from_point.y < to_point.y ? 1 : -1
generate_points(error, y, ordinate_step,from_point, to_point)
end
def generate_points(error, y, ordinate_step, from_point, to_point)
(from_point.x..to_point.x).each_with_object([]) do |x,points|
points << (steep_line? ? Point.new(y,x) : Point.new(x,y))
error -= delta(from_point.y,to_point.y)
if error < 0
y += ordinate_step
error += delta(from_point.x,to_point.x)
end
end
end
def steep_line?
((@to.y-@from.y).abs) > ((@to.x-@from.x).abs)
end
def delta(from_coordinate, to_coordinate)
(from_coordinate - to_coordinate).abs
end
end
class Rectangle
attr_reader :left,:right,:top_left,:top_right,:bottom_left,:bottom_right
def initialize(first_point, second_point)
@left, @right = [first_point,second_point].sort
@top_left, @bottom_right = @left, @right
@bottom_left = Point.new(@left.x,@right.y)
@top_right = Point.new(@right.x,@left.y)
end
alias_method :==, :eql?
def ==(other)
@left == other.left and @right == other.right
end
def points
[Line.new(@top_left,@top_right).points,
Line.new(@top_right,@bottom_right).points,
Line.new(@bottom_right,@bottom_left).points,
Line.new(@bottom_left,@top_left).points].flatten
end
end
module Renderers
class Ascii
def self.render(canvas)
canvas.render_image({true => '@', false => '-'},"\n")
end
end
class Html
def self.render(canvas)
add_html canvas.render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
end
end
def add_html(drawing)
"<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title> " +
"<style type=\"text/css\"> .canvas {font-size: 1px;line-height: 1px;} "+
".canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;} " +
".canvas i {background-color: #eee;} " +
".canvas b {background-color: #333;} " +
"</style> </head> <body> <div class=\"canvas\">#{drawing}</div> </body> </html>"
end
end
-end
-
+end
-module Graphics
- canvas = Canvas.new 30, 30
-
- # Door frame and window
- canvas.draw Rectangle.new(Point.new(3, 3), Point.new(18, 12))
- canvas.draw Rectangle.new(Point.new(1, 1), Point.new(20, 28))
-
- # Door knob
- canvas.draw Line.new(Point.new(4, 15), Point.new(7, 15))
- canvas.draw Point.new(4, 16)
-
- # Big "R"
- canvas.draw Line.new(Point.new(8, 5), Point.new(8, 10))
- canvas.draw Line.new(Point.new(9, 5), Point.new(12, 5))
- canvas.draw Line.new(Point.new(9, 7), Point.new(12, 7))
- canvas.draw Point.new(13, 6)
- canvas.draw Line.new(Point.new(12, 8), Point.new(13, 10))
-
- puts canvas.render_as(Renderers::Ascii)
-end

Бележки:

  • Относно render_image, помисли кой трбява да носи отговорността за "рендериране" на пано. Пано или рендерер? P.S. Ако беше private в пано, пак може да си го викаш без send, не виждам какво общо има send тук.
  • Ред 8, понеже това са блокове, няма нужда да им дефинираш аргументи, ако не ти трябват стойностите им; _ има смисъл само ако няма как да не сложиш променлива там, а тук има как да не сложиш.
  • Помисли дали няма друг по-оптимален начин в Ruby за реализация вътрешното представяне на пано от двумерен масив. Ruby не е C и има по-удобни структури от данни :)
  • Също така, pixel_at? може да връща нещо, което се оценява като истина или лъжа; не е задължително да е true/false.
  • Пробвай да направиш render_image с map и join.
  • В условието нямаме специални изисквания да се дефинира метод <=>. Може само с равенство да минеш.
  • Погледни пак дали си направил правилно синонимите на методите за равенство.
  • Не виждам да си имплементирал hash методите.
  • Слага се интервал след запетая на ред 61, ред 64, ред 69...
  • Няма нужда от скоби на ред 73, както и от тази променлива на 74. Спокойно може да е на един ред, нищо не се печели, като е на два реда. Помисли кое име от двете е по-подходящо. Скобите се изпускат и при дефиниция на метод, когато нямаме аргументи.
  • Оставяй празни редове тук-там за четимост, те не се броят в редовете в метод. Например, аз бих сложил празни редове м/у 83 и 84, както и м/у 86 и 87.
  • В Ruby няма практика да има думата get_ в името на методите; тази дума просто генерира шум и не носи информация.
  • Трудно ми е да схвана добре какво прави всеки един от методите, на които си разбил Брезенхам. Поработи над имената им и над това разбиване още.
  • Няма нужда от най-външните скоби на ред 109.
  • Не съм сигурен, че смяташ правилно ъглите на правоъгълника. Провери.
  • В Rectangle#points бих подредил нещата така:

      [
        [@top_left, @top_right],
        [.....................],
        [.....................],
        [.....................],
      ].map { |a, b| Line.new(a, b).points }.flatten
    

    Или, ако това ти се струва по-неясно, поне подреди малко тези Line.new в списъка.

  • Методът Renderers#add_html има няколко проблема. Първо, щеше да е по-добре да се казва layout, add_layout, или нещо такова. Второ, не му е мястото в този модул, а и не мисля, че така ще работи. Ако го има, трябва да е в Html. Четвърто и маловажно, този HTML код е удачно да е изведен в константа, а не да се дефинира имплицитно всеки път и да се garbage collect-ва при всяко извикване на метода add_html. За дефиницията на този низ може да ползваш други ограничители, а не двойни кавички, за да не трябва да ги екранираш вътре в низа. Например, %(foo), или HEREDOC, или да ползваш String#% и низ с единични кавички, за да нямаш интерполация.

Ангел обнови решението на 22.12.2013 22:16 (преди почти 11 години)

module Graphics
class Canvas
- attr_reader :width,:height
+ attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
- @canvas = 1.upto(height).map { |_| 1.upto(width).map { |_| false } }
+ @canvas = 1.upto(height).map { 1.upto(width).map { false } }
end
def set_pixel(x, y)
@canvas[y][x] = true
end
def pixel_at?(x, y)
@canvas[y][x]
end
def draw(shape)
- shape.points.each { |point| set_pixel(point.x,point.y) }
+ shape.points.each { |point| set_pixel(point.x, point.y) }
end
def render_as(renderer)
renderer.render(self)
end
def render_image(pixels, delimiter)
- @canvas.each_with_object("") do |row, rendered|
- row.each { |element| rendered << pixels[element] }
- rendered << delimiter
- end.chomp
+ @canvas.map do |pixel_row|
+ pixel_row.map do |pixel|
+ pixels[pixel]
+ end.join
+ end.join(delimiter)
end
end
class Point
include Comparable
- attr_reader :x,:y
+ attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
- alias_method :==, :eql?
+ alias_method :eq?, :==
def ==(other)
- x == other.x and y == other.y
+ @x == other.x and @y == other.y
end
def <=>(other)
- x == other.x ? y <=> other.y : x <=> other.x
+ # @x == other.first.x ? @y <=> other.first.y : @x <=> other.first.x
+
+ comparison = self.x <=> other.x
+
+ if comparison == 0
+ return self.y <=> other.y
+ else
+ return comparison
+ end
end
+ def hash
+ @x.hash - @y.hash
+ end
+
def points
[self]
end
end
class Line
- attr_reader :from,:to
+ attr_reader :from, :to
def initialize(first_point, second_point)
@from, @to = [first_point,second_point].sort
end
- alias_method :==, :eql?
+ alias_method :eq?, :==
def ==(other)
- [@from,@to].sort == [other.from,other.to].sort
+ [@from, @to].sort == [other.from, other.to].sort
end
+ def hash
+ @from.hash + @to.hash
+ end
+
def points
- bresenham_points = prepare_coordinates()
- get_line_points(*bresenham_points)
+ line_points(*bresenham_points)
end
private
- def prepare_coordinates()
- from_x,from_y,to_x,to_y = @from.x,@from.y,@to.x,@to.y
+ def bresenham_points()
+ from_x, from_y, to_x, to_y = @from.x, @from.y, @to.x, @to.y
if steep_line?
- from_x,from_y,to_x,to_y = from_y,from_x,to_y,to_x
+ from_x, from_y, to_x, to_y = from_y, from_x, to_y, to_x
end
+
if from_x > to_x
- from_x,to_x,from_y,to_y = to_x,from_x,to_y,from_y
+ from_x, to_x, from_y, to_y = to_x, from_x, to_y, from_y
end
- [Point.new(from_x,from_y),Point.new(to_x,to_y)]
+
+ [Point.new(from_x, from_y), Point.new(to_x, to_y)]
end
- def get_line_points(from_point, to_point)
- error = (delta(from_point.x,to_point.x) / 2).to_i
+ def line_points(from_point, to_point)
+ error = (delta(from_point.x, to_point.x) / 2).to_i
y = from_point.y
ordinate_step = from_point.y < to_point.y ? 1 : -1
- generate_points(error, y, ordinate_step,from_point, to_point)
+ generate_points(error, y, ordinate_step, from_point, to_point)
end
def generate_points(error, y, ordinate_step, from_point, to_point)
- (from_point.x..to_point.x).each_with_object([]) do |x,points|
- points << (steep_line? ? Point.new(y,x) : Point.new(x,y))
- error -= delta(from_point.y,to_point.y)
+ (from_point.x..to_point.x).each_with_object([]) do |x, points|
+ points << (steep_line? ? Point.new(y, x) : Point.new(x, y))
+ error -= delta(from_point.y, to_point.y)
if error < 0
y += ordinate_step
- error += delta(from_point.x,to_point.x)
+ error += delta(from_point.x, to_point.x)
end
end
end
def steep_line?
- ((@to.y-@from.y).abs) > ((@to.x-@from.x).abs)
+ (@to.y-@from.y).abs > (@to.x-@from.x).abs
end
def delta(from_coordinate, to_coordinate)
(from_coordinate - to_coordinate).abs
end
end
class Rectangle
- attr_reader :left,:right,:top_left,:top_right,:bottom_left,:bottom_right
+ attr_reader :left, :right, :top_left, :top_right, :bottom_left, :bottom_right
- def initialize(first_point, second_point)
- @left, @right = [first_point,second_point].sort
+ def initialize(first, second)
+ @left = Point.new([first.x,second.x].min,[first.y,second.y].min)
+ @right = Point.new([first.x,second.x].max,[first.y,second.y].max)
@top_left, @bottom_right = @left, @right
@bottom_left = Point.new(@left.x,@right.y)
@top_right = Point.new(@right.x,@left.y)
end
- alias_method :==, :eql?
+ alias_method :eq?, :==
def ==(other)
@left == other.left and @right == other.right
end
+ def hash
+ @left.hash + @right.hash
+ end
+
def points
- [Line.new(@top_left,@top_right).points,
- Line.new(@top_right,@bottom_right).points,
- Line.new(@bottom_right,@bottom_left).points,
- Line.new(@bottom_left,@top_left).points].flatten
+ [
+ [@top_left,@top_right],
+ [@top_right,@bottom_right],
+ [@bottom_right,@bottom_left],
+ [@bottom_left,@top_left]
+ ].map { |a, b| Line.new(a,b).points }.flatten
end
end
module Renderers
class Ascii
def self.render(canvas)
- canvas.render_image({true => '@', false => '-'},"\n")
+ hash = {true => '@', false => '-'}
+ canvas.render_image(hash, "\n")
end
end
class Html
+ LAYOUT_HEADER = %(<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title>
+ <style type="text/css"> .canvas {font-size: 1px;line-height: 1px;}
+ .canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;}
+ .canvas i {background-color: #eee;}
+ .canvas b {background-color: #333;}
+ </style> </head> <body> <div class="canvas">)
+
+ LAYOUT_FOOTER = %(</div> </body> </html>)
+
def self.render(canvas)
- add_html canvas.render_image({true => "<b></b>", false => "<i></i>"}, "<br />")
+ hash = {true => "<b></b>", false => "<i></i>"}
+ self.new.add_layout canvas.render_image(hash, "<br />")
end
- end
- def add_html(drawing)
- "<!DOCTYPE html> <html> <head> <title> Rendered Canvas </title> " +
- "<style type=\"text/css\"> .canvas {font-size: 1px;line-height: 1px;} "+
- ".canvas * {display: inline-block;width: 10px;height: 10px;border-radius: 5px;} " +
- ".canvas i {background-color: #eee;} " +
- ".canvas b {background-color: #333;} " +
- "</style> </head> <body> <div class=\"canvas\">#{drawing}</div> </body> </html>"
+ def add_layout(drawing)
+ LAYOUT_HEADER + drawing + LAYOUT_FOOTER
+ end
end
+
end
end
  • Въпреки, че съм ползвал хеш в моето решение за представяне на пано, ми допада идеята с map-овете при теб. Все пак, смятам, че хешът е по-удобен в случая.
  • Ред 34 не трябва да го има. Същото за ред 192.
  • Не оставяй такива коментари в кода си.
  • Нямаш нужда от return-ите на ред 58 и 60.
  • Трябва да има интервали около - на ред 127.
  • Името hash на ред 184 не е добро.

Като цяло, решението ти е прилично, но има още места, които могат да се подобрят. Виж моето решение за справка.