Lines 9753: День Сурка

                                                                                            Если долго играть в кубики,
                                                                                            то они превращаются в шарики.

Глядя на название игры Lines 9753, можно подумать, что этот рассказ про 9753-ий клон известной игры Color Lines, в которой надо составлять по 5 шариков в ряд одного цвета. Да, так и есть, клон. Ну что ещё может быть нового? Я поделюсь своими идеями и некоторыми тонкостями реализации на HTML+CSS+JavaScript. Расскажу что значат цифры 9753. Предложу способ найти другую комбинацию цифр и посмотреть, как изменится игра. Станет она интереснее или скучнее — решать вам. Ещё не догадались, что значит 9753? Тогда, прошу под кат.

Многообразие вариантов игры

Никогда не задумывались, откуда в оригинальной игре взялась размерность поля 9×9? Удивительно, насколько хорошо подобраны не только размерность игры, но и другие параметры, так что играть интересно: количество цветов — 7, количество шариков в ряд — 5 и количество появляющихся шаров за ход — 3. Эти 4 параметра поставлены в название игры — Lines 9753. Именно эта четвёрка по-большому счёту определяет сложность игры, которая скорее всего, подходит не всем.

Возможно, кому-то придётся по душе другой набор. Например:

  • 3333 — легкотня типа пасьянса, можно не бояться зайти в тупик и постараться схлопывать как можно больше шариков за раз (5 ещё легко, 6 уже сложнее, 7 и 8 практически нереально), подойдёт в случае, когда совсем не хочется думать;
  • 5643 — на доске 5×5 клетки не такие мелкие как 9×9, поэтому удобно играть на телефоне, однако сложность, кажется чуть выше, чем в 9753;
  • 5543 — несколько более лёгкий вариант чем 5643, потому что количество различных цветов меньше, а значит больше вероятность составить ряд шаров одного цвета;
  • 4443 — по сложности сравнима с 5643, но шарики ещё крупнее — всего 4 шара по короткой стороне экрана.

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

Ход назад

Разрешается делать ходы назад. Давным-давно, когда деревья были высокими, а я был ещё студентом довелось, мне поиграть в Color Lines. Игра очень понравилась. Один раз, проиграв в неё полдня и, набрав несколько тысяч очков, я зашел в тупик. Пришло осознание того, что это была случайность, что шары выпадали удачно, и что в ближайшее время так далеко я пройти не смогу. Чтобы побить свой же рекорд, мне надо будет убить раз в десять больше времени, и то если повезёт. С тех пор я долго не играл в эту игру. Ощущение вселенской несправедливости засело глубоко в душе. И сидело оно там до тех пор, пока не выкристаллизовался коварный план мести — сделать такую же игру, но с возможностью отматывать ходы назад хоть до самого начала. Именно эта опция стала для меня главной мотивирующей причиной написать игру по-своему.

Фиксированная случайность

Некоторые игроки говорили, что иногда машина специально ставит шары в неудобные места, чтобы усложнить прохождение. Особенно, когда остаётся совсем мало свободных клеток, невозможно дождаться нужного шара в нужном месте. Другими, словами, случайность в игре совсем не случайна. С новой возможностью отматывать ходы назад, было бы не логично предлагать каждый раз новое место для новых шаров. Так можно было бы ходить вперёд и назад до тех пор, пока не появится нужный шар в нужном месте. Поэтому в игре все ходы машины детерминированы и зависят случайным образом от номера хода и расположения шаров на поле. Мало того, все играют одну и туже игру. То есть, игра начинается с одной и той же расстановки (первый ход машины), и далее ходы машины зависят от того, как сходит игрок: если игроки ходят одинаково, то и на поле у них будет одинаковая расстановка шаров. Это позволяет объективно и независимо от случайности сравнивать разные решения. Некоторые любят меряться кто дальше, кто быстрее,… Решения в виде последовательности ходов игрока отправляются на сервер. Ходы машины вычисляются всё по тому же случайному алгоритму. Обмануть сервер не получится, вся последовательность проверяется на корректность, и только потом результат заносится в таблицу. Все рекорды сохраняются вместе с последовательностью ходов игрока. Потом проанализируем и опубликуем лучшие решения тех кто наберет 9999 очков. Нужно будет придумать, по какому критерию выбирать лучших.

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

Подсказка

Показываются позиции и цвета следующих шариков. Из-за того, что игру можно перематывать вперёд и назад, нет смысла скрывать следующие ходы. Можно было бы показывать на несколько ходов вперёд, но простого интерфейса в голову не пришло (нужно учитывать, что ходы машины меняются, если клетки оказываются занятыми в результате действий игрока).

Подсчёт очков

Подсчёт очков сделан по классической формуле N * (1 + N — DIMENSION), где N — количество шаров в линии, DIMENSION — размерность игрового поля. То есть 5, 6, 7, 8 собранных вместе шаров дают соответственно 5, 12, 21 и 32 очка. Для варианта игры 3333 при схлопывании 3, 4, 5 и 6 шаров получается 3, 8, 15, 24.

Графика

Графика только векторная — не люблю артефакты масштабирования, особенно нечёткие границы. Основные анимированные графические элементы: винтажный магнитофонный счётчик и прыгающие шарики с бликами и градиентным освещением. Они выполнены с использованием CSS и приправлены кусочками JavaScript. Изначально была сделана тёмная цветовая схема. Её можно попробовать, включив в настройках. Однако всё выглядит мрачновато. Светлая мне больше нравится.

Трёхмерный шарик

Как ни парадоксально звучит, но из 3-х вложенных друг в друга прямоугольников получается трёхмерный шарик:

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

С контейнером всё просто. Его CSS атрибуты position, left и top задают координаты шарика на поле, а font-size задаёт размер, относительно которого масштабируются два других квадрата. Круглый квадрат, это, как подавляющее большинство кругляшков, на наших web страницах, просто квадрат с максимально закруглёнными углами. Когда на него нажимаем мышкой, его плющит, сжимая сверху и расширяя бока. А если нажатие оказывается быстрым как мышиный клик, то шарик аж подпрыгивать начинает.

@keyframes flat {
            0%   {height: 1.0em; width: 1.0em; top: 0; left: 0em;}
            100% {height: 0.9em; width: 1.05em; top: 0.1em; left: -0.025em;}
        }

@keyframes jumping {
            0%   {height: 0.9em;   width: 1.025em; top: 0.1em;   left: -0.0125em;}
            10%  {height: 1.02em;  width: 1.0em;   top: -0.02em; left: 0em;}
            40%  {height: 1.008em; width: 1.0em;   top: -0.18em; left: 0em;}
            50%  {height: 1.01em;  width: 1.0em;   top: -0.2em;  left: 0em;}
            60%  {height: 1.0em;   width: 1.0em;   top: -0.18em; left: 0em;}
            70%  {height: 1.0em;   width: 1.0em;   top: -0.1em;  left: 0em;}
            90%  {height: 1.0em;   width: 1.0em;   top: 0em;     left: 0em;}
            100% {height: 0.9em;   width: 1.025em; top: 0.1em;   left: -0.0125em;}
        }

Максимальное количество цветов 15.

Не все цвета имеют достаточно плавные градиенты даже на хорошем мониторе, поэтому я подбирал цвета на глаз. Чем больше цветов, тем сложнее отличать шарики по цвету на поле. Кажется, 15 цветов достаточно.

Счётчик

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

Звук

Звуки (взрыв и прыжки) синтетические. Они генерируются по незамысловатым математическим формулам. Это создаёт некоторый привкус 80-х, когда большинство звуков в играх генерировалось с использованием одноголосного частотного генератора. К плюсам можно отнести компактность программы: простенький генератор таких звуковых эффектов, написанный на JavaScript, оказывается значительно меньше по размеру, чем mp3 файл. Для каждого шарика выбираем свою пару нот lowFreq и highFreq (см. wikipedia) и заполняем аудиобуфер примерно вот так:

    var soundUpBuffer = audioCtx.createBuffer(1, frameCount, sampleRate);
    for (var i = 0; i < frameCount; i++) {
      var x = i / frameCount; // from 0.0 to 1.0
      var time = duration * x;
      var volume = Math.sin(x * Math.PI);
      var freq = lowFreq + Math.sqrt(x) * (highFreq - lowFreq);
      buffer[i] = volume * Math.sin(2 * Math.PI * freq * time);
    }

Здесь частота изменяется от lowFreq до highFreq по кривой x½, а громкость sin(π x).
У взрыва громкость затухает по формуле 1/(1+10*x+1000*x*x), а частота выбирается случайно с первой по пятую октаву.

Адаптивный размер диалогового окна

Реализован алгоритм подбора размера диалогового окна, так чтобы оно занимало как можно больше пространства и не выходило за границы видимой области. Проблема заключается в том, что на разных устройствах размер диалогов разный — иногда получается слишком мелко, а иногда не входит в экран. Я сделал размер диалогов зависимым от размера шрифта (CSS единицы em). Далее, перебираю размер шрифта, чтобы диалог занимал не более 95% любой из размерностей экрана. Так не пришлось городить кейсы для адаптивного CSS.

Небольшой размер программы

Вся игра умещается в одном html файле около 150Кб. Около 3000 строк исходного кода (конечно без серверной части). Никакие JavaScript библиотеки и фреймворки не использовались — всё натурально, как в каменном веке. Иногда просто не хочется разбираться со сторонним API, а иногда полезно понять, насколько сильно фреймворки упрощают жизнь изобретателя велосипедов и испытателя новых граблей. Полезно бывает сначала придумать самому как можно реализовать ту или иную идею, а уж потом сравнить с тем как это сделали другие. Например, версия под Андроид занимает всего 250Кб. Она состоит из тонкой WebView обёртки и пачки png-шек под разные разрешения телефонов. Кстати, png-шки можно было бы заменить одной векторной xml-кой. Получилось бы точно в духе минимализма.

Форум

Форум работает на phpBB. Эккаунт позволяет:

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

i18n и l10n

Интернационализация и локализация. Игра переведена на русский и английский. Всего 35 коротких фраз. Я бы не отказался от бесплатной помощи по переводу ещё на несколько популярных языков (Китайский, Немецкий, …).

Никакой рекламы

Часто можно услышать мнение, что если программа бесплатная и без рекламы, то значит троян, бэкдор, вирус, … Другими словами, бесплатный сыр только в мышеловке. Иначе зачем? Почему кто-то готов делать что-то для других безвозмездно? Я общего ответа не знаю. Мало того, не знаю точно, почему сам это делаю. Мне просто интересно. Если что-то нравится, то я часто задумываюсь, как автору это удалось. Хочется попробовать сделать так же или даже лучше. Иногда всё нравится, за исключением каких-то деталей, и возникает желание сделать подобное, но со своим блэк джеком и финтифлюшками.

Заключение

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

Ссылки

Специально для сайта ITWORLD.UZ. Новость взята с сайта Хабр