Решение на Трета задача от Борислава Аладжова

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

Към профила на Борислава Аладжова

Резултати

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

Код

module Graphics
module Renderers
class Ascii
def self.visualize(canvas)
(canvas.send :canvas).map(&:join).join "\n"
end
end
class Html
HTML_BEGINNING = "<!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\">\n"
HTML_ENDING = " </div>
</body>
</html>"
def self.visualize(canvas)
html = (canvas.send :canvas).map(&:join).join "<br>\n"
html.gsub!('@', "<b></b>").gsub!('-', "<i></i>")
html.insert(0, HTML_BEGINNING).concat('\n' + HTML_ENDING)
end
end
end
class Canvas
include Renderers
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
@canvas = Array.new(width) { Array.new(height, '-') }
end
def set_pixel(width, height)
@canvas[height][width] = '@'
end
def pixel_at?(width, height)
@canvas[height][width] == '@'
end
def draw(figure)
figure.draw self
end
def render_as(renderer)
renderer.visualize self
end
private
attr_reader :canvas
end
class Point
include Comparable
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def ==(other)
@x == other.x and @y == other.y
end
def eql?(other)
self == other
end
def hash
(@x.to_s + @y.to_s).hash
end
def <(other)
if @x == other.x
@y < other.y
elsif @x < other.x
true
else
false
end
end
def <=>(other)
if @x == other.x
@y <=> other.y
else
@x <=> other.x
end
end
def draw(canvas)
canvas.set_pixel x, y
end
end
module BresenhamAlgorithm
def bresenham(left, right)
left, right, slope = adjust_points left, right
characteristics = set_characteristics left, right
compute_points left, right, slope, characteristics
end
private
def adjust_points(left, right)
slope = (left.y - right.y).abs > (left.x - right.x).abs
if slope
[Point.new(left.y, left.x), Point.new(right.y, right.x), slope]
else
[left, right, slope]
end
end
def set_characteristics(left, right, characteristics = {})
characteristics[:dx] = right.x - left.x
characteristics[:dy] = (left.y - right.y).abs
characteristics[:error] = (characteristics[:dx] / 2).to_i
characteristics[:y_step] = left.y < right.y ? 1 : -1
characteristics[:y] = left.y
characteristics
end
def compute_points(left, right, slope, characteristics)
points = []
left.x.upto right.x do |x|
y = characteristics[:y]
slope ? points << Point.new(y, x) : points << Point.new(x, y)
compute_error characteristics
end
points
end
def compute_error(characteristics)
characteristics[:error] -= characteristics[:dy]
if characteristics[:error] < 0
characteristics[:y] += characteristics[:y_step]
characteristics[:error] += characteristics[:dx]
end
end
end
class Line
include BresenhamAlgorithm
attr_reader :from, :to
def initialize(from, to)
@from, @to = from < to ? [from, to] : [to, from]
end
def ==(other)
@from == other.from and @to == other.to
end
def eql?(other)
self == other
end
def hash
(@from.to_s + @to.to_s).hash
end
def draw(canvas)
points = bresenham @from, @to
points.map { |point| canvas.set_pixel point.x, point.y }
end
end
class Rectangle
attr_reader :left, :right
def initialize(left, right)
@left, @right = left < right ? [left, right] : [right, left]
end
def top_left
if @left.y > @right.y
Point.new @left.x, @right.y
else
@left
end
end
def top_right
if @left.y < @right.y
Point.new @right.x, @left.y
else
@right
end
end
def bottom_left
if @left.y < @right.y
Point.new @left.x, @right.y
else
@left
end
end
def bottom_right
if @left.y > @right.y
Point.new @right.x, @left.y
else
@right
end
end
def ==(other)
get_points.sort == (other.send :get_points).sort
end
def eql?(other)
self == other
end
def hash
(@left.to_s + @right.to_s).hash
end
def draw(canvas)
sides = get_sides
sides.map { |line| canvas.draw line }
end
private
def get_points
[] << top_right << top_left << bottom_right << bottom_left
end
def get_sides
sides = []
sides << Line.new(top_left, top_right)
sides << Line.new(bottom_left, bottom_right)
sides << Line.new(top_left, bottom_left)
sides << Line.new(top_right, bottom_right)
sides
end
end
end

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

..........FF.F.F.FFF.FFFFFF.......................F...........FF.....

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-1360ix3/solution.rb:58:in `set_pixel'
     # /tmp/d20131223-4637-1360ix3/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 Canvas drawing of shapes and rasterization of lines works with simple horizontal lines
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "--------\n---@@@@-\n--------"
            got: "---\n---@@@@\n---\n---\n---\n---\n---\n---"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,9 @@
       ---------
       ----@@@@-
       ---------
       +---
       +---@@@@
       +---
       +---
       +---
       +---
       +---
       +---
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:73: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)>'

  3) Graphics Canvas drawing of shapes and rasterization of lines works with lines with a small slope
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@@-------\n---@@@@---\n-------@@-\n----------"
            got: "-----\n-@@--\n---@@@@\n-----@@\n-----\n-----\n-----\n-----\n-----\n-----"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,11 @@
       -----------
       --@@-------
       ----@@@@---
       --------@@-
       -----------
       +-----
       +-@@--
       +---@@@@
       +-----@@
       +-----
       +-----
       +-----
       +-----
       +-----
       +-----
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:100: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)>'

  4) Graphics Canvas drawing of shapes and rasterization of lines works with multiple lines
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "-@--------\n-@@-------\n-@-@@@@---\n-@-----@@-\n----------"
            got: "-@---\n-@@--\n-@-@@@@\n-@---@@\n-----\n-----\n-----\n-----\n-----\n-----"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,11 @@
       --@--------
       --@@-------
       --@-@@@@---
       --@-----@@-
       -----------
       +-@---
       +-@@--
       +-@-@@@@
       +-@---@@
       +-----
       +-----
       +-----
       +-----
       +-----
       +-----
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:132: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 Canvas drawing of shapes and rasterization of rectangles works with simple rects
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@@@@@@@@-\n-@------@-\n-@@@@@@@@-\n----------"
            got: "-----\n-@@@@@@@@\n-@---@\n-@@@@@@@@\n-----\n-----\n-----\n-----\n-----\n-----"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,11 @@
       -----------
       --@@@@@@@@-
       --@------@-
       --@@@@@@@@-
       -----------
       +-----
       +-@@@@@@@@
       +-@---@
       +-@@@@@@@@
       +-----
       +-----
       +-----
       +-----
       +-----
       +-----
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:158: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 Canvas drawing of shapes and rasterization of rectangles works with rects defined with their bottom left and top right points
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@@@@@@@@-\n-@------@-\n-@@@@@@@@-\n----------"
            got: "-----\n-@@@@@@@@\n-@---@\n-@@@@@@@@\n-----\n-----\n-----\n-----\n-----\n-----"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,11 @@
       -----------
       --@@@@@@@@-
       --@------@-
       --@@@@@@@@-
       -----------
       +-----
       +-@@@@@@@@
       +-@---@
       +-@@@@@@@@
       +-----
       +-----
       +-----
       +-----
       +-----
       +-----
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:171: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 Canvas drawing of shapes and rasterization of rectangles works with rects with a zero height as a line
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@@@@@@@@-\n----------"
            got: "---\n-@@@@@@@@\n---\n---\n---\n---\n---\n---\n---\n---"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,11 @@
       -----------
       --@@@@@@@@-
       -----------
       +---
       +-@@@@@@@@
       +---
       +---
       +---
       +---
       +---
       +---
       +---
       +---
     # /tmp/d20131223-4637-1360ix3/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:184: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)>'

  8) Graphics Renderers Ascii renders a grid of the size of the canvas
     Failure/Error: lines.size.should eq 3
       
       expected: 3
            got: 4
       
       (compared using ==)
     # /tmp/d20131223-4637-1360ix3/spec.rb:240: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)>'

  9) Graphics Renderers Ascii renders blank canvases
     Failure/Error: canvas.render_as(ascii).should eq rendering('
       
       expected: "----\n----\n----"
            got: "---\n---\n---\n---"
       
       (compared using ==)
       
       Diff:
       @@ -1,4 +1,5 @@
       -----
       -----
       -----
       +---
       +---
       +---
       +---
     # /tmp/d20131223-4637-1360ix3/spec.rb:245: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)>'

  10) Graphics Renderers Ascii renders simple canvases
     Failure/Error: canvas.render_as(ascii).should eq rendering('
       
       expected: "@---\n----\n---@"
            got: "@--\n---\n---@\n---"
       
       (compared using ==)
       
       Diff:
       
       @@ -1,4 +1,5 @@
       -@---
       -----
       +@--
       +---
        ---@
       +---
     # /tmp/d20131223-4637-1360ix3/spec.rb:256: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)>'

  11) Graphics Renderers Html returns html
     Failure/Error: rendering = normalize_html canvas.render_as(html)
     NoMethodError:
       undefined method `gsub!' for nil:NilClass
     # /tmp/d20131223-4637-1360ix3/solution.rb:41:in `visualize'
     # /tmp/d20131223-4637-1360ix3/solution.rb:70:in `render_as'
     # /tmp/d20131223-4637-1360ix3/spec.rb:272: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)>'

  12) Graphics Renderers Html renders a grid of the size of the canvas
     Failure/Error: rendering = normalize_html canvas.render_as(html)
     NoMethodError:
       undefined method `gsub!' for nil:NilClass
     # /tmp/d20131223-4637-1360ix3/solution.rb:41:in `visualize'
     # /tmp/d20131223-4637-1360ix3/solution.rb:70:in `render_as'
     # /tmp/d20131223-4637-1360ix3/spec.rb:309:in `html_rendering_of'
     # /tmp/d20131223-4637-1360ix3/spec.rb:281: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)>'

  13) 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><br><i></i><b></b><i></i><br><i></i><b></b><i></i><br><i></i><i></i><i></i>\\n"
       
       (compared using ==)
     # /tmp/d20131223-4637-1360ix3/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)>'

  14) Graphics shapes Line comparison for equality returns the same hash if the lines are the same
     Failure/Error: a.hash.should eq b.hash
       
       expected: -504653624
            got: -441344324
       
       (compared using ==)
     # /tmp/d20131223-4637-1360ix3/spec.rb:465: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)>'

  15) Graphics shapes Rectangle comparison for equality returns the same hash if the rectangles are the same
     Failure/Error: a.hash.should eq b.hash
       
       expected: -288598887
            got: 427458396
       
       (compared using ==)
     # /tmp/d20131223-4637-1360ix3/spec.rb:556: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)>'

  16) Graphics shapes Rectangle comparison for equality returns the same hash for rectangles defined with different diagonal corners
     Failure/Error: a.hash.should eq b.hash
       
       expected: 376454583
            got: -814311166
       
       (compared using ==)
     # /tmp/d20131223-4637-1360ix3/spec.rb:563: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.08606 seconds
69 examples, 16 failures

Failed examples:

rspec /tmp/d20131223-4637-1360ix3/spec.rb:51 # Graphics Canvas drawing of shapes and rasterization of points works for multiple ones
rspec /tmp/d20131223-4637-1360ix3/spec.rb:69 # Graphics Canvas drawing of shapes and rasterization of lines works with simple horizontal lines
rspec /tmp/d20131223-4637-1360ix3/spec.rb:96 # Graphics Canvas drawing of shapes and rasterization of lines works with lines with a small slope
rspec /tmp/d20131223-4637-1360ix3/spec.rb:127 # Graphics Canvas drawing of shapes and rasterization of lines works with multiple lines
rspec /tmp/d20131223-4637-1360ix3/spec.rb:154 # Graphics Canvas drawing of shapes and rasterization of rectangles works with simple rects
rspec /tmp/d20131223-4637-1360ix3/spec.rb:167 # Graphics Canvas drawing of shapes and rasterization of rectangles works with rects defined with their bottom left and top right points
rspec /tmp/d20131223-4637-1360ix3/spec.rb:180 # Graphics Canvas drawing of shapes and rasterization of rectangles works with rects with a zero height as a line
rspec /tmp/d20131223-4637-1360ix3/spec.rb:237 # Graphics Renderers Ascii renders a grid of the size of the canvas
rspec /tmp/d20131223-4637-1360ix3/spec.rb:244 # Graphics Renderers Ascii renders blank canvases
rspec /tmp/d20131223-4637-1360ix3/spec.rb:252 # Graphics Renderers Ascii renders simple canvases
rspec /tmp/d20131223-4637-1360ix3/spec.rb:271 # Graphics Renderers Html returns html
rspec /tmp/d20131223-4637-1360ix3/spec.rb:280 # Graphics Renderers Html renders a grid of the size of the canvas
rspec /tmp/d20131223-4637-1360ix3/spec.rb:287 # Graphics Renderers Html renders simple canvases
rspec /tmp/d20131223-4637-1360ix3/spec.rb:461 # Graphics shapes Line comparison for equality returns the same hash if the lines are the same
rspec /tmp/d20131223-4637-1360ix3/spec.rb:552 # Graphics shapes Rectangle comparison for equality returns the same hash if the rectangles are the same
rspec /tmp/d20131223-4637-1360ix3/spec.rb:559 # Graphics shapes Rectangle comparison for equality returns the same hash for rectangles defined with different diagonal corners

История (2 версии и 1 коментар)

Борислава обнови решението на 19.12.2013 19:11 (преди почти 11 години)

+module Graphics
+ module Renderers
+ class Ascii
+ def self.visualize(canvas)
+ (canvas.send :canvas).map(&:join).join "\n"
+ end
+ end
+
+ class Html
+ HTML_BEGINNING = "<!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\">\n"
+ HTML_ENDING = " </div>
+ </body>
+ </html>"
+
+ def self.visualize(canvas)
+ html = (canvas.send :canvas).map(&:join).join "<br>\n"
+ html.gsub!('@', "<b></b>").gsub!('-', "<i></i>")
+ html.insert(0, HTML_BEGINNING).concat('\n' + HTML_ENDING)
+ end
+ end
+ end
+
+ class Canvas
+ include Renderers
+ attr_reader :width, :height
+
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @canvas = Array.new(width) { Array.new(height, '-') }
+ end
+
+ def set_pixel(width, height)
+ @canvas[height][width] = '@'
+ end
+
+ def pixel_at?(width, height)
+ @canvas[height][width] == '@'
+ end
+
+ def draw(figure)
+ figure.draw self
+ end
+
+ def render_as(renderer)
+ renderer.visualize self
+ end
+
+ private
+ attr_reader :canvas
+ end
+
+ class Point
+ include Comparable
+ attr_reader :x, :y
+
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ def ==(other)
+ @x == other.x and @y == other.y
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ def hash
+ (@x.to_s + @y.to_s).hash
+ end
+
+ def <(other)
+ if @x == other.x
+ @y < other.y
+ elsif @x < other.x
+ true
+ else
+ false
+ end
+ end
+
+ def <=>(other)
+ if @x == other.x
+ @y <=> other.y
+ else
+ @x <=> other.x
+ end
+ end
+
+ def draw(canvas)
+ puts "in Point#draw"
+ canvas.set_pixel x, y
+ end
+ end
+
+ module BresenhamAlgorithm
+ def bresenham(left, right)
+ left, right, slope = adjust_points left, right
+
+ characteristics = set_characteristics left, right
+
+ compute_points left, right, slope, characteristics
+ end
+
+ private
+
+ def adjust_points(left, right)
+ slope = (left.y - right.y).abs > (left.x - right.x).abs
+
+ if slope
+ [Point.new(left.y, left.x), Point.new(right.y, right.x), slope]
+ else
+ [left, right, slope]
+ end
+ end
+
+ def set_characteristics(left, right, characteristics = {})
+ characteristics[:dx] = right.x - left.x
+ characteristics[:dy] = (left.y - right.y).abs
+
+ characteristics[:error] = (characteristics[:dx] / 2).to_i
+
+ characteristics[:y_step] = left.y < right.y ? 1 : -1
+ characteristics[:y] = left.y
+
+ characteristics
+ end
+
+ def compute_points(left, right, slope, characteristics)
+ points = []
+
+ left.x.upto right.x do |x|
+ y = characteristics[:y]
+ slope ? points << Point.new(y, x) : points << Point.new(x, y)
+
+ compute_error characteristics
+ end
+
+ points
+ end
+
+ def compute_error(characteristics)
+ characteristics[:error] -= characteristics[:dy]
+
+ if characteristics[:error] < 0
+ characteristics[:y] += characteristics[:y_step]
+ characteristics[:error] += characteristics[:dx]
+ end
+ end
+ end
+
+ class Line
+ include BresenhamAlgorithm
+ attr_reader :from, :to
+
+ def initialize(from, to)
+ @from, @to = from < to ? [from, to] : [to, from]
+ end
+
+ def ==(other)
+ @from == other.from and @to == other.to
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ def hash
+ (@from.to_s + @to.to_s).hash
+ end
+
+ def draw(canvas)
+ points = bresenham @from, @to
+ points.map { |point| canvas.set_pixel point.x, point.y }
+ end
+ end
+
+ class Rectangle
+ attr_reader :left, :right
+
+ def initialize(left, right)
+ @left, @right = left < right ? [left, right] : [right, left]
+ end
+
+ def top_left
+ if @left.y > @right.y
+ Point.new @left.x, @right.y
+ else
+ @left
+ end
+ end
+
+ def top_right
+ if @left.y < @right.y
+ Point.new @right.x, @left.y
+ else
+ @right
+ end
+ end
+
+ def bottom_left
+ if @left.y < @right.y
+ Point.new @left.x, @right.y
+ else
+ @left
+ end
+ end
+
+ def bottom_right
+ if @left.y > @right.y
+ Point.new @right.x, @left.y
+ else
+ @right
+ end
+ end
+
+ def ==(other)
+ get_points.sort == (other.send :get_points).sort
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ def hash
+ (@left.to_s + @right.to_s).hash
+ end
+
+ def draw(canvas)
+ puts "in Rectangle#draw"
+ sides = get_sides
+ sides.map { |line| canvas.draw line }
+ end
+
+ private
+
+ def get_points
+ [] << top_right << top_left << bottom_right << bottom_left
+ end
+
+ def get_sides
+ sides = []
+
+ sides << Line.new(top_left, top_right)
+ sides << Line.new(bottom_left, bottom_right)
+ sides << Line.new(top_left, bottom_left)
+ sides << Line.new(top_right, bottom_right)
+
+ sides
+ end
+ end
+end

Борислава обнови решението на 19.12.2013 19:16 (преди почти 11 години)

module Graphics
module Renderers
class Ascii
def self.visualize(canvas)
(canvas.send :canvas).map(&:join).join "\n"
end
end
class Html
HTML_BEGINNING = "<!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\">\n"
HTML_ENDING = " </div>
</body>
</html>"
def self.visualize(canvas)
html = (canvas.send :canvas).map(&:join).join "<br>\n"
html.gsub!('@', "<b></b>").gsub!('-', "<i></i>")
html.insert(0, HTML_BEGINNING).concat('\n' + HTML_ENDING)
end
end
end
class Canvas
include Renderers
attr_reader :width, :height
def initialize(width, height)
@width = width
@height = height
@canvas = Array.new(width) { Array.new(height, '-') }
end
def set_pixel(width, height)
@canvas[height][width] = '@'
end
def pixel_at?(width, height)
@canvas[height][width] == '@'
end
def draw(figure)
figure.draw self
end
def render_as(renderer)
renderer.visualize self
end
private
attr_reader :canvas
end
class Point
include Comparable
attr_reader :x, :y
def initialize(x, y)
@x = x
@y = y
end
def ==(other)
@x == other.x and @y == other.y
end
def eql?(other)
self == other
end
def hash
(@x.to_s + @y.to_s).hash
end
def <(other)
if @x == other.x
@y < other.y
elsif @x < other.x
true
else
false
end
end
def <=>(other)
if @x == other.x
@y <=> other.y
else
@x <=> other.x
end
end
def draw(canvas)
- puts "in Point#draw"
canvas.set_pixel x, y
end
end
module BresenhamAlgorithm
def bresenham(left, right)
left, right, slope = adjust_points left, right
characteristics = set_characteristics left, right
compute_points left, right, slope, characteristics
end
private
def adjust_points(left, right)
slope = (left.y - right.y).abs > (left.x - right.x).abs
if slope
[Point.new(left.y, left.x), Point.new(right.y, right.x), slope]
else
[left, right, slope]
end
end
def set_characteristics(left, right, characteristics = {})
characteristics[:dx] = right.x - left.x
characteristics[:dy] = (left.y - right.y).abs
characteristics[:error] = (characteristics[:dx] / 2).to_i
characteristics[:y_step] = left.y < right.y ? 1 : -1
characteristics[:y] = left.y
characteristics
end
def compute_points(left, right, slope, characteristics)
points = []
left.x.upto right.x do |x|
y = characteristics[:y]
slope ? points << Point.new(y, x) : points << Point.new(x, y)
compute_error characteristics
end
points
end
def compute_error(characteristics)
characteristics[:error] -= characteristics[:dy]
if characteristics[:error] < 0
characteristics[:y] += characteristics[:y_step]
characteristics[:error] += characteristics[:dx]
end
end
end
class Line
include BresenhamAlgorithm
attr_reader :from, :to
def initialize(from, to)
@from, @to = from < to ? [from, to] : [to, from]
end
def ==(other)
@from == other.from and @to == other.to
end
def eql?(other)
self == other
end
def hash
(@from.to_s + @to.to_s).hash
end
def draw(canvas)
points = bresenham @from, @to
points.map { |point| canvas.set_pixel point.x, point.y }
end
end
class Rectangle
attr_reader :left, :right
def initialize(left, right)
@left, @right = left < right ? [left, right] : [right, left]
end
def top_left
if @left.y > @right.y
Point.new @left.x, @right.y
else
@left
end
end
def top_right
if @left.y < @right.y
Point.new @right.x, @left.y
else
@right
end
end
def bottom_left
if @left.y < @right.y
Point.new @left.x, @right.y
else
@left
end
end
def bottom_right
if @left.y > @right.y
Point.new @right.x, @left.y
else
@right
end
end
def ==(other)
get_points.sort == (other.send :get_points).sort
end
def eql?(other)
self == other
end
def hash
(@left.to_s + @right.to_s).hash
end
def draw(canvas)
- puts "in Rectangle#draw"
sides = get_sides
sides.map { |line| canvas.draw line }
end
private
def get_points
[] << top_right << top_left << bottom_right << bottom_left
end
def get_sides
sides = []
sides << Line.new(top_left, top_right)
sides << Line.new(bottom_left, bottom_right)
sides << Line.new(top_left, bottom_left)
sides << Line.new(top_right, bottom_right)
sides
end
end
end

Бележки:

  • Помисли дали няма друг по-оптимален начин в Ruby за реализация вътрешното представяне на пано от двумерен масив. Ruby не е C и има по-удобни структури от данни :)
  • Също така, струва ми се лоша идея да пазиш двата вида пиксели вътрешно като низове. Помисли дали няма друго възможно представяне.
  • Не виждам причина да include-ваш Renderers в пано.
  • Лош дизайн е да ползваш този send във visualize методите. Освен това, правилното записване е canvas.send(:canvas).map.... Намери друг начин да правиш това.
  • Освен това, имаш малко дублираща се логика в двата рендерера, свързана с обхождането на пикселите на паното, която зависи от вътрешното представяне на пано. Ако смениш вътрешното представяне на пано, ще трябва да промениш и тези два метода, а това не е добре. Опитай да ползваш нещо, което не зависи от това вътрешно представяне. Нещо, което ползва само публичния интерфейс на пано, дефиниран в условието, например.
  • В рендерерите, "header" и "footer" са по-добри имена на "beginning" и "ending" :)
  • Може да ползваш синоними на методи, за да реализираш eql?.
  • Условието на задачата не изисква да дефинираш <=>. Ако ти трябва за друго нещо – окей.
  • Не би трябвало да се налага да дефинираш <, ако си дефинирала <=>.
  • Харесва ми, че си извела Брезенхам в модул, но си мисля, че спокойно може да стане и клас, имащ тази единствена "отговорност". Сега е просто купчина инструменти на едно място. Ако трябваше да го unit test-неш, щеше да усетиш ползите от отделен клас. Мисля, че това ще ти помогне и да измислиш малко по-добри имена на някои от нещата в алгоритъма.
  • get_points не е добро и идиоматично име; по-добре само points. Същото важи и за get_sides.
  • В get_sides по-добре ползвай литералния синтаксис за дефиниране на масив, отколкото да пълниш така вътре. Нещо като това:

      [
        Line.new(...),
        Line.new(...),
        Line.new(...),
        Line.new(...),
      ]
    

Стилът ти е добър, именуването ти е на ниво и дизайнът ти е добър.