Я пытаюсь реализовать игру Конвея в Haskell для класса и имею код, который выглядит следующим образом для проверки соседей ячейки (x, y) на квадратной доске размером (w, h):
liveNeighbors :: ((Int,Int), [[Bool]]) -> (Int,Int) -> Int
liveNeighbors arr@((w,h), cells) (x,y) =
let
j = w -1
k = h - 1
checkcell (x,y) = if (cells !! y) !! x then 1 else 0 in
case (x,y) of
-- Check right, bottom, and bottom right
(0,0) -> checkcell (x,y+1) + checkcell (x+1,y) + checkcell (x+1,y+1)
-- Check bottom, left, right, bottom left, bottom right
(_,0) -> checkcell (x,y+1) + checkcell (x-1,y) + checkcell (x+1,y)
+ checkcell (x+1,y+1) + checkcell (x-1,y+1)
-- check bottom, top, right, top right, bottom right
(0,_) -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x+1,y)
+ checkcell (x+1,y+1) + checkcell (x+1,y-1)
-- check top, left, right,top left, top right
(_, k) -> checkcell (x,y-1) + checkcell (x-1,y) + checkcell (x+1,y)
+ checkcell (x-1,y-1) + checkcell (x+1,y-1)
-- check bottom, top, left, top left, bottom left
(j, _) -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x-1,y)
+ checkcell (x-1,y+1) + checkcell (x-1,y-1)
-- check top, left, top left
(j,k) -> checkcell (x,y-1) + checkcell (x-1,y) + checkcell (x-1,y-1)
-- check bottom, left, bottom left
(j,0) -> checkcell (x,y+1) + checkcell (x-1,y) + checkcell (x-1,y+1)
-- check top, top right, right
(0,k) -> checkcell (x,y-1) + checkcell (x+1,y-1) + checkcell (x+1,y)
-- check all surrounding
(_,_) -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x+1,y)
+ checkcell (x-1,y) + checkcell (x+1,y+1) + checkcell (x+1,y-1)
+ checkcell (x-1,y+1) + checkcell (x-1,y-1)
Я использую j
и k
, чтобы соответствовать границам массива. Я проверяю все крайние случаи и углы, однако код не работает для размеров массива и строк в (_, max_row)
, (max_column, _)
, (max_column, max_row)
и дает неверные значения. Это вывод, который дает мне ghci:
> world = ((3,3), [[False, False, False], [False, False, False], [False, True,True]])
> liveNeighbors world (2,2)
*** Exception: Prelude.!!: index too large
> liveNeighbors world (0,2)
*** Exception: Prelude.!!: index too large
> liveNeighbors world (0,1)
1
> liveNeighbors world (1,1)
0
Значение для (1,1)
должно давать 2, так как нижнее и нижнее правое значения верны, однако оно дает только значение 1. Есть идеи, почему это происходит?
Я не ищу решение для кода, если бы кто-то мог концептуально указать мне правильное направление в отношении того, что происходит не так, это было бы очень признательно. Это для проекта, ограничивающегося обработкой списков, поэтому никакие другие структуры данных, кроме списков или кортежей, не допускаются.
В этом коде есть 2 проблемы:
порядок узоров: в вашем примере вы получаете сообщение об ошибке при проверке кода на позицию (2,2)
. Это потому, что шаблон (_, k)
идет раньше, чем (j, k)
, поэтому вы проверяете «верхний, левый, правый, верхний левый, верхний правый», а не «верхний, левый, верхний левый», как вы ожидали.
переменные в шаблонах: шаблон (_, k)
успешно совпадает с другими значениями (x, y)
в вашем коде, а шаблоны под ним перекрываются. Вы просто присваиваете k
y
, а не сравниваете их
Итак, вы можете написать такой код (я использую расширение MultiWayIf
для большего сходства с оригиналом, но это просто синтаксический сахар):
{-# LANGUAGE MultiWayIf #-}
neighbors :: ((Int,Int), [[Bool]]) -> (Int,Int) -> Int
neighbors arr@((w,h), cells) (x,y) =
let
j = w -1
k = h - 1
checkcell (x,y) = if (cells !! y) !! x then 1 else 0 in
if
-- Check right, bottom, and bottom right
| (x, y) == (0,0) -> checkcell (x,y+1) + checkcell (x+1,y) + checkcell (x+1,y+1)
--check top, left, top left
| (x, y) == (j,k) -> checkcell (x,y-1) + checkcell (x-1,y) + checkcell (x-1,y-1)
--check bottom, left, bottom left
| (x, y) == (j,0) -> checkcell (x,y+1) + checkcell (x-1,y) + checkcell (x-1,y+1)
-- check top, top right, right
| (x, y) == (0,k) -> checkcell (x,y-1) + checkcell (x+1,y-1) + checkcell (x+1,y)
-- Check bottom, left, right, bottom left, bottom right
| y == 0 -> checkcell (x,y+1) + checkcell (x-1,y) + checkcell (x+1,y) + checkcell (x+1,y+1) + checkcell (x-1,y+1)
--check bottom, top, right, top right, bottom right
| x == 0 -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x+1,y) + checkcell (x+1,y+1) + checkcell (x+1,y-1)
-- check top, left, right,top left, top right
| y == k -> checkcell (x,y-1) + checkcell (x-1,y) + checkcell (x+1,y) + checkcell (x-1,y-1) + checkcell (x+1,y-1)
-- check bottom, top, left, top left, bottom left
| x == j -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x-1,y) + checkcell (x-1,y+1) + checkcell (x-1,y-1)
--check all surrounding
| otherwise -> checkcell (x,y+1) + checkcell (x,y-1) + checkcell (x+1,y) + checkcell (x-1,y) + checkcell (x+1,y+1) +checkcell (x+1,y-1) + checkcell (x-1,y+1) + checkcell (x-1,y-1)