Ивайло обнови решението на 15.12.2011 02:15 (преди около 13 години)
+require 'set'
+
+module GameOfLife
+ class Board
+ include Enumerable
+
+ def initialize *live_cells
+ @live_cells = live_cells.to_set
+ end
+
+ def each &block
+ @live_cells.each(&block)
+ end
+
+ def [] x, y
+ @live_cells.include? [x, y]
+ end
+
+ def next_generation
+ new_cells = []
+
+ each do |x, y|
+ new_cells += next_generation_neighbours(x, y)
+ end
+
+ Board.new *new_cells
+ end
+
+ def neighbours_count x, y
+ valid_neighbours = neighbours(x, y).map do |neighbour_x, neighbour_y|
+ # I would rather use if, but I can not affort three lines of code and I don't
+ # want another method for this, so I'll stick with this not so beautiful line
+ (neighbour_x != x or neighbour_y != y and self[neighbour_x, neighbour_y]) ? 1 : 0
+ end
+
+ valid_neighbours.inject(&:+)
+ end
+
+ def neighbours x, y
+ [*-1..1].product([*-1..1]).map { |move_x, move_y| [move_x + x, move_y + y] }
+ end
+
+ def next_generation_neighbours x, y
+ neighbours(x, y).select do |neighbour_x, neighbour_y|
+ count = neighbours_count(neighbour_x, neighbour_y)
+
+ count == 2 and self[neighbour_x, neighbour_y] or count == 3
+ end
+ end
+ end
+end
Няколко дребни коментара:
-
Set
е добър избор -
initialize
:... { @live_cells << pair }
, понеже надали ще променяш тези двойки впоследствие. Още повече, че може направо@live_cells = args.to_set
:) Акоargs
се очаква да съдържа консистентен тип неща, преименувай го на нещо по-смислено. - Стига си
dup
-вал :) Добра практика е да се внимава, но обикновено който приема аргумента, се грижи да не го модифицира (и даdup
-ва, ако трбява); с други думи, подавай спокойно не-dup
-нати неща на други функции. - Дефинирал си
neighbours
по забавен начин :)
Като за 3 часа през нощта, толкова коментари :)