Шеста задача
- Краен срок:
- 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. Ако пуснете кода си публично (например във форумите), ще смятаме
това за преписване.