O Badoo participa regularmente de um estande em exposições de conferências de TI. Portanto, a cada ano, nós, com colegas - engenheiros e demônios -, encontramos algo para fazer uma coisa tão boa, a fim de não ficar entediado entre os relatórios.
Meu nome é Ivan, sou desenvolvedor frontend. Neste artigo, juntamente com o nosso colega e entusiasta lilek, Yura Lilekov, mostraremos como, usando dois botões vermelhos, um microcontrolador, um código React e 250 palavras para tópicos de TI, criamos o jogo “jogo de adivinhação de TI” e montamos um hangout aconchegante no Highload ++ e Heisenbug.

Conteúdo:
TK não trivial
Mecânica de jogo e pontuação
Animação
Frontend
Hardware: dois botões vermelhos
Resultado
TK não trivial
As conferências de TI têm sua própria atmosfera: há muitas pessoas, não é fácil capturar e manter a atenção delas. Para que eles cheguem até nós precisamente em intervalos entre os relatórios, precisamos oferecer a eles uma lição interessante, mas sem complicações.
Inspirados no sucesso do alias de TI do ano passado, formulamos os seguintes requisitos para o novo jogo:
- Deve ser multiusuário. A comunicação dos participantes é a principal tarefa. Sabemos que muitos vêm sozinhos e nem sempre iniciam facilmente uma conversa com estranhos. O jogo deve dar a oportunidade de conversar entre si e com os engenheiros do Badoo.
- As sessões devem ser curtas. Pode haver de uma a três mil pessoas na conferência, e queríamos alcançar o maior número possível de participantes.
- O jogo deve ser espetacular. Se uma pessoa não participar do jogo, deixe-a pelo menos assistir. Isso desperta interesse, ajuda a entender a essência e decide participar.
- O gerenciamento mais simples. Como todos sabemos, se algo pode quebrar, certamente quebrará.
- Sem registro e SMS!
Após o brainstorming, pensando em um sonho e tentando obter inspiração, criamos o "IT Guessing Game" - um jogo simples com palavras sobre tópicos de TI.

Qual é a essência de:
- dois participantes jogam simultaneamente.
- A tarefa é pressionar o botão mais rápido que o oponente e adivinhar a palavra criptografada exibida na tela.
- Dois estágios: o simples (“Atalhos”), onde as palavras são escritas de trás para a frente, e o difícil (“Agitadores”), no qual todas as letras das palavras são misturadas.
- Se a resposta estiver correta, um ponto é contado para o respondente. Errado - a pontuação vai para o oponente.
- Quem marcou pelo menos N pontos pode participar do sorteio de fones de ouvido com cancelamento de ruído.
Parece que tudo é fácil e simples, mas as dificuldades estavam à frente.
Mecânica de jogo e pontuação

Nesse gif, fica bem claro como é o processo da rodada:
- Uma palavra criptografada aparece.
- O temporizador inicia por 10 segundos.
- Se um dos jogadores pressiona o botão, esse cronômetro para e o outro inicia - por 3 segundos. Durante esse período, o jogador deve receber uma resposta, caso contrário, a pontuação é creditada ao oponente.
- Se ninguém pressionou o botão, a palavra muda automaticamente e todo o processo é repetido novamente.
O principal problema que estamos enfrentando é como manter a pontuação e o recorde de jogadores. Queríamos tornar o jogo o mais rápido possível e não forçar os participantes a se registrarem em qualquer lugar. Mas era necessário entender qual deles conseguiu um certo número de pontos e poderia receber o prêmio principal.
Não inventamos a mecânica de cálculo pela primeira vez. Somente após vários testes, ficou claro como tornar o jogo conveniente e simples - tanto para os participantes quanto para os anfitriões.
Idéias fracassadas e brilhantes
Idéia # 1: um ponto = uma pequena barra de chocolate
Para cada resposta correta, a princípio, decidimos dar uma barra de chocolate. No final, o participante traz chocolates (ou pelo menos embalagens de doces dos "pontos" comidos), e é isso! Contamos troféus, reconhecemos o vencedor.
Mas existem alguns pontos.
Primeiro: é inconveniente. Tornou-se impossível moderar o jogo e calcular pontos simultaneamente; era necessário um segundo assistente. Isso é muito caro em termos de força e tempo dos caras no estande. E, dada a velocidade do jogo, eu teria que literalmente jogar chocolate nos participantes.
O segundo ponto - como distinguir meus chocolates dos chocolates de outras pessoas? Os jogadores podem cooperar e empilhar chocolates ou embalagens de doces com eles. Então, o circuito não funciona! Além disso, as conferências são de dois dias e você pode coletar chocolates sem parar.
Terceiro ponto: você precisa de muitos chocolates. Verificamos experimentalmente quantos pontos um jogador consegue em média e percebemos que pelo menos uma carroça precisa de chocolate. E tínhamos apenas um carrinho pequeno.

Idéia # 2: classificação
Enquanto alguns estão jogando, outros se registram na classificação. Nesse caso, encontraríamos todos os problemas possíveis: nomes, nomes, erros de escrita, escrita ilegível (ou a necessidade de colocar outro monitor e laptop grande para entrada), relutância em deixar seus contatos e muito mais.
Portanto, continuamos a pesquisar.
Idéia # 3: marcações nos crachás dos participantes
Para se livrar do problema de registrar jogadores, usamos uma solução pronta: emblemas que foram distribuídos a todos os participantes. Isso é algo que ninguém vai perder e que todo participante tem. Decidimos escrever uma conta diretamente para eles. O participante chegou à contagem, olhou para o crachá, contou os pontos e determinou o vencedor. Mas havia um problema nesse esquema também: os jogadores podiam "falsificar" a pontuação final em seus crachás.
Então abandonamos completamente a ideia de gravar a partitura. Aqueles que obtiverem o limiar de pontos receberão uma marca especial no crachá - um selo em forma de coração - que eles passaram para o círculo dos "escolhidos". E entre esses participantes, sortearemos aleatoriamente os principais prêmios. E o restante ainda receberá presentes: nossos adesivos, cadernos, portadores de cartão e tomadores de decisão.

Obviamente, os funcionários do Badoo não participaram da oferta.
O esquema é o seguinte:
- Um convidado é marcado no crachá com uma marca de participação: preto por atingir 20 pontos ou roxo apenas por jogar. Três tentativas poderiam ser feitas por dia e três corações obtidos.
- Todos os vencedores com marcas negras se reúnem em um determinado momento no estande e, com a ajuda de um tambor de loteria de tubo quente, tocamos fones de ouvido entre eles.

Depois de testar o jogo em pessoas vivas, calculamos a marca do limiar: marcar 20 pontos no jogo não foi fácil. Mas em cada um dos dois dias do Highload ++, cerca de 15 pessoas foram as vencedoras!
É verdade que na lista de palavras havia alguns termos que foram entendidos principalmente pelo desenvolvedor, portanto, para a conferência de controle de qualidade da Heisenbug, concordamos com um limite de 15 pontos. Talvez em vão: os semifinalistas se revelaram muitas vezes mais, o que significa que há menos chances de ganhar fones de ouvido.
PS No estágio inicial, decidimos que, com a resposta errada, o participante perderá a pontuação da barra de chocolate. Mas ninguém gosta de ser roubado de alguma coisa. “Esta é minha barra de chocolate, eu ganhei!” - os primeiros testadores de jogos ficaram indignados. Portanto, como penalidade, começamos a dar um ponto ao oponente.

Animação
Uma palavra de adivinhação deve aparecer com algum tipo de animação. Isso nos dá duas vantagens:
- Uma espécie de "surra" entre as palavras. É muito mais fácil para os jogadores mudarem de uma palavra para outra se houver alguma ação entre eles. Lembre-se dos velhos jogos de luta. Cada nova rodada começou com "3..2..1..FIGHT!". Não queria criar outro cronômetro aqui - já os temos em massa.
- Variedade visual. Tentamos tornar o jogo o mais simples possível, não tínhamos fundos animados e transições entre níveis. Também não conseguimos usar o som, para não interferir com outros participantes da conferência. Portanto, a animação era necessária.
Com ela, nem tudo correu bem imediatamente. Nas corridas de teste, havia caras espertos que não esperaram o final da animação e clicaram no botão anteriormente. Para ensinar os truques, começamos a impedir que a palavra aparecesse quando um dos jogadores pressionou prematuramente um botão. A parte da palavra que ainda não teve tempo de aparecer, nesse caso, permaneceu oculta por trás dos personagens. Quer o jogador quisesse ou não, ele teve que responder. É difícil adivinhar uma palavra com várias letras, então o oponente entendeu.
Veja como é a palavra se você clicar no botão com antecedência:

O segundo momento difícil é a duração da animação. Sempre durou o mesmo tempo - 1,5 segundos. Se você "pegar o ritmo", poderá pressionar o botão mais rápido que o oponente. Para resolver o problema, adicionamos uma variável aleatória de 0 a 500 ms. Nesse caso, ajustar o ritmo se tornou muito mais difícil.
Frontend
Vou falar um pouco sobre a parte do software. Se você não estiver interessado, vá direto à história de como pesquisamos os botões vermelhos .
A mecânica do jogo acabou sendo bastante simples, e eu não queria reinventar a roda. Tomou create-react-app
para o lado do cliente. Mas o desafio ainda era necessário.
Então ganchos! Os ganchos apareceram no horizonte por um longo tempo, mas sua aplicação nos principais produtos Badoo exigiu uma reflexão bastante séria do processo de desenvolvimento. Um pequeno projeto paralelo foi um excelente trampolim para seu uso.
Sem redux! Redux é uma grande coisa, e nós o usamos em nosso trabalho todos os dias. Mas para uma aplicação tão pequena, o uso do redux não se justificava. Além disso, há um novo gancho useContext
.
const { score, changeScore } = useContext(SessionContext); const { next } = useContext(QuestionsContext); const steps = useContext(StepsContext);
Sim, em vez de uma história global, temos três contextos que não se cruzam.
SessionContext
marcou.
StepsContext
foi responsável por alternar as telas dos aplicativos: introdução, loop, outro ...
QuestionsContext
sabia tudo sobre as perguntas: quais foram respondidas, qual será a próxima, quanto resta.
Fornecedor
Cada contexto precisa de um provedor que entregue dados aos componentes finais. Como exemplo, usaremos um provedor simples para pontuação.
const increment = score => score + 1; const SessionProvider = props => { const [leftPlayerScore, changeLeftPlayerScore] = useState(0); const [rightPlayerScore, changeRightPlayerScore] = useState(0); const resetScore = useCallback(() => { changeLeftPlayerScore(0); changeRightPlayerScore(0); }, []); const changeScore = useCallback(player => { player === 'left' ? changeLeftPlayerScore(increment) : changeRightPlayerScore(increment); }, []); const score = { left: leftPlayerScore, right: rightPlayerScore }; return ( <SessionContext.Provider value={{ score, changeScore, resetScore }}> {props.children} </SessionContext.Provider> ); };
Como você pode ver no código, mantemos o controle dos pontos de forma independente. Existe um estado separado para os jogadores "esquerdo" e "direito". Além de funções para gerenciar a conta: redefina a conta e altere.
A API do provedor resultante é muito simples. Em geral, quase toda a lógica associada aos provedores era muito simples, portanto, não vamos nos concentrar nela.
Temporizadores
O componente da rodada principal do jogo acabou sendo interessante: houve momentos ambíguos nele. O componente é o mesmo para os dois modos (reverso e aleatório - “Atalhos” e “Misturadores”).
Pela descrição da rodada, fica claro que há muita interação com o tempo.
Em primeiro lugar, existe um cronômetro responsável pela duração da palavra na tela: 10 segundos para pressionar um botão. Se ninguém pressionar o botão, a próxima palavra aparecerá automaticamente.
Em segundo lugar, um cronômetro que inicia quando um dos jogadores clica em um botão. Então ele tem 3 segundos para adivinhar a palavra.
Parece que não há nada complicado nisso, então escrevemos um código aparentemente óbvio:
const [time, nextTick] = useState(0); useEffect(() => { let id = setInterval(() => { nextTick(time + 1); }, 1000); return () => clearInterval(id); }, []);
Mas, como você pode imaginar, isso não funcionou. Simplesmente não funcionou!
O cronômetro chegou a 1 e parou. O fato é que o valor do time
antigo foi "bloqueado" dentro da função do manipulador. Portanto, a cada tick, setInterval
refere ao valor de time
da primeira renderização.
Havia uma solução para o problema de usar uma função em vez de um valor direto:
nextTick(currentTime => currentTime + 1);
Sim, dessa forma, sempre tivemos um valor "novo" para o cronômetro. Mas não foi possível obter props
"novos", por exemplo.
Obviamente, uma abordagem diferente teve que ser encontrada. E a decisão mais certa foi tornar a função do manipulador mutável. O React possui um gancho useRef
especial para useRef
.
Na maioria das vezes, é usado para trabalhar com elementos DOM, mas esse não é seu único aplicativo. Podemos "lembrar" qualquer variável na propriedade current
e atualizá-la a cada renderização.
function callback() { setCount(count + 1); } useEffect(() => { savedCallback.current = callback; }); useEffect(() => { function tick() { savedCallback.current(); } let id = setInterval(tick, 1000); return () => clearInterval(id); }, []);
Há um bom artigo de Dan Abramov sobre como trabalhar com ganchos setInterval
e React: Tornando setInterval declarativo com ganchos React . Ele descreveu perfeitamente todas as armadilhas e todos os estágios de reflexão sobre a implementação do gancho useInterval
, que tomamos como solução para o nosso problema.
Como o estande foi aberto durante toda a conferência, uma sessão contínua usando o jogo foi muito grande. A página não foi atualizada (F5), portanto, era muito importante monitorar a memória no estágio de desenvolvimento. Como você sabe, vazamentos andam de mãos dadas com temporizadores, e se tudo isso for temperado com re-renderizações da reação, seria muito fácil escrever código que consome muita, muita memória.
Countdown
começou, parou, reiniciou e reiniciou dezenas (ou talvez centenas) de vezes em uma sessão do jogo. Para não nos incomodar com as verificações, o que deveria ter sido muito, usamos um "truque" bastante simples - adicionamos elementos- key
a esse componente.
<QuestionCountdown key={question.text} onComplete={nextQuestion} />
Não vamos nos aprofundar nisso em detalhes, uma descrição exaustiva do processo de reconciliação da reação ainda está com o mesmo Dan Abramov: React como um UI Runtime .
Hardware: dois botões vermelhos
Portanto, já cumprimos alguns dos requisitos do jogo: era multiplayer, rápido e com mecânica simples. Resta acrescentar ao seu entretenimento. A história será seguida por Yura Lilekov lilek - nosso entusiasta do bricolage, um orador constante da comunidade, criador da asa voadora e outros dispositivos do tipo faça você mesmo.
Nós realmente queríamos encontrar dois botões mecânicos grandes. E para garantir que os vermelhos - como naquele meme com Agutin.
Infelizmente, tudo o que foi encontrado nas lojas on-line era muito pequeno (5 cm de diâmetro) ou não havia botões. Obviamente, havia um bom AliExpress antigo, mas não havia tempo para esperar pela entrega.
Como resultado, encontramos os botões certos no site de uma agência de criação. Botões em algum lugar nos subúrbios fizeram Alexander (aparentemente, um graduado da Faculdade de Rádio Eletrônica). Telefonamos, perguntamos qual microcontrolador está costurado na caixa e pedimos para deixar o acesso a ele, porque precisamos reprogramar.
Alexander, para dizer o mínimo, ficou surpreso com essas perguntas. Quando perguntamos se os botões suportariam a pressão de programadores entusiasmados, ele garantiu que esses botões estão nas máquinas caça-níqueis do "Cosmic" e lidam perfeitamente com o fluxo de crianças. Olhando para o futuro, direi que os engenheiros aquecidos também mantiveram os botões (apenas a bateria precisou ser trocada uma vez).

Recheio
Infelizmente, o dispositivo acabado não possuía todas as qualidades necessárias. E se você ainda conseguisse se livrar dos efeitos sonoros cortando o fio do alto-falante, determinar automaticamente qual botão foi pressionado não era uma tarefa fácil. Uma opção simples sugeria-se: de alguma forma, conecte este dispositivo ao computador e traduza a pressão desses botões nas teclas digitadas no teclado.
A solução sem frescuras - pegar o antigo teclado USB e levar alguns botões com fios adicionais do teclado para o dispositivo - imediatamente descartada como uma "fazenda coletiva". E conectou o poder do bricolage.
Após algumas deliberações, decidimos usar a placa Arduino Pro Micro baseada no microcontrolador ATmega32u4 da família AVmel da Atmel com a ligação necessária. Nesta placa, entre outras coisas, as portas de E / S e MicroUSB são separadas. E o mais importante - o microcontrolador ATmega32u4 pode atuar como um dispositivo HID, ou seja, no nosso caso, emular as teclas digitadas sob certas condições.

Para programar este microcontrolador, você precisa apenas do fio MicroUSB usual e do ambiente de desenvolvimento Arduino IDE.
Após a instalação do ambiente de desenvolvimento, os exemplos de código mais simples estarão imediatamente disponíveis.
Por exemplo, um programa que emula a digitação em um teclado quando um botão é pressionado (conectado a uma porta de entrada / saída) está localizado aqui:
Arquivo-> Exemplos-> USB-> Teclado-> KeyboardMessage
A emulação de pressionamentos de tecla é conseguida de maneira muito simples:
A leitura do status da porta de entrada também é fácil:
int button_pin = 7; // , void setup() { pinMode(button_pin, INPUT); // } void loop() { if (digitalRead(button_pin)) { // } else { // } }
Como a porta é aberta por tensão, e não por corrente, as menores correntes induzidas nos fios que vão do botão à porta podem fornecer falsos positivos. Portanto, recomendamos “puxar” as portas de entrada com resistores de alta resistência: a corrente induzida flui instantaneamente através dela para o “terra”, impedindo que ocorra uma alta diferença de tensão entre a entrada e o “terra” e, portanto, não haverá falso acionamento. Para esses propósitos, basta um resistor de 5 a 10 kΩ, que é conectado entre a entrada do microcontrolador e seu "terra".
Assim, obtemos o seguinte esquema:

A placa Arduino Pro Micro via microUSB se conecta a um laptop com a parte do software do jogo, dois botões se conectam a duas entradas da placa e à fonte de alimentação geral. Além disso, essas duas entradas são puxadas para o chão por dois resistores.
Quando um dos botões é pressionado, a corrente da fonte de alimentação da placa através do botão vai para a entrada correspondente da placa - portanto, veremos uma "unidade" lógica na entrada.
Na parte do software, decidimos emular as teclas "seta esquerda" e "seta direita" e também atrasar 4 segundos após determinar a pressão, a fim de evitar cliques repetidos pelos participantes ou barulho de contatos nos botões.
Assim, com a ajuda de dispositivos simples, ensinamos os botões a identificar o jogador que clica neles mais rápido que o oponente e sinalizar isso para o apresentador.
Gerência
Completamente manual e o mais simples possível:
- Espaço - Próxima Palavra
- Enter - mostra a palavra correta
- + - verdadeiro (1 ponto)
- 0 - incorreto (aponte para o oponente)
- Turno Esquerdo - Próximo Passo
- Deslocamento à direita - etapa anterior
Resultado
Todos os quatro dias de conferências (dois no Highload ++, dois no Heisenbug) no estande do Badoo jogaram continuamente "IT adivinhando jogo". Todas as nossas esperanças se tornaram realidade:
- O jogo atraiu participantes e espectadores: toda uma fila de pessoas se reuniu no estande. É especialmente agradável que no segundo dia das conferências o número de participantes não tenha diminuído.
- Os botões são os melhores! Adicione +100 à emoção. Mesmo que os rivais fossem estranhos um ao outro, o jogo causou muitas emoções.
- A ideia com marcas no crachá funcionou: não há dificuldade em encontrar vencedores e colecionar contatos. Algumas pessoas pediram para deixar seu crachá limpo, então colocamos marcas em nossos pulsos (eles foram facilmente apagados).
- Distribuímos 14 pares de fones de ouvido e várias centenas de pequenos prêmios. Ninguém ficou sem um presente!

Para participar do jogo na conferência, apresentamos um ótimo conteúdoTomadores de decisão em um ímã. Não sei o que fazer com a tarefa - gire o disco mágico! Só temos algumas fotos, pois distribuímos tudo:

Para engenheiros de controle de qualidade

Para desenvolvedores
Cookies preditivos de TI
As próprias previsões foram inventadas (52 opções). Torne-se realidade com uma probabilidade de até 100%.

