{"id":321149,"date":"2021-04-08T21:00:41","date_gmt":"2021-04-08T21:00:41","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=321149"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2021-04-08T21:00:41","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=321149","title":{"rendered":"\u0417\u043c\u0435\u0439\u043a\u0430 \u043d\u0430 Haskell \u0441 \u0446\u0438\u043a\u043b\u043e\u043c \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p> \u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u0430 \u043f\u043e Haskell \u0440\u0435\u0448\u0438\u043b \u0437\u0430\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u0437\u043d\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u044b\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c. \u041f\u0438\u0441\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u0437\u043c\u0435\u0439\u043a\u0443 \u0434\u043b\u044f \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0434\u0430\u0442\u044c \u0438\u0433\u0440\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0431\u043e\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0438\u0433\u0440\u0443. <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/21a\/0f2\/80c\/21a0f280c9ea29c6dd6ff07aedd0dfd3.png\" width=\"476\" height=\"435\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 haskell-platform, Ubuntu 20.04.<\/p>\n<p><a href=\"https:\/\/github.com\/letov\/terminal-snake-hamilton\/\" rel=\"noopener noreferrer nofollow\">GitHub \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<h2>\u0418\u0433\u0440\u043e\u0432\u043e\u0439 \u0446\u0438\u043a\u043b<\/h2>\n<p> \u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0433\u0440\u043e\u0432\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430. \u0417\u043c\u0435\u044f \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u0430. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c Control.Concurrent. \u041e\u0442\u0432\u0435\u0442\u0432\u043b\u044f\u0435\u043c\u0441\u044f \u043e\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 forkIO \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0442\u043e\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 MVar. \u0421 \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u0433\u0440\u043e\u0432\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430, tryInput \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c Maybe Char \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0432\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041f\u043e\u0442\u043e\u043a\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u0414\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0432\u043e\u0434\u0430 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f System.IO &#8212; \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043c \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 EOL \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u043f\u0440\u0438 \u0432\u0432\u043e\u0434\u0435 \u0438 \u0443\u0431\u0435\u0440\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430. \u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0447\u0442\u043e hSetBuffering stdin NoBuffering \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043b\u044f Windows \u043a\u043e\u043d\u0441\u043e\u043b\u0438 &#8212;  getChar \u0431\u0443\u0434\u0435\u0442 \u0436\u0434\u0430\u0442\u044c EOL \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438\u0433\u0440\u0443 \u0432 \u0444\u043e\u0440\u0442\u043e\u0447\u043a\u0430\u0445 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0432\u0438\u0434\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c System.Console.ANSI \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u044d\u043a\u0440\u0430\u043d\u0430 \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430.<\/p>\n<pre><code class=\"haskell\">import Control.Concurrent import System.Console.ANSI import System.IO  gameLoop :: ThreadId -&gt; MVar Char -&gt; IO () gameLoop inputThId input = do   tryInput &lt;- tryTakeMVar input   gameLoop inputThId input    inputLoop :: MVar Char -&gt; IO () inputLoop input = (putMVar input =&lt;&lt; getChar) &gt;&gt; inputLoop input   main = do   hSetBuffering stdin NoBuffering    hSetEcho stdin False   clearScreen   input &lt;- newEmptyMVar   inputThId &lt;- forkIO $ inputLoop input   gameLoop inputThId input<\/code><\/pre>\n<h2>\u041c\u0438\u0440 \u0434\u043b\u044f \u0437\u043c\u0435\u0438<\/h2>\n<p> \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u0423 \u0438\u0433\u0440\u044b \u0431\u0443\u0434\u0435\u0442 4 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f: Process &#8212; \u0437\u043c\u0435\u0435\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0433\u0440\u043e\u043a, Bot &#8212; \u0437\u043c\u0435\u0435\u0439 \u0440\u0443\u043b\u0438\u0442 \u0438\u0433\u0440\u0430, GameOver \u0438 Quit. \u041c\u0438\u0440 \u0438\u0433\u0440\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0442\u0438\u043f\u043e\u043c data World, \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0438\u0433\u0440\u043e\u0432\u043e\u043c \u0446\u0438\u043a\u043b\u0435 gameLoop. \u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043c\u0435\u044e, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0435\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443 \u0444\u0440\u0443\u043a\u0442\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0438\u0433\u0440\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0414\u0430\u043b\u0435\u0435 \u043f\u043e \u043c\u0435\u0440\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0435\u0433\u043e \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f. \u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 (0,0) \u0431\u0443\u0434\u0435\u0442 \u0432\u0435\u0440\u0445\u043d\u0438\u043c \u043b\u0435\u0432\u044b\u043c \u043a\u0440\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u0438. \u0417\u043c\u0435\u044f \u0434\u0432\u0438\u0433\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0441\u044f\u043c, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0443 \u043d\u0430\u0441 4 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"haskell\">data StepDirection  =  DirUp                    \t | DirDown                      | DirLeft                    \t | DirRight deriving (Eq)    type Point = (Int, Int) type Snake = [Point] data WorldState = Process                 \t    | GameOver                \t    | Quit                 \t    | Bot deriving (Eq)    data World = World  { snake :: Snake                     , direction :: StepDirection                     , fruit :: Point                     , worldState :: WorldState                     }         gameLoop :: ThreadId -&gt; MVar Char -&gt; World -&gt; IO () {-- \u2026 --}<\/code><\/pre>\n<h2>\u0422\u0430\u0439\u043c\u0435\u0440<\/h2>\n<p> \u0414\u043b\u044f \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u043c Data.Time.Clock. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448 \u043c\u0438\u0440 3 \u043f\u043e\u043b\u044f: lastUpdateTime &#8212; \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430,  updateDelay &#8212; \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0434\u0435\u043c \u0434\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 isUpdateIteration &#8212; \u0444\u043b\u0430\u0433 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043c\u0438\u0440 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438.  \u0423\u043a\u0430\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430 \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a timerController. \u041e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0444\u043b\u0430\u0433 isUpdateIteration, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f.  <\/p>\n<pre><code class=\"haskell\">import Data.Time.Clock  data World \t\t\t\t\t\t\t = World  {                     \t          \t\t{-- \u2026 --}                                   , lastUpdateTime :: UTCTime                                   , updateDelay :: NominalDiffTime                                   , isUpdateIteration :: Bool                                   }    initWorld :: UTCTime -&gt; World initWorld timePoint = World { snake = [(10, y) | y &lt;- [3..10]]                              , direction = DirRight                             , fruit = (3, 2)                             , lastUpdateTime = timePoint                             , updateDelay = 0.3                             , isUpdateIteration = True                             , worldState = Process                             }    timerController :: UTCTime -&gt; World -&gt; World timerController timePoint world   | isUpdateTime timePoint world = world { lastUpdateTime = timePoint                                          , isUpdateIteration = True                                           }   | otherwise                    = world where     isUpdateTime timePoint world =      \tdiffUTCTime timePoint (lastUpdateTime world) &gt;= updateDelay world  gameLoop inputThId input oldWorld = do  {-- \u2026 --}   timePoint &lt;- getCurrentTime   let newWorld = timerController timePoint oldWorld   gameLoop inputThId input newWorld { isUpdateIteration = False }    main = do   {-- \u2026 --}   timePoint &lt;- getCurrentTime   gameLoop inputThId input (initWorld timePoint)<\/code><\/pre>\n<h2>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u0432\u043e\u0434\u0430<\/h2>\n<p> \u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0432\u043e\u0434\u0430 inputController. \u041a\u043b\u0430\u0432\u0438\u0448\u0438 WSAD \u043c\u0435\u043d\u044f\u044e\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0439 \u0437\u043c\u0435\u0438. \u0421\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0437\u043c\u0435\u044f \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0437\u0430\u0434, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u044f, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 1 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0441\u043b\u0438 \u043d\u043e\u0432\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0434\u0435\u0442 \u043a\u043e \u0432\u0442\u043e\u0440\u043e\u043c\u0443 \u043e\u0442 \u0433\u043e\u043b\u043e\u0432\u044b \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0443 \u0437\u043c\u0435\u0438, \u043c\u044b \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0432\u0432\u043e\u0434. \u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u043c, \u0442\u043e \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0436\u0430\u043b \u043a\u043b\u0430\u0432\u0438\u0448\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0443\u0441\u043a\u043e\u0440\u0438\u043c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u0437\u043c\u0435\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0432 updateDelay. \u0424\u0443\u043d\u043a\u0446\u0438\u044f pointStep \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0442\u043e\u0447\u043a\u0443, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f \u043d\u043e\u0432\u0443\u044e \u0442\u043e\u0447\u043a\u0443, \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043d\u0443\u044e \u043d\u0430 \u043e\u0434\u0438\u043d \u0448\u0430\u0433 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438.  <\/p>\n<pre><code class=\"haskell\">pointStep :: StepDirection -&gt; Point -&gt; Point pointStep direction (x, y) = case direction of   DirUp -&gt; (x, y - 1)    DirDown -&gt; (x, y + 1)   DirLeft -&gt; (x - 1, y)   DirRight -&gt; (x + 1, y)  inputController :: Maybe Char -&gt; World -&gt; World 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      case command of     Just 'w' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirUp                        , updateDelay = boost (direction world) DirUp                       , worldState = Process                       }     Just 's' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirDown                       , updateDelay = boost (direction world) DirDown                       , worldState = Process                       }     Just 'a' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirLeft                        , updateDelay = boost (direction world) DirLeft                       , worldState = Process                       }     Just 'd' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirRight                        , updateDelay = boost (direction world) DirRight                       , worldState = Process                       }     Just 'q' -&gt; world { worldState = Quit }     Just 'h' -&gt; world { worldState = Bot }     _        -&gt; world { updateDelay =  0.3 }<\/code><\/pre>\n<h2>\u0414\u0432\u0438\u0433\u0430\u0435\u043c \u0437\u043c\u0435\u044e<\/h2>\n<p> \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 moveController \u0441\u0434\u0432\u0438\u043d\u0435\u0442 \u043d\u0430\u0448\u0443 \u0437\u043c\u0435\u044e, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f  isUpdateIteration \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430.  <\/p>\n<pre><code class=\"haskell\">snakeStep :: StepDirection -&gt; Snake -&gt;  Snake snakeStep direction snake = (pointStep direction $ head snake):(init snake)  moveController :: World -&gt; World moveController world    | not $ isUpdateIteration world = world   | otherwise = world { snake = snakeStep (direction world) (snake world) }<\/code><\/pre>\n<h2>\u0421\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438<\/h2>\n<h4>\u0413\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e\u043b\u044f<\/h4>\n<p> \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438. \u0412 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0430\u043c\u0430 \u0437\u043c\u0435\u044f \u0438 \u0444\u0440\u0443\u043a\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0442\u0435\u043d\u044b. \u041e\u043d\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0432 \u043c\u0438\u0440 \u0438 \u043e\u0431\u044a\u044f\u0432\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u0430\u0440\u044b (1,1) \u2014 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u043b\u0435\u0432\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0441\u0442\u0435\u043d\u044b \u0438 (20,20) \u2014 \u043d\u0438\u0436\u043d\u044f\u044f \u043f\u0440\u0430\u0432\u0430\u044f.   <\/p>\n<pre><code class=\"haskell\">initWalls :: Walls initWalls = ((1,1),(20,20))<\/code><\/pre>\n<h4>\u0413\u0421\u0427<\/h4>\n<p> \u0424\u0440\u0443\u043a\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0413\u0421\u0427. \u0412 Haskell \u043e\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 System.Random, \u0444\u0443\u043d\u043a\u0446\u0438\u044f randomR. \u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0447\u0438\u0441\u0442\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u043f\u0440\u0438 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0432\u0442\u043e\u0440\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c randomR \u0441\u043b\u0443\u0436\u0438\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u043a\u0430\u043a \u043f\u043e\u043b\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0438\u0440\u0430 \u0438 \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0435\u043c\u0443 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u041a\u043e\u0433\u0434\u0430 \u0437\u043c\u0435\u044f \u0435\u0441\u0442 \u0444\u0440\u0443\u043a\u0442, \u043e\u043d\u0430 \u0440\u0430\u0441\u0442\u0435\u0442 \u0432 \u0445\u0432\u043e\u0441\u0442\u0435. \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443 \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u0442\u043e\u0447\u043a\u0438 \u0445\u0432\u043e\u0441\u0442\u0430 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043c\u0438\u0440\u0430.  <\/p>\n<pre><code class=\"haskell\">import System.Random  data World = World  {                      {-- \u2026 --}                     , oldLast :: Point                     , rand :: StdGen                     }     initWorld timePoint = World   {                               {-- \u2026 --}                               , oldLast = (0, 0)                               , rand = mkStdGen 0                               }   {-- \u2026 --} timerController timePoint world   | isUpdateTime timePoint world  = world {                                            {-- \u2026 --}                                           , oldLast = last $ snake world                                           } {-- \u2026 --}                                       <\/code><\/pre>\n<h4>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439<\/h4>\n<p> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439 \u0437\u043c\u0435\u0438 \u0441 \u0442\u0435\u043b\u043e\u043c \u0438 \u0433\u043e\u043b\u043e\u0432\u044b \u0441\u043e \u0441\u0442\u0435\u043d\u043e\u0439.<\/p>\n<pre><code class=\"haskell\">collisionSnake :: Snake -&gt; Bool collisionSnake (x:xs) = any (== x)  xs  collisionWall :: Point -&gt; Walls -&gt; Bool collisionWall (sx,sy) ((wx1,wy1),(wx2,wy2)) =  \tsx &lt;= wx1 || sx &gt;= wx2 || sy &lt;= wy1 || sy &gt;= wy2<\/code><\/pre>\n<p> \u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439 collisionController.  \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0438\u0440\u0430 \u0432 GameOver \u043f\u0440\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0438 \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438 \u0438 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c \u0434\u043b\u0438\u043d\u0443 \u0437\u043c\u0435\u0438 \u0432 \u0445\u0432\u043e\u0441\u0442\u0435 \u043f\u0440\u0438 \u0441\u044a\u0435\u0434\u0430\u043d\u0438\u0438 \u0444\u0440\u0443\u043a\u0442\u0430, \u0442\u0430\u043a\u0436\u0435 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0432 \u043d\u043e\u0432\u044b\u0439 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u0441\u0442\u0435\u043d. \u0415\u0441\u043b\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u0444\u0440\u0443\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0439 \u0442\u0435\u043b\u0430 \u0437\u043c\u0435\u0438, \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443. \u0422\u0430\u043a\u0436\u0435, \u0435\u0441\u043b\u0438 \u0434\u043b\u0438\u043d\u0430 \u0437\u043c\u0435\u0438 \u0441\u0442\u0430\u043b\u0430 \u043d\u0430 1 \u043c\u0435\u043d\u044c\u0448\u0435 \u0435\u043c\u043a\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044f, \u0432\u044b\u0445\u043e\u0434\u0438\u043c \u0432 GameOver \u0441 \u043f\u043e\u0431\u0435\u0434\u043e\u0439.<\/p>\n<pre><code class=\"haskell\">collisionController :: World -&gt; World collisionController world   | not $ isUpdateIteration world                = world   | collisionSnake $ snake world                 = world { worldState = GameOver }    | collisionWall (head $ snake world) initWalls = world { worldState = GameOver }   | checkWin (snake world) initWalls             = world { worldState = GameOver }   | collisionFruit (snake world) (fruit world)   = world { snake =                                                             (snake world) ++ [oldLast world]                                                          , fruit = newFruit                                                          , rand = newRand                                                          }   | otherwise                                    = world where     checkWin snake ((x1, y1),(x2, y2)) = (x2 - x1 - 1) * (y2 - y1 - 1) - length snake == 1     collisionFruit snake fruit = fruit == head snake     (newFruit, newRand) = freeRandomPoint world (rand world)     randomPoint ((minX, minY), (maxX, maxY)) g = let       (x, g1) = randomR (minX + 1, maxX - 1) g       (y, g2) = randomR (minY + 1, maxY - 1) g1 in          ((x, y), g2)     freeRandomPoint world g | not $ elem point ((fruit world):(snake world)) =                                  (point, g1)                             | otherwise = freeRandomPoint world g1 where                                 (point, g1) = randomPoint initWalls g<\/code><\/pre>\n<h2>\u0413\u0440\u0430\u0444\u0438\u043a\u0430<\/h2>\n<p> \u041f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044e \u043c\u0438\u0440\u0430. \u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043d\u0430\u0448\u0435\u0439 \u0433\u0440\u0430\u0444\u0438\u043a\u0438 drawPoint \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u0438\u043c\u0432\u043e\u043b \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u0435\u0433\u043e \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u044d\u043a\u0440\u0430\u043d\u0430. \u0424\u0443\u043d\u043a\u0446\u0438\u044f renderWorld \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043d\u0430\u0448 \u043c\u0438\u0440. \u0411\u0435\u0437 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u043e\u0433\u043e \u0444\u043b\u0430\u0433\u0430 isUpdateIteration, \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b moveController, collisionController \u0438 renderWorld \u043d\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u044f\u0442 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439. \u0420\u0435\u043d\u0434\u0435\u0440 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442 \u043d\u0430\u0448 \u0444\u0440\u0443\u043a\u0442, \u043d\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0437\u043c\u0435\u0438 \u0438 \u0437\u0430\u0442\u0438\u0440\u0430\u0435\u0442 \u0435\u0435 \u0445\u0432\u043e\u0441\u0442. \u0421\u0442\u0435\u043d\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u043e\u0434\u0438\u043d \u0440\u0430\u0437 \u043f\u0440\u0438 \u0441\u0442\u0430\u0440\u0442\u0435 \u0438 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0442\u0441\u044f.<\/p>\n<pre><code class=\"haskell\">renderWorld :: World -&gt; IO () renderWorld world   | not $ isUpdateIteration world = return ()   | otherwise = do     drawPoint '@' (fruit world)       drawPoint ' ' (oldLast world)     mapM_ (drawPoint 'O') (snake world)     setCursorPosition 0 0   drawPoint :: Char -&gt; Point -&gt; IO () drawPoint char (x, y) = setCursorPosition y x &gt;&gt; putChar char     drawWalls :: Char -&gt; Walls -&gt; IO () drawWalls char ((x1, y1),(x2, y2)) = do   mapM_ (drawPoint char) [(x1, y)| y &lt;- [y1..y2]]   mapM_ (drawPoint char) [(x, y1)| x &lt;- [x1..x2]]   mapM_ (drawPoint char) [(x2, y)| y &lt;- [y1..y2]]    mapM_ (drawPoint char) [(x, y2)| x &lt;- [x1..x2]]  main = do   {-- \u2026 --}   drawWalls '#' initWalls   {-- \u2026 --}<\/code><\/pre>\n<p> \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0432\u0441\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u044b \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0440\u0435\u043d\u0434\u0435\u0440 \u0432 \u0438\u0433\u0440\u043e\u0432\u043e\u043c \u0446\u0438\u043a\u043b\u0435.<\/p>\n<pre><code class=\"haskell\">gameLoop inputThId input oldWorld = do   {-- \u2026 --}   let newWorld = collisionController . moveController $ timerController timePoint (inputController tryInput oldWorld)    renderWorld newWorld   {-- \u2026 --}<\/code><\/pre>\n<p> \u041d\u0430 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u044d\u0442\u0430\u043f\u0435 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0447\u0430\u044f \u0437\u043c\u0435\u0439\u043a\u0430 \u0441 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0435\u043c \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0433\u0440\u0435 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0441\u0435\u0431\u044f \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e. \u0417\u0430\u0434\u0430\u0447\u0443 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0439\u043a\u0438 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0432 \u0441\u0432\u043e\u0438\u0445 \u0432\u0438\u0434\u0435\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b \u0430\u0432\u0441\u0442\u0440\u0430\u043b\u0438\u0439\u0441\u043a\u0438\u0439 \u043a\u043e\u0434\u0435\u0440 CodeBullet. \u0422\u0430\u043a\u0436\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0443 RussianDragon <a href=\"https:\/\/habr.com\/ru\/post\/544696\/\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a>. \u041f\u043e\u0437\u0430\u0438\u043c\u0441\u0442\u0432\u0443\u0435\u043c \u0438\u0434\u0435\u044e \u0441 \u0446\u0438\u043a\u043b\u043e\u043c \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0438 \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c.  <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bee\/38b\/d97\/bee38bd975d75c8330f168f2f29292da.gif\" width=\"398\" height=\"409\"><figcaption><\/figcaption><\/figure>\n<h2>\u0426\u0438\u043a\u043b \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430<\/h2>\n<p> \u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0442\u0435\u043e\u0440\u0438\u0438: \u0446\u0438\u043a\u043b \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0432 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u043d\u0430\u0448\u0435\u0439 \u0438\u0433\u0440\u044b &#8212; \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u044b\u0439 \u043f\u0443\u0442\u044c, \u043f\u0440\u043e\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0447\u0435\u0440\u0435\u0437 \u043a\u0430\u0436\u0434\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u043f\u043e\u043b\u044f \u0440\u043e\u0432\u043d\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437. \u041c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0435\u0433\u043e \u043d\u0430\u0439\u0442\u0438 \u0441\u0434\u0435\u043b\u0430\u0432 \u043f\u0440\u043e\u0445\u043e\u0434 \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0435\u043b\u043a\u0435. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0443 \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u043e\u0432 \u0442\u0438\u043f\u043e\u0432: Path &#8212; \u043f\u0443\u0442\u044c \u043f\u043e \u0438\u0433\u0440\u043e\u0432\u043e\u043c\u0443 \u043f\u043e\u043b\u044e,  ClosedPath &#8212; \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u044b\u0439 \u043f\u0443\u0442\u044c \u0438\u043b\u0438 \u0446\u0438\u043a\u043b \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044f.<\/p>\n<pre><code class=\"haskell\">type Path = [Point] type ClosedPath = [Point]<\/code><\/pre>\n<p> \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439, wallsFirstPoint \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0443\u043b\u0435\u0432\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0438\u0433\u0440\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0442\u0435\u043d. \u0421 \u043d\u0435\u0435 \u043c\u044b \u043d\u0430\u0447\u043d\u0435\u043c \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0446\u0438\u043a\u043b\u0430 \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043d\u0435\u0435 \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f. isPathContain \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u0441 \u0442\u0435\u043b\u043e\u043c, \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043b\u0438 \u043f\u0443\u0442\u044c \u0442\u043e\u0447\u043a\u0443. clockwise \u0432\u0435\u0440\u043d\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0439 \u043f\u043e \u0447\u0430\u0441\u043e\u0432\u043e\u0439 \u0441\u0442\u0440\u0435\u043b\u043a\u0435. distBetweenPoints &#8212; \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0443\u043c\u044f \u0442\u043e\u0447\u043a\u0430\u043c\u0438, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0447\u0442\u043e \u0437\u043c\u0435\u044f \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0434\u0438\u0430\u0433\u043e\u043d\u0430\u043b\u0438.<\/p>\n<pre><code class=\"haskell\">clockwise = [DirUp, DirRight, DirDown, DirLeft]  wallsFirstPoint :: Point wallsFirstPoint = ((fst $ fst initWalls) + 1, (snd $ fst initWalls) + 1)  isPathContain :: Path -&gt; Point -&gt; Bool isPathContain path point = any (== point) path  distBetweenPoints :: Point -&gt; Point -&gt; Int distBetweenPoints (x1, y1) (x2, y2) = abs (x1 - x2) + abs (y1 - y2)<\/code><\/pre>\n<p> \u0418 \u0441\u0430\u043c\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u0438\u0441\u043a\u0430 \u0446\u0438\u043a\u043b\u0430 \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 getHamPath. \u041e\u043d\u0430 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u0446\u0438\u043a\u043b\u0430, \u0432\u0442\u043e\u0440\u043e\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0430\u043a\u043a\u0443\u043c\u0443\u043b\u044f\u0442\u043e\u0440\u043e\u043c \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438, \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0441\u043f\u0438\u0441\u043e\u043a. \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0440\u0430\u0432\u043d\u0430 \u043b\u0438 \u043f\u043b\u043e\u0449\u0430\u0434\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044f \u0434\u043b\u0438\u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0438 \u0440\u0430\u0432\u043d\u043e \u043b\u0438 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u043f\u0435\u0440\u0432\u043e\u0439 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u043f\u0443\u0442\u0438 \u0435\u0434\u0438\u043d\u0438\u0446\u0435, \u0442\u043e \u0435\u0441\u0442\u044c \u043f\u0443\u0441\u0442\u044c \u0437\u0430\u043c\u043a\u043d\u0443\u043b\u0441\u044f \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0446\u0438\u043a\u043b\u043e\u043c. \u0415\u0441\u043b\u0438 \u043d\u0435\u0442, \u0438\u0449\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0443\u044e \u0442\u043e\u0447\u043a\u0443 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 nextHamPathPoint. \u0414\u0430\u0435\u043c \u0435\u0439 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0439 \u043f\u0443\u0442\u044c \u0438 4 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f. \u0415\u0441\u043b\u0438 \u0442\u043e\u0447\u043a\u0430 \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u043a\u043e\u043b\u043b\u0438\u0437\u0438\u0439 \u0441\u043e \u0441\u0442\u0435\u043d\u043e\u0439 \u0438 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u043c \u043f\u0443\u0442\u0435\u043c, \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0435\u0435 \u0438 \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0432 \u043f\u0443\u0442\u044c. \u0412\u0430\u0440\u0438\u0430\u043d\u0442, \u0447\u0442\u043e nextHamPathPoint \u043d\u0435 \u043d\u0430\u0448\u0435\u043b \u043d\u0438 \u043e\u0434\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043a\u0440\u0430\u0448\u0438\u0442 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u0446\u0438\u043a\u043b \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043d\u0430\u0439\u0434\u0435\u043d. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0442\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u0443\u0441\u043b\u043e\u0432\u0438\u0438, \u0447\u0442\u043e \u0443 \u043f\u043e\u043b\u044f \u043e\u0431\u0435 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u0447\u0435\u0442\u043d\u044b\u0435 \u0438 \u0443 \u043f\u0443\u0442\u0438 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0435.<\/p>\n<pre><code class=\"haskell\">getHamPath :: Point -&gt; ClosedPath -&gt; ClosedPath getHamPath currentPoint hamPath  | hamPathCapacity initWalls == length (currentPoint:hamPath)                                     &amp;&amp; distBetweenPoints currentPoint (last hamPath) == 1                                       = currentPoint:hamPath                                  | otherwise = getHamPath newPoint (currentPoint:hamPath) where                                     newPoint = nextHamPathPoint (currentPoint:hamPath) clockwise                                     hamPathCapacity ((x1, y1),(x2, y2)) = (x2 - x1 - 1) * (y2 - y1 - 1)   nextHamPathPoint :: Path -&gt; [StepDirection] -&gt; Point nextHamPathPoint _       []         = error \"incorrect initWalls\" nextHamPathPoint hamPath (dir:dirs) | isPathContain hamPath virtualPoint                                        || collisionWall virtualPoint initWalls =                                        \t\tnextHamPathPoint hamPath dirs                                      | otherwise = virtualPoint where   virtualPoint = pointStep dir (head hamPath)<\/code><\/pre>\n<p> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 \u0432 \u043d\u0430\u0448 \u043c\u0438\u0440.<\/p>\n<pre><code class=\"haskell\">data World = World  {                      {-- \u2026 --}                      , hamPath :: ClosedPath                     }     initWorld timePoint = World   {                               {-- \u2026 --}                               , hamPath = getHamPath wallsFirstPoint []                               } <\/code><\/pre>\n<p> \u0412\u043d\u0443\u0442\u0440\u0438 \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u043d\u0430\u0448\u0430 \u0437\u043c\u0435\u044f \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u0432 2\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\u0445. \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u0443\u0442\u044c \u0437\u0430\u0446\u0438\u043a\u043b\u0435\u043d, \u043e\u043d \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0437 \u0441\u0435\u0431\u044f \u0441\u043f\u0438\u0441\u043e\u043a \u0441 \u0433\u043e\u043b\u043e\u0432\u043e\u0439 \u0438 \u0445\u0432\u043e\u0441\u0442\u043e\u043c. \u0411\u0443\u0434\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u043e\u0442 \u0433\u043e\u043b\u043e\u0432\u044b \u043a \u0445\u0432\u043e\u0441\u0442\u0443 DirFromHead \u0438 DirFromTail \u0432 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7ca\/8dd\/177\/7ca8dd17755e0bb54e061994a2a3a698.jpg\" width=\"724\" height=\"64\"><figcaption><\/figcaption><\/figure>\n<pre><code class=\"haskell\">data PathDirection = DirFromHead | DirFromTail deriving (Eq)<\/code><\/pre>\n<p> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0431\u043e\u0442\u043e\u043c \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 nextDirOnPath, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043e\u043f\u0438\u0448\u0435\u043c \u043f\u043e\u0437\u0436\u0435. \u041e\u043d\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0430\u0440\u0443 (botStepDir, botPathDir) \u043f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u0430\u0435\u0442 \u043d\u0430\u043c \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0431\u043e\u0442\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0437\u043c\u0435\u0438 \u043d\u0430 \u043f\u043e\u043b\u0435. \u0412\u0442\u043e\u0440\u043e\u0439 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043d\u0430 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0446\u0438\u043a\u043b\u0430 \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430. \u0415\u0441\u043b\u0438 \u0432\u0435\u0440\u043d\u0443\u043b\u043e\u0441\u044c DirFromHead, \u0442\u043e \u0435\u0441\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u043c\u0443, \u043f\u0435\u0440\u0435\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0435\u043c \u0446\u0438\u043a\u043b.  <\/p>\n<pre><code class=\"haskell\">moveController world    {-- \u2026 --}   | worldState world == Process = world {snake = snakeStep (direction world) (snake world)}   | otherwise                   = world { snake = snakeStep botStepDir (snake world)                                         , hamPath = if botPathDir == DirFromTail then hamPath world else reverse $ hamPath world                                           } where                                             (botStepDir, botPathDir) = nextDirOnPath (snake world) (hamPath world)   nextDirOnPath :: Snake -&gt; ClosedPath -&gt; (StepDirection, PathDirection) nextDirOnPath = undefined<\/code><\/pre>\n<p> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043f\u0430\u0440\u0443 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439: dirBetweenPoints \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0430\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 \u0434\u0432\u0443\u043c\u044f \u0442\u043e\u0447\u043a\u0430\u043c\u0438 \u043f\u043e \u043c\u0435\u043d\u044c\u0448\u0435\u043c\u0443 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043f\u043e \u043e\u0441\u0438 \u0438 pointNeighborsOnPath \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u0430\u0440\u0443 \u0441\u043e\u0441\u0435\u0434\u0435\u0439 \u0442\u043e\u0447\u043a\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u043e\u0433\u043e \u043f\u0443\u0442\u0438.  <\/p>\n<pre><code class=\"haskell\">dirBetweenPoints :: Point -&gt; Point -&gt; StepDirection dirBetweenPoints (x1, y1) (x2, y2)  | x1 == x2 = if y1 &gt; y2 then DirUp else DirDown                                     | y1 == y2 = if x1 &gt; x2 then DirLeft else DirRight                                      | otherwise = if abs (x1 - x2) &lt; abs (y1 - y2) then                                          dirBetweenPoints (x1, 0) (x2, 0) else                                         dirBetweenPoints (0, y1) (0, y2)    pointNeighborsOnPath :: Point -&gt; ClosedPath -&gt; (Point, Point) pointNeighborsOnPath point path | not $ isPathContain path point || length path &lt; 4 = error \"incorrect initWalls\"                                 | point == head path = (last path, head $ tail path)                                 | point == last path = (last $ init path, head path)                                 | otherwise = _pointNeighborsOnPath point path where                                   _pointNeighborsOnPath point (a:b:c:xs) = if point == b then (a,c) else _pointNeighborsOnPath point (b:c:xs)<\/code><\/pre>\n<p> \u041d\u0430\u0439\u0434\u0435\u043c \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u043e\u043c \u043f\u0443\u0442\u0438 \u0441\u043e\u0441\u0435\u0434\u043d\u0438\u0435 \u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u0433\u043e\u043b\u043e\u0432\u044b \u0437\u043c\u0435\u0438, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u044c\u044e \u0437\u043c\u0435\u0438 \u0438 \u0432\u0435\u0440\u043d\u0435\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u043e\u043c\u0443 \u0441\u043e\u0441\u0435\u0434\u0443.<\/p>\n<pre><code class=\"haskell\">nextDirOnPath :: Snake -&gt; ClosedPath -&gt; (StepDirection, PathDirection) nextDirOnPath (snakeHead:snakeTail)  path   | snakeTail == [] = (dirBetweenPoints snakeHead point1, DirFromTail)                                              | point1 == head snakeTail = (dirBetweenPoints snakeHead point2, DirFromHead)                                             | otherwise = (dirBetweenPoints snakeHead point1, DirFromTail) where                                                  (point1, point2) = pointNeighborsOnPath snakeHead path<\/code><\/pre>\n<p> \u0421\u0435\u0439\u0447\u0430\u0441 \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0431\u043e\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0437\u043c\u0435\u0439\u043a\u0443 \u043f\u043e \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438, \u043d\u0435 \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u044f \u0435\u0433\u043e.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fe8\/c26\/d79\/fe8c26d793adc1a766964dcda13265f6.gif\" width=\"441\" height=\"409\"><figcaption><\/figcaption><\/figure>\n<h2>\u0423\u0441\u043a\u043e\u0440\u044f\u0435\u043c \u0431\u043e\u0442\u0430<\/h2>\n<p> \u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0431\u043e\u0442\u0430, \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u0439: collisionSnakeOnPath \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442, \u0441\u0432\u043e\u0431\u043e\u0434\u0435\u043d \u043b\u0438 \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u044b\u0439 \u043f\u0443\u0442\u044c \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u0442\u043e\u0447\u043a\u0438 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u043e\u0442 \u0442\u0435\u043b\u0430 \u0437\u043c\u0435\u0438 \u0438 distBetweenPointsOnPath \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u0435\u0440\u043d\u0435\u0442 \u043f\u0430\u0440\u0443 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u043e\u0442 \u0442\u043e\u0447\u043a\u0438 \u0434\u043e \u0442\u043e\u0447\u043a\u0438 \u043d\u0430 \u0437\u0430\u043c\u043a\u043d\u0443\u0442\u043e\u043c \u043f\u0443\u0442\u0438. \u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0434\u043b\u044f DirFromTail \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0432\u0442\u043e\u0440\u043e\u0439 \u0434\u043b\u044f DirFromHead.  <\/p>\n<pre><code class=\"haskell\">collisionSnakeOnPath :: Snake -&gt; Point -&gt; ClosedPath -&gt; PathDirection -&gt; Bool collisionSnakeOnPath snake point path pathDir | null $ common snake pathPart = False                                               | otherwise = True where   pathPart = takePathPart point (if pathDir == DirFromHead then path else reverse path) (length snake)   common xs ys = [ x | x &lt;- xs , y &lt;- ys, x == y]   takePathPart point path len = _takePathPart point (path ++ (take len path)) len where     _takePathPart _     []     _    = []     _takePathPart point (x:xs) len  | x == point = x:(take (len - 1) xs)                                     | otherwise = _takePathPart point xs len  distBetweenPointsOnPath :: Point -&gt; Point -&gt; ClosedPath -&gt; (Int, Int) distBetweenPointsOnPath point1 point2 path  | point1 == point2 = (0, 0)                                             | id1 &lt; id2 = (length path - id2 + id1,id2 - id1)                                             | otherwise = (id1 - id2, length path - id1 + id2) where   (id1,id2) = pointIndexOnPath (point1,point2) path 0 (0,0)   pointIndexOnPath _               []     _   ids                     = ids   pointIndexOnPath (point1,point2) (x:xs) acc (id1,id2) | x == point1 = pointIndexOnPath (point1,point2) xs (acc+1) (acc,id2)                                                         | x == point2 = pointIndexOnPath (point1,point2) xs (acc+1) (id1,acc)                                                          | otherwise   = pointIndexOnPath (point1,point2) xs (acc+1) (id1,id2)  <\/code><\/pre>\n<p> \u0421\u0432\u0435\u0434\u0435\u043c \u0432\u0441\u0435 \u0432 \u043d\u043e\u0432\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0435\u0439. \u041d\u0430\u0445\u043e\u0434\u0438\u043c \u0442\u043e\u0447\u043a\u0443 \u0434\u043b\u044f \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0446\u0438\u043a\u043b\u0430 \u0413\u0430\u043c\u0438\u043b\u044c\u0442\u043e\u043d\u0430 enterPointBypass \u0432 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0444\u0440\u0443\u043a\u0442\u0430, \u0438\u0449\u0435\u043c \u0441\u0430\u043c\u044b\u0439 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043f\u0443\u0442\u044c \u0434\u043e \u0444\u0440\u0443\u043a\u0442\u0430 \u0432 \u043f\u0440\u044f\u043c\u043e\u043c \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043b\u044f\u0436\u0435\u0442 \u043b\u0438 \u0442\u0443\u0434\u0430 \u0437\u043c\u0435\u044f. \u0415\u0441\u043b\u0438 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0430\u0448\u043b\u0438, \u0434\u0432\u0438\u0433\u0430\u0435\u043c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u043e \u0446\u0438\u043a\u043b\u0443 \u0447\u0435\u0440\u0435\u0437 nextDirOnPath.<\/p>\n<pre><code class=\"haskell\">nextDirBot :: Snake -&gt; Point -&gt; ClosedPath -&gt; (StepDirection, PathDirection) nextDirBot snake fruit path | distBypass1 &lt; distBypass2 &amp;&amp; distBypass1 &lt; distToFruit1                                &amp;&amp; not (collisionSnakeOnPath snake enterPointBypass path DirFromTail)                                  = (dirBetweenPoints (head snake) enterPointBypass, DirFromTail)                             | distBypass2 &lt; distToFruit1                                &amp;&amp; not (collisionSnakeOnPath snake enterPointBypass path DirFromHead)                                  = (dirBetweenPoints (head snake) enterPointBypass, DirFromHead)                              | otherwise = nextDirOnPath snake path where   dirBypass = dirBetweenPoints (head snake) fruit   enterPointBypass = pointStep dirBypass (head snake)   (distBypass1, distBypass2) = distBetweenPointsOnPath enterPointBypass fruit path   (distToFruit1, _) = distBetweenPointsOnPath (head snake) fruit path<\/code><\/pre>\n<p> \u041f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u043f\u0442\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0438 \u0438\u043c\u0435\u0435\u0442 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u044f\u0432\u043d\u044b\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b. \u041c\u044b \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c \u0442\u043e\u0447\u043a\u0443 \u0432\u0445\u043e\u0434\u0430 \u0434\u043b\u044f \u0441\u043e\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043f\u0443\u0442\u0438 enterPointBypass \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u043a \u0444\u0440\u0443\u043a\u0442\u0443, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0435\u0441\u043b\u0438 \u0446\u0438\u043a\u043b \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043b \u0432\u0438\u0434\u0438\u043c\u043e\u0441\u0442\u044c \u0444\u0440\u0443\u043a\u0442\u0430, \u0437\u043c\u0435\u044f \u043f\u043e\u0439\u0434\u0435\u0442 \u043f\u043e \u043f\u043e\u043b\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438. \u0422\u0430\u043a\u0436\u0435, \u0435\u0441\u043b\u0438 \u0437\u043c\u0435\u044f \u0438 \u0444\u0440\u0443\u043a\u0442 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043d\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0438, \u043a\u0430\u043a \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435, \u0431\u043e\u0442 \u0432\u044b\u0431\u0435\u0440\u0435\u0442 \u0437\u0435\u043b\u0435\u043d\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u043c\u0435\u0441\u0442\u043e \u0431\u0435\u043b\u043e\u0433\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u043b\u0438\u043d\u0443 \u043f\u0443\u0442\u0438 \u0434\u043e \u0444\u0440\u0443\u043a\u0442\u0430 \u043c\u044b \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u043d\u0435 \u043f\u043e \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u043c\u0443 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u044e \u043d\u0430 \u043f\u043e\u043b\u0435, \u0430 \u043f\u043e \u0434\u043b\u0438\u043d\u0435 \u0446\u0438\u043a\u043b\u0430.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/14e\/fa7\/844\/14efa7844d7e27d981a901a1b079c20c.png\" width=\"194\" height=\"370\"><figcaption><\/figcaption><\/figure>\n<p> \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c \u043d\u0430\u0448 nextDirBot \u043a \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0443 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0435\u0439, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043c\u0435\u043d\u044e \u0438 \u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.   <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c83\/3b8\/cac\/c833b8cac7d9e83006f012b65104594b.gif\" width=\"449\" height=\"409\"><figcaption><\/figcaption><\/figure>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/551504\/\"> https:\/\/habr.com\/ru\/post\/551504\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p> \u041f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u0430 \u043f\u043e Haskell \u0440\u0435\u0448\u0438\u043b \u0437\u0430\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u0437\u043d\u0430\u043d\u0438\u044f \u043f\u0435\u0440\u0432\u044b\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c. \u041f\u0438\u0441\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c \u0437\u043c\u0435\u0439\u043a\u0443 \u0434\u043b\u044f \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430. \u0427\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0434\u0430\u0442\u044c \u0438\u0433\u0440\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0431\u043e\u0442\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0430\u043c \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u0438\u0433\u0440\u0443. <\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043d\u0430 haskell-platform, Ubuntu 20.04.<\/p>\n<p><a href=\"https:\/\/github.com\/letov\/terminal-snake-hamilton\/\" rel=\"noopener noreferrer nofollow\">GitHub \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/a><\/p>\n<h2>\u0418\u0433\u0440\u043e\u0432\u043e\u0439 \u0446\u0438\u043a\u043b<\/h2>\n<p> \u041d\u0430\u0447\u043d\u0435\u043c \u0441 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0433\u0440\u043e\u0432\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430. \u0417\u043c\u0435\u044f \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e \u043e\u0442 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043a\u043b\u0430\u0432\u0438\u0448, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u0430. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c Control.Concurrent. \u041e\u0442\u0432\u0435\u0442\u0432\u043b\u044f\u0435\u043c\u0441\u044f \u043e\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 forkIO \u0438 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0442\u043e\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 MVar. \u0421 \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u0433\u0440\u043e\u0432\u043e\u0433\u043e \u0446\u0438\u043a\u043b\u0430, tryInput \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c Maybe Char \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0432\u043e\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u041f\u043e\u0442\u043e\u043a\u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0435 \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u0414\u043b\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0431\u0443\u0444\u0435\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0432\u0432\u043e\u0434\u0430 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f System.IO &#8212; \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u043c \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 EOL \u0441\u0438\u043c\u0432\u043e\u043b\u0430 \u043f\u0440\u0438 \u0432\u0432\u043e\u0434\u0435 \u0438 \u0443\u0431\u0435\u0440\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u0432\u044b\u0432\u043e\u0434\u0430. \u0418\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e, \u0447\u0442\u043e hSetBuffering stdin NoBuffering \u043d\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043b\u044f Windows \u043a\u043e\u043d\u0441\u043e\u043b\u0438 &#8212;  getChar \u0431\u0443\u0434\u0435\u0442 \u0436\u0434\u0430\u0442\u044c EOL \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0438\u0433\u0440\u0443 \u0432 \u0444\u043e\u0440\u0442\u043e\u0447\u043a\u0430\u0445 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0432\u0438\u0434\u0435 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u0422\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043c System.Console.ANSI \u0434\u043b\u044f \u043e\u0447\u0438\u0441\u0442\u043a\u0438 \u044d\u043a\u0440\u0430\u043d\u0430 \u0438 \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430.<\/p>\n<pre><code class=\"haskell\">import Control.Concurrent import System.Console.ANSI import System.IO  gameLoop :: ThreadId -&gt; MVar Char -&gt; IO () gameLoop inputThId input = do   tryInput &lt;- tryTakeMVar input   gameLoop inputThId input    inputLoop :: MVar Char -&gt; IO () inputLoop input = (putMVar input =&lt;&lt; getChar) &gt;&gt; inputLoop input   main = do   hSetBuffering stdin NoBuffering    hSetEcho stdin False   clearScreen   input &lt;- newEmptyMVar   inputThId &lt;- forkIO $ inputLoop input   gameLoop inputThId input<\/code><\/pre>\n<h2>\u041c\u0438\u0440 \u0434\u043b\u044f \u0437\u043c\u0435\u0438<\/h2>\n<p> \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f\u044b \u0434\u0430\u043d\u043d\u044b\u0445. \u0423 \u0438\u0433\u0440\u044b \u0431\u0443\u0434\u0435\u0442 4 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f: Process &#8212; \u0437\u043c\u0435\u0435\u0439 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438\u0433\u0440\u043e\u043a, Bot &#8212; \u0437\u043c\u0435\u0435\u0439 \u0440\u0443\u043b\u0438\u0442 \u0438\u0433\u0440\u0430, GameOver \u0438 Quit. \u041c\u0438\u0440 \u0438\u0433\u0440\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d \u0442\u0438\u043f\u043e\u043c data World, \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u0432 \u0438\u0433\u0440\u043e\u0432\u043e\u043c \u0446\u0438\u043a\u043b\u0435 gameLoop. \u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0437\u043c\u0435\u044e, \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0435\u0435 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443 \u0444\u0440\u0443\u043a\u0442\u0430 \u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0438\u0433\u0440\u043e\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0414\u0430\u043b\u0435\u0435 \u043f\u043e \u043c\u0435\u0440\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043d\u0435\u0433\u043e \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f. \u041d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0442\u043e\u0447\u043a\u0430 (0,0) \u0431\u0443\u0434\u0435\u0442 \u0432\u0435\u0440\u0445\u043d\u0438\u043c \u043b\u0435\u0432\u044b\u043c \u043a\u0440\u0430\u0435\u043c \u043a\u043e\u043d\u0441\u043e\u043b\u0438. \u0417\u043c\u0435\u044f \u0434\u0432\u0438\u0433\u0430\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0441\u044f\u043c, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0443 \u043d\u0430\u0441 4 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"haskell\">data StepDirection  =  DirUp                    \t | DirDown                      | DirLeft                    \t | DirRight deriving (Eq)    type Point = (Int, Int) type Snake = [Point] data WorldState = Process                 \t    | GameOver                \t    | Quit                 \t    | Bot deriving (Eq)    data World = World  { snake :: Snake                     , direction :: StepDirection                     , fruit :: Point                     , worldState :: WorldState                     }         gameLoop :: ThreadId -&gt; MVar Char -&gt; World -&gt; IO () {-- \u2026 --}<\/code><\/pre>\n<h2>\u0422\u0430\u0439\u043c\u0435\u0440<\/h2>\n<p> \u0414\u043b\u044f \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c. \u0412\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u043c\u043e\u0434\u0443\u043b\u0435\u043c Data.Time.Clock. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448 \u043c\u0438\u0440 3 \u043f\u043e\u043b\u044f: lastUpdateTime &#8212; \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430,  updateDelay &#8212; \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0434\u0435\u043c \u0434\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0438 isUpdateIteration &#8212; \u0444\u043b\u0430\u0433 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043c\u0438\u0440 \u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438.  \u0423\u043a\u0430\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430 \u0438 \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a timerController. \u041e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0444\u043b\u0430\u0433 isUpdateIteration, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c\u0441\u044f.  <\/p>\n<pre><code class=\"haskell\">import Data.Time.Clock  data World \t\t\t\t\t\t\t = World  {                     \t          \t\t{-- \u2026 --}                                   , lastUpdateTime :: UTCTime                                   , updateDelay :: NominalDiffTime                                   , isUpdateIteration :: Bool                                   }    initWorld :: UTCTime -&gt; World initWorld timePoint = World { snake = [(10, y) | y &lt;- [3..10]]                              , direction = DirRight                             , fruit = (3, 2)                             , lastUpdateTime = timePoint                             , updateDelay = 0.3                             , isUpdateIteration = True                             , worldState = Process                             }    timerController :: UTCTime -&gt; World -&gt; World timerController timePoint world   | isUpdateTime timePoint world = world { lastUpdateTime = timePoint                                          , isUpdateIteration = True                                           }   | otherwise                    = world where     isUpdateTime timePoint world =      \tdiffUTCTime timePoint (lastUpdateTime world) &gt;= updateDelay world  gameLoop inputThId input oldWorld = do  {-- \u2026 --}   timePoint &lt;- getCurrentTime   let newWorld = timerController timePoint oldWorld   gameLoop inputThId input newWorld { isUpdateIteration = False }    main = do   {-- \u2026 --}   timePoint &lt;- getCurrentTime   gameLoop inputThId input (initWorld timePoint)<\/code><\/pre>\n<h2>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0432\u0432\u043e\u0434\u0430<\/h2>\n<p> \u0414\u0430\u043b\u0435\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u0432\u043e\u0434\u0430 inputController. \u041a\u043b\u0430\u0432\u0438\u0448\u0438 WSAD \u043c\u0435\u043d\u044f\u044e\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0439 \u0437\u043c\u0435\u0438. \u0421\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0437\u043c\u0435\u044f \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0437\u0430\u0434, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u044f, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d\u0430 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 1 \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0430. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0441\u043b\u0438 \u043d\u043e\u0432\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0434\u0435\u0442 \u043a\u043e \u0432\u0442\u043e\u0440\u043e\u043c\u0443 \u043e\u0442 \u0433\u043e\u043b\u043e\u0432\u044b \u0441\u0435\u0433\u043c\u0435\u043d\u0442\u0443 \u0437\u043c\u0435\u0438, \u043c\u044b \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0432\u0432\u043e\u0434. \u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u043b\u0438 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u043c, \u0442\u043e \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0436\u0430\u043b \u043a\u043b\u0430\u0432\u0438\u0448\u0443 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f, \u0443\u0441\u043a\u043e\u0440\u0438\u043c \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435 \u0437\u043c\u0435\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0432 updateDelay. \u0424\u0443\u043d\u043a\u0446\u0438\u044f pointStep \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0438 \u0442\u043e\u0447\u043a\u0443, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f \u043d\u043e\u0432\u0443\u044e \u0442\u043e\u0447\u043a\u0443, \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u043d\u0443\u044e \u043d\u0430 \u043e\u0434\u0438\u043d \u0448\u0430\u0433 \u0432 \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0438.  <\/p>\n<pre><code class=\"haskell\">pointStep :: StepDirection -&gt; Point -&gt; Point pointStep direction (x, y) = case direction of   DirUp -&gt; (x, y - 1)    DirDown -&gt; (x, y + 1)   DirLeft -&gt; (x - 1, y)   DirRight -&gt; (x + 1, y)  inputController :: Maybe Char -&gt; World -&gt; World 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      case command of     Just 'w' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirUp                        , updateDelay = boost (direction world) DirUp                       , worldState = Process                       }     Just 's' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirDown                       , updateDelay = boost (direction world) DirDown                       , worldState = Process                       }     Just 'a' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirLeft                        , updateDelay = boost (direction world) DirLeft                       , worldState = Process                       }     Just 'd' -&gt; world { direction = filterSecondSegmentDir (snake world) (direction world) DirRight                        , updateDelay = boost (direction world) DirRight                       , worldState = Process                       }     Just 'q' -&gt; world { worldState = Quit }     Just 'h' -&gt; world { worldState = Bot }     _        -&gt; world { updateDelay =  0.3 }<\/code><\/pre>\n<h2>\u0414\u0432\u0438\u0433\u0430\u0435\u043c \u0437\u043c\u0435\u044e<\/h2>\n<p> \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 moveController \u0441\u0434\u0432\u0438\u043d\u0435\u0442 \u043d\u0430\u0448\u0443 \u0437\u043c\u0435\u044e, \u0435\u0441\u043b\u0438 \u043f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f  isUpdateIteration \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u0438\u0440\u0430.  <\/p>\n<pre><code class=\"haskell\">snakeStep :: StepDirection -&gt; Snake -&gt;  Snake snakeStep direction snake = (pointStep direction $ head snake):(init snake)  moveController :: World -&gt; World moveController world    | not $ isUpdateIteration world = world   | otherwise = world { snake = snakeStep (direction world) (snake world) }<\/code><\/pre>\n<h2>\u0421\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438<\/h2>\n<h4>\u0413\u0440\u0430\u043d\u0438\u0446\u044b \u043f\u043e\u043b\u044f<\/h4>\n<p> \u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u044f \u0437\u043c\u0435\u0438 \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438. \u0412 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0438\u043c\u0438 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0430\u043c\u0430 \u0437\u043c\u0435\u044f \u0438 \u0444\u0440\u0443\u043a\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0441\u0442\u0435\u043d\u044b. \u041e\u043d\u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u043d\u044b, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0432 \u043c\u0438\u0440 \u0438 \u043e\u0431\u044a\u044f\u0432\u0438\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e. \u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u0430\u0440\u044b (1,1) \u2014 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u043b\u0435\u0432\u0430\u044f \u0442\u043e\u0447\u043a\u0430 \u0441\u0442\u0435\u043d\u044b \u0438 (20,20) \u2014 \u043d\u0438\u0436\u043d\u044f\u044f \u043f\u0440\u0430\u0432\u0430\u044f.   <\/p>\n<pre><code class=\"haskell\">initWalls :: Walls initWalls = ((1,1),(20,20))<\/code><\/pre>\n<h4>\u0413\u0421\u0427<\/h4>\n<p> \u0424\u0440\u0443\u043a\u0442 \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u043b\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u0435, \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0413\u0421\u0427. \u0412 Haskell \u043e\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u0432 \u043c\u043e\u0434\u0443\u043b\u0435 System.Random, \u0444\u0443\u043d\u043a\u0446\u0438\u044f randomR. \u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0441 \u0447\u0438\u0441\u0442\u044b\u043c\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u043f\u0440\u0438 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u0432\u0442\u043e\u0440\u044b\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u043c randomR \u0441\u043b\u0443\u0436\u0438\u0442 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u043e\u043c. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u043a\u0430\u043a \u043f\u043e\u043b\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043c\u0438\u0440\u0430 \u0438 \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0435\u043c\u0443 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u041a\u043e\u0433\u0434\u0430 \u0437\u043c\u0435\u044f \u0435\u0441\u0442 \u0444\u0440\u0443\u043a\u0442, \u043e\u043d\u0430 \u0440\u0430\u0441\u0442\u0435\u0442 \u0432 \u0445\u0432\u043e\u0441\u0442\u0435. \u0421\u043e\u0445\u0440\u0430\u043d\u0438\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443 \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u0442\u043e\u0447\u043a\u0438 \u0445\u0432\u043e\u0441\u0442\u0430 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043c\u0438\u0440\u0430.  <\/p>\n<pre><code class=\"haskell\">import System.Random  data World = World  {                      {-- \u2026 --}                     , oldLast :: Point                     , rand :: StdGen                     }     initWorld timePoint = World   {                               {-- \u2026 --}                               , oldLast = (0, 0)                               , rand = mkStdGen 0                               }   {-- \u2026 --} timerController timePoint world   | isUpdateTime timePoint world  = world {                                            {-- \u2026 --}                                           , oldLast = last $ snake world                                           } {-- \u2026 --}                                       <\/code><\/pre>\n<h4>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439<\/h4>\n<p> \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439 \u0437\u043c\u0435\u0438 \u0441 \u0442\u0435\u043b\u043e\u043c \u0438 \u0433\u043e\u043b\u043e\u0432\u044b \u0441\u043e \u0441\u0442\u0435\u043d\u043e\u0439.<\/p>\n<pre><code class=\"haskell\">collisionSnake :: Snake -&gt; Bool collisionSnake (x:xs) = any (== x)  xs  collisionWall :: Point -&gt; Walls -&gt; Bool collisionWall (sx,sy) ((wx1,wy1),(wx2,wy2)) =  \tsx &lt;= wx1 || sx &gt;= wx2 || sy &lt;= wy1 || sy &gt;= wy2<\/code><\/pre>\n<p> \u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u0434\u043b\u044f \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440\u0430 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0439 collisionController.  \u041f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0438\u0440\u0430 \u0432 GameOver \u043f\u0440\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u043e\u0432\u0435\u043d\u0438\u0438 \u0441 \u043f\u0440\u0435\u043f\u044f\u0442\u0441\u0442\u0432\u0438\u044f\u043c\u0438 \u0438 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u043c \u0434\u043b\u0438\u043d\u0443 \u0437\u043c\u0435\u0438 \u0432 \u0445\u0432\u043e\u0441\u0442\u0435 \u043f\u0440\u0438 \u0441\u044a\u0435\u0434\u0430\u043d\u0438\u0438 \u0444\u0440\u0443\u043a\u0442\u0430, \u0442\u0430\u043a\u0436\u0435 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0432 \u043d\u043e\u0432\u044b\u0439 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u0441\u0442\u0435\u043d. \u0415\u0441\u043b\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u0444\u0440\u0443\u043a\u0442\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043e\u0439 \u0442\u0435\u043b\u0430 \u0437\u043c\u0435\u0438, \u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443. \u0422\u0430\u043a\u0436\u0435, \u0435\u0441\u043b\u0438 \u0434\u043b\u0438\u043d\u0430 \u0437\u043c\u0435\u0438 \u0441\u0442\u0430\u043b\u0430 \u043d\u0430 1 \u043c\u0435\u043d\u044c\u0448\u0435 \u0435\u043c\u043a\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044f, \u0432\u044b\u0445\u043e\u0434\u0438\u043c \u0432 GameOver \u0441 \u043f\u043e\u0431\u0435\u0434\u043e\u0439.<\/p>\n<pre><code class=\"haskell\">collisionController :: World -&gt; World collisionController world   | not $ isUpdateIteration world                = world   | collisionSnake $ snake world                 = world { worldState = GameOver }    | collisionWall (head $ snake world) initWalls = world { worldState = GameOver }   | checkWin (snake world) initWalls             = world { worldState = GameOver }   |<\/code><\/pre>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-321149","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321149","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=321149"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321149\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=321149"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=321149"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=321149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}