Badoo participa regularmente con un stand en exposiciones de conferencias de TI. Por lo tanto, cada año, nosotros, con colegas, ingenieros y devels, tenemos algo para hacer algo tan bueno para no aburrirnos entre los informes.
Mi nombre es Ivan, soy un desarrollador frontend. En este artículo, junto con nuestro colega y entusiasta del bricolaje lilek, Yura Lilekov, le diremos cómo, usando dos botones rojos, un microcontrolador, un código de Reacción y 250 palabras para temas de TI, creamos el juego "Juego de adivinanzas de TI" y organizamos un acogedor encuentro en Highload ++ y Heisenbug.

Contenido:
TK no trivial
Mecánica del juego y puntuación
Animación
Frontend
Hardware: dos botones rojos
Resultado
TK no trivial
Las conferencias de TI tienen su propia atmósfera: hay mucha gente, no es fácil captar y mantener su atención en el stand. Para que vengan a nosotros precisamente en los descansos entre informes, debemos ofrecerles una lección interesante pero sin complicaciones.
Inspirados por el éxito del alias de TI del año pasado, formulamos los siguientes requisitos para el nuevo juego:
- Debe ser multiusuario. La comunicación de los participantes es la tarea principal. Sabemos que muchos vienen solos y no siempre comienzan fácilmente una conversación con extraños. El juego debería dar ocasión de hablar entre ellos y con los ingenieros de Badoo.
- Las sesiones deben ser cortas. Podría haber de una a tres mil personas en la conferencia, y queríamos llegar a la mayor cantidad de participantes posible.
- El juego debería ser espectacular. Si una persona no participa en el juego, entonces al menos déjelo verlo. Esto enciende el interés, ayuda a comprender la esencia y decide participar.
- La gestión más sencilla. Como todos sabemos, si algo puede romperse, entonces ciertamente se romperá.
- Sin registro y SMS!
Después de una lluvia de ideas, pensar en un sueño e intentar encontrar inspiración, se nos ocurrió el "Juego de adivinanzas de TI", un juego simple con palabras sobre temas de TI.

¿Cuál es la esencia de:
- Dos participantes juegan simultáneamente.
- La tarea es presionar el botón más rápido que el oponente y adivinar la palabra cifrada que se muestra en la pantalla.
- Dos etapas: la simple ("Atajos"), donde las palabras se escriben de frente a frente, y la difícil ("Agitadores"), en la que se mezclan todas las letras de las palabras.
- Si la respuesta es correcta, se cuenta un punto para el respondedor. Incorrecto: el puntaje va para el oponente.
- Aquellos que obtuvieron al menos N puntos pueden participar en el sorteo de auriculares geniales con cancelación de ruido.
Parece que todo es bastante fácil y simple, pero había dificultades por delante.
Mecánica del juego y puntuación

De este gif, queda bastante claro cómo se ve el proceso redondo:
- Aparece una palabra encriptada.
- El temporizador comienza durante 10 segundos.
- Si uno de los jugadores presiona el botón, este temporizador se detiene y el otro comienza en su lugar, durante 3 segundos. Durante este tiempo, el jugador debe recibir una respuesta, de lo contrario, el puntaje se acredita al oponente.
- Si nadie presionó el botón, la palabra cambia automáticamente y todo el proceso se repite nuevamente.
El principal problema al que nos enfrentamos es cómo llevar el puntaje y registrar a los jugadores. Queríamos hacer el juego lo más rápido posible y no obligar a los participantes a registrarse en ningún lado. Pero era necesario entender cuál de ellos obtuvo un cierto número de puntos y podría recibir el premio principal.
No se nos ocurrió la mecánica de cálculo la primera vez. Solo después de varias pruebas quedó claro cómo hacer que el juego sea conveniente y simple, tanto para los participantes como para los anfitriones.
Ideas fallidas y brillantes
Idea # 1: un punto = una pequeña barra de chocolate
Para cada respuesta correcta, al principio decidimos dar una barra de chocolate. Al final, el participante trae chocolates (o al menos envoltorios de dulces de los "puntos" comidos), ¡y eso es todo! Contamos trofeos, reconocemos al ganador.
Pero hay algunos puntos.
Primero: es inconveniente. Resultó imposible moderar el juego y calcular puntos simultáneamente; se necesitaba un segundo asistente. Esto es demasiado costoso en términos de la fuerza y el tiempo de los muchachos en el stand. Y dada la velocidad del juego, literalmente tendría que tirar chocolate a los participantes.
El segundo punto: ¿cómo distinguir mis chocolates de los chocolates de otras personas? Los jugadores podrían cooperar y apilar chocolates o envoltorios de dulces de ellos. Entonces, el circuito no funciona! Además, las conferencias duran dos días y puedes recolectar chocolates sin cesar.
Tercer punto: necesitas demasiados chocolates. Verificamos experimentalmente cuántos puntos obtiene un jugador promedio y nos dimos cuenta de que al menos un carro necesita chocolate. Y solo teníamos un carrito pequeño.

Idea # 2: clasificación
Mientras algunos juegan, otros se grabarán en la clasificación. En este caso, encontraríamos todos los problemas posibles: homónimos, homónimos, errores de escritura, escritura ilegible (o la necesidad de poner otro monitor grande y una computadora portátil para la entrada), renuencia a dejar sus contactos y mucho más.
Por lo tanto, continuamos buscando.
Idea # 3: marcas en las insignias de los participantes
Para deshacernos del problema de registrar jugadores, utilizamos una solución preparada: insignias que se distribuyeron a todos los participantes. Esto es algo que nadie perderá y que todos los participantes tienen. Decidimos escribir una cuenta directamente a ellos. El participante llegó al conteo, miró la insignia, contó los puntos y determinó el ganador. Pero también hubo una trampa en este esquema: los jugadores podían "falsificar" el puntaje final en sus insignias.
Luego abandonamos por completo la idea de grabar la partitura. Permita que aquellos que obtienen el valor umbral de los puntos reciban una marca especial en la insignia, un sello en forma de nuestro corazón, que pasaron al círculo de los "elegidos". Y entre estos participantes sortearemos al azar los premios principales. Y el resto seguirá recibiendo regalos: nuestras pegatinas, cuadernos, titulares de tarjetas y tomadores de decisiones.

Por supuesto, los empleados de Badoo no participaron en el sorteo.
El esquema es el siguiente:
- Un invitado está marcado en la insignia con una marca de participación: negro por alcanzar 20 puntos o púrpura solo por jugar. Se podrían hacer tres intentos al día y obtener tres corazones.
- Todos los ganadores con marcas negras se reúnen en un momento determinado en el stand, y con la ayuda de un tambor de lotería de tubo caliente tocamos auriculares entre ellos.

Después de probar el juego con personas vivas, calculamos el umbral: anotar 20 puntos en el juego no fue fácil. Pero en cada uno de los dos días de Highload ++, ¡unas 15 personas fueron las ganadoras!
Es cierto que en la lista de palabras había bastantes términos que el desarrollador entendía en primer lugar, por lo que para la conferencia Heisenbug QA acordamos un umbral de 15 puntos. Quizás en vano: los semifinalistas resultaron ser muchas veces más, lo que significa que hay menos posibilidades de ganar auriculares.
PD: En la etapa inicial, decidimos que con la respuesta incorrecta, el participante perderá el puntaje de la barra de chocolate. Pero a nadie le gusta que le roben algo. "¡Esta es mi barra de chocolate, me la gané!" - los primeros probadores del juego estaban indignados. Por lo tanto, como penalización, comenzamos a darle un punto a un oponente.

Animación
Una palabra de adivinanza debería aparecer con algún tipo de animación. Esto nos da dos ventajas:
- Una especie de "paliza" entre palabras. Es mucho más fácil para los jugadores cambiar de una palabra a otra si hay alguna acción entre ellos. Recuerda los viejos juegos de lucha. Cada nueva ronda comenzó con "3..2..1..FIGHT!". No quería hacer otro temporizador aquí, ya los tenemos a granel.
- Variedad visual Intentamos hacer el juego lo más simple posible, no teníamos fondos animados ni transiciones entre niveles. Tampoco podíamos usar el sonido, para no interferir con otros participantes de la conferencia. Por lo tanto, la animación era necesaria.
Con ella, no todo salió bien de inmediato. En las pruebas, había muchachos astutos que no esperaban el final de la animación y hacían clic en el botón antes. Para enseñar los trucos, comenzamos a evitar que apareciera la palabra cuando uno de los jugadores presionó prematuramente un botón. La parte de la palabra que aún no ha tenido tiempo de aparecer, en este caso permaneció oculta detrás de los caracteres. Si el jugador lo quería o no, tenía que responder. Es difícil adivinar una palabra por varias letras, por lo que el oponente obtuvo un punto.
Así es como se ve la palabra si hace clic en el botón con anticipación:

El segundo momento difícil es la duración de la animación. Siempre duró el mismo tiempo: 1,5 segundos. Si "captas el ritmo", entonces puedes acostumbrarte a presionar un botón más rápido que el oponente. Para resolver el problema, agregamos una variable aleatoria que era de 0 a 500 ms. En este caso, adaptarse al ritmo se ha vuelto mucho más difícil.
Frontend
Te contaré un poco sobre la parte del software. Si no está interesado, vaya directamente a la historia de cómo buscamos los botones rojos .
La mecánica del juego resultó ser bastante simple, y no quería reinventar la rueda. Tomó create-react-app
para el lado del cliente. Pero el desafío aún era necesario.
¡Así que ganchos! Los ganchos aparecieron en el horizonte durante mucho tiempo, pero su aplicación en los principales productos de Badoo requirió un replanteamiento bastante serio del proceso de desarrollo. Un pequeño proyecto paralelo fue un gran trampolín para su uso.
No redux! Redux es una gran cosa, y lo usamos en nuestro trabajo todos los días. Pero para una aplicación tan pequeña, el uso de redux no estaba justificado. Además, hay un nuevo gancho useContext
.
const { score, changeScore } = useContext(SessionContext); const { next } = useContext(QuestionsContext); const steps = useContext(StepsContext);
Sí, en lugar de una historia global, tenemos tres contextos que no se cruzan en absoluto.
SessionContext
anotado.
StepsContext
fue responsable de cambiar las pantallas de la aplicación: intro, loop, outro ...
QuestionsContext
sabía todo acerca de las preguntas: cuáles fueron respondidas, qué pregunta será la siguiente, cuánto queda.
Proveedor
Cada contexto necesita un proveedor que entregue datos a los componentes finales. Como ejemplo, utilizaremos un proveedor simple para la puntuación.
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 puede ver en el código, hacemos un seguimiento de los puntos de forma independiente. Hay un estado separado para los jugadores "izquierdo" y "derecho". Además de las funciones para administrar la cuenta: restablezca la cuenta y cambie.
La API del proveedor resultante es muy simple. En general, casi toda la lógica asociada con los proveedores era muy simple, por lo que no nos centraremos en ella.
Temporizadores
El componente de la ronda principal del juego resultó ser interesante: hubo momentos ambiguos en él. El componente es el mismo para ambos modos (inverso y aleatorio - "Atajos" y "Mezcladores").
Según la descripción de la ronda, está claro que hay mucha interacción con el tiempo.
En primer lugar, hay un temporizador responsable de la duración de la palabra en la pantalla: 10 segundos para presionar un botón. Si nadie ha presionado el botón, la siguiente palabra aparece automáticamente.
En segundo lugar, un temporizador que comienza cuando uno de los jugadores hace clic en un botón. Luego tiene 3 segundos para adivinar la palabra.
Parece que no hay nada complicado en esto, por lo que escribimos un código aparentemente obvio:
const [time, nextTick] = useState(0); useEffect(() => { let id = setInterval(() => { nextTick(time + 1); }, 1000); return () => clearInterval(id); }, []);
Pero, como puedes adivinar, esto no funcionó. ¡Simplemente no funcionó en absoluto!
El temporizador llegó a 1 y se detuvo en eso. El hecho es que el antiguo valor estaba "bloqueado" dentro de la función del controlador. Por lo tanto, con cada marca, setInterval
refiere al valor de time
del primer render.
Había una solución al problema de usar una función en lugar de un valor directo:
nextTick(currentTime => currentTime + 1);
Sí, de esa manera siempre tuvimos un valor "nuevo" para el temporizador. Pero no pudimos obtener props
"frescos", por ejemplo.
Obviamente, se tuvo que encontrar un enfoque diferente. Y la decisión más segura fue hacer que la función del controlador sea mutable. React tiene un uso especial useRef
gancho para useRef
.
La mayoría de las veces se usa para trabajar con elementos DOM, pero esta no es su única aplicación. Podemos "recordar" cualquier variable en la propiedad current
y actualizarla con cada render.
function callback() { setCount(count + 1); } useEffect(() => { savedCallback.current = callback; }); useEffect(() => { function tick() { savedCallback.current(); } let id = setInterval(tick, 1000); return () => clearInterval(id); }, []);
Hay un buen artículo de Dan Abramov sobre cómo trabajar con los ganchos setInterval
y React: hacer setInterval declarative with React Hooks . Describió perfectamente todas las trampas y todas las etapas de reflexión sobre la implementación del gancho useInterval
, que tomamos como solución a nuestro problema.
Como el stand estuvo abierto durante toda la conferencia, una sesión continua con el juego fue muy grande. La página no se actualizó en absoluto (F5), por lo que era muy importante controlar la memoria en la etapa de desarrollo. Como saben, las filtraciones van de la mano con los temporizadores, y si todo esto se condimenta con reproducciones de la reacción, entonces sería bastante fácil escribir código que consuma mucha, mucha memoria.
Countdown
comenzó, se detuvo, reinició y reinició docenas (o tal vez cientos) de veces en una sesión de juego. Para no molestarnos con los cheques, lo que debería haber sido mucho, utilizamos un "truco" bastante simple: agregamos accesorios key
a este componente.
<QuestionCountdown key={question.text} onComplete={nextQuestion} />
No nos detendremos en esto en detalle, una descripción exhaustiva del proceso de reconciliación de la reacción sigue con el mismo Dan Abramov: Reaccionar como un UI Runtime .
Hardware: dos botones rojos
Entonces, ya hemos cumplido algunos de los requisitos para el juego: era multijugador, rápido y con una mecánica simple. Queda por agregar a su entretenimiento. La historia será seguida por Yura Lilekov lilek , nuestro entusiasta del bricolaje, un orador constante de la comunidad, el creador del ala voladora y otros dispositivos de bricolaje.
Realmente queríamos encontrar dos botones mecánicos grandes. Y para asegurarse de los rojos, como en ese meme con Agutin.
Desafortunadamente, todo lo que se encontró en las tiendas en línea era demasiado pequeño (5 cm de diámetro) o no había botones en absoluto. Por supuesto, había un buen AliExpress antiguo, pero no había tiempo para esperar la entrega.
Como resultado, encontramos los botones correctos en el sitio de una agencia creativa. Botones en algún lugar de los suburbios hicieron Alexander (aparentemente, un graduado de la Facultad de Radio Electrónica). Llamamos por teléfono, preguntamos qué microcontrolador está cosido en la caja y pedimos que dejen acceso a él, porque necesitamos reprogramarlo.
Alexander, por decirlo suavemente, se sorprendió de tales preguntas. Cuando le preguntamos si los botones resistirían la presión de los programadores entusiastas, nos aseguró que tales botones están en las máquinas tragamonedas en "Cosmic" y manejan perfectamente el flujo de los niños. Mirando hacia el futuro, diré que los ingenieros con calefacción también mantuvieron los botones (solo la batería tuvo que cambiarse una vez).

Relleno
Pero, desafortunadamente, el dispositivo terminado no tenía todas las cualidades que necesitábamos. Y si aún puede deshacerse de los efectos de sonido cortando el cable al altavoz, entonces determinar automáticamente qué botón se presionó no fue una tarea fácil. Se sugirió una opción simple: de alguna manera conecte este dispositivo a la computadora y traduzca las pulsaciones de estos botones en las pulsaciones del teclado.
La solución sin lujos - para tomar el viejo teclado USB y llevar un par de botones con cables adicionales desde el teclado al dispositivo - inmediatamente se descarta como una "granja colectiva". Y conectó el poder del bricolaje.
Después de algunas deliberaciones, decidimos usar la placa Arduino Pro Micro basada en el microcontrolador ATmega32u4 de la familia AVmel de Atmel con la unión necesaria. En esta placa, entre otras cosas, los puertos de E / S y MicroUSB están separados. Y lo más importante: el microcontrolador ATmega32u4 puede actuar como un dispositivo HID, es decir, en nuestro caso, emular pulsaciones de teclas bajo ciertas condiciones.

Para programar este microcontrolador, solo necesita el cable MicroUSB habitual y el entorno de desarrollo Arduino IDE.
Después de instalar el entorno de desarrollo, los ejemplos de código más simples estarán disponibles de inmediato.
Por ejemplo, un programa que emula la escritura en un teclado cuando se presiona un botón (conectado a un puerto de entrada / salida) se encuentra aquí:
Archivo-> Ejemplos-> USB-> Teclado-> KeyboardMessage
La emulación de las pulsaciones de teclas se logra de manera muy simple:
Leer el estado del puerto de entrada tampoco es nada lujoso:
int button_pin = 7; // , void setup() { pinMode(button_pin, INPUT); // } void loop() { if (digitalRead(button_pin)) { // } else { // } }
Dado que el puerto se abre por voltaje, y no por corriente, las corrientes inducidas más pequeñas en los cables que van desde el botón al puerto pueden dar falsos positivos. Por lo tanto, recomendamos "tirar" de los puertos de entrada con resistencias de alta resistencia: la corriente inducida fluye instantáneamente a través de él hacia la "tierra", evitando que ocurra una diferencia de alto voltaje entre la entrada y la "tierra" y, por lo tanto, no habrá disparos falsos. Para estos fines, una resistencia de 5-10 kΩ es suficiente, que está conectada entre la entrada del microcontrolador y su "tierra".
Por lo tanto, obtenemos el siguiente esquema:

La placa Arduino Pro Micro a través de microUSB se conecta a una computadora portátil con la parte de software del juego, dos botones se conectan a dos entradas de la placa y la fuente de alimentación general. Además, estas dos entradas son llevadas a tierra por dos resistencias.
Cuando se presiona uno de los botones, la corriente de la salida de la fuente de alimentación de la placa a través del botón va a la entrada correspondiente de la placa, por lo que veremos una "unidad" lógica en la entrada.
En la parte del software, decidimos emular las pulsaciones de teclas "flecha izquierda" y "flecha derecha", y también dar un retraso de 4 segundos después de determinar la presión, para evitar clics repetidos por parte de los participantes o ruidos de contactos en los botones.
Entonces, con la ayuda de dispositivos simples, enseñamos los botones para identificar al jugador que hace clic en ellos más rápido que el oponente, y para señalar esto al presentador.
Gestión
Completamente manual y lo más simple posible:
- Espacio - Siguiente palabra
- Entrar - muestra la palabra correcta
- + - verdadero (1 punto)
- 0 - incorrecto (señalar al oponente)
- Desplazamiento a la izquierda: siguiente paso
- Desplazamiento a la derecha: paso anterior
Resultado
Los cuatro días de conferencias (dos en Highload ++, dos en Heisenbug) en el stand de Badoo jugaron continuamente "juego de adivinanzas de TI". Todas nuestras esperanzas se hicieron realidad:
- El juego atrajo tanto a participantes como a espectadores: toda una fila de personas se reunieron en el stand. Es especialmente agradable que en el segundo día de las conferencias el número de participantes no disminuyó.
- Los botones son la parte superior! Agregue +100 a la emoción. Incluso si los rivales eran extraños entre sí, el juego causaba muchas emociones.
- La idea con marcas en la insignia funcionó: sin dificultad para encontrar ganadores y recolectar contactos. Un par de personas pidieron dejar su placa limpia, por lo que pusimos marcas en nuestras muñecas (se borraron fácilmente).
- Entregamos 14 pares de auriculares y varios cientos de premios pequeños. ¡Nadie se quedó sin un regalo!

Para participar en el juego en la conferencia, presentamos una mercancía genialTomadores de decisiones en un imán. No sé qué hacer con la tarea: ¡gira el disco mágico! Solo nos quedan un par de fotos, ya que entregamos todo:

Para ingenieros de control de calidad

Para desarrolladores
Cookies predictivas de TI
Las predicciones se inventaron ellos mismos (52 opciones). Hecho realidad con una probabilidad de hasta el 100%.

