Шеста задача

Предадени решения

Краен срок:
02.12.2015 17:30
Точки:
6

Срокът за предаване на решения е отминал

Костенурка

Turtle graphics е метод на рисуване чрез обект (костенурка), който се движи спрямо предната си позиция. Костенурката се поставя на определено място в двумерна координатна система и може да приема следните команди:

  • премести се напред
  • завърти се наляво
  • завърти се надясно

Завъртанията ще стават винаги на 90 градуса и спрямо текущата ориентация на костенурката - ако гледа на запад и я накараме да се завърти наляво - ще се завърти на юг.

Пътят, по който е минала костенурката, описва рисунка (включително първото и последното ѝ местоположение).

Вашата задача

Имплементирайте клас TurtleGraphics::Turtle, чрез който можете да управлявате такава костенурка. Ще опростим нещата, като ограничим стойностите на координаните само до цели числа. Тоест ще "рисуваме" в матрица.

Класът се инициализира с два параметъра - брой редове и брой колони на матрицата (платното, върху което ще рисуваме).

Самата матрица представлява масив от масиви (редове). Стойността във всяка клетка е цяло число, съответстващо на броя пъти, които костенурката е стъпвала на съответната позиция. Завъртането не се брои за отделно стъпване.

Ето пример за използване на класа:

canvas = TurtleGraphics::Turtle.new(3, 3).draw do
  move
  turn_right
  move
  turn_left
  move
end

canvas #=> [
             [1, 1, 0],
             [0, 1, 1],
             [0, 0, 0],
           ]

От примера можете да забележите няколко неща:

  • Класът трябва да има метод draw, който приема блок
  • Вътре в блока трябва да съществуват методите:

    • move - костенурката се премества една позиция напред, в зависимост от това на къде гледа.
    • turn_left - завърта се наляво, без да се мести.
    • turn_right - завърта се надясно, без да се мести.
    • spawn_at(row, column) - задава началната позиция на костенурката.
    • look(orientation) - задава конкретна ориентация на костенурката. Ориентациите са :left, :up, :right и :down.
  • По подразбиране, резултатът от draw е матрицата със стойности, съответстващи на броя стъпки на костенурката върху съответните клетки. Това означава, че може да има произволно положително число като стойност на клетка.

  • Ако костенурката излезе извън матрицата, трябва да бъде "прехвърлена" в началото на съответния ред или колона. Например ако е в края на ред, ориентирана на изток и бъде изпълнена командата move, новата позиция на костенурката трябва да е в началото на същия ред, със същата ориентация.

ASCII canvas

Казваме, че това е начин за рисуване, но една матрица с числа не прилича много на рисунка. Затова ще създадем още два начина за представяне на рисунката - чрез ASCII графика и HTML. За ASCII рисуването трябва да се напише клас TurtleGraphics::Canvas::ASCII, чиято инстанция може да бъде подадена като аргумент на TurtleGraphics::Turtle#draw. Тогава резултатът от този метод трябва да върне низ, представляващ рисунката с определени символи.

Пиксел ще наричаме дадена клетка от двумерната ASCII рисунка. Интензитет на този пискел ще наричаме отношението на цялото неотрицателно число, съответстващо на броя пъти, в които костенурката е минала през пиксела (позицията), спрямо максималния брой стъпки, направени от костенурката в която и да е клетка от canvas-а.

initialize методът на ASCII приема като аргумент списък от символи (не инстанции на Ruby-типа Symbol, а букви, знаци, пунктуация...), които да се използват за рисуването. Първият от тези символи се използва за изобразяване на пиксели с интензитет 0. Символите след първия се използват за изобразяване на равни интервали на интензитет на всеки пиксел.

Например, след изпълнение на следния код:

ascii_canvas = TurtleGraphics::Canvas::ASCII.new([' ', '-', '=', '#'])
ascii = TurtleGraphics::Turtle.new(2, 2).draw(ascii_canvas) do
  move
  turn_right
  move
  2.times { turn_right }
  move
  turn_left
  move
  turn_left
  move
  2.times { turn_right }
  move
end

Стойността на ascii ще е:

#=
--

В примера, символите представляват следните интензитети:

  • ' ' - == 0
  • '-' - > 0 и <= 0.(3)
  • '=' - > 0.(3) и <= 0.(6)
  • '#' - > 0.(6) и <= 1

HTML canvas

За да рисуваме с HTML, трябва да се напише клас TurtleGraphics::Canvas::HTML.

initialize методът на HTML приема един аргумент - размера във физически пиксели на един виртуален пиксел от рисунката (една клетка от таблицата). Реално, виртуалните пиксели от картинката ще се изобразяват с квадрати, чиито размер задаваме в този конструктор.

Ето пример:

html_canvas = TurtleGraphics::Canvas::HTML.new(5)
html = TurtleGraphics::Turtle.new(3, 3).draw(html_canvas) do
  move
  turn_right
  move
  turn_left
  move
end

Стойността на html трябва да е следната:

<!DOCTYPE html>
<html>
<head>
  <title>Turtle graphics</title>

  <style>
    table {
      border-spacing: 0;
    }

    tr {
      padding: 0;
    }

    td {
      width: 5px;
      height: 5px;

      background-color: black;
      padding: 0;
    }
  </style>
</head>
<body>
  <table>
    <tr>
      <td style="opacity: 1.00"></td>
      <td style="opacity: 1.00"></td>
      <td style="opacity: 0.00"></td>
    </tr>
    <tr>
      <td style="opacity: 0.00"></td>
      <td style="opacity: 1.00"></td>
      <td style="opacity: 1.00"></td>
    </tr>
    <tr>
      <td style="opacity: 0.00"></td>
      <td style="opacity: 0.00"></td>
      <td style="opacity: 0.00"></td>
    </tr>
  </table>
</body>
</html>

Размерът на виртуален пиксел в таблицата се определя от следните два реда CSS:

width: 5px;
height: 5px;

Всеки виртуален пиксел се състои от <td> елемент, на който е приложен стил (CSS) чрез свойството opacity. opacity може да има стойности числа с плаваща запетая, от 0.0 до 1.0. Всеки ред от виртуалната матрица, тоест, всяка последователност от N на брой <td> елемента, трябва да е ограден в <tr> HTML таг. Стойността на opacity трябва да съответства на интензитета на съответния виртуален пиксел (клетка). Така, opacity: 1.00 съответства на максималния интензитет, а opacity: 0.00 - на 0 стъпки. Закръгляйте стойността на opacity до втори знак. Може да използвате format('%.2f', intensity), за да превърнете число в низ с два символа след десетичната точка.

Така, ако матрицата на стъпките изглежда по този начин:

[
  [0, 2, 3],
  [1, 1, 0]
]

То HTML таблицата ще е:

<table>
  <tr>
    <td style="opacity: 0.00"></td>
    <td style="opacity: 0.67"></td>
    <td style="opacity: 1.00"></td>
  </tr>
  <tr>
    <td style="opacity: 0.33"></td>
    <td style="opacity: 0.33"></td>
    <td style="opacity: 0.00"></td>
  </tr>
</table>

Драконов фрактал

За награда, след като сте си решили задачата, изпълнете следния код, запазете резултатната стойност на html във файл (например така: File.write('dragon.html', html) и отворете HTML файла в браузър:

canvas = TurtleGraphics::Canvas::HTML.new(5)
html = TurtleGraphics::Turtle.new(200, 200).draw(canvas) do
  spawn_at 100, 100

  step = 0

  4300.times do
    is_left = (((step & -step) << 1) & step) != 0

    if is_left
      turn_left
    else
      turn_right
    end
    step += 1

    move
  end
end

Примерен тест

Примерните тестове се намират в GitHub хранилището с домашните. За информация как да ги изпълните, погледнете README-то на хранилището.

Ограничения

Тази задача има следните ограничения:

  • Най-много 85 символа на ред
  • Най-много 10 реда на метод
  • Най-много 2 нива на влагане
  • Най-много 4 аргумента на метод

Ако искате да проверите дали задачата ви спазва ограниченията, следвайте инструкциите в описанието на хранилището за домашните.

Няма да приемаме решения, които не спазват ограниченията. Изпълнявайте rubocop редовно, докато пишете кода. Ако смятате, че rubocop греши по някакъв начин, пишете ни на fmi@ruby.bg, заедно с прикачен код или линк към такъв като private gist. Ако пуснете кода си публично (например във форумите), ще смятаме това за преписване.