Недавно у нас была задачка про то, как угадать любое число от 1 до 100 за 7 попыток. Кратко логика такая:
- Оппонент загадывает целое число от 1 до 100.
- Мы называем своё целое число в этом диапазоне.
- Оппонент говорит, наше число больше или меньше, чем то, которое он загадал.
- Удивительно то, что за 7 попыток можно гарантированно угадать любое число от 1 до 100.
Секрет решения в том, что с каждой попыткой мы делим диапазон поиска числа вдвое. Например, наша первая догадка будет 50, и после неё мы узнаем, в каком отрезке искомое число: от 1 до 50 или от 51 до 100. Например, оно больше 50. Мы берём диапазон чисел от 51 до 100 и делим его пополам — получается 75. Это следующее предположение. И так мы «складываем вдвое» область поиска, пока не останется одно число. А математические законы таковы, что если 100 поделить на 2 семь раз подряд, получится плюс-минус единица.
Теперь сделаем следующий шаг — автоматизируем эту игру в виде простого веб-приложения. Пусть потеет машина.
Логика угадывателя
Наш угадывательный скрипт будет работать ровно так же, как в исходной игре: на каждом шаге делим диапазон угадывания пополам, округляем до целого и называем это значение. Если больше или меньше — делаем то же самое с новым диапазоном, а если угадали — выводим сообщение.
Сделаем проект в виде HTML-страницы: так нам будет удобнее выводить сообщения, а пользователю — нажимать на кнопки.
Готовим страницу
За основу возьмём страницу со стилями из проекта про калькулятор и выкинем из неё всё лишнее. Заодно сразу наполним её двумя элементами:
- Приветственной надписью, которая скажет, что нужно сделать.
- Кнопкой, по нажатию на которую запустится игровой скрипт.
<!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>
Мы сразу добавили сюда скрипт с пустой функцией, чтобы страница не выдавала ошибку при нажатии. Теперь надо сделать так, чтобы эти два элемента, текст и кнопка, исчезли по нажатию, а вместо них появился интерфейс игры.
Скрываем элементы
Чтобы скрыть то, что сейчас на экране, мы сделаем так:
- В том же проекте с калькулятором возьмём код, который добавляет и убирает классы у разных элементов на странице.
- Добавим класс скрытия текущим элементам.
Первый пункт делается просто — добавим в стили такой блок:
.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");
Обрабатываем нажатия на кнопки
Задача кнопок «Больше» и «Меньше» — сократить пополам диапазон возможных значений. Для этого они делят его пополам и округляют до целого числа. Проблема в том, что иногда компьютер может округлить число обратно до минимальной или максимальной границы и предложить это же число заново.
Чтобы такого не было, добавим в функции простую проверку: если новая граница совпадает с минимальным или максимальным значением диапазона, то принудительно двигаем её на единицу в нужную сторону.
Финальный штрих: добавим в каждую функцию в конце проверку на выигрыш. Так как проверка везде одинаковая, то сделаем её отдельной функцией.
// если загадано число, которое меньше, чем на экране
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;
}
<!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>
Поиграть в угадайку на странице проекта.