Рисуем лабиринты любой сложности
hard

Рисуем лабиринты любой сложности

Вообще любой сложности, какой захотите

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

Что делаем

У нас уже есть скрипт, который составляет карту лабиринта. Теперь нам нужно нарисовать её на странице, чтобы можно было распечатать и отдать детям или пройти лабиринт самому.

Для этого мы добавим на страницу новый скрипт, который возьмёт готовую карту лабиринта, обработает её и покажет красивый результат в браузере.

Логика нового скрипта будет такой:

  1. Прописываем все необходимые переменные, которые нам понадобятся.
  2. Нарисуем на холсте рамку по периметру — это будет внешняя граница лабиринта.
  3. По очереди проходим по карте лабиринта и закрашиваем чёрным те ячейки, которые на карте помечены как стены.

Всё это сделаем в новом скрипте drawMaze.js.

Переменные

Чтобы было удобнее управлять всеми параметрами лабиринта из одного места, добавим в этот скрипт переменные, которые отвечают за количество клеток в лабиринте по вертикали и горизонтали. Ещё нам понадобится размер каждой клетки, чтобы знать, какого размера лабиринт у нас будет на экране. Финальный штрих — добавляем внешнюю границу лабиринта и тоже пишем её размер.

Также в этом блоке подключим холст — найдём его по тегу и сразу зададим нужные параметры.

// количество колонок в лабиринте
const columnsSize = 15;
// количество строк в лабиринте
const rowsSize = 15;
// размер клетки в лабиринте
const fieldSize = 7;
 // рамка (внешняя граница лабиринта)
const padding = 10;

// находим холст на странице по имени элемента
const canvas = document.querySelector('canvas');
// создаём переменную, через которую будем работать с холстом
const context = canvas.getContext('2d');
// создаём новую карту лабиринта, которую будем отрисовывать
const map = generateMaze(columnsSize, rowsSize);

Подготавливаем холст

Теперь у нас есть всё, чтобы начать рисовать. Вот что нам нужно сделать на этом этапе:

  1. Посчитать и установить фактические размеры холста в пикселях.
  2. Закрасить весь холст чёрным.
  3. Отступить на расстояние рамки от каждого угла и нарисовать белый прямоугольник внутри. 

У нас получится на холсте чёрная рамка, внутри которой ничего нет. Это нормально — стены мы добавим на следующем шаге.

// рисуем рамку и готовимся к отрисовке лабиринта
function init () {
	// устанавливаем размеры холста
	canvas.width = padding * 2 + columnsSize * fieldSize;
	canvas.height = padding * 2 + rowsSize * fieldSize;

	// цвет заливки
	context.fillStyle = 'black';
	// рисуем прямоугольник на весь холст с координатами левого верхнего и правого нижнего углов
	context.rect(0, 0, canvas.width, canvas.height);
	// закрашиваем его выбранным цветом
	context.fill();

	// делаем белое поле внутри рамки, чтобы потом нарисовать на нём стены
	context.fillStyle = 'white';
	// сообщаем скрипту, что сейчас будем рисовать новую фигуру
	context.beginPath();
	// рисуем прямоугольник, отступив от границ холста на толщину рамки
	context.rect(padding, padding, canvas.width - padding * 2, canvas.height - padding * 2);
	// закрашиваем его белым 
	context.fill();
}

Рисуем стены

Нам осталось нарисовать стены на холсте, постоянно сверяясь с картой лабиринта. Мы будем каждый раз брать новые координаты лабиринта, смотреть, что там лежит, и, если там стена, — рисуем на этом месте чёрную клетку.

Получать значения ячейки из переменной с лабиринтом мы будем тем же способом, что и в прошлый раз: напишем отдельную функцию. Её задачей будет проверить координаты на правильность: если мы запросим значение ячейки с неверными координатами, то у нас программа не остановится, а просто проигнорирует этот запрос.

// получаем значение ячейки из лабиринта
function getField (x, y) {
	// если хотя бы одна из координат не находится в границах карты
	if (x < 0 || x >= columnsSize || y < 0 || y >= rowsSize) {
		// выходим из функции и говорим, что такой ячейки нет
		return null;
	}
	// если дошли досюда, значит, координата верная, и мы возвращаем её значение из карты лабиринта
	return map[y][x];
}

Теперь рисуем стены так: по очереди переберём все координаты карты с лабиринтом и те клетки, которые помечены как стена, на холсте нарисуем чёрными. 

// отрисовываем карту
function drawMap () {
	// обрабатываем по очереди все ячейки в каждом столбце и строке
	for (let x = 0; x < columnsSize; x++) {
		for (let y = 0; y < rowsSize; y++) {
			// если на карте лабиринта эта ячейка помечена как стена
			if (getField(x, y) === '▉') {
				// берём чёрный цвет
				context.fillStyle = 'black';
				// начинаем рисовать новую фигуру
				context.beginPath();
				// делаем прямоугольник на месте этой ячейки
				context.rect(padding + x * fieldSize, padding + y * fieldSize, fieldSize, fieldSize);
				// закрашиваем его чёрным
				context.fill();
			}
		}
	}
}

Запускаем скрипт

Для запуска нам нужно добавить вызов функций init() и drawMap() в самый конец файла:

// рисуем рамку и готовимся к отрисовке лабиринта
init();
// рисуем лабиринт
drawMap();

Чтобы скрипт сработал, добавим его в наш исходный HTML-файл:

<!DOCTYPE html>
<html lang="ru">
<head>
	<meta charset="UTF-8">
	<title>Лабиринт</title>
</head>
<body>
	<!-- подготавливаем пустой холст, чтобы работать с ним из скрипта -->
	<canvas></canvas>
	<!-- скрипт, который создаёт лабиринт -->
	<script src="generateMaze.js">	</script>
	<!-- этот скрипт отвечает за отрисовку лабиринта -->
	<script src="drawMaze.js"></script>
</body>
</html>
Рисуем лабиринты любой сложности
Лабиринт размером 51 на 51 клетку.

Посмотреть лабиринт на странице проекта.

// количество колонок в лабиринте
const columnsSize = 51;
// количество строк в лабиринте
const rowsSize = 51;
// размер клетки в лабиринте
const fieldSize = 7;
 // рамка (внешняя граница лабиринта)
const padding = 10;

// находим холст на странице по имени элемента
const canvas = document.querySelector('canvas');
// создаём переменную, через которую будем работать с холстом
const context = canvas.getContext('2d');
// создаём новую карту лабиринта, которую будем отрисовывать
const map = generateMaze(columnsSize, rowsSize);


// рисуем рамку и готовимся к отрисовке лабиринта
function init () {
	// устанавливаем размеры холста
	canvas.width = padding * 2 + columnsSize * fieldSize;
	canvas.height = padding * 2 + rowsSize * fieldSize;

	// цвет заливки
	context.fillStyle = 'black';
	// рисуем прямоугольник на весь холст с координатами левого верхнего и правого нижнего углов
	context.rect(0, 0, canvas.width, canvas.height);
	// закрашиваем его выбранным цветом
	context.fill();

	// делаем белое поле внутри рамки, чтобы потом нарисовать на нём стены
	context.fillStyle = 'white';
	// сообщаем скрипту, что сейчас будем рисовать новую фигуру
	context.beginPath();
	// рисуем прямоугольник, отступив от границ холста на толщину рамки
	context.rect(padding, padding, canvas.width - padding * 2, canvas.height - padding * 2);
	// закрашиваем его белым 
	context.fill();
}


// получаем значение ячейки из лабиринта
function getField (x, y) {
	// если хотя бы одна из координат не находится в границах карты
	if (x < 0 || x >= columnsSize || y < 0 || y >= rowsSize) {
		// выходим из функции и говорим, что такой ячейки нет
		return null;
	}
	// если дошли досюда, значит, координата верная, и мы возвращаем её значение из карты лабиринта
	return map[y][x];
}

// отрисовываем карту
function drawMap () {
	// обрабатываем по очереди все ячейки в каждом столбце и строке
	for (let x = 0; x < columnsSize; x++) {
		for (let y = 0; y < rowsSize; y++) {
			// если на карте лабиринта эта ячейка помечена как стена
			if (getField(x, y) === '▉') {
				// берём чёрный цвет
				context.fillStyle = 'black';
				// начинаем рисовать новую фигуру
				context.beginPath();
				// делаем прямоугольник на месте этой ячейки
				context.rect(padding + x * fieldSize, padding + y * fieldSize, fieldSize, fieldSize);
				// закрашиваем его чёрным
				context.fill();
			}
		}
	}
}

// рисуем рамку и готовимся к отрисовке лабиринта
init();
// рисуем лабиринт
drawMap();

Что дальше

У нашего лабиринта есть две маленькие проблемы:

  1. Если число клеток по горизонтали и вертикали больше 150, страница грузится очень долго. Всё потому, что у нас только один трактор, который чистит лабиринт. 
  2. Непонятно, откуда начинать и куда идти, чтобы пройти лабиринт.

Исправим эти недочёты в следующей версии. Потом будет ещё одно продолжение этого же проекта.

Код:

Сергей Григорович

Текст:

Михаил Полянин

Редактор:

Михаил Полянин

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Юлия Зубарева

Научитесь программировать на JavaScript
На новом курсе «Практикума» о фронтенде вас обучат самым востребованным технологиям: JS и TypeScript, Flexbox и Grid, React, Git, Bash и др. Это то, что нужно работодателям сегодня. Старт — бесплатно.
Начать бесплатно
Научитесь программировать на JavaScript Научитесь программировать на JavaScript Научитесь программировать на JavaScript Научитесь программировать на JavaScript
Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
hard