А теперь представим, что мы хотим сделать софтину, способную работать с любыми клеточными автоматами. И мы хотим чтобы это всё было расширяемо плагинами.
Так вот если в life есть понятие "живой клетки", а в некоторых других есть по крайней мере понятие "клетки" и "фона", то в общем случае
эффективный алгоритм уже неприменим. И надо сканировать всё игровое поле, для каждой клетки вызывая из плагина функцию new_state(neighbours[9]).
FAIL.
Допустим мы всё же хотим оптимизации. Тогда API плагинов усложняется - нам уже надо позволять плагину перехватить вычисление полного состояния по известным ему алгоритмам. С другой стороны, получается в какой-то мере дублирование кода между плагинами, различающими "фон".
Если мы готовы всё это делать объектно-ориентированным, то будет базовый класс CellAutomata от которого все наследуются, переопределяя как минимум getCellNewState, а кому надо - ещё и getFullNextState, которое в прототипе будет реализовано тупым полным просмотром. Ну дальше конечно экспортируем наследующий его класс LifeStyle с хорошим getFullNextState, заточенным под жизне-образные.
На самом деле в ООП это просто красиво выглядит - но вообще-то никто не мешает сделать register_plugin(string name, string description, void * getCellNewState, void * getFullNextState, ...) куда передаём колбэки или NULL если не хотим переопределять.
Ну хорошо. А в каком виде этот самый getFullNextState должен возвращать новое состояние? Заполнять БольшойМассив™? Насколько большой, если мы конкретно в life хотим играть на потенциально бесконечном поле?