Игра жизни в Haskell


Мне интересно, что люди думают о этой игре жизни. Я не думаю, что он работает правильно, но мне интересно, что люди думают общего дизайна.

module GameOfLife where
import Data.List

--Our types 
type Coord = (Int, Int)
origin = (0, 0)

--Think of an image more like an infinite manifold  
type DiscreteImage a = (Coord -> a)

--Cat is our "out of bounds"
data GamePiece = Alive | Dead | Cat
    deriving(Show, Eq)

type GameBoard = DiscreteImage GamePiece

type TransitionRule = (GameBoard -> GamePiece)

under_population = make_population_transition Alive Dead (<2)
overcrowding     = make_population_transition Alive Dead (>3)
birth            = make_population_transition Dead Alive (==3)

--Order is not important. Only one transition applies per peice, 
--the others operate like id 
transition_rule :: TransitionRule
transition_rule image = foldl (\image f -> f image) image 
    [
        under_population,
        overcrowding,
        birth
    ] origin

--return a new gameboard that applies the transition rule to every 
--spot on the gameboard
step :: TransitionRule -> GameBoard -> GameBoard
step trans gb = trans . offset gb  

--apply the tranisition rule n number of times
simulate :: Int -> TransitionRule -> GameBoard -> GameBoard
simulate count trans gb = foldl apply_step gb steps where 
  steps = replicate count (step trans)
  apply_step gb s = s gb

--translate the image
offset :: DiscreteImage a -> Coord -> Coord -> a
offset image (offsetX, offsetY) (x, y) = image (offsetX + x, offsetY + y) 

--make a square of coords
square_coords :: Int -> [Coord] 
square_coords radius = [(x, y) | x <- diameter_list, y <- diameter_list] where
        diameter_list = [(negate radius) .. radius]

--return a neighborhood with the center missing
punctured_neighborhood :: Int -> DiscreteImage a -> [a] 
punctured_neighborhood size image = map image $ filter (not . (==origin)) $ square_coords size 


--a little test
main = do 
    let (width, height) = get_bounds test_list
    let board1 = simulate 1 transition_rule test_board
    putStr $ unlines [ unwords [ ppr_peice $ board1 (x, y) | x <- [0..(width -1)]] | 
                y <- [0..(height - 1)]]

test_list = 
    [
        [Alive, Dead, Alive, Dead],
        [Alive, Dead, Dead, Alive],
        [Dead, Alive, Dead, Alive],
        [Dead, Alive, Alive, Alive]
    ]

test_board = image_from_lists test_list Cat

--helpers
population_count = length . filter is_Alive . punctured_neighborhood 1

--does nothing if the origin peice /= from, otherwise test for replacement
make_population_transition from to condition image | image origin == from = 
    if (condition . population_count) image 
            then replace image origin to 
            else image
make_population_transition from to test image | otherwise = image

replace image coord_to_replace value coord = if coord_to_replace == coord 
                                                 then value 
                                                 else image coord

bi f g h x = f x `h` g x

is_Alive Alive = True
is_Alive _ = False

from_Bool True = Alive
from_Bool False = Dead

image_from_lists xs default_value coord@(x, y) = if is_in_bounds1 coord
    then (xs !! x) !! y
    else default_value where
      bounds = get_bounds xs
      is_in_bounds1   = is_in_bounds bounds 

get_bounds xs = if null xs
    then (0, 0)
    else (length xs, length $ head xs)

is_in_bounds (xb, yb) (x, y) = (y < yb && y > -1) && (x < xb && x > -1)

ppr_peice Dead = "O"
ppr_peice Alive = "X"
ppr_peice Cat = "C"


1425
11
задан 9 марта 2011 в 03:03 Источник Поделиться
Комментарии
2 ответа

Ответ рефакторинг-это отличный и захватывает наибольшее количество очков. Одна вещь, в коде которого торчит является чрезмерным , если. Е. Г. рассмотрим

get_bounds xs = if null xs
then (0, 0)
else (length xs, length $ head xs)

Я думаю

getBounds [] = (0,0)
getBounds xs = (length xs, length $ head xs)

гораздо яснее. Или этот, который выглядит очень странно:

make_population_transition from to condition image | image origin == from = 
if (condition . population_count) image
then replace image origin to
else image
make_population_transition from to test image | otherwise = image

Вы должны написать

make_population_transition from to condition image 
| image origin == from &&
(condition . population_count) image = replace image origin to
| otherwise = image

11
ответ дан 30 мая 2011 в 06:05 Источник Поделиться

Во-первых, организовать ваш код сверху вниз уровня или наоборот сделало бы это немного легче читать. Кроме того, Хаскел код форматирования на заказ, как правило, использует корпус как - то , а не some_thing.

Также есть несколько предметов, которые могут быть более просто определяется (или не совсем):

isAlive = (==Alive)
bi = Data.Function.on
tranisitionRule = foldr (.) [underPopulation, overcrowding, birth]

Рекомендуется использовать Hoogle , чтобы проверить, если нечто уже существует, так что вы не должны переопределить его.

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

По идее бесконечной доски, вы должны рассмотреть возможность использования молнии какой-то. Это простая структура данных, которая отделяет другая структура данных на куски, чтобы позволить движение внутри, а не хранить путь к текущему элементу и изменить. Например,

toZipper xs      = ([],xs)
current (_,x:_) = x
left (x:ls,rs) = (ls,x:rs)
right (ls,x:rs) = (x:ls,rs)
unzipper (ls,rs) = reverse ls ++ rs

Эти функции работают с молнией, который представляет элементы при и после текущей позиции и элементы за текущей позицией в паре. Представлять что-то, что бесконечно в одном направлении тривиально (([],повтор 0), Луч), как бы что-то, что бесконечно в двух направлениях ((повтор 0,повтор 0), строки). Конечно, этот конкретный молнии могут быть сделаны более безопасными с Может быть и другие вещи, но я просто демонстрация концепции.

Надеюсь, вы видите, что это теперь может быть продлен на двухстороннюю бесконечные молнии из двусторонней бесконечные молнии, эффективно давая вам двумерной плоскости! Вот прямой и плоскости, с точки зрения точки:

line = (repeat point, repeat point)
plane = (repeat line, repeat line)

Эта модель может быть распространена на любое число измерений, но в конкретном случае игры в жизни самолет должно хватить. Для целей печати, вы можете создать 5-ти застежки вдоль линии (никогда не заглядывали левых элементов, пересекаемых левых элементов, текущий элемент, маятник вправо элементы, никогда не заглядывали элементов), так вы только распечатать соответствующие элементы. Из чего я могу почерпнуть из вашего кода, молния схеме не отличается от того, что вы делаете прямо сейчас, так что это не должно быть слишком трудно реализовать. Это также хорошо, чтобы abtract как узор молнии в другом модуле для дальнейшего использования. Даже если ваш самолет не будет бесконечной, попробуйте использовать молнии.

Я думаю, я остановлюсь здесь ради длины. Пожалуйста, задавайте вопросы.

10
ответ дан 29 мая 2011 в 06:05 Источник Поделиться