Olá pessoal! Alguns dias atrás,
publicamos um post sobre os quebra-cabeças que foram apresentados na conferência Joker 2018. Mas isso não é tudo! Este ano, especialmente para o Joker, fizemos um jogo inteiro com tarefas Java não menos interessantes (e não apenas), sobre as quais falaremos hoje.
Fizemos jogos semelhantes em conferências antes, por exemplo, no último JPoint nesta primavera. Para fazer um jogo, tivemos que: 1) criar mecânica de jogo, 2) criar perguntas, 3) perceber tudo isso.
A mecânica do jogo deve ser muito simples, intuitiva, mas ao mesmo tempo não muito banal, para não privar a emoção. Como resultado de longas discussões, criamos o seguinte jogo:

Ao responder perguntas, devemos tentar levar o Duke (no canto superior esquerdo) a uma das portas dos outros cantos. Para uma sessão do jogo são alocados 3 minutos. Para abrir a célula, você precisa responder corretamente à pergunta, que é selecionada a cada vez aleatoriamente, de acordo com a categoria. Os pontos são concedidos por respostas corretas, uma penalidade por respostas incorretas, a célula com a pergunta é bloqueada e terá que ser contornada. Como resultado, o caminho pode acabar sendo bastante longo ou mesmo bloqueado. Os jogadores podem escolher estratégias diferentes: chegar à saída o mais rápido possível e obter um bônus adicional; responda o maior número possível de perguntas no tempo previsto; percorra um longo caminho em perguntas simples ou diretamente em questões mais complexas e obtenha mais pontos.

Lidamos com a mecânica, agora precisamos fazer perguntas. Eles devem ter três categorias de complexidade, do mais simples ao mais grave. A redação das perguntas deve ser extremamente curta, não haverá tempo para ler as folhas do texto. As opções de resposta devem ser tais que não dêem muitas pistas. Bem, as perguntas em si devem ser interessantes e práticas. Além disso, deveria ter havido muitos deles para que não se repetissem muito rapidamente. Como resultado de esforços conjuntos, conseguimos elaborar 130 perguntas sobre estruturas de dados, algoritmos, "quebra-cabeças java", perguntas graves sobre os componentes internos da JVM e até algumas perguntas sobre o Docker.

Há um problema: o jogo é exibido na tela grande e aqueles que estão por perto lembram a maioria das respostas para vários jogos. A princípio, parecia que precisaríamos de centenas de perguntas para minimizar a possibilidade de memorização. Mas, refletindo, eles perceberam que existem opções mais simples. Para iniciar, remova o controle do mouse, deixando apenas o teclado. Agora, aqueles que estão próximos vêem a pergunta, mas não vêem qual resposta o jogador escolheu. Adicionado mix de opções de resposta, complicando a memorização. Para cada pergunta, vários formulários semelhantes, mas ligeiramente diferentes, foram adicionados. Obviamente, você ainda se lembra das respostas, e isso ficou evidente nos resultados visivelmente aprimorados no segundo dia da conferência. Muitos usuários passaram dezenas de minutos jogando, tentando quebrar o recorde de outros. Mas aqui tudo é como na vida - o zelo é recompensado.
Duas semanas se passaram desde a ideia até a realização de perguntas e implementação. Claro, tudo está em Java. Bota usada Spring e Gradle. A interface WEB é feita no Angular. Como armazenamento, é usado o banco de dados H2 interno, que sai da caixa com uma interface da web, o que é muito conveniente. A configuração do suporte é de dois MacBooks, cuja imagem é duplicada em duas TVs. Para facilitar a configuração, o aplicativo foi implantado remotamente em nossa nuvem (
https://habr.com/company/odnoklassniki/blog/346868/ ).
Aprendemos há muito tempo: nenhum recurso deve ser desenvolvido sem a coleta de estatísticas. Obviamente, também anexamos estatísticas detalhadas ao jogo, que podemos compartilhar.
No total, o jogo foi jogado 811 vezes em dois dias. Estatísticas de respostas, dependendo da complexidade da pergunta:
DIFICULDADE
| COUNT
| CORRECT_PERCENT
|
1
| 3552
| 61
|
2
| 2031
| 49.
|
3
| 912
| 46.
|
Para quais células do campo e com que frequência os jogadores chegaram:
A porcentagem de respostas corretas em cada célula do campo:
Mas o mais interessante é, é claro, as estatísticas das perguntas. Não foi tão fácil avaliar sua complexidade levando em conta a distribuição por categoria, a avaliação é sempre subjetiva e uma pergunta simples para um usuário é difícil para outro.
Oleg sugeriu que se jogasse uma das perguntas com as palavras "até os limpadores sabem disso", mas descobriu-se que poucos limpadores sabem as respostas certas para muitas perguntas "simples" e também para os programadores. Nós oferecemos algumas perguntas do nosso jogo - líderes em respostas incorretas, tente avaliar sua força!
- O resultado de chamar esse código?
System.out.println(1/0d)
- Lança uma ArithmeticException
- Imprime o Infinito
- Imprime "NaN"
- Irá imprimir 0
A respostaParece uma pergunta muito simples. Aqui está uma aritmética simples: qual poderia ser o problema? Por que apenas 28% dos jogadores deram a resposta correta? Dividir um número inteiro por 0 em Java resulta em uma ArithmeticException
. Mas existem números inteiros aqui? Vamos olhar com cuidado. O que é esse "d" depois de 0? Esta letra significa que esta não é uma constante inteira 0, mas um valor do tipo double
. E acontece que a expressão é idêntica a 1.0 / 0.0. E essa é uma divisão por um ponto flutuante por zero, cujo resultado é Double.POSITIVE_INFINITY
. Portanto, a resposta correta é "b".
- O resultado de chamar esse código?
System.out.println( Long.MAX_VALUE==(long)Float.MAX_VALUE );
- imprimir verdadeiro
- imprimirá falso
- lança uma ArithmeticException
A respostaPrimeiro, você precisa entender o que é mais: Float.MAX_VALUE
ou Long.MAX_VALUE
? Embora o float
tenha um intervalo de valores menor que o double
, seu valor máximo é de aproximadamente 20 ordens de magnitude além do intervalo de possíveis valores long
. Mas como a conversão de texto funciona nesse caso? Pode-se adivinhar, mas é melhor executar o código. Melhor ainda, abra a Java Language Specification, uma seção sobre Narrowing Primitive Conversion, e leia que, se o valor de um número de ponto flutuante for muito grande e ficar fora do intervalo dos valores disponíveis de um tipo inteiro, o resultado da conversão será igual ao valor máximo que pode ser representado usando um tipo inteiro . I.e. O resultado da conversão é Long.MAX_VALUE
. A resposta correta foi dada por 27% dos entrevistados.
- Qual classe não é
Comparable
?
- java.lang.String
- java.util.TreeSet
- java.io.File
- java.lang.Enum
A respostaEssa pergunta aparentemente simples confundiu muitos, ou melhor, 76% dos que responderam. Adivinhe por si mesmo qual das respostas aqui está correta e qual foi a resposta mais popular - 61% dos jogadores a escolheram.
- Qual é o código idêntico?
Object o = Math.min(-1, Double.MIN_VALUE)
- Objeto o = -1
- Objeto o = Double.MIN_VALUE
- Objeto o = -1,0
A respostaO valor mínimo double
certamente é menor que -1, o que novamente poderia estar errado? Claro, nem tudo é tão simples, caso contrário, não pediríamos. Acontece que Double.MIN_VALUE
não contém exatamente o que é esperado, ou seja, “mantendo constante o menor valor positivo diferente de zero”, de acordo com a documentação. Seria Double.MIN_POSITIVE_VALUE
correto chamá-lo Double.MIN_POSITIVE_VALUE
. Double
novamente circulou seu dedo! A resposta correta é: Object o = -1.0
e, portanto, apenas 22% dos jogadores responderam.
- Qual linha resultará da chamada desse código?
Long.toHexString(0x1_0000_0000L + 0xcafe_babe)
- 1cafebabe
- cafebabe
- ffffffffcafebabe
A respostaSe você escolheu a segunda resposta, estará entre 22% dos que responderam corretamente. Essa pergunta é retirada do livro Java Puzzlers: Traps, Pitfalls e Corner Cases, de autoria de Joshua Bloch e Neal Gafter. Se você respondeu incorretamente, não desanime e corra para ler este livro!
- O JDK 8 apresenta suporte para anotações nos parâmetros do método. É possível adicionar anotação ao parâmetro
this
método?
- É impossível
- Talvez, mas apenas no bytecode
- Talvez definindo
this
explicitamente como o primeiro parâmetro do método
A respostaQuando a capacidade de colocar anotações nos parâmetros do método foi incluída no JDK 8,
this
parâmetro não foi privado. Para esse fim, agora
this
pode ser especificado explicitamente nas assinaturas do método:
class Foo { public void test(@Annotated Foo this) {} }
Embora você possa discutir sobre seus benefícios práticos, agora é um recurso do idioma. 32% dos jogadores adivinharam a resposta correta.
- No JDK 8, o parâmetro
concurrencyLevel
no construtor ConcurrentHashMap
afeta:
- Simultaneidade de leitura / gravação disponível
- Tamanho da tabela inicial
- Nos dois parâmetros
A respostaSe você escolheu a opção 2, estará entre os 15% que responderam corretamente a essa pergunta mais difícil do jogo. O fato é que, no JDK 8, eles abandonaram segmentos no ConcurrentHashMap
, de modo que concurrencyLevel
perdeu seu significado anterior. Afeta apenas o tamanho inicial da tabela e até isso limita apenas o valor initialCapacity
partir de baixo.