Pull to refresh

Comments 20

Что происходит, если новый фрукт попадает на тело змеи всё время? (т.е. змея размером с поле?)

… уходит в вечный поиск координаты для фрукта. Спасибо, добавил условия победы.

Для змеи поиск пути лучше все таки алгоритмом А-звезды с приоритетом движения вдоль себя и вдоль стенок, чтобы избежать закольцевания.

Гамильтон по-моему единственное решение змейки для гарантированного идеального прохождения.

Судя по гифке, змейка с вашим алгоритмом движется крайне неоптимально. Может быть поведение становится лучше на большой длине, но на том что показали — есть много ненужных метаний из стороны в сторону

Совершенно верно, я и в самой статье написал, что алгоритм, который сокращает цикл Гамильтона далеко не оптимальный и указал причины.

Тогда не понятно, почему вы считаете его "идеальным"

«Идеальным» в том смысле, что он заполняет телом змеи все игровое поле, то есть позволяет идеально пройти игру. Но это не делает его «Оптимальным» в плане нахождения оптимального пути до фрукта.

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


Вообще я видел что люди на первых этапах используют А-звезда, дальше цикл Гамильтона (иногда для зрелищности опять включают А-звезда если есть место, цель в прямой досягаемости, нет риска создать недоступную зону).

Можно и без параллеьных потоков, используя функцию hReady

import System.IO

whenKeyIsPressed :: Handle -> IO a -> IO (Maybe a)
whenKeyIsPressed handle getCh = hReady handle >>= go
where go True = getCh >>= return. Just
go _ = return Nothing

main :: IO ()
main = do
hSetBuffering stdin NoBuffering
ch < — whenKeyIsPressed stdin getChar
case ch of
Just c -> putStrLn $ «Key pressed: » ++ [c]
Nothing -> return () — putStrLn «Nothing is pressed»
main

Ещё не освоил здешний интерфейс. Переформатировал код свего комментария

import System.IO

whenKeyIsPressed :: Handle -> IO a -> IO (Maybe a)
whenKeyIsPressed handle getCh = hReady handle >>= go
   where go True = getCh >>= return . Just
         go _    = return Nothing

main :: IO ()
main = do
  hSetBuffering stdin NoBuffering
  ch <- whenKeyIsPressed stdin getChar
  case ch of
    Just c -> putStrLn $ "Key pressed: " ++ [c]
    Nothing -> return () -- putStrLn "Nothing is pressed"
  main<b></b>
Да, действительно, можно было не форкать. Не знал про hReady, спасибо)

А что за курс проходили, и как долго? Довольно впечатляющий уровень для человека, который просто прошел курс.

Курс Haskell от Дениса Москвина на степиках, бесплатный. По времени не скажу, тк проходил с большими перерывами. Довольно сложный, там можно по началу весь вечер на одну задачу потратить. При чем там есть еще вторая часть, думаю, кто проходит ее, сразу идет писать код для ядерных реакторов)

Отличная работа! Возможно, вам будет интересно посмотреть на немного иную реализацию змейки, которую я писал для RosettaCode (https://rosettacode.org/wiki/Snake#Haskell). Она не консольная, на Gloss и автоматический поиск не гарантирует прохождения, но показывает некоторые приёмы создания DSL и использования линз.

Спасибо, с удовольствием ознакомлюсь)

Очень неудобно читать код с такими случайными выравниваниями (да еще и tab-ы используются, которые у всех разные).


Надо что-то вроде


data WorldState
    = Process 
    | GameOver
    | ...
    deriving (Eq)   
data World 
    = World
      { snake :: Snake
      , ...
      }

let и where в конце строки, а не в начале новой — это что-то за гранью. Не надо так делать.


Этот код выглядит как обычный императивный код после do, но потом видишь определение ф-ии и выпадаешь в осадок:


inputController command world = let
  boost dir1 dir2 = if dir1 == dir2 then 0.05 else 0.3
  filterSecondSegmentDir (x:[]) dirOld dirNew = dirNew
  filterSecondSegmentDir (x:xs) dirOld dirNew | pointStep dirNew x == head xs = dirOld
                                              | otherwise = dirNew in 

надо


inputController command world =
    ...
    where boost dir1 dir2 = if dir1 == dir2 then 0.05 else 0.3
          filterSecondSegmentDir [x] dirOld dirNew = dirNew
          filterSecondSegmentDir (x:xs) dirOld dirNew
              -- условия на новой строке, чтобы у всех ф-ий они были
              -- в одном и том же месте, а не безумной лесенкой
              | pointStep dirNew x == head xs = dirOld
              | otherwise = dirNew in 

и, в целом, надо стараться укладываться по ширине в 80 символов. Не все разворачивают редактор на всю ширину экрана, не всем на Хабре хочется прокручивать код вправо/влево. Ну и код иногда печатают (особенно учебный).

спасибо за развернутый комментарий по оформлению кода, буду обращать на это внимание в будущем
Sign up to leave a comment.

Articles