Analyse des problèmes résolus du championnat de programmation de Yandex (développement front-end) 2019

Ma participation au championnat de programmation a pris fin. J'ai fait un bon travail de qualification, après avoir résolu 4 problèmes sur 6 et j'étais à la 20e place, donc on espérait que le top 20 serait aussi en finale.
Mais malheureusement, il n'est même pas entré dans le top 100. Après le combat, ils n'agitent pas leurs poings, mais j'ai pu résoudre plusieurs autres tâches à accomplir. J'attire votre attention sur toutes les tâches que j'ai résolues.

Remerciements particuliers pour l'aide apportée aux solutions:
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

Tour final


A. API asynchrone d'un univers parallèle. (15 points)
Condition.

Votre collègue développeur d'univers parallèle vous a envoyé sa nouvelle bibliothèque de contrôle de vaisseaux spatiaux. Parce que un vaisseau spatial est une chose compliquée, alors l'API de la bibliothèque est plutôt "lourde", le nombre exact de méthodes est inconnu, bien sûr, il n'y a pas de documentation. Mais il est connu que dans un univers parallèle, les gens marchent au plafond, dorment le jour, travaillent la nuit et n'utilisent que des fonctions asynchrones et passent toujours un rappel comme premier argument. Des gars étranges! Ici sur Terre, pendant longtemps, tout le monde écrit sur des promesses. Cependant, la bibliothèque doit être intégrée au projet. Par conséquent, vous avez reçu la tâche d'écrire un wrapper qui fournira la même API, mais à des promesses.

Format d'entrée

Exemple d'API source:

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

Format de sortie

Soumettez votre solution comme:

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

Exemple d'utilisation:

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

Remarques

le wrapper doit retourner une promesse rejetée en cas d'erreur lors de l'appel de l'API source, le rappel accepte toujours l'erreur avec le premier argument:

 callback(error, data) 

dans l'API d'origine, il peut y avoir des constantes (nombres, chaînes et booléens), elles doivent être retournées telles quelles:

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

l'initialisation du wrapper doit être "paresseuse": l'API source peut avoir un grand nombre d'espaces de noms, et vous devez y accéder lorsque vous les utilisez.

Solution

Lors du concours, je n'ai pas complètement résolu ce problème et j'ai obtenu 12,86 points sur 15 possibles pour la solution. J'ai utilisé la récursivité pour contourner toutes les propriétés et promiscurer s'il s'agit d'une fonction.
Et après la fin du test, j'ai finalisé la solution en utilisant l'objet Proxy. Seule une telle option a réussi tous les tests. Voici une solution, prenant en compte les améliorations.

 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 } }) } 

Notez que le nœud js possède déjà un tel utilitaire nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original , seulement il accepte une fonction dont le rappel est le dernier argument, et non le premier.

Tout sur le proxy:

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

Stagiaire en semaine (15 points)
Condition.

L'équipe Yandex a un stagiaire Stepan. Le temps est déjà écoulé, mais il n'a pas le temps avec la mise en page. Aidez Stepan à en créer un en fonction de la disposition de cette tâche.

Il ne doit pas y avoir de retrait sur la mise en page à partir des bords gauche et supérieur de la page. De plus, les images ne peuvent pas être utilisées. Voici la mise en page:



Comme vous pouvez le voir, la disposition se compose de deux tailles de tuiles: standard et double. La tuile standard occupe 1/3 de la largeur de l'écran, double - 2/3. Hauteur de tuile fixe - 200px. La distance entre les tuiles est de 20 pixels.

La couleur de fond de la tuile standard est # 8a2be2, la couleur du double est # 000.

Par conséquent, vous devriez obtenir une page HTML avec une mise en page pour la mise en page. La taille de la page ne doit pas dépasser 10 Ko.

Veuillez noter:

seuls les modèles et les styles peuvent être écrits dans des modèles - JavaScript et les images ne peuvent pas être utilisés;

Initialement, le fichier html suivant a été proposé:

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

Solution

En principe, vous pourriez vous passer du fichier source. Mais je l'ai toujours pris comme base.
Suppression des polices et du script du balisage pour réduire le poids du fichier.

Puis il a ajouté 6 divs et les a enveloppés dans un emballage.

Maquillage par affichage: grille

Le résultat est le suivant:

 <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. Rectangles parfaits (40 points)
Condition.

Bob est un artiste expressionniste. Toutes ses œuvres sont des rectangles strictement verticaux colorés sur fond blanc.

Récemment, son travail a été publié sur le site Internet du célèbre magazine Top Art. Bob a décidé d'examiner de plus près ses toiles, a zoomé sur la page et a été horrifié par les coins flous et les bords flous de ses rectangles parfaits.

Étant un homme minutieux, il a étudié le problème et a décidé de convertir ses chefs-d'œuvre en HTML afin que les lignes restent parfaites sous tous les angles et à toutes les échelles. Pour réaliser son plan, il vous a choisi.

Écrivez pour lui un service qui peut générer du html à partir d'images.

Format d'entrée

L'entrée est une chaîne qui contient une image en base64

Format de sortie

Renvoie la fonction traceImage, qui accepte un lien vers l'image en entrée et renvoie une promesse qui se résout en chaîne. La ligne doit avoir une disposition qui répète cette image.

Soumettez votre solution comme:

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

Remarques

L'image peut être de n'importe quelle taille
L'image n'est pas transparente
Couleur de pixel vierge - Blanc (r, g, b): (255, 255, 255)
L'image montre 1 rectangle opaque coloré
Toutes les lignes sont horizontales ou verticales.
Le code s'exécute dans le navigateur

Exemple

L'image est donnée sous forme de chaîne en base64 (sous la forme dans laquelle elle sera transmise à la fonction):

gist.github.com/senaev/50460323558db543256cb7f196e7d81d

image

Pour une telle image, vous pouvez générer une ligne:

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

Solution

L'essence de la solution consiste à créer un canevas pour s'adapter à l'image, à y dessiner une image et à commencer à utiliser getImageData pour obtenir des informations sur la couleur de chaque pixel, puis il reste à parcourir les pixels à partir du bord supérieur gauche jusqu'à ce que vous obteniez un pixel blanc. Ensuite, continuez à trier les pixels le long des axes horizontal et vertical jusqu'à ce que nous rencontrions à nouveau un pixel blanc, afin d'obtenir la largeur et la hauteur du rectangle.

Cet article détaille l'utilisation du canevas et l'utilisation de getImageData.
code.tutsplus.com/en/tutorials/canvas-from-scratch-pixel-manipulation--net-20573
Malheureusement, mon code n'a pas passé tous les tests et a marqué 30 points sur 40, et je ne comprenais toujours pas les erreurs, je vous serais reconnaissant de bien vouloir signaler les lacunes:

 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. Équitation (40 points).
Condition.

Gennady est un intellectuel. Il aime rencontrer des gens intéressants. Mais étant une personne prudente et incrédule, il ne le fait que sur Internet. Récemment, Gennady a découvert que des interlocuteurs comparables en QI peuvent être trouvés sur le forum des échecs, mais le problème est que Gennady n'est pas en mesure de jouer aux échecs, et tous les enseignants sont basés sur javascript, que Gennady désactive soigneusement pour éviter la possibilité de détecter le virus.

Pour aider Gennady, nous vous suggérons de faire un tutoriel pour jouer aux échecs sans javascript, qui montrera comment marche le cheval. L'enseignant devrait ressembler à un échiquier. Vous cliquez sur la cage - ils vous montrent où le cheval peut aller de cette cage.

Format d'entrée

document html lors du chargement d'un échiquier

Format de sortie

La tâche sera testée dans un vrai navigateur (Chrome 77).
Votre document html sera chargé dans le navigateur. Le robot clique dans différentes cellules du terrain d'échecs et prend des captures d'écran après les clics.
Les captures d'écran doivent respecter la référence

Exemple



Remarques

  • Implémentation CSS et HTML. Javascript ne peut pas être utilisé.
  • La disposition entière doit être carrée, sans ombres, dégradés, filets, etc.
  • Largeur et hauteur des cellules - 30 pixels
  • Le champ d'échecs est sur la page en haut à gauche, sans indentation
  • Couleur de cellule sélectionnée # ff0000
  • La couleur de la cellule qui figure # 0000ff peut aller à
  • Couleur des cellules lumineuses # f4cd8d
  • Couleur des cellules sombres # 745853
  • Lumière de cellule supérieure gauche
  • Initialement, aucune cellule sélectionnée

La sélection s'effectue en cliquant sur une cellule spécifique et reste jusqu'au clic suivant

Solution

Malheureusement, pendant les 4 heures allouées, je n'ai pas été en mesure de fournir une solution complète, j'ai réussi à faire la mise en page et à cliquer sur la cellule, et seulement après la fin du tournoi, non sans l'aide de collègues des forums, j'ai néanmoins terminé cette tâche.

Donc, la première chose à faire était de placer le type d'entrée = radio pour chaque cellule, de leur lier une étiquette, de définir la disposition via display: grid et de la diviser en 8 colonnes via grid-template-columns: repeat (8, 30px). J'ai passé beaucoup de temps à mettre en page les mouvements possibles des chevaux.

La difficulté était qu'en CSS, vous ne pouvez pas styliser les éléments précédents.

Et il n'est pas résoluble dans ce cas (lorsque le style nécessite à la fois le précédent et le suivant).
Par conséquent, afin de ne pas polluer le balisage, j'ai décidé d'ajouter des pseudo-éléments positionnés par l'absolu et par l'arrière-plan peints par un dégradé linéaire en carrés. Cela m'a pris beaucoup de temps et cela n'a pas fonctionné, mais on m'a conseillé une excellente solution: définir 8 nuances de bleu pour la cellule sélectionnée et l'arranger comme il se doit.

Le résultat est le suivant:

 <!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> 

Lien vers la version de travail

À suivre ...

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


All Articles