Змейка (исходный код на JS)


<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>Змейка</title>

    <style>
        html, body {
            height: 100%;
            margin: 0;
        }

        /* задаем стиль для игрового поля*/
        body {
            background: whitesmoke;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        canvas {
            border: 2px solid black;
        }
    </style>
</head>

<body>
    <!-- Задаем игровое поле -->
    <canvas width="640" height="704" id="game"></canvas>

    <!-- Сама программа  -->
    <script>
        var canvas = document.getElementById('game');
        var ctx = canvas.getContext('2d');
        // ширина сетки для движения змейки
        var grid = 16;
        ctx.font = grid + 'px Arial';

		// количество пикселей между полосками
		var Npols = 4;

		// смещение полосок на теле змейки
		var pols = 1;

        // счетчик прореживания кадров для задания скорости змейки
        var count = 0;

		// Счетчик времени отображения сообщения в нижней строке
		var timeMessage = 0;

        // переменные для головы, тела и хвоста змейки
        var head, body, tail;

        // Задаем объект для змейки
        var snake = {
            x: 160, y: 160,         // Координаты головы (начальное положение)
            tekNapr: 1,             // направление движения змейки 0 - стоим, 1 - вправо, 2 - вверх, 3 - влево, 4 - вниз
			tekPov: 0,		        // для прорисовки углов змейки - поворот NM N - предыдущее направление, M - текущее направление. 0 - нет поворота
            cells: [],              // массив для элементов тела змейки
            maxCells: grid / 2 + 3, // первоначальная длина змейки
            timeHungry: 100,        // время до голодания змейки
            dtHungry: 10,           // время голодания, через которое уменьшаем по одному кусочку
            cross: false            // Точка пересечения змейки самой себя.
        };
		let Hungry		= 200;
		let deltaHungry = 20;

        //let colorTest;

        // Задаем массив объектов для кроликов и мангустов: позиция x:y, тип ty = 0 - кролик, 2 - магнуст, вес p - от 1 до 4, t - время жизни в тиках 0 - постоянный, 1000 - максимальный,
        // dt - дельта времени(в тиках) для перемещения кролика или мангуста, 0 - не двигается, до 40 - скорость,
        var rabbmang = [];

        // функция для задания случаной позиции
        function getRandomInt(min, max) {
            return Math.floor(Math.random() * (max - min)) + min;
        }

        // функция проверки, что позиция x:y не находится в теле змейки
        function intoSnake(x, y) {
            var flag = false;
            snake.cells.forEach(function (cell, i, c) {
                if ((cell.x === x) && (cell.y === y)) flag = true;
            })
            return flag;
        }

        // функция установки признака пересечения змейки самой себя в позиции x:y
        function setCrossSnake(x, y) {
            snake.cells.forEach(function (cell, i, c) {
                if ((cell.x === x) && (cell.y === y)) cell.cross = true;
            })
        }

        // функция проверки, что позиция x:y не попала на кролика или мангуста
        function intoRabbit(x, y) {
            var flag = false;
            for (let rabbit of rabbmang) {  // для каждого кролика или мангуста
                if ((rabbit.x === x) && (rabbit.y === y)) {
                    flag = true;
                    break;
                }
            }
            return flag;
        }

        // функция вывода кролика (мангуста) на игровое поле
        function drawRabbit(rm) {
            // прорисовываем кролика
            if (rm.ty == 0) {
                ctx.fillStyle = 'red';
                ctx.fillRect(rm.x + rm.p/2, rm.y, grid - rm.p/2, grid);
                ctx.fillStyle = 'black';
                ctx.fillText(rm.p, rm.x + 3 + rm.p/4, rm.y + grid - 3);
            }
            // прорисовываем мангуста
            if (rm.ty == 1) {
                ctx.fillStyle = 'blue'
                ctx.fillRect(rm.x + rm.p/2, rm.y, grid - rm.p/2, grid - 1);
                ctx.fillStyle = 'black';
                ctx.fillText('&', rm.x + 3 + rm.p/4, rm.y + grid - 3);
            }
        }

        // функция создания кролика (мангуста) на игровом поле в случайной позиции
        function setRabbit() {

            let rabbit = { x: 0, y: 0, ty: 0, p: 0, t: 100, dt: 0 };
            // Находим случайную позицию на игровом поле
            rabbit.x = getRandomInt(0, canvas.width / grid) * grid;
            rabbit.y = getRandomInt(2, (canvas.height-32) / grid) * grid;
            // проверяем, что позиция не должна попадать на тело змейки
            while ((intoSnake(rabbit.x, rabbit.y) === true) || (intoRabbit(rabbit.x, rabbit.y) === true)) {
                rabbit.x = getRandomInt(0, canvas.width / grid) * grid;
                rabbit.y = getRandomInt(2, (canvas.height-32) / grid) * grid;
            }
            rabbit.ty = 0;                          // задаем тип - кролик (0) и мангуст (1)
            if (getRandomInt(0, 20) > 18) {         // кроликов в 9 раз чаще чем мангуста
                rabbit.ty = 1;
            }
            rabbit.p = getRandomInt(1, 10);         // задаем вес кролика - от 1 до 9
            rabbit.t = getRandomInt(0, 40) * 20;    // задаем время жизни кролика или мангуста 0 - вечный
            rabbit.dt = getRandomInt(1, 3) * 4;     // задаем скачок - то есть дельту времени или иначе - скорость передвижения кролика.

            drawRabbit(rabbit);                     // прорисовываем кролика или мангуста заданном месте
            rabbmang.push(rabbit);                  // заносим в массив кроликов и мангустов

        }

        // Начальное положение змейки
        function initSnake() {
            // задаем положение для головы змейки и запускаем змейку с начала
            snake.tekNapr = 1;
            snake.x = 160;
            snake.y = 160;
            snake.cells = [];
            snake.cells.push({ x: snake.x, y: snake.y, napr: snake.tekNapr })         // заносим голову
            snake.cells.push({ x: snake.x - grid, y: snake.y, napr: snake.tekNapr })    // и первый кусочек тела змеи - он же хвост
            snake.maxCells = grid/2 + 3;
            snake.timeHungry = Hungry;
            snake.dtHungry = deltaHungry;
            snake.cross = false;

			pols = 0;

            // обнуляем игровое поле
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            // прорисовываем верхную границу игрового поля
            ctx.fillStyle = 'black';                    // границы рисуем черным
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.moveTo(0, 31);
            ctx.lineTo(640, 31);
            ctx.stroke();
            // прорисовываем нижную границу игрового поля
            ctx.beginPath();
            ctx.moveTo(0, 673);
            ctx.lineTo(640, 673);
            ctx.stroke();

            // при перезагрузке змейки также убираем старых кроликов
            let temp = rabbmang.splice(0, rabbmang.length);
            for (let rabbit of temp) {
                ctx.clearRect(rabbit.x, rabbit.y, grid, grid);
            }

            // ставим первого кролика
            //setRabbit();
        }

        // Укус мангуста
        function cutSnake(cutX, cutY, napr) {
            let flag = false;
            if ((snake.cells[0].x === cutX) && (snake.cells[0].y === cutY)) {
                switch (napr) {                                         // и тестируем новое место одновременно смотрим не вышли ли из игрового поля
                    case 1: if (snake.cells[0].napr != 3) { flag = true; break; }   // кусаем вправо - если голова не идет навстречу
                    case 2: if (snake.cells[0].napr != 4) { flag = true; break; }   // кусаем вверх (голова вниз)
                    case 3: if (snake.cells[0].napr != 1) { flag = true; break; }   // кусаем влево (голова вправо)
                    case 4: if (snake.cells[0].napr != 2) { flag = true; break; }   // кусаем вниз (голова враво)
                }
            }
            if ((snake.cells[1].x === cutX) && (snake.cells[1].y === cutY)) {
                flag = true;                                                        // укус за шею (второй кусочек тела змейки после головы) - тоже смерть змейки
            }
            if (flag) {    // укус за голову - мгновенная смерть змейки
                alert('Увы - мангуст слопал змейку - игра окончена');
                // Задаем новую змейку
                initSnake();
            }
            if (!flag) {        // укус не за голову змейки
                for (let tekPos of snake.cells) {
                    if ((tekPos.x === cutX) && (tekPos.y === cutY)) {                               // нашли место укуса мангуста
                        let indexSnake = snake.cells.indexOf(tekPos) - 1;                           // и с этого места
                        let temp = snake.cells.splice(indexSnake, snake.maxCells - indexSnake);     // удаляем все тело змейки до хвоста
                        for (let cutPos of temp) {
                            ctx.clearRect(cutPos.x, cutPos.y, grid, grid);                      	// тело змейки на канве
                        }
                        snake.maxCells = snake.cells.length;
						ctx.fillText('Ой - мангуст откусил половину змейки', 200, 675);
						timeMessage = 100;
                        break;
                    }
                }
            }
        }

        // Цикл отображения одного шага анимации игрового поля со змейкой
        function loop() {

            // Каждый кадр видиосистемы перезапускаем основной цикл игры
            requestAnimationFrame(loop);

            // прореживаем кадры для того чтобы прорисовывать змейку 8 раз в секунду. (60/8 - 7.5) - задает скорость змейки
            if (++count < 8) return;
            count = 0;              // обнуляем счетчик прореживания

            // выводим длину змейки, количество кроликов и мангустов, время жизни змейки и другую статистику
            ctx.fillStyle = 'black';                    // рисуем черным
            ctx.clearRect(600, 675, 40, 32);
            ctx.fillText(snake.maxCells, 600, 700);

			// При отображении сообщения ждем заданное время
			if (timeMessage-- > 0) {
				if (timeMessage === 0) {
				ctx.clearRect(180, 675, 400, 40);
				ctx.fillText("_____________________", 180, 700);	// и вытираем сообщение
				//	ctx.stroke;
					}
				}

            // выставляем случайным способом нового кролика в случайном месте игрового поля
            if (getRandomInt(0, 50) == 0) { setRabbit() };

            // обрабатываем массив кроликов и мангустов
            for (let rabbit of rabbmang) {  // для каждого кролика или мангуста
                // время жизни кролика или мангуста
                if (rabbit.t > 0) {         // если кролик или мангуст не вечный (0), то уменьшаем время жизни кроликов и убираем тех, чье время истекло
                    if (--rabbit.t === 0) {       // если время жизни кончилось
                        let indexRabbit = rabbmang.indexOf(rabbit);     // то этого кролика или мангуста
                        let temp = rabbmang.splice(indexRabbit, 1);     // удаляем
                        ctx.clearRect(rabbit.x, rabbit.y, grid, grid);  // очищаем место кроллика на канве
                    }
                }
                // скачок кролика или мангуста
                if (rabbit.dt > 0) {        // отсчитываем время до следующего перемещения кролика или мангуста 0 - неподвижный кролик или мангуст
                    if (--rabbit.dt === 0) {      // если время для следующего скачка кролика кончилось
                        rabbit.dt = getRandomInt(1, 4) * 4;                     // задаем новый скачок
                        let posX = rabbit.x; posY = rabbit.y;                   // то перемещаем этого кролика или мангуста на один шаг в случайную сторону
                        let napr = getRandomInt(0, 5);                          // определяем направление смещения
                        let flag = false;                                       // признак
                        switch (napr) {                                         // и тестируем новое место одновременно смотрим не вышли ли из игрового поля
                            case 0: break;                                                                       // не двигаемся
                            case 1: if (rabbit.x < canvas.width - grid)       { posX += grid; flag = true; break; }   // двигаемся вправо
                            case 2: if (rabbit.y > 32 + grid)                 { posY -= grid; flag = true; break; }   // двигаемся вверх
                            case 3: if (rabbit.x > grid)                      { posX -= grid; flag = true; break; }   // двигаемся влево
                            case 4: if (rabbit.y < canvas.height - 32 - grid) { posY += grid; flag = true; break; }   // двигаемся вниз
                        }
                        // проверяем не попали ли мы на змейку
                        if (flag && (intoSnake(posX, posY) === false) && (intoRabbit(posX, posY) === false)) {   // если перемещение допустимо, то меняем кролика в массиве
                            ctx.clearRect(rabbit.x, rabbit.y, grid, grid);                                   // очищаем старое место кролика на канве
                            rabbit.x = posX; rabbit.y = posY;                                                    // задаем новое положение в массив
                            drawRabbit(rabbit);                                                                  // прорисовываем кролика или мангуста на новом месте
                        }
                    }
                }
                // укус мангуста
                if (rabbit.ty == 1) {
                    if (intoSnake(rabbit.x + grid, rabbit.y) && !(intoSnake(rabbit.x + 2 * grid, rabbit.y))) {   // если справа от мангуста тело змейки '#', но неприкрытое '##'
                        cutSnake(rabbit.x + grid, rabbit.y, 1);                                                  // то кусаем
                    }
                    if (intoSnake(rabbit.x, rabbit.y - grid) && !(intoSnake(rabbit.x, rabbit.y - 2 * grid))) {   // если сверху от мангуста тело змейки '#', но неприкрытое '##'
                        cutSnake(rabbit.x, rabbit.y - grid, 2);                                                  // то кусаем
                    }
                    if (intoSnake(rabbit.x - grid, rabbit.y) && !(intoSnake(rabbit.x - 2 * grid, rabbit.y))) {   // если слева от мангуста тело змейки '#', но неприкрытое '##'
                        cutSnake(rabbit.x - grid, rabbit.y, 3);                                                  // то кусаем
                    }
                    if (intoSnake(rabbit.x, rabbit.y + grid) && !(intoSnake(rabbit.x, rabbit.y + 2 * grid))) {   // если снизу от мангуста тело змейки '#', но неприкрытое '##'
                        cutSnake(rabbit.x, rabbit.y + grid, 4);                                                  // то кусаем
                    }
                }
            }

            // пока нет голодания змейки
            if (snake.timeHungry > 0) {             // пока не голодаем (>0)
                snake.timeHungry--;                 // уменьшаем счетчик сытости
            }
            // змейка голодает
            if (snake.timeHungry === 0) {           // голодаем (0) - да
                if (--snake.dtHungry === 0) {       // отсчитываем очередную дельту до уменьшения длины змейки на 1 кусочек
                    let temp = snake.cells[--snake.maxCells-1];      // умираем с хвоста
                    ctx.clearRect(temp.x, temp.y, grid, grid);       // стираем хвост змеи (рисуем фоном)
                    snake.cells.pop();                               //
                    snake.dtHungry = deltaHungry;                    // новая дельта времени голодания
					ctx.fillText("Кушать хочу ", 200, 700);
					timeMessage = 4;
                    // а может сдохли уже - проверяем
                    if (snake.maxCells === 1) {
                        snake.tekNapr = 0;           // останавливаем змейку
                        alert('Увы - змейка сдохла от голода - игра окончена')
                        // Задаем новую змейку
                        initSnake();
                    };
                }
            }
            // по заданому направлению смещаем голову змеи
            switch (snake.tekNapr) {
                case 0: break;   // не двигаемся
                case 1: snake.x += grid; break;   // двигаемся вправо
                case 2: snake.y -= grid; break;   // двигаемся вверх
                case 3: snake.x -= grid; break;   // двигаемся влево
                case 4: snake.y += grid; break;   // двигаемся вниз
            }
		// ### to do.добавляем параметр для сглаживания углов - pov - поворот 12 - вправо-вверх, 14 - вправо-вниз, 21 - вверх-вправо, 23 - вверх-влево
		//                                                                    32 - влево-вверх,  34 - влево-вниз,  41 - вниз-вправо,  43 - вниз-влево
			if (snake.tekNapr != snake.cells[1].napr) {
				snake.tekPov = 0;
				switch (snake.cells[1].napr) {
				case 0: break;   //
				case 1: if (snake.tekNapr === 2) snake.tekPov = 12; else if (snake.tekNapr === 4) snake.tekPov = 14; break;   //  12 - вправо-вверх, 14 - вправо-вниз
                case 2: if (snake.tekNapr === 1) snake.tekPov = 21; else if (snake.tekNapr === 3) snake.tekPov = 23; break;   //  21 - вверх-вправо, 23 - вверх-влево
                case 3: if (snake.tekNapr === 2) snake.tekPov = 32; else if (snake.tekNapr === 4) snake.tekPov = 34; break;   //  32 - влево-вверх,  34 - влево-вниз
                case 4: if (snake.tekNapr === 1) snake.tekPov = 41; else if (snake.tekNapr === 3) snake.tekPov = 43; break;   //  41 - вниз-вправо,  43 - вниз-влево
				}
			}

            // останова перед вертикальной и горизонтальной границей игрового поля не делаем - а просто перепрыгиваем на противоположную сторону
            if (snake.x < 0) snake.x = canvas.width - grid;        // при движении влево
            else if (snake.x >= canvas.width) snake.x = 0;         // при движении вправо

            if (snake.y < 32) snake.y = canvas.height - 32 - grid; // при движении вверх
            else if (snake.y >= canvas.height - 32) snake.y = 32;  // при движении вниз

            // проверяем не заткнулась ли змейка на саму себя
            // if (colorTest[1] != 0) {
            snake.cross = false;                                    // задаем признак что нет пересечения змейки самой себя
			if (intoSnake(snake.x, snake.y)) {                      // и проверяем не пересечет ли голова змейки свое тело (причем проверяем толщину змейки - одиночную или двойную)
                let prEnd = false;
                setCrossSnake(snake.x, snake.y);                    // при пересечении змейки самой себя, ставим признак в старое тело змейки, не в голову.
                switch (snake.tekNapr) {
					case 1: if (intoSnake(snake.x+grid, snake.y) === true) prEnd = true; break;	// двигаемся вправо
					case 2: if (intoSnake(snake.x, snake.y-grid) === true) prEnd = true; break; // двигаемся вверх
					case 3: if (intoSnake(snake.x-grid, snake.y) === true) prEnd = true; break; // двигаемся влево
					case 4: if (intoSnake(snake.x, snake.y+grid) === true) prEnd = true; break; // двигаемся вниз
					}
				if (prEnd) {                                        // но если делее по ходу движения змейки есть кусочек ее тела, то через двойную толщину змеки не переползаем
					snake.tekNapr = 0;                              // останавливаем змейку
					alert('Увы - змейка укусила сама себя и скончалась в конвульсиях - игра окончена');
					// Задаем новую змейку
					initSnake();
					}
            };

            // вводим новое положение головы змейки в массив тела змейки
            if (snake.tekNapr > 0) snake.cells.unshift({ x: snake.x, y: snake.y, napr: snake.tekNapr, pov: snake.tekPov, cross: snake.cross });

            // убираем хвост из массива. А если длина змейки не выросла, тогда пропускаем удаление последнего элемента массива тела змеи
            if (snake.cells.length > snake.maxCells) snake.cells.pop();
 /*
            ctx.fillStyle = 'green'; 	                    // змейку рисуем зеленым
            ctx.fillRect(10,  674, grid, grid);				// Прорисовываем кусочки тела змеи в качестве легенды
			ctx.fillRect(50,  674, grid, grid);				// для отладки смещения полосок
			ctx.fillRect(90,  674, grid, grid);
			ctx.fillRect(130, 674, grid, grid);
			//alert('===11111===');
			ctx.fillStyle = 'black';                    	// а полоски на змейке рисуем чёрным
			ctx.lineWidth = 1;
            for (let j = pols; j < grid; j = j + Npols) {
				// движение вправо
				ctx.beginPath(); ctx.moveTo(10 + grid - j, 675);            ctx.lineTo(10 + grid - j,  674 + grid - 1); ctx.stroke();
				// движение вверх
				ctx.beginPath(); ctx.moveTo(51,            674 + grid - j); ctx.lineTo(50 + grid - 1,  674 + grid - j); ctx.stroke();
				// движение влево
				ctx.beginPath(); ctx.moveTo(90 + j,        675);            ctx.lineTo(90 + j,         674 + grid - 1); ctx.stroke();
				// движение вниз
				ctx.beginPath(); ctx.moveTo(131,           674 + j);        ctx.lineTo(130 + grid - 1, 674 + j);        ctx.stroke();
				//alert('===22222===')
			}
			//alert('===33333===')
*/
            let len = snake.cells.length - 1                // текущая длина змейки
            ctx.fillStyle = 'green'; 	                    // змейку рисуем зеленым
            head = snake.cells[0];                          // новая позиция головы змейки
            body = snake.cells[1];                          // прежняя позиция головы змейки - становиться телом
            tail = snake.cells[len];                        // позиция хвоста змейки - для того чтобы стереть ее

            //colorTest = ctx.getImageData(head.x, head.y, 1, 1).data;  // для проверки пустоты новой позиции змейки

            // Прорисовываем голову змейки (рисуем зеленым)
            ctx.fillRect(head.x+1, head.y+1, grid-2, grid-2);		// Прорисовываем голову змеи
            // и глазки змеи в зависимости от направления
            switch (snake.tekNapr) {    // левый глаз                                             правый глаз
                case 1: ctx.clearRect(head.x + grid - 5, head.y + grid - 5, 2, 2); ctx.clearRect(head.x + grid - 5, head.y + 3, 2, 2); break;   // двигаемся вправо
                case 2: ctx.clearRect(head.x + grid - 5, head.y + 3, 2, 2); ctx.clearRect(head.x + 3, head.y + 3, 2, 2); break;   // двигаемся вверх
                case 3: ctx.clearRect(head.x + 3, head.y + 3, 2, 2); ctx.clearRect(head.x + 3, head.y + grid - 5, 2, 2); break;   // двигаемся влево
                case 4: ctx.clearRect(head.x + 3, head.y + grid - 5, 2, 2); ctx.clearRect(head.x + grid - 5, head.y + grid - 5, 2, 2); break;   // двигаемся вниз
            }

			// прорисовываем тело змеи
            for (let i = 1; i <= len; i++) {
				body = snake.cells[i];
				ctx.fillStyle = 'green';                    // змейку рисуем зеленым
            	ctx.fillRect(body.x, body.y, grid, grid);	// Прорисовываем I-й кусочек тела змеи
				// прорисовываем полоски на теле змеи в зависимости от направления движения змейки, через два пикселя
				// смещая полоски каждый раз на один пиксель за тик.
				ctx.fillStyle = 'black';                    // а полоски на змейке рисуем чёрным
				ctx.lineWidth = 1;
            	for (let j = pols; j < grid; j = j + Npols) {
				    ctx.beginPath();
					switch (body.napr) {
						// двигаемся вправо
						case 1: ctx.moveTo(body.x + grid - j, body.y + 1       ); ctx.lineTo(body.x + grid - j, body.y + grid - 1); break;
						// двигаемся вверх
						case 2: ctx.moveTo(body.x + 1,        body.y + grid - j); ctx.lineTo(body.x + grid - 1, body.y + grid - j); break;
						// двигаемся ввлево
						case 3: ctx.moveTo(body.x + j,        body.y + 1       ); ctx.lineTo(body.x + j,        body.y + grid - 1); break;
						// двигаемся вниз
						case 4: ctx.moveTo(body.x + 1,        body.y + j       ); ctx.lineTo(body.x + grid - 1, body.y + j       ); break;
					}
					ctx.stroke();
        		}
			}

			// смещаемся на следующую позицию для полосок
			pols++
			if (pols > Npols) pols = 1; 
            // уменьшаем хвост змейки - последние grid/2 (8) кусочки стираем с боков линии в зависимости от направления текущего кусочка и предыдущего
            let i = 3;                                  // для последних grid/2 (8) кусочков делаем
            if (len > grid / 2 + 2) i = len - grid / 2;
            for (let j = 1; i <= len; j++) {
                let pred = snake.cells[i - 1].napr      // Направление предыдующего кусочка
                body = snake.cells[i++];
                if (body.cross) continue;               // пересечение змейки самой с собой - хвост не рисуем, чтобы не было разрыва в теле змейки
                switch (body.napr) { //левый бок                                              правый бок
                    // двигаемся вправо
                    case 1: ctx.clearRect(body.x, body.y, grid, j); ctx.clearRect(body.x, body.y + grid - j, grid, j);
                        switch (pred) {
                            // повернули наверх
                            case 2: ctx.clearRect(body.x + grid - j, body.y, j, grid); 					// срезали правый бок
                                ctx.fillRect(body.x + j, body.y, grid - j - j, j); break;				// дорисовали верх
                            // повернули вниз
                            case 4: ctx.clearRect(body.x + grid - j, body.y, j, grid);					// срезали правый бок
                                ctx.fillRect(body.x + j, body.y + grid - j, grid - j - j, j); break;	// дорисовали низ
                        } // для 0, 1, 3 - ничего не меняем
                        break;
                    // двигаемся вверх
                    case 2: ctx.clearRect(body.x + grid - j, body.y, j, grid); ctx.clearRect(body.x, body.y, j, grid);
                        switch (pred) {
                            // повернули налево
                            case 3: ctx.clearRect(body.x, body.y, grid, j);								// срезали верхний бок
                                ctx.fillRect(body.x, body.y + j, j, grid - j - j); break;				// дорисовали слева
                            // повернули направо
                            case 1: ctx.clearRect(body.x, body.y, grid, j); 							// срезали верхний бок
                                ctx.fillRect(body.x + grid - j, body.y + j, j, grid - j - j); break;	// дорисовали справа
                        } // для 0, 2, 4 - ничего не меняем
                        break;
                    // двигаемся влево
                    case 3: ctx.clearRect(body.x, body.y, grid, j); ctx.clearRect(body.x, body.y + grid - j, grid, j);
                        switch (pred) {
                            // повернули наверх
                            case 2: ctx.clearRect(body.x, body.y, j, grid); 							// срезали левый бок
                                ctx.fillRect(body.x + j, body.y, grid - j - j, j); break;				// дорисовали верх
                            // повернули вниз
                            case 4: ctx.clearRect(body.x, body.y, j, grid);								// срезали левый бок
                                ctx.fillRect(body.x + j, body.y + grid - j, grid - j - j, j); break;	// дорисовали низ
                        } // для 0, 1, 3 - ничего не меняем
                        break;
                    // двигаемся вниз
                    case 4: ctx.clearRect(body.x + grid - j, body.y, j, grid); ctx.clearRect(body.x, body.y, j, grid);
                        switch (pred) {
                            // повернули налево
                            case 3: ctx.clearRect(body.x, body.y + grid - j, grid, j);					// срезали нижний бок
                                ctx.fillRect(body.x, body.y + j, j, grid - j - j); break;				// дорисовали слева
                            // повернули направо
                            case 1: ctx.clearRect(body.x, body.y + grid - j, grid, j); 					// срезали нижний бок
                                ctx.fillRect(body.x + grid - j, body.y + j, j, grid - j - j); break;	// дорисовали справа
                        } // для 0, 2, 4 - ничего не меняем
                        break;
                }
            }
            // Стираем хвост змеи (рисуем фоном)/ Но если хвост сползает с точки пересечения, то не убираем точку пересечения, чтобы не было разрыва в змейке. 
            if (tail.cross === false) ctx.clearRect(tail.x, tail.y, grid, grid);

            // проверяем не съели ли кролика
            // if ((colorTest[0] == 255) || (colorTest[0] == 127)) { // - это была проверка по цвету элемента
            for (let rabbit of rabbmang) {
                if (head.x === rabbit.x && head.y === rabbit.y /* && rabbit.ty == 0*/ ) {
                    // если съели - увеличиваем тело змейки на вес кролика - но на следующем ходе оставим p кусочеков тела змейки нестертыми
                    for (let i = 0; i < rabbit.p; i++) { snake.maxCells++ };
                    let indexRabbit = rabbmang.indexOf(rabbit);
                    let temp = rabbmang.splice(indexRabbit, 1);
                    // выключаем голодание змейки
                    snake.timeHungry = Hungry;
                    snake.dtHungry = deltaHungry;
                    break;
                }
            }

        }

        // обработчик событий - нажатия на кнопки "влево" (37) и вправо (39)
        document.addEventListener('keydown', function (e) {

            if (e.which === 37) {               // при нажатии влево смещаем направление по кругу на следующее
                snake.tekNapr += 1;
                if (snake.tekNapr == 5) {                   // замыкаем круг по следующему направлению
                    snake.tekNapr = 1;
                }
            }

            if (e.which === 39) {               // при нажатии вправо смещаем направление по кругу на предыдующее
                snake.tekNapr -= 1;
                if (snake.tekNapr == 0) {                   // замыкаем круг по предыдующему направлению
                    snake.tekNapr = 4;
                }
            }

        });

        // Начало игры

        // обнуляем игровое поле
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Задаем змейку и выставляем первого кролика в случайном месте игрового поля
        initSnake();
        // Запускаем первый анимационный цикл
        requestAnimationFrame(loop);

    </script>


</body>
</html>