2016-12-10 2 views
0

Я пытаюсь сделать игру жизни Конвея для практики, поскольку я сделал это раньше в C++, но мне было интересно, как я мог бы создать массив 2d с учетом высоты и ширины в Ocaml со случайно назначенными икру на основе определенной плотности населения, я пытаюсь использовать make_matrix кстати. Все обучающие материалы, которые я нашел в Интернете, используют графику или какой-либо другой жесткий код (например, код rosetta), но я хотел бы попытаться избежать этого, чтобы у меня были некоторые вариации. Спасибо.OCaml 2D-массив для игры в жизни

Это мой текущий код, и он возвращает несвязанное значение.

print_string "Input width "; 
let num = read_int() in 
print_string "Input height"; 
let num2 = read_int() in 
    myArray = Array.make_matrix num num2 0; 

error: Unbound value myArray; 

ответ

1

У вас есть несколько вариантов здесь:

  1. биг-массивы
  2. массивы
  3. Карты

Bigarrays может иметь только цифры, как их значения, но они могут быть произвольные размеры и размеры. Они также имеют приятный интерфейс для доступа к многомерным данным. Массивы могут иметь значения любого типа, и вы можете создавать массивы массивов для моделирования многомерных пространств. Оба массива и bigarrays являются обязательными структурами данных, так что это будет ваш код. Вот почему я предлагаю вам использовать постоянные карты, чтобы сделать ваш код чисто функциональным (если вы хотите практиковать в OCaml, лучше практиковать в его функциональном подпространстве).

Пример с картами

Итак, давайте определим тип для государства и координат:

type state = Dead | Live 

type coord = {x : int; y : int} 

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

Теперь мы можем определить реализацию структуры платы данных:

module Board = Map.Make(struct 
    type t = coord 
    let compare = compare 
    end) 

В качестве примера использования, давайте определим функцию высшего порядка под названием fold_neighbors, которая будет применяться в условии пользователя функции для каждой заселенной соседней соты ,

let neighbors {x;y} = [ 
    x, y+1; 
    x+1,y+1; 
    x+1,y; 
    x+1,y-1; 
    x, y-1; 
    x-1,y-1; 
    x-1,y; 
    x-1,y+1; 
] |> List.map (fun (x,y) -> {x;y}) 

let fold_neighbors board cell ~f ~init = 
    neighbors cell |> 
    List.fold_left (fun acc n -> 
     try f acc (Board.find n board) 
     with Not_found -> acc) 
    init 

Используя эту обобщенную функцию итератора, мы можем определить специализированные функции, такие как count_live_neighbors:

let count_live_neighbors = 
    fold_neighbors ~init:0 ~f:(fun count nb -> match nb with 
     | Live -> count + 1 
     | Dead -> count) 

Реализация предполагает бесконечный совет, если вы хотите сделать это ограничено, то вам необходимо адаптировать fold_neighbors чтобы исключить тех, кто лежит вне доски.

Пример с массивами

Другой альтернативой является использование обычных массивов. Мы можем использовать удобную make_matrix функцию, которая будет создавать 2d массива заданных размеров и заполнить его с значением параметра, например,

make_matrix 300 400 0 

создаст нулевой заполненную матрицу с 300 строк и 400 столбцов.

В нашем случае мы не хотим заполнять матрицу числами, но с состояниями.Нам понадобится тип для состояния, который может представлять 3 состояния, мы будем повторно использовать тип state из предыдущего примера, но мы также переносим его в тип option, чтобы была представлена ​​или мертвая или живая ячейка в Some Dead или Some Live, а безлюдный один будет просто None, так что мы можем создать пустую доску с

Array.make_matrix width height None 

чтобы инициализировать нашу доску можно сначала создать, а затем дать жизнь случайно выбранные клетки на основе требуемой плотности населения. Мы будем представлять плотность с числом с плавающей запятой между нулем и одним, например, плотность 0.1 означает, что примерно 10% ячеек будут жить. Чтобы оставаться более функциональным, мы будем использовать Array.map для преобразования наших массивов. Явные циклы и итерации с INPLACE модификаций будет быстрее и более идиоматических конечно, но только ради эксперимента мы будем использовать более функциональный подход:

let create_board width height density = 
    Array.make_matrix width height None |> 
    Array.map (Array.map (fun cell -> 
     if Random.float 1.0 > density then Some Live else None)) 

Однако, мы можем сделать это более приятно, если мы не будем создавать пустая доска с первого взгляда, но начнется с инициализированной платы. Для этого мы можем использовать Array.init функции, что позволяет придать различное значение для каждой ячейки, вот пример лучше create_board функции:

let create_board width height density = 
    Array.init height (fun _ -> 
     Array.init width (fun _ -> 
      if Random.float 1.0 > density then Some Live else None)) 

Array.init функция называет при условии пользовательской функции с индексом элемента , В нашем случае вероятность жизни не зависит от координат, поэтому мы можем просто игнорировать позицию, поэтому мы использовали _ для обозначения того, что мы не будем использовать аргумент.

+0

Спасибо за ответ, я решил пойти с матрицей, и знаете ли вы, может ли этот способ заполнить матрицу живыми или мертвыми клетками на основе определенной плотности населения? Я использую плотность ширины ширины make_matrix –

+0

Хорошо, я обновил ответ с помощью массива. Счастливая верховая езда :) – ivg

Смежные вопросы