Eu recentemente implementei por diversão o Jogo da Vida de Conway em Javascript (na verdade, coffeescript, mas a mesma coisa). Como o javascript pode ser usado como uma linguagem funcional, eu estava tentando permanecer nesse extremo do espectro. Eu não estava feliz com meus resultados. Sou um programador de OO razoavelmente bom e minha solução tem a mesma idade e a mesma idade. Pergunta longa: qual é o estilo funcional (pseudocódigo) de fazê-lo?
Aqui está o Pseudocódigo para minha tentativa:
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)
functional-programming
George Mauer
fonte
fonte
Respostas:
Bem, algumas idéias. Não sou especialista em FP, mas ...
É bastante claro que deveríamos ter um tipo
Board
que represente um estado de jogo. A base da implementação deve ser umaevolve
função do tipoevolve :: Board -> Board
; significando que produz aBoard
partir da aplicação das regras do jogo a aBoard
.Como devemos implementar
evolve
? ABoard
provavelmente deve ser uma matriz nxm deCell
s. Poderíamos implementar uma funçãocellEvolve
do tipocellEvolve :: Cell -> [Cell] -> Cell
que, dado aCell
e seus vizinhos,Cell
calcule oCell
estado na próxima iteração.Também devemos implementar uma
getCellNeighbors
função que extrai osCell
vizinhos de aBoard
. Não tenho muita certeza da assinatura desse método; dependendo de como você implementaCell
eBoard
você poderia ter, por exemplogetCellNeighbors :: Board -> CoordElem -> CoordElem -> [Cell]
, que dado um quadro e duas coordenadas (CoordElem
seria o tipo usado para indexar posições em aBoard
), fornece uma lista de comprimento variável dos vizinhos (nem todas as células no quadro têm o mesmo número de vizinhos - os cantos têm 3 vizinhos, as fronteiras 5 e todos os demais, 8).evolve
Assim, pode ser implementado combinandocellEvolve
egetCellNeighbors
para todas as células no quadro, novamente a implementação exata dependerá de como você o implementaBoard
eCell
, mas deve ser algo como "para todas as células no quadro atual, obtenha seus vizinhos e use-os para calcular o célula correspondente da nova placa '. Isso deve ser possível com uma aplicação genérica dessas funções em toda a placa usando um "mapa da função da célula da placa".Outros pensamentos:
Você realmente deve implementar
cellEvolve
para que tome como parâmetro um tipoGameRules
que codifique as regras do jogo - digamos, uma lista de tuplas(State,[(State,NumberOfNeighbors)],State)
que diz um determinado estado e o número de vizinhos em cada estado, que deve ser o estado na próxima iteração .cellEvolve
A assinatura decellEvolve :: GameRules -> Cell -> [Cell] -> Cell
Isso levaria você logicamente a se
evolve :: Board -> Board
transformarevolve :: GameRules -> Board -> Board
, para que você pudesse usarevolve
inalterado com diferenteGameRules
, mas você poderia dar um passo adiante e torná-locellEvolve
plugável em vez deGameRules
.Brincar com
getCellNeighbors
você também pode serevolve
genérico em relação àBoard
topologia da - você pode tergetCellNeighbors
que envolver as bordas das placas, placas 3D etc.fonte
Se você está escrevendo uma versão de programação funcional do Life, deve a si mesmo aprender sobre o Algoritmo de Gosper. Ele usa idéias da programação funcional para atingir trilhões de gerações por segundo em quadros trilhões de quadrados de um lado. Parece impossível, eu sei, mas é perfeitamente possível; Eu tenho uma implementação pequena e agradável em C # que lida facilmente com placas quadradas 2 ^ 64 quadrados de um lado.
O truque é tirar proveito da enorme auto-similaridade das placas Life no tempo e no espaço. Memorizando o estado futuro de grandes seções do quadro, você pode avançar rapidamente grandes seções ao mesmo tempo.
Estou planejando publicar uma introdução para o algoritmo de Gosper há muitos anos, mas nunca tive tempo. Se eu terminar, postarei um link aqui.
Observe que você deseja procurar os cálculos do algoritmo de Gosper para a vida , não o algoritmo de Gosper para calcular somas hipergeométricas.
fonte
Boa coincidência, abordamos esse problema exato em nossa palestra de Haskell hoje. A primeira vez que o vi, mas aqui está um link para o código-fonte que recebemos:
http://pastebin.com/K3DCyKj3
fonte
Você pode olhar as implementações no RosettaCode para obter inspiração.
Por exemplo, existem versões funcionais de Haskell e OCaml que criam uma nova placa a cada turno aplicando uma função à anterior, enquanto a versão gráfica do OCaml usa duas matrizes e as atualiza alternadamente para velocidade.
Algumas das implementações decompõem a função de atualização do quadro em funções para contar a vizinhança, aplicar a regra de vida e iterar sobre o quadro. Esses parecem componentes úteis para basear um design funcional. Tente modificar apenas o quadro, mantendo tudo o resto como funções puras.
fonte
Aqui está uma versão curta e puramente funcional no Clojure. Todo o crédito é para Christophe Grand, que publicou isso em seu blog: Game of Life, de Conway
O jogo pode ser jogado aplicando repetidamente a função "step" a um conjunto de células, por exemplo:
A inteligência é a parte (células vizinhas mapcat) - o que isso faz é criar uma lista de oito vizinhos para cada uma das células ativas e concatená-los todos juntos. Em seguida, o número de vezes que cada célula aparece nesta lista pode ser contada (frequências ...) e, finalmente, as que possuem a contagem correta de frequências passam para a próxima geração.
fonte