Hola a todos! Hace unos días,
publicamos una publicación sobre los rompecabezas que se dieron en la conferencia Joker 2018. ¡Pero eso no es todo! Este año, especialmente para Joker, creamos un juego completo con tareas Java no menos interesantes (y no solo), de las que hablaremos hoy.
Hicimos juegos similares en conferencias antes, por ejemplo, en el último JPoint de esta primavera. Para hacer un juego, teníamos que: 1) pensar en la mecánica del juego, 2) hacer preguntas, 3) darse cuenta de todo esto.
La mecánica del juego debe ser muy simple, intuitiva, pero al mismo tiempo no demasiado banal, para no privar a la emoción. Como resultado de largas discusiones, se nos ocurrió el siguiente juego:

Al responder preguntas, debemos tratar de llevar a Duke (en la esquina superior izquierda) a una de las puertas en otras esquinas. Para una sesión del juego se asignan 3 minutos. Para abrir la celda, debe responder correctamente la pregunta, que se selecciona cada vez al azar de acuerdo con la categoría. Se otorgan puntos por las respuestas correctas, una penalización por las respuestas incorrectas, la celda con la pregunta está bloqueada y deberá omitirse. Como resultado, el camino puede llegar a ser bastante largo o incluso bloquearse. Los jugadores pueden elegir diferentes estrategias: llegar a la salida lo antes posible y obtener un bono adicional; responda tantas preguntas como sea posible en el tiempo asignado; recorrer un largo camino en preguntas simples, o directamente en preguntas más complejas y obtener más puntos.

Nos ocupamos de la mecánica, ahora tenemos que hacer preguntas. Deben ser de tres categorías de complejidad, desde la más simple hasta la más hardcore. La redacción de las preguntas debe ser extremadamente corta, no habrá tiempo para leer las hojas del texto. Las opciones de respuesta deben ser tales como no dar demasiadas pistas. Bueno, las preguntas mismas deberían ser interesantes y prácticas. Además, debería haber habido muchos de ellos para que no se repitan demasiado rápido. Como resultado de los esfuerzos conjuntos, logramos formular 130 preguntas sobre estructuras de datos, algoritmos, "java puzzle", preguntas difíciles sobre elementos internos de JVM e incluso algunas preguntas sobre Docker.

Hay un problema: el juego se muestra en la pantalla grande, y aquellos que estén cerca recordarán la mayoría de las respuestas para varios juegos. Al principio, parecía que necesitaríamos cientos de preguntas para minimizar la posibilidad de memorización. Pero, después de reflexionar, se dieron cuenta de que hay opciones más simples. Para comenzar, eliminó el control del mouse, dejando solo el teclado. Ahora los que están cerca ven la pregunta, pero no ven qué respuesta eligió el jugador. Mezcla agregada de opciones de respuesta, lo que complica la memorización. Para cada pregunta, se agregaron varias palabras similares pero ligeramente diferentes. Por supuesto, aún puede recordar las respuestas, y esto fue evidente por los resultados notablemente mejorados en el segundo día de la conferencia. Muchos usuarios pasaron decenas de minutos jugando, intentando romper el récord de otros. Pero aquí todo es como en la vida: el celo es recompensado.
Pasaron dos semanas desde la idea hasta la realización de las preguntas y su implementación. Por supuesto, todo está en Java. Bota Spring usada y Gradle. La interfaz WEB está hecha en Angular. Como almacenamiento, se utiliza la base de datos H2 incorporada, que viene de fábrica con una interfaz web, lo cual es muy conveniente. La configuración del soporte es dos MacBooks, cuya imagen se duplica en dos televisores. Para facilitar la configuración, la aplicación se ha implementado de forma remota en nuestra nube (
https://habr.com/company/odnoklassniki/blog/346868/ ).
Aprendimos hace mucho tiempo: no se debe desarrollar ninguna característica sin recopilar estadísticas. Por supuesto, también adjuntamos estadísticas detalladas al juego, que podemos compartir.
En total, el juego se jugó 811 veces en dos días. Estadísticas de respuestas según la complejidad de la pregunta:
DIFICULTAD
| COUNT
| CORRECTO_PERCENT
|
1
| 3552
| 61
|
2
| 2031
| 49
|
3
| 912
| 46
|
A qué celdas del campo y con qué frecuencia llegaron los jugadores:
El porcentaje de respuestas correctas en cada celda del campo:
Pero lo más interesante es, por supuesto, las estadísticas de las preguntas. No fue tan fácil evaluar su complejidad teniendo en cuenta la distribución por categoría, la evaluación siempre es subjetiva y una pregunta simple para un usuario es difícil para otro.
Oleg sugirió lanzar una de las preguntas con las palabras "incluso los limpiadores lo saben", pero resultó que no muchos limpiadores conocen las respuestas correctas a muchas preguntas "simples", y también los programadores. Le ofrecemos algunas preguntas de nuestro juego: líderes en respuestas incorrectas, ¡trate de evaluar su fortaleza!
- ¿El resultado de llamar a este código?
System.out.println(1/0d)
- Lanza una excepción aritmética
- Imprime Infinito
- Imprime "NaN"
- Imprimirá 0
La respuestaEsto parece una pregunta muy simple. Aquí hay una simple aritmética, ¿cuál podría ser el truco? ¿Por qué solo el 28% de los jugadores dieron la respuesta correcta? Dividir un entero por 0 en Java da como resultado una ArithmeticException
. ¿Pero hay enteros aquí? Lo veremos con cuidado. ¿Qué es esa "d" después de 0? Esta letra significa que esta no es una constante entera 0, sino un valor de tipo double
. Y resulta que la expresión es idéntica a 1.0 / 0.0. Y esta es una división por un punto flotante por cero, cuyo resultado es Double.POSITIVE_INFINITY
. Entonces la respuesta correcta es "b".
- ¿El resultado de llamar a este código?
System.out.println( Long.MAX_VALUE==(long)Float.MAX_VALUE );
- imprimir verdadero
- imprimirá falso
- lanza una ArithmeticException
La respuestaPrimero, debe comprender qué es más: Float.MAX_VALUE
o Long.MAX_VALUE
? Aunque float
tiene un rango de valores menor que el double
, su valor máximo es de aproximadamente 20 órdenes de magnitud más allá del rango de valores long
posibles. Pero, ¿cómo funciona la conversión de tipos en este caso? Uno puede adivinar, pero es mejor ejecutar el código. Mejor aún, abra la Especificación del lenguaje Java, una sección sobre Reducción de la conversión primitiva, y lea que si el valor de un número de punto flotante es demasiado grande y cae fuera del rango de valores disponibles de un tipo entero, entonces el resultado de la conversión es igual al valor máximo que se puede representar usando un tipo entero . Es decir El resultado de la conversión es Long.MAX_VALUE
. La respuesta correcta fue dada por el 27% de los encuestados.
- ¿Qué clase no es
Comparable
?
- java.lang.String
- java.util.TreeSet
- java.io.File
- java.lang.Enum
La respuestaEsta pregunta aparentemente simple desconcertó a muchos, o más bien, al 76% de los que respondieron. Adivina cuál de las respuestas aquí es correcta y cuál fue la más popular: fue elegida por el 61% de los jugadores.
- ¿Cuál es el código idéntico a?
Object o = Math.min(-1, Double.MIN_VALUE)
- Objeto o = -1
- Objeto o = Double.MIN_VALUE
- Objeto o = -1.0
La respuestaEl valor double
mínimo es ciertamente menor que -1, ¿qué otra vez podría estar mal? Por supuesto, no todo es tan simple, de lo contrario no lo pediríamos. Resulta que Double.MIN_VALUE
no contiene exactamente lo que se espera, es decir, "constante manteniendo el menor valor positivo distinto de cero", según la documentación. Sería Double.MIN_POSITIVE_VALUE
correcto llamarlo Double.MIN_POSITIVE_VALUE
. Double
nuevamente rodeó su dedo! La respuesta correcta es: Object o = -1.0
, por lo que solo el 22% de los jugadores respondieron.
- ¿Qué línea resultará de llamar a este código?
Long.toHexString(0x1_0000_0000L + 0xcafe_babe)
- 1cafebabe
- cafebabe
- ffffffffcafebabe
La respuestaSi elige la segunda respuesta, se encuentra entre el 22% de los que respondieron correctamente. Esta pregunta está tomada del libro Java Puzzlers: Traps, Pitfalls, and Corner Cases, escrito por Joshua Bloch y Neal Gafter. Si respondió incorrectamente, ¡no se desanime y corra a leer este libro!
- JDK 8 introduce soporte para anotaciones en los parámetros del método. ¿Es posible agregar anotaciones al parámetro de
this
método?
- Es imposible
- Quizás, pero solo en bytecode
- Quizás definiendo
this
explícitamente como el primer parámetro del método
La respuestaCuando se agregó la capacidad de poner anotaciones en los parámetros del método en JDK 8,
this
parámetro no se vio privado. Para este propósito,
this
ahora se puede especificar explícitamente en las firmas de métodos:
class Foo { public void test(@Annotated Foo this) {} }
Aunque puede discutir sus beneficios prácticos, ahora es una característica del lenguaje. El 32% de los jugadores adivinó la respuesta correcta.
- En JDK 8, el parámetro
concurrencyLevel
en el constructor ConcurrentHashMap
afecta:
- Concurrencia de lectura / escritura disponible
- Tamaño de tabla inicial
- En ambos parámetros
La respuestaSi eliges la opción 2, entonces estás entre el 15% que dio la respuesta correcta a esta pregunta más difícil del juego. Lo que pasa es que en JDK 8 abandonaron segmentos en ConcurrentHashMap
, por lo que concurrencyLevel
perdió su significado anterior. Afecta solo el tamaño inicial de la tabla, e incluso eso solo limita el valor initialCapacity
la initialCapacity
desde abajo.