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

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

Към профила на Антонио Николов

Резултати

  • 5 точки от тестове
  • 1 бонус точка
  • 6 точки общо
  • 58 успешни тест(а)
  • 11 неуспешни тест(а)

Код

module Graphics
class Canvas
def initialize(width, height)
@width = width
@height = height
@pixels = []
end
attr_reader :width, :height
def set_pixel(x, y)
@pixels << [x, y] unless @pixels.include? [x, y]
end
def pixel_at?(x, y)
@pixels.include? [x, y]
end
def draw(shape)
shape.draw(self)
end
def render_as(renderer)
renderer.new.set_render(self)
end
end
module Renderers
class Ascii
def set_render(pane)
panel = ""
0.upto(pane.width * pane.height - 1) do |x|
panel += "\n" if (x).remainder(pane.width).zero? and not x == 0
panel += '-'
panel[panel.length - 1] = '@' if pane.pixel_at? x % pane.width, x / pane.width
end
panel
end
end
class Html
PANEL_FIRST_PART = "
<!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"
PANEL_SECOND_PART = "
</div>
</body>
</html>"
def set_canvas(pane, count, pixel = "")
pixel += "<br>\n" if (count).remainder(pane.width).zero? and not count == 0
if pane.pixel_at? count % pane.width, count / pane.width
pixel += "<b></b>"
else
pixel += "<i></i>"
end
pixel
end
def set_render(pane)
panel = PANEL_FIRST_PART
0.upto(pane.width * pane.height - 1) do |x|
panel += set_canvas(pane, x)
end
panel += PANEL_SECOND_PART
end
end
end
class Point
def initialize(x, y)
@x = x
@y = y
end
attr_reader :x, :y
def ==(point)
x == point.x and y == point.y
end
alias_method :eql?, :==
def draw(canvas)
canvas.set_pixel(x, y)
end
def hash
[x, y].hash
end
end
class Line
def initialize(from_point, to_point)
@from_point = from_point
@to_point = to_point
end
def from
if @from_point.x == @to_point.x
@from_point.y < @to_point.y ? @from_point : @to_point
else
@from_point.x < @to_point.x ? @from_point : @to_point
end
end
def to
if @from_point.x == @to_point.x
@from_point.y > @to_point.y ? @from_point : @to_point
else
@from_point.x > @to_point.x ? @from_point : @to_point
end
end
def ==(line)
from == line.from and to == line.to
end
alias_method :eql?, :==
def draw_by_y(canvas, length_x, length_y)
from.y.upto(to.y) do |y|
x = (from.x + length_x * (y - from.y) / length_y).abs
canvas.set_pixel(x, y)
end
end
def draw_by_x(canvas, length_x, length_y)
from.x.upto(to.x) do |x|
y = (from.y + length_y * (x - from.x) / length_x).abs
canvas.set_pixel(x, y)
end
end
def draw(canvas)
length_x, length_y = to.x - from.x, to.y - from.y
if length_x == 0 || length_y >= length_x
draw_by_y(canvas, length_x, length_y)
else
draw_by_x(canvas, length_x, length_y)
end
end
def hash
[from.hash, to.hash].hash
end
end
class Rectangle
def initialize(left_point, right_point)
@left_point = left_point
@right_point = right_point
end
def left
if @left_point.x == @right_point.x
@left_point.y < @right_point.y ? @left_point : @right_point
else
@left_point.x < @right_point.x ? @left_point : @right_point
end
end
def right
if @left_point.x == @right_point.x
@left_point.y > @right_point.y ? @left_point : @right_point
else
@left_point.x > @right_point.x ? @left_point : @right_point
end
end
def top_left
if left.y < right.y
left
else
Point.new left.x, right.y
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
right
else
Point.new left.x, right.y
end
end
def ==(rectangle)
top_left == rectangle.top_left and bottom_right == rectangle.bottom_right
end
alias_method :eql?, :==
def draw(canvas)
canvas.draw Line.new(top_left, top_right)
canvas.draw Line.new(top_left, bottom_left)
canvas.draw Line.new(bottom_left, bottom_right)
canvas.draw Line.new(top_right, bottom_right)
end
def hash
[top_left.hash, bottom_right.hash, bottom_left.hash].hash
end
end
end

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

........F....FFFF.FFF.......................................F..F...F.

Failures:

  1) Graphics Canvas drawing of shapes and rasterization renders multiple drawn shapes
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "@@@@@@@@@@@@@@@\n@-------------@\n@-@@@@@@@@@@@-@\n@-@---------@-@\n@-@------@@-@-@\n@-@---@@@---@-@\n@-@-@@------@-@\n@-@---------@-@\n@-@-@@@@----@-@\n@-@-@-------@-@\n@-@---------@-@\n@-@---------@-@\n@-@@@@@@@@@@@-@\n@-------------@\n@@@@@@@@@@@@@@@"
            got: "@@@@@@@@@@@@@@@\n@-------------@\n@-@@@@@@@@@@@-@\n@-@---------@-@\n@-@-----@@@-@-@\n@-@--@@@----@-@\n@-@-@-------@-@\n@-@---------@-@\n@-@-@@@@----@-@\n@-@-@-------@-@\n@-@---------@-@\n@-@---------@-@\n@-@@@@@@@@@@@-@\n@-------------@\n@@@@@@@@@@@@@@@"
       
       (compared using ==)
       
       Diff:
       @@ -2,9 +2,9 @@
        @-------------@
        @-@@@@@@@@@@@-@
        @-@---------@-@
       -@-@------@@-@-@
       -@-@---@@@---@-@
       -@-@-@@------@-@
       +@-@-----@@@-@-@
       +@-@--@@@----@-@
       +@-@-@-------@-@
        @-@---------@-@
        @-@-@@@@----@-@
        @-@-@-------@-@
     # /tmp/d20131223-4637-10aukv/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-10aukv/spec.rb:211: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)>'

  2) 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----------"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,6 @@
        ----------
       --@@-------
       ----@@@@---
       --------@@-
       +-@@@@-----
       +-----@@@--
       +--------@-
        ----------
     # /tmp/d20131223-4637-10aukv/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-10aukv/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)>'

  3) Graphics Canvas drawing of shapes and rasterization of lines works with lines with a significant slope, with swapped ends
     Failure/Error: ascii.should eq rendering(expected)
       
       expected: "----------\n-@--------\n-@--------\n--@-------\n--@-------\n--@-------\n--@-------\n---@------\n---@------\n----------"
            got: "----------\n-@--------\n-@--------\n-@--------\n-@--------\n--@-------\n--@-------\n--@-------\n---@------\n----------"
       
       (compared using ==)
       
       Diff:
       
       @@ -1,11 +1,11 @@
        ----------
        -@--------
        -@--------
       +-@--------
       +-@--------
        --@-------
        --@-------
        --@-------
       ---@-------
       ----@------
        ---@------
        ----------
     # /tmp/d20131223-4637-10aukv/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-10aukv/spec.rb:113: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----------"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,6 @@
        -@--------
       --@@-------
       --@-@@@@---
       --@-----@@-
       +-@@@@-----
       +-@---@@@--
       +-@------@-
        ----------
     # /tmp/d20131223-4637-10aukv/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-10aukv/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 lines draws lines with two equal ends as points
     Failure/Error: canvas.draw make_line(make_point(1, 1), make_point(1, 1))
     ZeroDivisionError:
       divided by 0
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `/'
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `block in draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `upto'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:162:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:20:in `draw'
     # /tmp/d20131223-4637-10aukv/spec.rb:143: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----------"
       
       (compared using ==)
       
       Diff:
       @@ -1,6 +1,6 @@
        ----------
        -@@@@@@@@-
       --@------@-
       --@@@@@@@@-
       +-@--------
       +-@--------
        ----------
     # /tmp/d20131223-4637-10aukv/spec.rb:624:in `check_rendering_of'
     # /tmp/d20131223-4637-10aukv/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: canvas.draw make_rectangle(make_point(1, 1), make_point(8, 1))
     ZeroDivisionError:
       divided by 0
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `/'
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `block in draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `upto'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:162:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:20:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:235:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:20:in `draw'
     # /tmp/d20131223-4637-10aukv/spec.rb:182: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 Canvas drawing of shapes and rasterization of rectangles works with rects with a zero width and height as a single point
     Failure/Error: canvas.draw make_rectangle(make_point(1, 1), make_point(1, 1))
     ZeroDivisionError:
       divided by 0
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `/'
     # /tmp/d20131223-4637-10aukv/solution.rb:147:in `block in draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `upto'
     # /tmp/d20131223-4637-10aukv/solution.rb:146:in `draw_by_y'
     # /tmp/d20131223-4637-10aukv/solution.rb:162:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:20:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:234:in `draw'
     # /tmp/d20131223-4637-10aukv/solution.rb:20:in `draw'
     # /tmp/d20131223-4637-10aukv/spec.rb:193: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)>'

  9) Graphics shapes Rectangle comparison for equality is true for rectangles defined with different diagonal corners
     Failure/Error: (a == b).should be_true
       expected: true value
            got: false
     # /tmp/d20131223-4637-10aukv/spec.rb:540: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)>'

  10) 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: 735325981
            got: 180657761
       
       (compared using ==)
     # /tmp/d20131223-4637-10aukv/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)>'

  11) Graphics shapes Rectangle corners bottom right
     Failure/Error: rect.bottom_right.x.should eq 5
       
       expected: 5
            got: 1
       
       (compared using ==)
     # /tmp/d20131223-4637-10aukv/spec.rb:589: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.11084 seconds
69 examples, 11 failures

Failed examples:

rspec /tmp/d20131223-4637-10aukv/spec.rb:203 # Graphics Canvas drawing of shapes and rasterization renders multiple drawn shapes
rspec /tmp/d20131223-4637-10aukv/spec.rb:96 # Graphics Canvas drawing of shapes and rasterization of lines works with lines with a small slope
rspec /tmp/d20131223-4637-10aukv/spec.rb:109 # Graphics Canvas drawing of shapes and rasterization of lines works with lines with a significant slope, with swapped ends
rspec /tmp/d20131223-4637-10aukv/spec.rb:127 # Graphics Canvas drawing of shapes and rasterization of lines works with multiple lines
rspec /tmp/d20131223-4637-10aukv/spec.rb:141 # Graphics Canvas drawing of shapes and rasterization of lines draws lines with two equal ends as points
rspec /tmp/d20131223-4637-10aukv/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-10aukv/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-10aukv/spec.rb:191 # Graphics Canvas drawing of shapes and rasterization of rectangles works with rects with a zero width and height as a single point
rspec /tmp/d20131223-4637-10aukv/spec.rb:536 # Graphics shapes Rectangle comparison for equality is true for rectangles defined with different diagonal corners
rspec /tmp/d20131223-4637-10aukv/spec.rb:559 # Graphics shapes Rectangle comparison for equality returns the same hash for rectangles defined with different diagonal corners
rspec /tmp/d20131223-4637-10aukv/spec.rb:587 # Graphics shapes Rectangle corners bottom right

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

Антонио обнови решението на 18.12.2013 15:25 (преди над 10 години)

+module Graphics
+ class Canvas
+ def initialize(width, height)
+ @width = width
+ @height = height
+ @pixels = []
+ end
+
+ attr_reader :width, :height
+
+ def set_pixel(x, y)
+ @pixels << [x, y] unless @pixels.include? [x, y]
+ end
+
+ def pixel_at?(x, y)
+ @pixels.include? [x, y]
+ end
+
+ def draw(form)
+ form.draw(self)
+ end
+
+ def render_as(renderer)
+ renderer.new.render_as(self)
+ end
+ end
+
+ module Renderers
+ class Ascii
+ def render_as(sail)
+ panel = ""
+ 0.upto(sail.width * sail.height - 1) do |x|
+ panel += "\n" if (x).remainder(sail.width).zero? and not x == 0
+ panel += '-'
+ panel[panel.length - 1] = '@' if sail.pixel_at? x % sail.width, x / sail.width
+ end
+ panel
+ end
+ end
+
+ class Html
+ def set_sail(sail, count, pixel = "")
+ pixel += "<br>\n" if (count).remainder(sail.width).zero? and not count == 0
+ if sail.pixel_at? count % sail.width, count / sail.width
+ pixel += "<b></b>"
+ else
+ pixel += "<i></i>"
+ end
+ pixel
+ end
+
+ def render_as(sail)
+ panel = "
+ <!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"
+ 0.upto(sail.width * sail.height - 1) do |x|
+ panel += set_sail(sail, x)
+ end
+ panel += "
+ </div>
+ </body>
+ </html>"
+ end
+ end
+ end
+
+ class Point
+ def initialize(x, y)
+ @x = x
+ @y = y
+ end
+
+ attr_reader :x, :y
+
+ def ==(point)
+ x == point.x and y == point.y
+ end
+
+ def eql?(point)
+ x.eql? point.x and y.eql? point.y
+ end
+
+ def draw(sail)
+ sail.set_pixel(x, y)
+ end
+
+ def hash
+ [x, y].hash
+ end
+ end
+
+ class Line
+ def initialize(from_point, to_point)
+ @from_point = from_point
+ @to_point = to_point
+ end
+
+ def from
+ if @from_point.x == @to_point.x
+ if @from_point.y < @to_point.y
+ return @from_point
+ else
+ return @to_point
+ end
+ end
+ @from_point.x < @to_point.x ? @from_point : @to_point
+ end
+
+ def to
+ if @from_point.x == @to_point.x
+ if @from_point.y > @to_point.y
+ return @from_point
+ else
+ return @to_point
+ end
+ end
+ @from_point.x > @to_point.x ? @from_point : @to_point
+ end
+
+ def ==(line)
+ from == line.from and to == line.to
+ end
+
+ def eql?(line)
+ from.eql? line.from and to.eql? line.to
+ end
+
+ def set_draw_y(sail, length_x, length_y)
+ from.y.upto(to.y) do |y|
+ x = (from.x + length_x * (y - from.y) / length_y).abs
+ sail.set_pixel(x, y)
+ end
+ end
+
+ def set_draw_x(sail, length_x, length_y)
+ from.x.upto(to.x) do |x|
+ y = (from.y + length_y * (x - from.x) / length_x).abs
+ sail.set_pixel(x, y)
+ end
+ end
+
+ def draw(sail)
+ length_x, length_y = to.x - from.x, to.y - from.y
+ if length_x == 0 || length_y >= length_x
+ set_draw_y(sail, length_x, length_y)
+ else
+ set_draw_x(sail, length_x, length_y)
+ end
+ end
+
+ def hash
+ [from.x, from.y, to.x, to.y].hash
+ end
+ end
+
+ class Rectangle
+ def initialize(left_point, right_point)
+ @left_point = left_point
+ @right_point = right_point
+ end
+
+ def left
+ if @left_point.x == @right_point.x
+ if @left_point.y < @right_point.y
+ return @left_point
+ else
+ return @right_point
+ end
+ end
+ @left_point.x < @right_point.x ? @left_point : @right_point
+ end
+
+ def right
+ if @left_point.x == @right_point.x
+ if @left_point.y > @right_point.y
+ return @left_point
+ else
+ return @right_point
+ end
+ end
+ @left_point.x > @right_point.x ? @left_point : @right_point
+ end
+
+ def top_left
+ if left.y < right.y
+ left
+ else
+ Point.new left.x, right.y
+ 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
+ right
+ else
+ Point.new left.x, right.y
+ end
+ end
+
+ def ==(rectangle)
+ top_left == rectangle.top_left and bottom_right == rectangle.bottom_right
+ end
+
+ def eql?(rectangle)
+ top_left.eql? rectangle.top_left and bottom_right.eql? rectangle.bottom_right
+ end
+
+ def draw(sail)
+ sail.draw Line.new(top_left, top_right)
+ sail.draw Line.new(top_left, bottom_left)
+ sail.draw Line.new(bottom_left, bottom_right)
+ sail.draw Line.new(top_right, bottom_right)
+ end
+
+ def hash
+ [top_left.x, top_left.y, bottom_right.x, bottom_right.y, bottom_left.x].hash
+ end
+ end
+end

Бележки:

  • Помисли дали няма друг по-оптимален начин в Ruby за реализация вътрешното представяне на пано от двумерен масив. Това линейно търсене за проверка дали пиксел е set-нат, е ненужно и лесно може да бъде избегнато.
  • form не ми се струва като особено добро име; може би си искал да кажеш shape или figure?
  • Мисля, че методът render_as на рендерер не е кръстен удачно. Потърси по-подходящо име.
  • Не мога да повярвам, че виждам платно (sail) в рендерерите :)) Какво ще го правиш това платно :) Ако си искал да кажеш "пано", можеш спокойно да ползваш или "canvas", което така или иначе е част от домейн-терминологията вече, или поне нещо от рода на pane.
  • Мисля, че ще е по-удачно ако обикаляш по височина и ширина на дадено пано, вместо да правиш акробатики с аритметиката, които, на всичкото отгоре, се дублират в двата рендерера. Рефакторирай и изсуши малко тази част. Пробвай да ползваш map и join там, също така.
  • "To set sail" означава "да отплавам" :)
  • По-оптимално (а и по-четимо) е да не дефинираш HTML низовете в render_as, а да ги изведеш като константи в Html. Ползвай други кавички/ограничител, за да ги дефинираш тези низове, за да не трябва да ги екранираш вътре.
  • Имаш дребни проблеми с идентацията на места. Оправи ги (напр. редове 92 и 93). Ако останат във финалното решение, ще ти взема точка за наказание.
  • Имаш дублираща се логика в == и eql? във фигурите; условието е такова, че позволява и прави удачно да се ползват синоними на методи тук.
  • Добре си се оправил с Point#hash.
  • Рефакторирай Line#from и to. Не искам да виждам return там.
  • В Line#hash може да се възползваш от факта, че имаш Point#hash.
  • Поработи над имената на методите, които ти участват в растеризацията на линии. Не са достатъчно добри.
  • Почти същите забележки имам и за Rectangle.

Иначе стилът ти е що-годе приличен и откъм дизайн си на прав път.

Антонио обнови решението на 21.12.2013 22:21 (преди над 10 години)

module Graphics
class Canvas
def initialize(width, height)
@width = width
@height = height
@pixels = []
end
attr_reader :width, :height
def set_pixel(x, y)
@pixels << [x, y] unless @pixels.include? [x, y]
end
def pixel_at?(x, y)
@pixels.include? [x, y]
end
- def draw(form)
- form.draw(self)
+ def draw(shape)
+ shape.draw(self)
end
def render_as(renderer)
- renderer.new.render_as(self)
+ renderer.new.set_render(self)
end
end
module Renderers
class Ascii
- def render_as(sail)
+ def set_render(pane)
panel = ""
- 0.upto(sail.width * sail.height - 1) do |x|
- panel += "\n" if (x).remainder(sail.width).zero? and not x == 0
+ 0.upto(pane.width * pane.height - 1) do |x|
+ panel += "\n" if (x).remainder(pane.width).zero? and not x == 0
panel += '-'
- panel[panel.length - 1] = '@' if sail.pixel_at? x % sail.width, x / sail.width
+ panel[panel.length - 1] = '@' if pane.pixel_at? x % pane.width, x / pane.width
end
panel
end
end
class Html
- def set_sail(sail, count, pixel = "")
- pixel += "<br>\n" if (count).remainder(sail.width).zero? and not count == 0
- if sail.pixel_at? count % sail.width, count / sail.width
- pixel += "<b></b>"
- else
- pixel += "<i></i>"
- end
- pixel
- end
-
- def render_as(sail)
- panel = "
+ PANEL_FIRST_PART = "
<!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"
- 0.upto(sail.width * sail.height - 1) do |x|
- panel += set_sail(sail, x)
- end
- panel += "
+
+ PANEL_SECOND_PART = "
</div>
</body>
</html>"
+
+ def set_canvas(pane, count, pixel = "")
+ pixel += "<br>\n" if (count).remainder(pane.width).zero? and not count == 0
+ if pane.pixel_at? count % pane.width, count / pane.width
+ pixel += "<b></b>"
+ else
+ pixel += "<i></i>"
+ end
+ pixel
end
+
+ def set_render(pane)
+ panel = PANEL_FIRST_PART
+ 0.upto(pane.width * pane.height - 1) do |x|
+ panel += set_canvas(pane, x)
+ end
+ panel += PANEL_SECOND_PART
+ end
end
end
class Point
def initialize(x, y)
- @x = x
- @y = y
+ @x = x
+ @y = y
end
attr_reader :x, :y
def ==(point)
x == point.x and y == point.y
end
- def eql?(point)
- x.eql? point.x and y.eql? point.y
- end
+ alias_method :eql?, :==
- def draw(sail)
- sail.set_pixel(x, y)
+ def draw(canvas)
+ canvas.set_pixel(x, y)
end
def hash
[x, y].hash
end
end
class Line
def initialize(from_point, to_point)
@from_point = from_point
@to_point = to_point
end
def from
if @from_point.x == @to_point.x
- if @from_point.y < @to_point.y
- return @from_point
- else
- return @to_point
- end
+ @from_point.y < @to_point.y ? @from_point : @to_point
+ else
+ @from_point.x < @to_point.x ? @from_point : @to_point
end
- @from_point.x < @to_point.x ? @from_point : @to_point
end
def to
if @from_point.x == @to_point.x
- if @from_point.y > @to_point.y
- return @from_point
- else
- return @to_point
- end
+ @from_point.y > @to_point.y ? @from_point : @to_point
+ else
+ @from_point.x > @to_point.x ? @from_point : @to_point
end
- @from_point.x > @to_point.x ? @from_point : @to_point
end
def ==(line)
from == line.from and to == line.to
end
- def eql?(line)
- from.eql? line.from and to.eql? line.to
- end
+ alias_method :eql?, :==
- def set_draw_y(sail, length_x, length_y)
+ def draw_by_y(canvas, length_x, length_y)
from.y.upto(to.y) do |y|
x = (from.x + length_x * (y - from.y) / length_y).abs
- sail.set_pixel(x, y)
+ canvas.set_pixel(x, y)
end
end
- def set_draw_x(sail, length_x, length_y)
+ def draw_by_x(canvas, length_x, length_y)
from.x.upto(to.x) do |x|
y = (from.y + length_y * (x - from.x) / length_x).abs
- sail.set_pixel(x, y)
+ canvas.set_pixel(x, y)
end
end
- def draw(sail)
+ def draw(canvas)
length_x, length_y = to.x - from.x, to.y - from.y
if length_x == 0 || length_y >= length_x
- set_draw_y(sail, length_x, length_y)
+ draw_by_y(canvas, length_x, length_y)
else
- set_draw_x(sail, length_x, length_y)
+ draw_by_x(canvas, length_x, length_y)
end
end
def hash
- [from.x, from.y, to.x, to.y].hash
+ [from.hash, to.hash].hash
end
end
class Rectangle
def initialize(left_point, right_point)
@left_point = left_point
@right_point = right_point
end
def left
if @left_point.x == @right_point.x
- if @left_point.y < @right_point.y
- return @left_point
- else
- return @right_point
- end
+ @left_point.y < @right_point.y ? @left_point : @right_point
+ else
+ @left_point.x < @right_point.x ? @left_point : @right_point
end
- @left_point.x < @right_point.x ? @left_point : @right_point
end
def right
if @left_point.x == @right_point.x
- if @left_point.y > @right_point.y
- return @left_point
- else
- return @right_point
- end
+ @left_point.y > @right_point.y ? @left_point : @right_point
+ else
+ @left_point.x > @right_point.x ? @left_point : @right_point
end
- @left_point.x > @right_point.x ? @left_point : @right_point
end
def top_left
if left.y < right.y
left
else
Point.new left.x, right.y
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
right
else
Point.new left.x, right.y
end
end
def ==(rectangle)
top_left == rectangle.top_left and bottom_right == rectangle.bottom_right
end
- def eql?(rectangle)
- top_left.eql? rectangle.top_left and bottom_right.eql? rectangle.bottom_right
- end
+ alias_method :eql?, :==
- def draw(sail)
- sail.draw Line.new(top_left, top_right)
- sail.draw Line.new(top_left, bottom_left)
- sail.draw Line.new(bottom_left, bottom_right)
- sail.draw Line.new(top_right, bottom_right)
+ def draw(canvas)
+ canvas.draw Line.new(top_left, top_right)
+ canvas.draw Line.new(top_left, bottom_left)
+ canvas.draw Line.new(bottom_left, bottom_right)
+ canvas.draw Line.new(top_right, bottom_right)
end
def hash
- [top_left.x, top_left.y, bottom_right.x, bottom_right.y, bottom_left.x].hash
+ [top_left.hash, bottom_right.hash, bottom_left.hash].hash
end
end
end
  • Може би вече си видял, че имах предвид вътрешно представяне на пано като Hash :)
  • renderer.new.set_render(self)? По-добре така: renderer.new(self).render. Също не разбирам защо има "set" в името на този метод.
  • Логиката за рендериране ще изглежда по-добре с map и join, вместо да се бутат неща в един низ. И ако ще буташ неща в низ, по-добре го прави с <<, поне е по-бързо от +=. Последното създава нови низове при всяка операция.
  • Header/footer е по-подходящо от prefix/suffix в контекста на HTML код.
  • Представяне на пано като хеш, вместо като списък, ми се струва по-подходящо в случая. Виж моето решение, как съм го направил аз.
  • В hash може да напишеш просто [from, to].hash.

Като изключим тези неща, решението ти е прилично. Давам ти бонус точка.