Решаем кодом: программа угадает число за 7 попыток

Решаем кодом: программа угадает число за 7 попыток

Попробуйте её победить

Недавно у нас была задачка про то, как угадать любое число от 1 до 100 за 7 попыток. Кратко логика такая:

  1. Оппонент загадывает целое число от 1 до 100.
  2. Мы называем своё целое число в этом диапазоне.
  3. Оппонент говорит, наше число больше или меньше, чем то, которое он загадал.
  4. Удивительно то, что за 7 попыток можно гарантированно угадать любое число от 1 до 100.

Секрет решения в том, что с каждой попыткой мы делим диапазон поиска числа вдвое. Например, наша первая догадка будет 50, и после неё мы узнаем, в каком отрезке искомое число: от 1 до 50 или от 51 до 100. Например, оно больше 50. Мы берём диапазон чисел от 51 до 100 и делим его пополам — получается 75. Это следующее предположение. И так мы «складываем вдвое» область поиска, пока не останется одно число. А математические законы таковы, что если 100 поделить на 2 семь раз подряд, получится плюс-минус единица. 

Теперь сделаем следующий шаг — автоматизируем эту игру в виде простого веб-приложения. Пусть потеет машина. 

Логика угадывателя

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

Сделаем проект в виде HTML-страницы: так нам будет удобнее выводить сообщения, а пользователю — нажимать на кнопки.

Готовим страницу

За основу возьмём страницу со стилями из проекта про калькулятор и выкинем из неё всё лишнее. Заодно сразу наполним её двумя элементами:

  1. Приветственной надписью, которая скажет, что нужно сделать.
  2. Кнопкой, по нажатию на которую запустится игровой скрипт.

<!DOCTYPE html>
<html lang="ru">
<head>
	<meta charset="utf-8">
	<title>Как угадать число от 0 до 100</title>

	<style type="text/css">
		/*задаём общие параметры для всей страницы: шрифт и отступы*/
		body {
		  text-align: center;
		  margin: 10;
		  font-family: Verdana, Arial, sans-serif;
		  font-size: 16px;
		}

		/* внешний вид кнопок */
		button{
		  font-family: Verdana, Arial, sans-serif;
		  font-size: 16px;
		  margin: 10px;
		  padding: 10px;
		}

	</style>

</head>
<body>

	<!-- после нажатия на кнопку это исчезнет -->
	<p id="startText">Загадайте целое число от 0 до 100, и я угадаю его за 7 попыток или меньше!</p>
	<!-- и сама кнопка тоже исчезнет -->
	<button onclick="start()" id="startButton">Начинаем!</button>

	
	<!-- наш скрипт -->
	<script>
		
		// обрабатываем нажатие на стартовую кнопку
		function start() {
			
		}

	</script>

</body>
</html>

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

Скрываем элементы

Чтобы скрыть то, что сейчас на экране, мы сделаем так: 

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

Первый пункт делается просто — добавим в стили такой блок:

.notOnScreen{
display: none;
}

А чтобы скрыть элементы, в стартовой функции напишем две команды:

// скрываем приветственную надпись и кнопку
document.getElementById("startText").classList.add("notOnScreen");
document.getElementById("startButton").classList.add("notOnScreen");

Всё, теперь при нажатии на кнопку у нас снова будет пустая страница.

Добавляем элементы с интерфейсом игры

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

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

Игровой интерфейс будет простой: текст с угадыванием и кнопки. В тексте мы сразу выводим номер попытки и середину нашего диапазона. Если попытки закончились — называем число. Чтобы за всем этим следить, сразу заведём нужные переменные:

// границы, в которых мы угадываем число
var min = 0;
var max = 100;

// середина диапазона
var middle = Math.floor((min + max)/2);

// количество попыток
var stage = 1;

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

<!-- здесь будем задавать свои вопросы -->
<p id="question"></p>
<br>
<!-- а этими кнопками — отвечать на предположения компьютера -->
<button id="small" onclick="smallClick()" class="notOnScreen">Меньше</button>
<button id="bingo" onclick="bingoClick()" class="notOnScreen">Угадал!</button>
<button id="big" onclick="bigClick()" class="notOnScreen">Больше</button>

Теперь, чтобы магия сработала, добавим этот код в функцию start():

// показываем текст с первой попыткой
document.getElementById("question").classList.remove("notOnScreen");
document.getElementById("question").innerHTML = "Попытка " + stage + ": " + middle;

// и три игровые кнопки
document.getElementById("small").classList.remove("notOnScreen");
document.getElementById("bingo").classList.remove("notOnScreen");
document.getElementById("big").classList.remove("notOnScreen");
Решаем кодом: программа угадает число за 7 попыток
Нужное появляется, ненужное исчезает. Красота!

Обрабатываем нажатия на кнопки

Задача кнопок «Больше» и «Меньше» — сократить пополам диапазон возможных значений. Для этого они делят его пополам и округляют до целого числа. Проблема в том, что иногда компьютер может округлить число обратно до минимальной или максимальной границы и предложить это же число заново. 

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

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

// если загадано число, которое меньше, чем на экране
function smallClick() {
	// уменьшаем диапазон
	max = middle;

	// находим новую середину диапазона
	middle = Math.floor((min + max)/2);

	// если при округлении середина совпала с текущей верхней границей — уменьшаем её на единицу
	if (max == middle) {
		middle -= 1;
	}

	// проверяем результат
	checkGame();
}

// если загадано число, которое больше, чем на экране
function bigClick() {
	// уменьшаем диапазон
	min = middle;

	// находим новую середину диапазона
	middle = Math.floor((min + max)/2);

	// если при округлении середина совпала с текущей нижней границей — увеличиваем её на единицу
	if (min == middle) {
		middle += 1;
	}

	// проверяем результат
	checkGame();
}

// если мы угадали
function bingoClick() {
	// уменьшаем диапазон до одного числа
	max = middle;
	min = middle;

	// проверяем результат
	checkGame();
}
Решаем кодом: программа угадает число за 7 попыток
Пока всё идёт как надо

Добавляем проверку на выигрыш

Если наш диапазон сузился до одного числа — значит, мы его нашли и можно выводить сообщение о победе. Также сразу предусмотрим сценарий, когда игрок жульничает и специально нажимает не те кнопки:

// проверяем, угадали или нет
  	function checkGame() {
  		// если в диапазоне осталось только одно число — называем его
		if (min == max){
		 	document.getElementById("question").innerHTML = "Вы загадали число " + min;
		 	// убираем кнопки
			document.getElementById("small").classList.add("notOnScreen");
			document.getElementById("bingo").classList.add("notOnScreen");
			document.getElementById("big").classList.add("notOnScreen");

		 	// игра закончена
		 	return;
		}

		// увеличиваем счётчик попыток
		stage += 1;
		if (stage == 8){
			// выводим сообщение об ошибке
			document.getElementById("question").innerHTML = "Жульничать — нехорошо!";	
		}

		// выводим новое предположение на экран
		document.getElementById("question").innerHTML = "Попытка " + stage + ": " + middle;
    }

<!DOCTYPE html>
<html lang="ru">
<head>
	<meta charset="utf-8">
	<title>Как угадать число от 0 до 100</title>

	<style type="text/css">
		/*задаём общие параметры для всей страницы: шрифт и отступы*/
		body {
		  text-align: center;
		  margin: 10;
		  font-family: Verdana, Arial, sans-serif;
		  font-size: 16px;
		}

		/* внешний вид кнопок */
		button{
		  font-family: Verdana, Arial, sans-serif;
		  font-size: 16px;
		  margin: 10px;
		  padding: 10px;
		}

		.notOnScreen{
			display: none;
		}
	</style>

</head>
<body>

	<!-- после нажатия на кнопку это исчезнет -->
	<p id="startText">Загадайте целое число от 0 до 100 и я угадаю его за 7 попыток или меньше!</p>
	<!-- и сама кнопка тоже исчезнет -->
	<button onclick="start()" id="startButton">Начинаем!</button>

	<!-- здесь будем задавать свои вопросы -->
	<p id="question"></p>
	<br>
	<!-- а этими кнопками — отвечать на предположения компьютера -->
	<button id="small" onclick="smallClick()" class="notOnScreen">Меньше</button>
	<button id="bingo" onclick="bingoClick()" class="notOnScreen">Угадал!</button>
	<button id="big" onclick="bigClick()" class="notOnScreen">Больше</button>


	<!-- наш скрипт -->
	<script>
		// границы, в которых мы угадываем число
		var min = 0;
		var max = 100;

		// середина диапазона
		var middle = Math.floor((min + max)/2);

		// количество попыток
		var stage = 1;

		// обрабатываем нажатие на стартовую кнопку
		function start() {
			// скрываем приветственную надпись и кнопку
			document.getElementById("startText").classList.add("notOnScreen");
			document.getElementById("startButton").classList.add("notOnScreen");

			// показываем текст с первой попыткой
			document.getElementById("question").classList.remove("notOnScreen");
			document.getElementById("question").innerHTML = "Попытка " + stage + ": " + middle;

			// и три игровые кнопки
			document.getElementById("small").classList.remove("notOnScreen");
			document.getElementById("bingo").classList.remove("notOnScreen");
			document.getElementById("big").classList.remove("notOnScreen");
		}

		// если загадано число, которое меньше, чем на экране
		function smallClick() {
			// уменьшаем диапазон
			max = middle;

			// находим новую середину диапазона
			middle = Math.floor((min + max)/2);

			// если при округлении середина совпала с текущей верхней границей — уменьшаем её на единицу
			if (max == middle) {
				middle -= 1;
			}

			// проверяем результат
			checkGame();
		}

		// если загадано число, которое больше, чем на экране
		function bigClick() {
			// уменьшаем диапазон
			min = middle;

			// находим новую середину диапазона
			middle = Math.floor((min + max)/2);

			// если при округлении середина совпала с текущей нижней границей — увеличиваем её на единицу
			if (min == middle) {
				middle += 1;
			}

			// проверяем результат
			checkGame();
		}

		// если мы угадали
		function bingoClick() {
			// уменьшаем диапазон до одного числа
			max = middle;
			min = middle;

			// проверяем результат
			checkGame();
		}
	  	
	  	// проверяем, угадали или нет
	  	function checkGame() {
	  		// если в диапазоне осталось только одно число — называем его
			if (min == max){
			 	document.getElementById("question").innerHTML = "Вы загадали число " + min;
			 	// убираем кнопки
				document.getElementById("small").classList.add("notOnScreen");
				document.getElementById("bingo").classList.add("notOnScreen");
				document.getElementById("big").classList.add("notOnScreen");

			 	// игра закончена
			 	return;
			}

			// увеличиваем счётчик попыток
			stage += 1;
			if (stage == 8){
				// выводим сообщение об ошибке
				document.getElementById("question").innerHTML = "Жульничать — нехорошо!";	
			}

			// выводим новое предположение на экран
			document.getElementById("question").innerHTML = "Попытка " + stage + ": " + middle;
	    }
	  
	</script>

</body>
</html>

Поиграть в угадайку на странице проекта.

Текст:

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

Редактор:

Максим Ильяхов

Художник:

Даня Берковский

Корректор:

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

Вёрстка:

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

Соцсети:

Олег Вешкурцев

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства и компенсация, если вы пойдёте работать в «Яндекс».
Начать карьеру в ИТ
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Еще по теме
Делаем свой таймер на Python

Код — проще, возможностей — больше.

Делаем свой текстовый редактор с автосохранением

Это не так сложно, как звучит.

Делаем собственный таймер для спорта
Делаем собственный таймер для спорта

Без рекламы и встроенных покупок.

Что означает ошибка Exception has occurred: TypeError
Что означает ошибка Exception has occurred: TypeError

Неочевидная ошибка в типах данных Python.

Прокачиваем собственный генератор паролей

Тройная защита для вашей семьи!

Универсальная аналитическая машина

Она может предсказать что угодно, если правильно её настроить.

Как устроен проект «Гадаем на статьях Кода»
Как устроен проект «Гадаем на статьях Кода»

Никакой магии, только JavaScript.

Проект: генератор тупых новогодних поздравлений

Новый год спасён!

Делаем свой таймер на Python

Код — проще, возможностей — больше.

Телеграм-бот для учёбы

Собираем за 10 минут.

Делаем собственный таймер для спорта
Делаем собственный таймер для спорта

Без рекламы и встроенных покупок.

Как работает язык SQL
Как работает язык SQL

Управляем базой данных из командной строки

Генератор лабиринтов
Генератор лабиринтов

Рисуем лабиринты любого размера.

easy