Análisis de los problemas resueltos del campeonato de programación de Yandex (desarrollo front-end) 2019

Mi participación en el campeonato de programación terminó. Hice un buen trabajo calificando, resolviendo 4 de 6 problemas y estaba en el puesto 20, por lo que había esperanzas de que los 20 primeros estarían en la final.
Pero desafortunadamente ni siquiera llegó al top 100. Después de la pelea, no agitan los puños, pero pude resolver varias tareas más para completar. Traigo a su atención todas las tareas que he resuelto.

Un agradecimiento especial por la ayuda en las soluciones:
miraage toster.ru/user/miraage
profesor08 toster.ru/user/profesor08
SmthTo toster.ru/user/SmthTo
RAX7 toster.ru/user/RAX7
dimoff66 toster.ru/user/dimoff66
vk.com/vladilen.minin

Ronda final


A. API asíncrona de un universo paralelo. (15 puntos)
Condición

Su compañero desarrollador de universo paralelo le ha enviado su nueva biblioteca de control de naves espaciales. Porque una nave espacial es algo complicado, entonces la API de la biblioteca es bastante "pesada", se desconoce el número exacto de métodos, por supuesto, no hay documentación. Pero se sabe que en un universo paralelo, las personas caminan en el techo, duermen durante el día, trabajan por la noche y también usan solo funciones asincrónicas y siempre pasan una devolución de llamada como primer argumento. ¡Chicos extraños! Aquí en la Tierra, durante mucho tiempo, todos escriben sobre promesas. Sin embargo, la biblioteca debe integrarse en el proyecto. Por lo tanto, recibió la tarea de escribir un contenedor que proporcionará la misma API, pero con promesas.

Formato de entrada

API de origen de ejemplo:

const api = { a: { b: { c: callback => setTimeout(() => callback(null, 'hello'), 100) } }, aa: { bb: (callback, x, y) => setTimeout(() => callback(null, x + y), 200) } }; 

Formato de salida

Envíe su solución como:

 /** * @param {Object} api -  API * @returns {Object} */ module.exports = function promisify(api) { // ... return promisedApi; }; 

Ejemplo de uso:

 const promisedApi = promisify(api); promisedApi.abc() .then(res => console.log(res)); // => 'hello' 

Notas

el contenedor debe devolver una promesa rechazada en caso de un error al llamar a la API de origen, la devolución de llamada siempre acepta el error con el primer argumento:

 callback(error, data) 

en la API original, puede haber constantes (números, cadenas y booleanos), deben devolverse tal como están:

 api.foo.myConst = 1; promisedApi.foo.myConst === 1; 

La inicialización del reiniciador debe ser "perezosa": la API de origen puede tener una gran cantidad de espacios de nombres, y debe acceder a ellos a medida que los usa.

Solución

En el concurso no resolví este problema por completo y obtuve 12.86 puntos de los 15 posibles para la solución. Utilicé la recursión para omitir todas las propiedades y promisar si es una función.
Y después del final de la prueba, finalicé la solución usando el objeto Proxy. Solo esa opción pasó todas las pruebas. A continuación se muestra una solución, teniendo en cuenta las mejoras.

 function promisify (obj) { const cache = {} return new Proxy(obj, { get (target, prop) { const value = target[prop] const type = Object.prototype.toString.call(value) if (type === '[object Object]') { cache[prop] = promisify(value) //  ,   -  return cache[prop] } else if (type === '[object Function]') { cache[prop] = function (...args) { //    return new Promise((resolve, reject) => { const callback = (err, result) => { if (err) { reject(err) } else { resolve(result) } } value.call(this, callback, ...args) }) } return cache[prop] } return value } }) } 

Tenga en cuenta que el nodo js ya tiene dicha utilidad nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original , solo acepta una función cuya devolución de llamada es el último argumento, y no el primero.

Todo sobre el proxy:

learn.javascript.ru/proxy
www.youtube.com/watch?v=np08WdS9OXg

B. Entre semana de prácticas (15 puntos)
Condición

El equipo de Yandex tiene un aprendiz Stepan. El tiempo ya se está agotando, pero no tiene tiempo con el diseño de la página. Ayuda a Stepan a hacer uno de ellos de acuerdo con el diseño de esta tarea.

No debe haber sangría en el diseño desde los bordes izquierdo y superior de la página. Además, las imágenes no se pueden usar. Aquí está el diseño:



Como puede ver, el diseño consta de dos tamaños de mosaicos: estándar y doble. El mosaico estándar ocupa 1/3 del ancho de la pantalla, doble - 2/3. Altura fija del azulejo - 200px. La distancia entre los mosaicos es de 20 píxeles.

El color de fondo del mosaico estándar es # 8a2be2, el color del doble es # 000.

Como resultado, debería obtener una página HTML con diseño para el diseño. El tamaño de la página no debe exceder los 10 KB.

Por favor tenga en cuenta:

solo se pueden escribir plantillas y estilos en plantillas: no se pueden usar JavaScript ni imágenes;

Inicialmente, se propuso el siguiente archivo html:

github.com/vito2005/YandexCompetition/blob/master/final-2019/B/original.html

Solución

En principio, podría prescindir del archivo fuente. Pero todavía lo tomé como base.
Se eliminaron las fuentes y el script del marcado para reducir el peso del archivo.

Luego agregó 6 divs y los envolvió en una envoltura.

Maquillaje a través de la pantalla: cuadrícula

El resultado es el siguiente:

 <html> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <style> body { margin: 0; padding: 0; } .wrapper { display: grid; grid-template-columns: repeat(3, 1fr); grid-gap: 20px; } .banner1, .banner2, .banner3, .banner4, .banner7 { background: #8a2be2; height: 200px; } .banner5 { background: #000; grid-column: 2 / 4; } .banner6 { background: #000; grid-column: 1 / 3; } </style> </head> <body> <div class="wrapper"> <div class="banner1"></div> <div class="banner2"></div> <div class="banner3"></div> <div class="banner4"></div> <div class="banner5"></div> <div class="banner6"></div> <div class="banner7"></div> </div> </body> </html> 

C. Rectángulos perfectos (40 puntos)
Condición

Bob es un artista expresionista. Todos sus trabajos son rectángulos coloreados estrictamente verticales sobre un fondo blanco.

Recientemente, su trabajo fue publicado en el sitio web de la famosa revista Top Art. Bob decidió mirar más de cerca sus lienzos, acercó la página y se horrorizó por las esquinas borrosas y los bordes borrosos de sus rectángulos perfectos.

Siendo un hombre minucioso, estudió el problema y decidió convertir sus obras maestras a HTML para que las líneas permanecieran perfectas en cualquier ángulo y en cualquier escala. Para cumplir su plan, te eligió a ti.

Escriba para él un servicio que pueda generar html a partir de imágenes.

Formato de entrada

La entrada es una cadena que contiene una imagen en base64

Formato de salida

Devuelve la función traceImage, que acepta un enlace a la imagen como entrada, y devuelve una promesa que se resuelve en la cadena. La línea debe tener un diseño que repita esta imagen.

Envíe su solución como:

 /** * * @param {String} imageSrc - base64 ,  'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...' * @returns {Promise} */ function traceImage(imageSrc) { //   } 

Notas

La imagen puede ser de cualquier tamaño.
La imagen no es transparente
Color de píxel en blanco - Blanco (r, g, b): (255, 255, 255)
La imagen muestra 1 rectángulo opaco de color.
Todas las líneas son horizontales o verticales.
El código se ejecuta en el navegador.

Ejemplo

La imagen se proporciona como una cadena en base64 (en la forma en que se pasará a la función):

gist.github.com/senaev/50460323558db543256cb7f196e7d81d

imagen

Para tal imagen, puede generar una línea:

 <div> <div style=" position: absolute; width: 11px; height: 15px; top: 135px; left: 109px; background-color: rgb(255, 255, 0); "></div> </div> 

Solución

La esencia de la solución es crear un lienzo que se ajuste a la imagen, dibujar una imagen allí y comenzar a usar getImageData para obtener información sobre el color de cada píxel, luego queda iterar sobre los píxeles desde el borde superior izquierdo hasta obtener un píxel blanco. A continuación, continúe clasificando los píxeles a lo largo de los ejes horizontal y vertical hasta que encontremos nuevamente un píxel blanco, de modo que obtengamos el ancho y la altura del rectángulo.

Este artículo detalla el trabajo con el lienzo y el uso de getImageData.
code.tutsplus.com/en/tutorials/canvas-from-scratch-pixel-manipulation--net-20573
Desafortunadamente, mi código no pasó todas las pruebas y obtuvo 30 de 40 puntos, y todavía no podía entender los errores, le agradeceré si señala las deficiencias:

 function traceImage (imgSrc) { function loadImg (src) { return new Promise((resolve, reject) => { if (typeof src !== 'string') reject('wrong data') const img = new Image() img.addEventListener('load', () => resolve(img)) img.addEventListener('error', err => reject(err)) img.src = src }) } function calcElementFromImage (img) { const TRANSPARENT_COLOR = [255, 255, 255, 1] const colorFromData = (data, i) => { return [data[i], data[i + 1], data[i + 2], data[i + 3] / 255] } const w = img.naturalWidth const h = img.naturalHeight const canvas = document.createElement('canvas') canvas.width = w canvas.height = h const ctx = canvas.getContext('2d') ctx.drawImage(img, 0, 0, w, h) const data = ctx.getImageData(0, 0, w, h).data let top, left, bgColor let lastIndex = 0 for (let i = lastIndex; i < data.length; i += 4) { const color = colorFromData(data, i) if (!color.every((c, i) => c === TRANSPARENT_COLOR[i])) { const px = i / 4 left = px % w top = px / w | 0 bgColor = color lastIndex = i break } } let width const maxLeftIndex = (w - left) * 4 + lastIndex for (let i = lastIndex; i < maxLeftIndex; i += 4) { const color = colorFromData(data, i) if (color.every((c, i) => c === TRANSPARENT_COLOR[i])) { const x = i / 4 % w width = x - left break } } let height const maxTopIndex = (h - top - 1) * w * 4 + lastIndex const hStep = w * 4 for (let i = lastIndex; i < maxTopIndex; i += hStep) { const color = colorFromData(data, i) if (color.every((c, i) => c === TRANSPARENT_COLOR[i])) { const y = i / 4 / w | 0 height = y - top break } } bgColor = Object.values(bgColor).join(',') return { parentWidth: w, parentHeight: h, top, left, width, height, bgColor } } return loadImg(imgSrc).then(img => { const data = calcElementFromImage(img) const { parentWidth, parentHeight, top, left, width, height, bgColor } = data const div = `<div style=" position: relative; width: ${parentWidth}px; height: ${parentHeight}px;"> <div style=" position: absolute; top: ${top}px; left: ${left}px; width: ${width}px; height: ${height}px; background-color: rgba(${bgColor})"></div> </div>` return Promise.resolve(div) }) } 

D. Equitación (40 puntos).
Condición

Gennady es un intelectual. Le gusta conocer gente interesante. Pero siendo una persona prudente e incrédula, lo hace solo en Internet. Recientemente, Gennady descubrió que se pueden encontrar interlocutores comparables en IQ en el foro de ajedrez, pero el problema es que Gennady no puede jugar ajedrez, y todos los maestros están basados ​​en javascript, que Gennady inhabilita con prudencia para evitar la posibilidad de contraer el virus.

Para ayudar a Gennady, sugerimos hacer un tutorial para jugar ajedrez sin javascript, que mostrará cómo camina el caballo. El profesor debe verse como un tablero de ajedrez. Hace clic en la jaula: le muestran a dónde puede ir el caballo desde esta jaula.

Formato de entrada

documento html al cargar que se dibuja un tablero de ajedrez

Formato de salida

La tarea se probará en un navegador real (Chrome 77).
Su documento html se cargará en el navegador. El robot hace clic en varias celdas del campo de ajedrez y toma capturas de pantalla después de hacer clic.
Las capturas de pantalla deben cumplir con la referencia

Ejemplo



Notas

  • Implementación de CSS y HTML. No se puede usar Javascript.
  • Todo el diseño debe ser cuadrado, sin sombras, degradados, filetes, etc.
  • Ancho y alto de celda: 30 píxeles
  • El campo de ajedrez está en la página en la parte superior izquierda, sin sangría
  • Color de celda seleccionado # ff0000
  • El color de la celda que la figura # 0000ff puede ir a
  • Color de celda de luz # f4cd8d
  • Color de celda oscura. # 745853
  • Luz de celda superior izquierda
  • Inicialmente no se seleccionó ninguna celda

La selección se realiza haciendo clic en una celda específica y permanece hasta el siguiente clic

Solución

Desafortunadamente, durante las 4 horas asignadas no pude proporcionar una solución completa, logré hacer el diseño y hacer clic en la celda, y solo después de que terminó el torneo, no sin la ayuda de colegas de los foros, todavía terminé esta tarea.

Entonces, lo primero que debía hacer era colocar el tipo de entrada = radio para cada celda, unirles una etiqueta, configurar el diseño a través de display: grid y dividirlo en 8 columnas a través de grid-template-columnas: repetir (8, 30px). Pasé mucho tiempo con el diseño de posibles movimientos de caballos.

La dificultad fue que en css no puedes diseñar elementos anteriores.

Y no es solucionable en este caso (cuando el estilo requiere tanto anterior como posterior).
Por lo tanto, para no contaminar el marcado, decidí agregar pseudoelementos que están posicionados por el absoluto y a través del fondo pintado por un gradiente lineal en cuadrados. Me llevó mucho tiempo y no funcionó, pero me aconsejaron una excelente solución: establecer 8 tonos de azul para la celda seleccionada y organizarlo como debería.

El resultado es el siguiente:

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style>.white { background: #f4cd8d; } .black { background: #746864; } body { margin: 0; display: flex } div { display: grid; grid-template-columns: repeat(8, 30px); overflow: hidden; } input { display: none; } label { width: 30px; height: 30px; background: #f4cd8d; display: block; position: relative; } input[type="radio"]:checked + label { background: #FF0000; box-shadow: -30px -60px 0 0 #0000FF, -60px -30px 0 0 #0000FF, 30px -60px 0 0 #0000FF, 60px -30px 0 0 #0000FF, -30px 60px 0 0 #0000FF, -60px 30px 0 0 #0000FF, 30px 60px 0 0 #0000FF, 60px 30px 0 0 #0000FF; z-index: 100; position: relative; } </style> </head> <body> <div> <input type="radio" name="tag" id="a1"> <label class="white a1" for="a1"></label> <input type="radio" name="tag" id="b1"> <label class="black b1" for="b1"></label> <input type="radio" name="tag" id="c1"> <label class="white c1" for="c1"></label> <input type="radio" name="tag" id="d1"> <label class="black d1" for="d1"></label> <input type="radio" name="tag" id="e1"> <label class="white e1" for="e1"></label> <input type="radio" name="tag" id="f1"> <label class="black f1" for="f1"></label> <input type="radio" name="tag" id="g1"> <label class="white g1" for="g1"></label> <input type="radio" name="tag" id="h1"> <label class="black h1" for="h1"></label> ........... ........... <input type="radio" name="tag" id="a8"> <label class="black a8" for="a8"></label> <input type="radio" name="tag" id="b8"> <label class="white b8" for="b8"></label> <input type="radio" name="tag" id="c8"> <label class="black c8" for="c8"></label> <input type="radio" name="tag" id="d8"> <label class="white d8" for="d8"></label> <input type="radio" name="tag" id="e8"> <label class="black e8" for="e8"></label> <input type="radio" name="tag" id="f8"> <label class="white f8" for="f8"></label> <input type="radio" name="tag" id="g8"> <label class="black g8" for="g8"></label> <input type="radio" name="tag" id="h8"> <label class="white h8" for="h8"></label> </div> </body> </html> 

Enlace a la versión de trabajo

Continuará ...

Source: https://habr.com/ru/post/474404/


All Articles