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

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

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

Резултати

  • 6 точки от тестове
  • 0 бонус точки
  • 6 точки общо
  • 14 успешни тест(а)
  • 0 неуспешни тест(а)

Код

class TurtleGraphics
class Turtle
ORIENTATIONS = [
[:up, [-1, 0]],
[:left, [0, -1]],
[:down, [1, 0]],
[:right, [0, 1]],
]
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
@orientation = :right
@canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
if [row, column] != [0, 0]
@canvas.remove_steps_at(0, 0)
@canvas.increment_value(row, column)
end
end
def move
@x += move_offset.first
@y += move_offset.last
correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
turn 1
end
def turn_right
turn -1
end
def draw(given_canvas = @canvas, &block)
instance_eval(&block) if block_given?
given_canvas.convert_canvas(@canvas)
end
def look(orientation)
@orientation = orientation
end
private
def turn(direction)
orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
end
def move_offset
ORIENTATIONS.find { |pair| @orientation == pair.first }.last
end
def correct_out_of_bounds_coordinates(x, y)
@x = x % @rows
@y = y % @columns
end
end
class Canvas
class Default
attr_reader :canvas, :maximum_steps
def initialize(rows, columns)
@canvas = Array.new(rows) { Array.new(columns, 0) }
@canvas[0][0] = 1
@maximum_steps = 1
end
def convert_canvas(_)
@canvas
end
def increment_value(x, y)
@canvas[x][y] += 1
@maximum_steps = @canvas[x][y] if @canvas[x][y] > @maximum_steps
end
def remove_steps_at(x, y)
@canvas[x][y] = 0
end
end
module Intensity
def convert_to_intensity(default_canvas)
maximum_steps = default_canvas.maximum_steps
row_to_intensity = -> (r) { r.map { |steps| steps.to_f / maximum_steps } }
default_canvas.canvas.map &row_to_intensity
end
end
class ASCII
include Intensity
def initialize(allowed_symbols)
@allowed_symbols = allowed_symbols
end
def convert_canvas(default_canvas)
limit_gaps = @allowed_symbols.size - 1
to_symbols = -> (symbol, index) { [index.to_f / limit_gaps, symbol] }
intensity_to_symbol = @allowed_symbols.map.with_index &to_symbols
intensity_canvas = convert_to_intensity(default_canvas)
symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
symbol_canvas.reduce("") { |memo, row| memo + row.join + "\n" }.chomp
end
def convert_to_symbols(intensity_canvas, intensity_to_symbol)
to_symbol = -> (intensity) do
intensity_to_symbol.find { |pair| pair.first >= intensity }.last
end
row_to_symbols = -> (row) { row.map &to_symbol }
intensity_canvas.map &row_to_symbols
end
end
class HTML
include Intensity
def initialize(pixel_size)
@pixel_size = pixel_size
end
def convert_canvas(default_canvas)
html_head + html_body(default_canvas)
end
def html_head
"<!DOCTYPE html><html><head><title>Turtle graphics</title><style>table
{border-spacing: 0;}tr {padding: 0;}td {width: #{@pixel_size}px;
height: #{@pixel_size}px;background-color: black;padding: 0;}
</style></head>"
end
def html_body(default_canvas)
body_start = "<body><table>"
body_end = "</table></body></html>"
body_mid = String.new
convert_to_intensity(default_canvas).each do |row|
body_mid << "<tr>"
row.each { |intensity| body_mid << html_opacity(intensity) }
body_mid << "</tr>"
end
body_start + body_mid + body_end
end
def html_opacity(intensity)
quotes = '"'
"<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
end
end
end
end

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

..............

Finished in 0.01233 seconds
14 examples, 0 failures

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

Алекс обнови решението на 26.11.2015 11:42 (преди над 8 години)

+class TurtleGraphics
+ class Turtle
+ def initialize(rows, columns)
+ @rows, @columns = rows, columns
+ @x, @y = 0, 0
+ @orientations = [
+ [:up, [-1, 0]],
+ [:left, [0, -1]],
+ [:down, [1, 0]],
+ [:right, [0, 1]],
+ ]
+ @orientation = :right
+ @move_offset = [0, 1]
+ @canvas = Canvas.new(rows, columns)
+ end
+
+ def spawn_at(row, column)
+ @x = row
+ @y = column
+ end
+
+ def move
+ @x += @move_offset.first
+ @y += @move_offset.last
+
+ fix_out_of_bounds(@x, @y)
+ @canvas.increment_value(@x, @y)
+ end
+
+ def turn_left
+ turn(:+)
+ end
+
+ def turn_right
+ turn(:-)
+ end
+
+ def draw(given_canvas = @canvas, &block)
+ instance_eval &block
+
+ if given_canvas.instance_of? Canvas
+ given_canvas.canvas
+ elsif given_canvas.instance_of? Canvas::ASCII
+ @canvas.convert_to_ascii(given_canvas.allowed_symbols)
+ elsif given_canvas.instance_of? Canvas::HTML
+ @canvas.convert_to_html(given_canvas.pixel_size)
+ end
+ end
+
+ def look(orientation)
+ @orientation = orientation
+ @move_offset = @orientations.find { |pair| pair.first == orientation }.last
+ end
+
+ private
+
+ def turn(direction)
+ orientation_index = @orientations.index { |pair| pair.first == @orientation }
+ changed_orientation = @orientations[orientation_index.send(direction, 1) % 4]
+
+ @orientation = changed_orientation.first
+ @move_offset = changed_orientation.last
+ end
+
+ def fix_out_of_bounds(x, y)
+ if x < 0
+ @x = @rows - 1
+ elsif y < 0
+ @y = @columns - 1
+ elsif x > @rows
+ @x = 0
+ elsif y > @rows
+ @y = 0
+ end
+ end
+ end
+
+ class Canvas
+ attr_reader :canvas
+
+ def initialize(rows, columns)
+ @canvas = Array.new(rows) { Array.new(columns, 0) }
+ @canvas[0][0] = 1
+ @maximum_steps = 1
+ end
+
+ def increment_value(x, y)
+ @canvas[x][y] += 1
+ if @canvas[x][y] > @maximum_steps
+ @maximum_steps = @canvas[x][y]
+ end
+ end
+
+ def convert_to_ascii(allowed_symbols)
+ intensity_to_symbol = Array.new
+ limit_gaps = allowed_symbols.size - 1
+
+ allowed_symbols.each_with_index do |symbol, index|
+ intensity_to_symbol << [index.to_f / limit_gaps, symbol]
+ end
+
+ intensity_canvas = convert_to_intensity
+ symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
+
+ canvas_to_string = String.new
+ symbol_canvas.each { |row| canvas_to_string << row.join.concat("\n") }
+ canvas_to_string.chomp
+ end
+
+ def convert_to_html(pixel_size)
+ html_code = "<!DOCTYPE html>\n<html>\n"
+ html_code.concat(html_head(pixel_size))
+ html_code.concat(html_body)
+ end
+
+ private
+
+ def convert_to_intensity
+ row_to_intensity = -> (r) { r.map { |steps| steps.to_f / @maximum_steps } }
+ @canvas.map &row_to_intensity
+ end
+
+ def convert_to_symbols(intensity_canvas, intensity_to_symbol)
+ convert_to_symbol = -> (intensity) do
+ intensity_to_symbol.find { |pair| pair.first >= intensity }.last
+ end
+ row_to_symbol = -> (row) { row.map &convert_to_symbol }
+ intensity_canvas.map &row_to_symbol
+ end
+
+ def html_head(pixel_size)
+ "<head>\n\t<title>Turtle graphics</title>\n\n\t<style>\n\t\ttable{\n\t\t\t
+ border-spacing: 0;\n\t\t\t}\n\n\t\ttr {\n\t\t\t}\n\n\t\ttd {\n\t\t\t
+ width: #{pixel_size}px;\n\t\t\theight: #{pixel_size}px;\n\n\t\t\t
+ background-color: black;\n\t\t\tpadding: 0;\n\t\t}\n\t</style>\n</head>\n"
+ end
+
+ def html_body
+ body_start = "<body>\n\t<table>"
+ body_end = "<\t</table>\n</body>\n</html>"
+ body_middle = String.new
+ convert_to_intensity.each do |row|
+ body_middle << "\t\t<tr>"
+ row.each { |intensity| body_middle << "\n\t\t\t" << html_opacity(intensity) }
+ body_middle << "\n\t\t</tr>"
+ end
+ body_start + body_middle + body_end
+ end
+
+ def html_opacity(intensity)
+ quotes = '"'
+ "<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
+ end
+
+ class ASCII
+ attr_reader :allowed_symbols
+
+ def initialize(allowed_symbols)
+ @allowed_symbols = allowed_symbols
+ end
+ end
+
+ class HTML
+ attr_reader :pixel_size
+
+ def initialize(pixel_size)
+ @pixel_size = pixel_size
+ end
+ end
+ end
+end

Здравей :) Ще "изсипя" малко коментари :)

  • @orientations изглежда като нещо, което не се променя. Има по-добро място за непроменящи се неща от инстанционна променлива.
  • @orientation и @move_offset съдържат неща, които могат да бъдат изведени едно от друго. Защо не оставиш само едното?
  • Не е много добра идея да използваш send. Защо просто не подаваш число на turn, с което да събираш, вместо символ-метод?
  • fix_out_of_bounds може да се напише по-елегантно, помисли го още. И името не е особено описателно. Името звучи като нещо, което е "залепено" в кода без да е домислено. :)
  • HTML кодът не е същият като този в условието. Може да изпуснеш спокойно празните места, ще ги игнорираме при тестовете. Но неща като padding: 0 ще се проверяват и не ги променяй.

И най-важното:
Не е хубаво, че draw знае за възможните canvas-и. Направи го така, че спокойно да може да се имплементира нов вид canvas, без да се променя класът Turtle. Turtle не би трябвало да се занимава с това как се показват нещата. Това е работа на самите canvas-и.
В момента логиката е доста оплетена, Canvas прави прекалено много неща. Отдели нещата, които са специфични за определен canvas само в този canvas.

Имаш възможност да го напишеш доста по-красиво, сигурен съм, че накрая и на теб ще ти хареса :)

Алекс обнови решението на 28.11.2015 00:25 (преди над 8 години)

class TurtleGraphics
class Turtle
+ ORIENTATIONS = [
+ [:up, [-1, 0]],
+ [:left, [0, -1]],
+ [:down, [1, 0]],
+ [:right, [0, 1]],
+ ]
+
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
- @orientations = [
- [:up, [-1, 0]],
- [:left, [0, -1]],
- [:down, [1, 0]],
- [:right, [0, 1]],
- ]
@orientation = :right
- @move_offset = [0, 1]
- @canvas = Canvas.new(rows, columns)
+ @canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
+ if [row, column] != [0, 0]
+ @canvas.remove_steps_at(0, 0)
+ @canvas.increment_value(row, column)
+ end
end
def move
- @x += @move_offset.first
- @y += @move_offset.last
+ @x += move_offset.first
+ @y += move_offset.last
- fix_out_of_bounds(@x, @y)
+ correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
- turn(:+)
+ turn 1
end
def turn_right
- turn(:-)
+ turn -1
end
def draw(given_canvas = @canvas, &block)
- instance_eval &block
+ instance_eval(&block) if block_given?
- if given_canvas.instance_of? Canvas
+ if given_canvas == @canvas
given_canvas.canvas
- elsif given_canvas.instance_of? Canvas::ASCII
- @canvas.convert_to_ascii(given_canvas.allowed_symbols)
- elsif given_canvas.instance_of? Canvas::HTML
- @canvas.convert_to_html(given_canvas.pixel_size)
+ else
+ given_canvas.convert_canvas(@canvas)
end
end
def look(orientation)
@orientation = orientation
- @move_offset = @orientations.find { |pair| pair.first == orientation }.last
end
private
def turn(direction)
- orientation_index = @orientations.index { |pair| pair.first == @orientation }
- changed_orientation = @orientations[orientation_index.send(direction, 1) % 4]
+ orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
+ changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
- @move_offset = changed_orientation.last
end
- def fix_out_of_bounds(x, y)
- if x < 0
- @x = @rows - 1
- elsif y < 0
- @y = @columns - 1
- elsif x > @rows
- @x = 0
- elsif y > @rows
- @y = 0
+ def move_offset
+ ORIENTATIONS.find { |pair| @orientation == pair.first }.last
+ end
+
+ def correct_out_of_bounds_coordinates(x, y)
+ case
+ when x < 0 then @x = @rows - 1
+ when y < 0 then @y = @columns - 1
+ when x > @rows - 1 then @x = 0
+ when y > @rows - 1 then @y = 0
end
end
end
class Canvas
- attr_reader :canvas
+ class Default
+ attr_reader :canvas
- def initialize(rows, columns)
- @canvas = Array.new(rows) { Array.new(columns, 0) }
- @canvas[0][0] = 1
- @maximum_steps = 1
- end
+ def initialize(rows, columns)
+ @canvas = Array.new(rows) { Array.new(columns, 0) }
+ @canvas[0][0] = 1
+ @maximum_steps = 1
+ end
- def increment_value(x, y)
- @canvas[x][y] += 1
- if @canvas[x][y] > @maximum_steps
- @maximum_steps = @canvas[x][y]
+ def increment_value(x, y)
+ @canvas[x][y] += 1
+ if @canvas[x][y] > @maximum_steps
+ @maximum_steps = @canvas[x][y]
+ end
end
- end
- def convert_to_ascii(allowed_symbols)
- intensity_to_symbol = Array.new
- limit_gaps = allowed_symbols.size - 1
-
- allowed_symbols.each_with_index do |symbol, index|
- intensity_to_symbol << [index.to_f / limit_gaps, symbol]
+ def remove_steps_at(x, y)
+ @canvas[x][y] = 0
end
- intensity_canvas = convert_to_intensity
- symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
+ def convert_to_ascii(allowed_symbols)
+ intensity_to_symbol = Array.new
+ limit_gaps = allowed_symbols.size - 1
- canvas_to_string = String.new
- symbol_canvas.each { |row| canvas_to_string << row.join.concat("\n") }
- canvas_to_string.chomp
- end
+ allowed_symbols.each_with_index do |symbol, index|
+ intensity_to_symbol << [index.to_f / limit_gaps, symbol]
+ end
- def convert_to_html(pixel_size)
- html_code = "<!DOCTYPE html>\n<html>\n"
- html_code.concat(html_head(pixel_size))
- html_code.concat(html_body)
- end
+ intensity_canvas = convert_to_intensity
+ symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
- private
+ canvas_to_string = String.new
+ symbol_canvas.each { |row| canvas_to_string << row.join.concat("\n") }
+ canvas_to_string.chomp
+ end
- def convert_to_intensity
- row_to_intensity = -> (r) { r.map { |steps| steps.to_f / @maximum_steps } }
- @canvas.map &row_to_intensity
- end
+ def convert_to_html(pixel_size)
+ html_code = "<!DOCTYPE html>\n<html>\n"
+ html_code.concat(html_head(pixel_size))
+ html_code.concat(html_body)
+ end
- def convert_to_symbols(intensity_canvas, intensity_to_symbol)
- convert_to_symbol = -> (intensity) do
- intensity_to_symbol.find { |pair| pair.first >= intensity }.last
+ private
+
+ def convert_to_intensity
+ row_to_intensity = -> (r) { r.map { |steps| steps.to_f / @maximum_steps } }
+ @canvas.map &row_to_intensity
end
- row_to_symbol = -> (row) { row.map &convert_to_symbol }
- intensity_canvas.map &row_to_symbol
- end
- def html_head(pixel_size)
- "<head>\n\t<title>Turtle graphics</title>\n\n\t<style>\n\t\ttable{\n\t\t\t
- border-spacing: 0;\n\t\t\t}\n\n\t\ttr {\n\t\t\t}\n\n\t\ttd {\n\t\t\t
- width: #{pixel_size}px;\n\t\t\theight: #{pixel_size}px;\n\n\t\t\t
- background-color: black;\n\t\t\tpadding: 0;\n\t\t}\n\t</style>\n</head>\n"
- end
+ def convert_to_symbols(intensity_canvas, intensity_to_symbol)
+ convert_to_symbol = -> (intensity) do
+ intensity_to_symbol.find { |pair| pair.first >= intensity }.last
+ end
+ row_to_symbol = -> (row) { row.map &convert_to_symbol }
+ intensity_canvas.map &row_to_symbol
+ end
- def html_body
- body_start = "<body>\n\t<table>"
- body_end = "<\t</table>\n</body>\n</html>"
- body_middle = String.new
- convert_to_intensity.each do |row|
- body_middle << "\t\t<tr>"
- row.each { |intensity| body_middle << "\n\t\t\t" << html_opacity(intensity) }
- body_middle << "\n\t\t</tr>"
+ def html_head(pixel_size)
+ "<head>\n\t<title>Turtle graphics</title>\n\n\t<style>\n\t\ttable{\n\t\t\t
+ border-spacing: 0;\n\t\t\t}\n\n\t\ttr {\n\t\t\tpadding: 0;\n\t\t}\n\n\t\t
+ td {\n\t\t\twidth: #{pixel_size}px;\n\t\t\theight: #{pixel_size}px;
+ \n\n\t\t\tbackground-color: black;\n\t\t\tpadding: 0;\n\t\t}\n\t
+ </style>\n</head>\n"
end
- body_start + body_middle + body_end
- end
- def html_opacity(intensity)
- quotes = '"'
- "<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
+ def html_body
+ body_start = "<body>\n\t<table>"
+ body_end = "<\t</table>\n</body>\n</html>"
+ body_mid = String.new
+ convert_to_intensity.each do |row|
+ body_mid << "\t\t<tr>"
+ row.each { |intensity| body_mid << "\n\t\t\t" << html_opacity(intensity) }
+ body_mid << "\n\t\t</tr>"
+ end
+ body_start + body_mid + body_end
+ end
+
+ def html_opacity(intensity)
+ quotes = '"'
+ "<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
+ end
end
class ASCII
attr_reader :allowed_symbols
def initialize(allowed_symbols)
@allowed_symbols = allowed_symbols
end
+
+ def convert_canvas(default_canvas)
+ default_canvas.convert_to_ascii(@allowed_symbols)
+ end
end
class HTML
attr_reader :pixel_size
def initialize(pixel_size)
@pixel_size = pixel_size
+ end
+
+ def convert_canvas(default_canvas)
+ default_canvas.convert_to_html(@pixel_size)
end
end
end
end

Искам да попитам дали е достатъчно подобрението в draw, тъй като при добавяне на ново платно няма да се промени Turtle и би било необходимо само да имплементираме #convert_canvas за съответния нов клас, или целта е дори това да не проверяваме и да потърся универсално решение, което да не зависи от конкретната инстанция?

Алекс обнови решението на 28.11.2015 18:43 (преди над 8 години)

class TurtleGraphics
class Turtle
ORIENTATIONS = [
[:up, [-1, 0]],
[:left, [0, -1]],
[:down, [1, 0]],
[:right, [0, 1]],
]
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
@orientation = :right
@canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
if [row, column] != [0, 0]
@canvas.remove_steps_at(0, 0)
@canvas.increment_value(row, column)
end
end
def move
@x += move_offset.first
@y += move_offset.last
correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
turn 1
end
def turn_right
turn -1
end
def draw(given_canvas = @canvas, &block)
instance_eval(&block) if block_given?
if given_canvas == @canvas
given_canvas.canvas
else
given_canvas.convert_canvas(@canvas)
end
end
def look(orientation)
@orientation = orientation
end
private
def turn(direction)
orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
end
def move_offset
ORIENTATIONS.find { |pair| @orientation == pair.first }.last
end
def correct_out_of_bounds_coordinates(x, y)
- case
- when x < 0 then @x = @rows - 1
- when y < 0 then @y = @columns - 1
- when x > @rows - 1 then @x = 0
- when y > @rows - 1 then @y = 0
+ case x
+ when -1 then @x = @rows - 1
+ when @rows then @x = 0
+ end
+
+ case y
+ when -1 then @y = @columns - 1
+ when @columns then @y = 0
end
end
end
class Canvas
class Default
attr_reader :canvas
def initialize(rows, columns)
@canvas = Array.new(rows) { Array.new(columns, 0) }
@canvas[0][0] = 1
@maximum_steps = 1
end
def increment_value(x, y)
@canvas[x][y] += 1
if @canvas[x][y] > @maximum_steps
@maximum_steps = @canvas[x][y]
end
end
def remove_steps_at(x, y)
@canvas[x][y] = 0
end
def convert_to_ascii(allowed_symbols)
intensity_to_symbol = Array.new
limit_gaps = allowed_symbols.size - 1
allowed_symbols.each_with_index do |symbol, index|
intensity_to_symbol << [index.to_f / limit_gaps, symbol]
end
intensity_canvas = convert_to_intensity
symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
canvas_to_string = String.new
symbol_canvas.each { |row| canvas_to_string << row.join.concat("\n") }
canvas_to_string.chomp
end
def convert_to_html(pixel_size)
html_code = "<!DOCTYPE html>\n<html>\n"
html_code.concat(html_head(pixel_size))
html_code.concat(html_body)
end
private
def convert_to_intensity
row_to_intensity = -> (r) { r.map { |steps| steps.to_f / @maximum_steps } }
@canvas.map &row_to_intensity
end
def convert_to_symbols(intensity_canvas, intensity_to_symbol)
convert_to_symbol = -> (intensity) do
intensity_to_symbol.find { |pair| pair.first >= intensity }.last
end
row_to_symbol = -> (row) { row.map &convert_to_symbol }
intensity_canvas.map &row_to_symbol
end
def html_head(pixel_size)
"<head>\n\t<title>Turtle graphics</title>\n\n\t<style>\n\t\ttable{\n\t\t\t
border-spacing: 0;\n\t\t\t}\n\n\t\ttr {\n\t\t\tpadding: 0;\n\t\t}\n\n\t\t
td {\n\t\t\twidth: #{pixel_size}px;\n\t\t\theight: #{pixel_size}px;
\n\n\t\t\tbackground-color: black;\n\t\t\tpadding: 0;\n\t\t}\n\t
</style>\n</head>\n"
end
def html_body
body_start = "<body>\n\t<table>"
body_end = "<\t</table>\n</body>\n</html>"
body_mid = String.new
convert_to_intensity.each do |row|
body_mid << "\t\t<tr>"
row.each { |intensity| body_mid << "\n\t\t\t" << html_opacity(intensity) }
body_mid << "\n\t\t</tr>"
end
body_start + body_mid + body_end
end
def html_opacity(intensity)
quotes = '"'
"<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
end
end
class ASCII
attr_reader :allowed_symbols
def initialize(allowed_symbols)
@allowed_symbols = allowed_symbols
end
def convert_canvas(default_canvas)
default_canvas.convert_to_ascii(@allowed_symbols)
end
end
class HTML
attr_reader :pixel_size
def initialize(pixel_size)
@pixel_size = pixel_size
end
def convert_canvas(default_canvas)
default_canvas.convert_to_html(@pixel_size)
end
end
end
end

Така draw е по-добре, на прав път си. Все още има нужда от изчистване, обаче:

  • Все още проверяваш дали canvas-а не е някакъв определен. Направи го без да има два клона в логиката. Дори това да означава да направиш клас с един метод, който не прави почти нищо. Ето, подсказах :)
  • Все още ползваш Default като някакъв сборен пункт за бездомни методи. А те всъщност си имат място - в класовете, които ги ползват. Интересува ли го Default как се конвертира до ascii или html? По-скоро не.
  • correct_out_of_bounds_coordinates може да се замени с два оператора % :)
  • Махни всичките \n\t\t\t. HTML кодът не се интересува от whitespace. И ние няма да ги тестваме.

Алекс обнови решението на 29.11.2015 13:10 (преди над 8 години)

class TurtleGraphics
class Turtle
ORIENTATIONS = [
[:up, [-1, 0]],
[:left, [0, -1]],
[:down, [1, 0]],
[:right, [0, 1]],
]
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
@orientation = :right
@canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
if [row, column] != [0, 0]
@canvas.remove_steps_at(0, 0)
@canvas.increment_value(row, column)
end
end
def move
@x += move_offset.first
@y += move_offset.last
correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
turn 1
end
def turn_right
turn -1
end
def draw(given_canvas = @canvas, &block)
instance_eval(&block) if block_given?
- if given_canvas == @canvas
- given_canvas.canvas
- else
- given_canvas.convert_canvas(@canvas)
- end
+ given_canvas.convert_canvas(@canvas)
end
def look(orientation)
@orientation = orientation
end
private
def turn(direction)
orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
end
def move_offset
ORIENTATIONS.find { |pair| @orientation == pair.first }.last
end
def correct_out_of_bounds_coordinates(x, y)
- case x
- when -1 then @x = @rows - 1
- when @rows then @x = 0
- end
-
- case y
- when -1 then @y = @columns - 1
- when @columns then @y = 0
- end
+ @x = x % @rows
+ @y = y % @columns
end
end
class Canvas
class Default
- attr_reader :canvas
+ attr_reader :canvas, :maximum_steps
def initialize(rows, columns)
@canvas = Array.new(rows) { Array.new(columns, 0) }
@canvas[0][0] = 1
@maximum_steps = 1
end
+ def convert_canvas(_)
+ @canvas
+ end
+
def increment_value(x, y)
@canvas[x][y] += 1
- if @canvas[x][y] > @maximum_steps
- @maximum_steps = @canvas[x][y]
- end
+ @maximum_steps = @canvas[x][y] if @canvas[x][y] > @maximum_steps
end
def remove_steps_at(x, y)
@canvas[x][y] = 0
end
+ end
- def convert_to_ascii(allowed_symbols)
- intensity_to_symbol = Array.new
- limit_gaps = allowed_symbols.size - 1
+ module Intensity
+ def convert_to_intensity(default_canvas)
+ maximum_steps = default_canvas.maximum_steps
+ row_to_intensity = -> (r) { r.map { |steps| steps.to_f / maximum_steps } }
+ default_canvas.canvas.map &row_to_intensity
+ end
+ end
- allowed_symbols.each_with_index do |symbol, index|
- intensity_to_symbol << [index.to_f / limit_gaps, symbol]
- end
+ class ASCII
+ include Intensity
- intensity_canvas = convert_to_intensity
- symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
-
- canvas_to_string = String.new
- symbol_canvas.each { |row| canvas_to_string << row.join.concat("\n") }
- canvas_to_string.chomp
+ def initialize(allowed_symbols)
+ @allowed_symbols = allowed_symbols
end
- def convert_to_html(pixel_size)
- html_code = "<!DOCTYPE html>\n<html>\n"
- html_code.concat(html_head(pixel_size))
- html_code.concat(html_body)
- end
+ def convert_canvas(default_canvas)
+ limit_gaps = @allowed_symbols.size - 1
- private
+ to_symbols = -> (symbol, index) { [index.to_f / limit_gaps, symbol] }
+ intensity_to_symbol = @allowed_symbols.map.with_index &to_symbols
- def convert_to_intensity
- row_to_intensity = -> (r) { r.map { |steps| steps.to_f / @maximum_steps } }
- @canvas.map &row_to_intensity
+ intensity_canvas = convert_to_intensity(default_canvas)
+ symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
+ symbol_canvas.reduce("") { |memo, row| memo + row.join + "\n" }.chomp
end
def convert_to_symbols(intensity_canvas, intensity_to_symbol)
convert_to_symbol = -> (intensity) do
intensity_to_symbol.find { |pair| pair.first >= intensity }.last
end
- row_to_symbol = -> (row) { row.map &convert_to_symbol }
- intensity_canvas.map &row_to_symbol
+ row_to_symbols = -> (row) { row.map &to_symbol }
+ intensity_canvas.map &row_to_symbols
end
-
- def html_head(pixel_size)
- "<head>\n\t<title>Turtle graphics</title>\n\n\t<style>\n\t\ttable{\n\t\t\t
- border-spacing: 0;\n\t\t\t}\n\n\t\ttr {\n\t\t\tpadding: 0;\n\t\t}\n\n\t\t
- td {\n\t\t\twidth: #{pixel_size}px;\n\t\t\theight: #{pixel_size}px;
- \n\n\t\t\tbackground-color: black;\n\t\t\tpadding: 0;\n\t\t}\n\t
- </style>\n</head>\n"
- end
-
- def html_body
- body_start = "<body>\n\t<table>"
- body_end = "<\t</table>\n</body>\n</html>"
- body_mid = String.new
- convert_to_intensity.each do |row|
- body_mid << "\t\t<tr>"
- row.each { |intensity| body_mid << "\n\t\t\t" << html_opacity(intensity) }
- body_mid << "\n\t\t</tr>"
- end
- body_start + body_mid + body_end
- end
-
- def html_opacity(intensity)
- quotes = '"'
- "<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
- end
end
- class ASCII
- attr_reader :allowed_symbols
+ class HTML
+ include Intensity
- def initialize(allowed_symbols)
- @allowed_symbols = allowed_symbols
+ def initialize(pixel_size)
+ @pixel_size = pixel_size
end
def convert_canvas(default_canvas)
- default_canvas.convert_to_ascii(@allowed_symbols)
+ html_head + html_body(default_canvas)
end
- end
- class HTML
- attr_reader :pixel_size
+ def html_head
+ "<!DOCTYPE html><html><head><title>Turtle graphics</title><style>table
+ {border-spacing: 0;}tr {padding: 0;}td {width: #{@pixel_size}px;
+ height: #{@pixel_size}px;background-color: black;padding: 0;}
+ </style></head>"
+ end
- def initialize(pixel_size)
- @pixel_size = pixel_size
+ def html_body(default_canvas)
+ body_start = "<body><table>"
+ body_end = "<</table></body></html>"
+ body_mid = String.new
+ convert_to_intensity(default_canvas).each do |row|
+ body_mid << "<tr>"
+ row.each { |intensity| body_mid << html_opacity(intensity) }
+ body_mid << "</tr>"
+ end
+ body_start + body_mid + body_end
end
- def convert_canvas(default_canvas)
- default_canvas.convert_to_html(@pixel_size)
+ def html_opacity(intensity)
+ quotes = '"'
+ "<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
end
end
end
end

Алекс обнови решението на 01.12.2015 23:14 (преди над 8 години)

class TurtleGraphics
class Turtle
ORIENTATIONS = [
[:up, [-1, 0]],
[:left, [0, -1]],
[:down, [1, 0]],
[:right, [0, 1]],
]
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
@orientation = :right
@canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
if [row, column] != [0, 0]
@canvas.remove_steps_at(0, 0)
@canvas.increment_value(row, column)
end
end
def move
@x += move_offset.first
@y += move_offset.last
correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
turn 1
end
def turn_right
turn -1
end
def draw(given_canvas = @canvas, &block)
instance_eval(&block) if block_given?
given_canvas.convert_canvas(@canvas)
end
def look(orientation)
@orientation = orientation
end
private
def turn(direction)
orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
end
def move_offset
ORIENTATIONS.find { |pair| @orientation == pair.first }.last
end
def correct_out_of_bounds_coordinates(x, y)
@x = x % @rows
@y = y % @columns
end
end
class Canvas
class Default
attr_reader :canvas, :maximum_steps
def initialize(rows, columns)
@canvas = Array.new(rows) { Array.new(columns, 0) }
@canvas[0][0] = 1
@maximum_steps = 1
end
def convert_canvas(_)
@canvas
end
def increment_value(x, y)
@canvas[x][y] += 1
@maximum_steps = @canvas[x][y] if @canvas[x][y] > @maximum_steps
end
def remove_steps_at(x, y)
@canvas[x][y] = 0
end
end
module Intensity
def convert_to_intensity(default_canvas)
maximum_steps = default_canvas.maximum_steps
row_to_intensity = -> (r) { r.map { |steps| steps.to_f / maximum_steps } }
default_canvas.canvas.map &row_to_intensity
end
end
class ASCII
include Intensity
def initialize(allowed_symbols)
@allowed_symbols = allowed_symbols
end
def convert_canvas(default_canvas)
limit_gaps = @allowed_symbols.size - 1
to_symbols = -> (symbol, index) { [index.to_f / limit_gaps, symbol] }
intensity_to_symbol = @allowed_symbols.map.with_index &to_symbols
intensity_canvas = convert_to_intensity(default_canvas)
symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
symbol_canvas.reduce("") { |memo, row| memo + row.join + "\n" }.chomp
end
def convert_to_symbols(intensity_canvas, intensity_to_symbol)
- convert_to_symbol = -> (intensity) do
+ to_symbol = -> (intensity) do
intensity_to_symbol.find { |pair| pair.first >= intensity }.last
end
row_to_symbols = -> (row) { row.map &to_symbol }
intensity_canvas.map &row_to_symbols
end
end
class HTML
include Intensity
def initialize(pixel_size)
@pixel_size = pixel_size
end
def convert_canvas(default_canvas)
html_head + html_body(default_canvas)
end
def html_head
"<!DOCTYPE html><html><head><title>Turtle graphics</title><style>table
{border-spacing: 0;}tr {padding: 0;}td {width: #{@pixel_size}px;
height: #{@pixel_size}px;background-color: black;padding: 0;}
</style></head>"
end
def html_body(default_canvas)
body_start = "<body><table>"
body_end = "<</table></body></html>"
body_mid = String.new
convert_to_intensity(default_canvas).each do |row|
body_mid << "<tr>"
row.each { |intensity| body_mid << html_opacity(intensity) }
body_mid << "</tr>"
end
body_start + body_mid + body_end
end
def html_opacity(intensity)
quotes = '"'
"<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
end
end
end
end

Алекс обнови решението на 01.12.2015 23:19 (преди над 8 години)

class TurtleGraphics
class Turtle
ORIENTATIONS = [
[:up, [-1, 0]],
[:left, [0, -1]],
[:down, [1, 0]],
[:right, [0, 1]],
]
def initialize(rows, columns)
@rows, @columns = rows, columns
@x, @y = 0, 0
@orientation = :right
@canvas = Canvas::Default.new(rows, columns)
end
def spawn_at(row, column)
@x = row
@y = column
if [row, column] != [0, 0]
@canvas.remove_steps_at(0, 0)
@canvas.increment_value(row, column)
end
end
def move
@x += move_offset.first
@y += move_offset.last
correct_out_of_bounds_coordinates(@x, @y)
@canvas.increment_value(@x, @y)
end
def turn_left
turn 1
end
def turn_right
turn -1
end
def draw(given_canvas = @canvas, &block)
instance_eval(&block) if block_given?
given_canvas.convert_canvas(@canvas)
end
def look(orientation)
@orientation = orientation
end
private
def turn(direction)
orientation_index = ORIENTATIONS.index { |pair| pair.first == @orientation }
changed_orientation = ORIENTATIONS[(orientation_index + direction) % 4]
@orientation = changed_orientation.first
end
def move_offset
ORIENTATIONS.find { |pair| @orientation == pair.first }.last
end
def correct_out_of_bounds_coordinates(x, y)
@x = x % @rows
@y = y % @columns
end
end
class Canvas
class Default
attr_reader :canvas, :maximum_steps
def initialize(rows, columns)
@canvas = Array.new(rows) { Array.new(columns, 0) }
@canvas[0][0] = 1
@maximum_steps = 1
end
def convert_canvas(_)
@canvas
end
def increment_value(x, y)
@canvas[x][y] += 1
@maximum_steps = @canvas[x][y] if @canvas[x][y] > @maximum_steps
end
def remove_steps_at(x, y)
@canvas[x][y] = 0
end
end
module Intensity
def convert_to_intensity(default_canvas)
maximum_steps = default_canvas.maximum_steps
row_to_intensity = -> (r) { r.map { |steps| steps.to_f / maximum_steps } }
default_canvas.canvas.map &row_to_intensity
end
end
class ASCII
include Intensity
def initialize(allowed_symbols)
@allowed_symbols = allowed_symbols
end
def convert_canvas(default_canvas)
limit_gaps = @allowed_symbols.size - 1
to_symbols = -> (symbol, index) { [index.to_f / limit_gaps, symbol] }
intensity_to_symbol = @allowed_symbols.map.with_index &to_symbols
intensity_canvas = convert_to_intensity(default_canvas)
symbol_canvas = convert_to_symbols(intensity_canvas, intensity_to_symbol)
symbol_canvas.reduce("") { |memo, row| memo + row.join + "\n" }.chomp
end
def convert_to_symbols(intensity_canvas, intensity_to_symbol)
to_symbol = -> (intensity) do
intensity_to_symbol.find { |pair| pair.first >= intensity }.last
end
row_to_symbols = -> (row) { row.map &to_symbol }
intensity_canvas.map &row_to_symbols
end
end
class HTML
include Intensity
def initialize(pixel_size)
@pixel_size = pixel_size
end
def convert_canvas(default_canvas)
html_head + html_body(default_canvas)
end
def html_head
"<!DOCTYPE html><html><head><title>Turtle graphics</title><style>table
{border-spacing: 0;}tr {padding: 0;}td {width: #{@pixel_size}px;
height: #{@pixel_size}px;background-color: black;padding: 0;}
</style></head>"
end
def html_body(default_canvas)
body_start = "<body><table>"
- body_end = "<</table></body></html>"
+ body_end = "</table></body></html>"
body_mid = String.new
convert_to_intensity(default_canvas).each do |row|
body_mid << "<tr>"
row.each { |intensity| body_mid << html_opacity(intensity) }
body_mid << "</tr>"
end
body_start + body_mid + body_end
end
def html_opacity(intensity)
quotes = '"'
"<td style=#{quotes}opacity: #{format('%.2f', intensity)}#{quotes}></td>"
end
end
end
end