Хабрахабр

Разбор игры от Одноклассников на Joker 2018

Всем привет! Несколько дней назад мы выкладывали пост про задачки, которые давали на конференции Joker 2018. Но это еще не всё! В этом году специально для Joker мы сделали целую игру с не менее интересными задачками по Java (и не только), про которую и расскажем сегодня.
Подобные игры на конференциях мы делали и раньше, например, на прошлом JPoint этой весной. Чтобы сделать игру, нам надо было: 1) придумать игровую механику, 2) придумать вопросы, 3) всё это реализовать.

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

Надо, отвечая на вопросы, постараться провести Дюка (в левом верхнем углу) в одну из дверей в других углах. На один сеанс игры отводится 3 минуты. Чтобы открыть клетку, нужно правильно ответить на вопрос, который выбирается каждый раз случайно в соответствии с категорией. За правильные ответы начисляются очки, за неправильные — штраф, клетка с вопросом блокируется и её придется обходить. В результате путь может получиться достаточно длинным или вовсе заблокироваться. Игроки могут выбрать разные стратегии: как можно быстрее дойти до выхода и получить дополнительный бонус; ответить на как можно больше вопросов за отведенное время; пройти более длинный путь по простым вопросам, либо напрямую по более сложным и получить больше очков.

С механикой разобрались, теперь надо придумать вопросы. Они должны быть трёх категорий сложности, от самых простых до хардкора. Формулировки вопросов должны быть предельно короткими, читать простыни текста не будет времени. Варианты ответов должны быть такими, чтобы не давать слишком много подсказок. Ну и сами вопросы должны быть интересными и практическими. При этом их должно было быть достаточно много, чтобы они не слишком быстро повторялись. В результате совместных усилий удалось придумать 130 вопросов про структуры данных, алгоритмы, «java-пазлеры», хардкорные вопросы по внутренностям JVM, и даже несколько вопросов про Docker.

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

Разумеется, всё на Java. С возникновения идеи до придумывания вопросов и реализации прошло две недели. WEB-интерфейс сделан на Angular. Использовался Spring boot и Gradle. Конфигурация стенда — два MacBook, картинка с которых дублируется на двух телевизорах. В качестве хранилища использована встроенная база данных H2, которая «из коробки» поставляется с web-интерфейсом, что очень удобно. Для удобства настройки приложение удаленно развернули в нашем облаке (https://habr.com/company/odnoklassniki/blog/346868/).

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

Статистика ответов в зависимости от сложности вопроса: Всего в игру за два дня сыграли 811 раз.

DIFFICULTY

COUNT

CORRECT_PERCENT

1

3552

61

2

2031

49

3

912

46

До каких клеток поля и как часто доходили игроки:

Процент правильных ответов на каждой клетке поля:

Но самое интересное — это, конечно, статистика вопросов. Оценить их сложность с учётом распределения по категориям оказалось не так просто, оценка всегда субъективна, и простой вопрос для одного пользователя оказывается сложным для другого. Олег предлагал выкинуть один из вопросов со словами «это даже уборщицы знают», но оказалось, что на многие «простые» вопросы правильные ответы знают далеко не все уборщицы, да и программисты тоже. Предлагаем вам некоторые вопросы из нашей игры — лидеры по неверным ответам, попробуйте оценить свои силы!

  1. Результат вызова этого кода?

    System.out.println(1/0d)

    • Выбросит ArithmeticException
    • Напечатает «Infinity»
    • Напечатает «NaN»
    • Напечатает 0

    Ответ

    Тут простая арифметика, какой может быть подвох, почему же правильный ответ дали только 28 % игроков? Это кажется очень простым вопросом. Но целые ли тут числа? Деление целого числа на 0 приводит в Java к ArithmeticException. Что там за «d» после 0? Посмотрим внимательно. И получается, что выражение идентично 1. Эта буква означает, что перед нами не целочисленная константа 0, а значение типа double. 0. 0/0. POSITIVE_INFINITY. А это уже деление с плавающей точкой на ноль, результат которого равен Double. Значит, правильный ответ — «b».

  2. Результат вызова этого кода?

    System.out.println( Long.MAX_VALUE==(long)Float.MAX_VALUE
    );

    • напечатает true
    • напечатает false
    • выбросит ArithmeticException

    Ответ

    MAX_VALUE или Long. Для начала, нужно понять, что больше: Float. Хотя float имеет меньший диапазон значений, чем double, всё равно его максимальное значение примерно на 20 порядков выходит за диапазон возможных значений long. MAX_VALUE? Можно гадать, но лучше запустить код. Но как в данном случае сработает приведение типов? Т.е. А еще лучше — открыть Java Language Specification, раздел про Narrowing Primitive Conversion, и прочитать, что если значение числа с плавающей точкой слишком велико и выходит за диапазон доступных значений целочисленного типа, то результат конверсии равен максимальному значению, которое можно представить с помощью целочисленного типа. MAX_VALUE. результат конверсии равен Long. Правильный ответ дали 27 % отвечавших.

  3. Какой класс не Comparable?
    • java.lang.String
    • java.util.TreeSet
    • java.io.File
    • java.lang.Enum

    Ответ

    Догадайтесь сами, какой из ответов тут правильный и какой ответ был самым популярным — его выбрали 61 % игроков.
    Этот, казалось бы, простой вопрос поставил в тупик многих, точнее — 76 % отвечавших.

  4. Чему идентичен код?

    Object o = Math.min(-1, Double.MIN_VALUE)

    • Object o = -1
    • Object o = Double.MIN_VALUE
    • Object o = -1.0

    Ответ

    Разумеется, всё не так просто, иначе мы не спрашивали бы. Минимальное значение double уж точно меньше -1, что тут опять может быть не так? MIN_VALUE содержит не совсем то, что ожидается, а именно «constant holding the smallest positive nonzero value», согласно документации. Оказывается, Double. MIN_POSITIVE_VALUE. Правильнее его было бы назвать Double. Правильный ответ: Object o = -1. Double опять обвел вокруг пальца! 0, и так ответили всего 22 % игроков.

  5. Какая строка получится в результате вызова этого кода?

    Long.toHexString(0x1_0000_0000L + 0xcafe_babe)

    • 1cafebabe
    • cafebabe
    • ffffffffcafebabe

    Ответ

    Этот вопрос взят из книги «Java Puzzlers: Traps, Pitfalls, and Corner Cases» за авторством Joshua Bloch и Neal Gafter. Если вы выбрали второй ответ, то вы среди 22 % ответивших правильно. Если ответили неправильно, не расстраивайтесь, и бегом читать эту книгу!

  6. В JDK 8 появилась поддержка аннотаций у параметров методов. Возможно ли добавить аннотацию к параметру метода this?
    • Нельзя
    • Возможно, но только в байткоде
    • Возможно, определив this явно первым параметром метода

    Ответ

    Когда в JDK 8 добавляли возможность ставить аннотации на параметры методов, параметр this не стали обделять. Именно для этой цели this теперь можно явно указывать в сигнатурах методов:

    class Foo
    }

    Хотя можно спорить о ее практической пользе, теперь это фича языка. До правильного ответа догадалось 32 % игроков.

  7. В JDK 8 параметр concurrencyLevel в конструкторе ConcurrentHashMap влияет на:
    • Доступный параллелизм при чтении/записи
    • Начальный размер таблицы
    • На оба параметра

    Ответ

    Всё дело в том, что в JDK 8 отказались от сегментов в ConcurrentHashMap, поэтому concurrencyLevel потерял свой прежний смысл. Если вы выбрали вариант 2, то вы среди 15 %, кто дал правильный ответ на этот самый сложный вопрос игры. Он влияет только на начальный размер таблицы, да и то лишь ограничивает снизу значение initialCapacity.

По многочисленным просьбам мы выложили игру на наш сайт, где вы можете сыграть прямо сейчас!

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»