Які функціонально-програмувальні способи реалізації гри Життя Конвея [закрито]


12

Нещодавно я реалізував для веселої гри «Життя життя Конвей» у Javascript (насправді coffeescript, але те саме). Оскільки javascript можна використовувати як функціональну мову, я намагався залишитися до цього кінця спектру. Я не був задоволений своїми результатами. Я досить хороший OO програміст, і моє рішення пришпилене «старе-те саме». Отже, коротке запитання: який функціональний стиль (псевдокоду) робити?

Ось псевдокод для моєї спроби:

class Node
  update: (board) ->
    get number_of_alive_neighbors from board
    get this_is_alive from board
    if this_is_alive and number_of_alive_neighbors < 2 then die
    if this_is_alive and number_of_alive_neighbors > 3 then die
    if not this_is_alive and number_of_alive_neighbors == 3 then alive

class NodeLocations
  at: (x, y) -> return node value at x,y
  of: (node) -> return x,y of node

class Board
  getNeighbors: (node) -> 
   use node_locations to check 8 neighbors 
   around node and return count

nodes = for 1..100 new Node
state = new NodeState(nodes)
locations = new NodeLocations(nodes)
board = new Board(locations, state)

executeRound:
  state = clone state
  accumulated_changes = for n in nodes n.update(board)
  apply accumulated_changes to state
  board = new Board(locations, state)


@Оддав, що гнітюче над головою. Я визнаю основні поняття, але лише ледь
Джордж Мауер

Шлях над головою теж ... Я щойно розмістив це як приклад того, що може зробити майстер спеціалізованої мови. Назвіть це натхненням для всіх нас :)
Oded

@GeorgeMauer "фактично coffeescript, але те саме", це сумний день
Raynos,

Відповіді:


11

Ну і пара ідей. Я не фахівець з ПЗ, але ...

Досить зрозуміло, що у нас повинен бути тип, Boardякий представляє ігровий стан. Основою реалізації повинна бути evolveфункція типу evolve :: Board -> Board; тобто це означає, що Boardвід застосування правил гри до а Board.

Як ми повинні реалізувати evolve? A, Boardмабуть, має бути матриця nxm Cells. Ми могли б реалізувати функцію cellEvolveтипу, cellEvolve :: Cell -> [Cell] -> Cellяка задає a Cellта сусідній Cells обчислює Cellстан у наступній ітерації.

Ми також повинні реалізувати getCellNeighborsфункцію, яка витягує Cellсусідів s з Board. Я не зовсім впевнений у підписанні цього методу; Залежно від того, як ти реалізуєш, Cellі Boardти можеш мати, наприклад getCellNeighbors :: Board -> CoordElem -> CoordElem -> [Cell], що дана дошка та дві координати ( CoordElemбуде типом, який використовується для індексації позицій в а Board), дає тобі список змінної довжини сусідів (не всі комірки в платі мають однакова кількість сусідів - куточки мають 3 сусіди, кордони 5 та всі інші, 8).

evolveтаким чином, можна реалізувати, комбінуючи cellEvolveі getCellNeighborsдля всіх комірок у дошці, знову ж таки точна реалізація буде залежати від того, як ви реалізуєте Boardі Cell, але це має бути щось на зразок "для всіх комірок поточної дошки, отримати їх сусідів і використовувати їх для обчислення Нова комірка відповідної комірки ". Це повинно бути можливо завдяки загальному застосуванню цих функцій на всій дошці, використовуючи" відображення функції комірки дошки комітету ".

Інші думки:

  • Вам слід реально реалізувати cellEvolveтак, щоб він приймав як параметр тип, GameRulesякий кодує правила гри - скажіть список кортежів, (State,[(State,NumberOfNeighbors)],State)який говорить про даний стан та кількість сусідів у кожному штаті, який повинен бути державою в наступній ітерації . cellEvolveПідпис тоді може бутиcellEvolve :: GameRules -> Cell -> [Cell] -> Cell

  • Це, логічно, призведе до того , що ви evolve :: Board -> Boardперетворитесь на них evolve :: GameRules -> Board -> Board, так що ви можете користуватися evolveнезмінними різними GameRules, але ви можете піти на крок далі і cellEvolveзамість цього зробити підключення GameRules.

  • Граючи з getCellNeighborsвами, ви також можете зробити evolveзагальні щодо Boardтопології '', - ви могли б getCellNeighborsобернутись навколо країв будь-якої дошки, 3d дощок тощо.


9

Якщо ви пишете функціональну версію програмування Life, ви зобов'язані самій дізнатися про Алгоритм Госпера. Він використовує ідеї функціонального програмування для досягнення трильйонів поколінь в секунду на дошках трильйонів квадратів збоку. Це звучить неможливо, я знаю, але цілком можливо; У мене є непогана реалізація в C #, яка легко обробляє квадратні дошки 2 ^ 64 квадрата збоку.

Хитрість полягає в тому, щоб скористатися величезною самоподібністю дощок Life у часі та просторі. Запам'ятовуючи майбутній стан великих ділянок дошки, ви можете швидко просунути величезні секції відразу.

Я вже багато років маю на увазі блог про вступ початківців до Алгоритму Госпера, але у мене ніколи не було часу. Якщо я закінчу це, я опублікую посилання тут.

Зауважте, що ви хочете шукати Алгоритм Госпера для обчислень життя , а не Алгоритм Госпера для обчислення гіпергеометричних сум.


виглядає цікаво - все ще чекаю на це посилання, хоча ...;)
jk.

3

Милий збіг обставин, ми вирішили саме цю проблему в нашій лекції Haskell. Перший раз я це бачив, але ось посилання на вихідний код нам дали:

http://pastebin.com/K3DCyKj3


Ви не проти пояснити, що це робить, і чому ви рекомендуєте це відповісти на поставлене запитання? "Відповіді лише на посилання" не дуже вітаються на Stack Exchange
gnat

3

Для натхнення ви можете подивитися на реалізації на RosettaCode .

Наприклад, є функціональні версії Haskell та OCaml, які створюють нову плату щоразу, застосовуючи функцію до попередньої, в той час як графічна версія OCaml використовує два масиви і по черзі оновлює їх для швидкості.

Деякі з реалізацій розкладають функцію оновлення плати на функції для підрахунку мікрорайону, застосовуючи правило життя та ітерацію над дошкою. Вони здаються корисними компонентами на основі функціонального дизайну. Спробуйте змінити лише плату, зберігаючи все інше як чисті функції.


1

Ось коротка суто функціональна версія у Clojure. Вся заслуга належить Крістофу Гранду, який опублікував це у своєму дописі в блозі: Гра життя Конвея

(defn neighbours [[x y]]
  (for [dx [-1 0 1] 
        dy (if (zero? dx) [-1 1] [-1 0 1])]
    [(+ dx x) (+ dy y)]))

(defn step [cells]
  (set (for [[loc n] (frequencies (mapcat neighbours cells))
             :when (or (= n 3) (and (= n 2) (cells loc)))]
         loc)))

Потім гру можна грати, повторно застосовуючи функцію "крок" до набору комірок, наприклад:

(step #{[1 0] [1 1] [1 2]})
=> #{[2 1] [1 1] [0 1]}

Розумність - це частина (клітини сусідів сусідів) - це означає, щоб створити список з восьми сусідів для кожної з активних комірок і об'єднати їх усіх разом. Тоді кількість показів кожної комірки у цьому списку можна порахувати (частоти ....), і нарешті ті, які мають правильний підрахунок частоти, переходять до наступного покоління.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.