Шеста задача

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

Краен срок:
20.12.2011 23:59
Точки:
6

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

Game of Life

Задачата ви на това домашно ще бъде да имплементирате Conway’s Game of Life. Правилата на играта са прости и целта ви ще е да създадете клас, отговарящ на безкрайния двуизмерен свят на играта и имплементиращ въпросните правила.

Правила

Светът, в който се развива действието, е безкрайна двуизмерна координатна система. Ще го наричаме игрална дъска. На всяка координата съответства клетка. Всяка клетка има точно две състояния — жива или мъртва. Освен състояния, клетката има и осем съседа — това са обграждащите я клетки (лява, дясна, горна, долна, горе-ляво, горе-дясно, долу-ляво и долу-дясно). Поколение ще наричаме моментното състояние на игралната дъска — т.е. координатите на живите в момента клетки. От текущото поколение можем да поискаме да еволюира в следващо, прилагайки следните правила:

  1. Всяка жива клетка с по-малко от два живи съседа, умира (underpopulation).
  2. Всяка жива клетка с два или три живи съседа продължава да живее и в следващото поколение.
  3. Всяка жива клетка с повече от три живи съседа, умира (overpopulation).
  4. Всяка мъртва клетка с точно три живи съседа, се ражда (reproduction).

Докато "генерирате" следващото поколение, текущото не се променя и правилта се прилагат само над текущото.

Обикновено играта започва с някакво ръчно зададено, първоначално състояние. За координати ще използваме цели числа.

Реализация

За да имплементирате гореописаните правила, ще искаме от вас да създадете клас Board, който има няколко определени метода.

Конструктор

За да можем да зададем началното състояние на игралната дъска, е необходимо да можем да конструираме инстанции на класа Board, подавайки на конструктора му нула или повече двойки координати на живите клетки в следния формат: [x1, y1], [x2, y2], …

Например:

board = GameOfLife::Board.new [1, 2], [1, 3], [5, 6]

Индексиране на живите клетки

Класът Board трябва да отговаря на #[] и при подадени две координати — x и y, да връща истина, ако клетката на въпросната координата е жива и лъжа, ако не е.

Например:

board = GameOfLife::Board.new [1, 2], [1, 3], [5, 6]

board[1, 2] # true
board[0, 2] # false

Итериране по живите клетки

Класът Board трябва да бъде Enumerable. Посредством Board#each трябва да може да се итерира по двойката координати [x, y] на живите в текущото поколение клетки. Освен стандартното итериране, Board трябва да поддържа и всички останали методи от Enumerable, с изключение на методите, изискващи наредба. Редът на обхождане на живите клетки не е от значение.

Примерно извикване:

board = GameOfLife::Board.new [1, 2], [1, 3]

board.each do |x, y|
  puts "The cell at (#{x}, #{y}) is alive"
end

Брой живи клетки

Както всеки Enumerable, всяка инстанция на Board трябва да отговаря и на метода #count, който да връща цяло число — броя на живите клетки в поколението.

Еволюция на поколението

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

Например:

board    = GameOfLife::Board.new [1, 2], [1, 3], [1, 4]
next_gen = board.next_generation

next_gen[1, 2] # false
next_gen[0, 3] # true
next_gen[2, 3] # true

Дизайн

За вътрешното представяне на игралната дъска може да използвате каквото прецените за удачно. Изисквания към публичното ви API:

  • Да имате клас Board, който се намира в собствено именовано пространство, кръстено GameOfLife
  • Конструктор, имплементиран по гореописания начин
  • Да имате метод Board#next_generation, връщащ нова дъска (инстанция на Board) със следващото поколение, без да мутира състоянието на текущата
  • Индексиращ метод Board#[] със семантиката, описана по-горе
  • Методи Board#each, #count и прочее, отговарящи на Enumerable-интерфейса

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

Малък примерен тест може да намерите в GitHub репозиторията с домашните. Тъй като задачата е лека и условието е много просто, оставяме на вас да допълните примерния тест с каквото прецените.

Ограничения

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

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

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

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