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

Решаем кодом: программа угадает число за 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

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

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

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

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

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

hard
Как быстро добавить логгер в проект на Python
Как быстро добавить логгер в проект на Python

Используем силу встроенных библиотек

easy
Делаем парсер, чтобы массово тянуть с сайтов что угодно
Делаем парсер, чтобы массово тянуть с сайтов что угодно

Собираем текст со всех страниц журнала «Код» простым скриптом

medium
Что означает ошибка UnboundLocalError: local variable referenced before assignment
Что означает ошибка UnboundLocalError: local variable referenced before assignment

Одна из самых частых ошибок у начинающих в Python.

easy
Блокировщик соцсетей, который спасёт вашу продуктивность

И поднимет осознанность.

easy
Анализ текста: последняя часть
Анализ текста: последняя часть

Смотрим на существительные, прилагательные и глаголы у Толстого

easy
Что делать, когда Python сам меняет значения списка
Что делать, когда Python сам меняет значения списка

Детективная история про указатели и память

medium
easy