Mit diesem Beitrag schlieĂen wir eine Reihe von Veröffentlichungen ĂŒber Yandex.Blitz-Wettbewerbe im Jahr 2018 ab. Wir hoffen, dass Sie die Gelegenheit hatten, teilzunehmen oder zumindest zu sehen, welche Art von Aufgaben wir in der NĂ€he der Produktion vorschlagen. Der letzte Wettbewerb war der Verwendung von Algorithmen im Frontend gewidmet. Heute veröffentlichen wir detaillierte Analysen von 12 Problemen: Die ersten 6 wurden in der Qualifikationsrunde und die Probleme 7â12 im Finale vorgeschlagen. Wir haben versucht zu erklĂ€ren, wie sich Bedingungen gebildet haben, auf welche FĂ€higkeiten wir geachtet haben. Vielen Dank an alle Teilnehmer fĂŒr ihr Interesse!

Aufgabe 1
Die erste Aufgabe bestand darin, sich 20 Minuten lang aufzuwĂ€rmen, und wir beschlossen, den Zustand so zu gestalten, dass er mit regulĂ€ren AusdrĂŒcken leicht gelöst werden konnte.
Alle Optionen fĂŒr die Aufgabe sind auf Ă€hnliche Weise aufgebaut - es gibt eine Aufteilung in Punkte, von denen jeder durch die Gruppe im endgĂŒltigen regulĂ€ren Ausdruck dargestellt werden kann.
Betrachten Sie eine der Optionen: Post.
Zustand
Auf dem Planeten Jopan-14-53 wollen sich die Einheimischen gegenseitig Briefe schicken, aber die Taubenroboter, die sie ausliefern sollen, sind in den Adressen verwirrt. Sie mĂŒssen ihnen beibringen, Buchstaben zu analysieren und ihre GĂŒltigkeit zu ĂŒberprĂŒfen.
Die Struktur des grundlegenden Teils der Adresse besteht aus der Region, der Gemeinde, dem Vor- und Nachnamen des EmpfÀngers. Die Gemeinde kann in Landkreise und PostÀmter unterteilt werden. Alle Teile sind durch ein Komma getrennt.
Die Namen von Regionen, Gemeinden und Bezirken sind Wörter, der erste Buchstabe in jedem Wort ist groĂ, der Rest ist klein. Es sind Namen mit zwei Wörtern möglich, die durch ein Leerzeichen oder ein Minuszeichen getrennt sind. In jedem Wort drei bis acht Buchstaben AZ.
Die Bewohner haben 6 Finger in der Hand, im Alltag das Duodezimalsystem. Die Zahlen 0â9 werden unverĂ€ndert verwendet, und 10 und 11 werden durch die Zeichen
~
und
â
angezeigt.
Die Postnummer in der Komposition besteht entweder aus 3 Ziffern in einer Reihe oder aus 4 Ziffern, die in 2 Gruppen zu je 2 Ziffern mit einem Minuszeichen unterteilt sind. Beispiele:
300
,
44-99
.
Manchmal schicken die Bewohner auf Anfrage Briefe an die Gemeinde. In diesem Fall gibt es keine Adresse: nur die Gemeinde und den Namen des EmpfÀngers.
Es ist lustig, aber die Menschen auf dem Planeten heiĂen nur sechs Namen und neun Nachnamen. Namen: Shob, Ryob, Mob, Xiang, Ryan, Mans. Nachnamen: Sho, Ryo, Mo, Xia, Rya, Ma, Syu, Ryu, Mu. Die Bewohner dieses Planeten sind ĂŒberhaupt keine TrĂ€umer.
Parsen
Die Struktur des grundlegenden Teils der Adresse besteht aus der Region, der Gemeinde, dem Vor- und Nachnamen des EmpfÀngers. Die Gemeinde kann in Landkreise und PostÀmter unterteilt werden. Alle Teile sind durch ein Komma getrennt.
In den ersten AbsÀtzen erfahren wir, wie Region, Gemeinde, Bezirk, Post, Name und Nachname angegeben werden und in welcher Reihenfolge sie in der getesteten Zeile folgen sollen.
Die Namen von Regionen, Gemeinden und Bezirken sind Wörter, der erste Buchstabe in jedem Wort ist groĂ, der Rest ist klein. Es sind Namen mit zwei Wörtern möglich, die durch ein Leerzeichen oder ein Minuszeichen getrennt sind. In jedem Wort drei bis acht Buchstaben AZ.
Aus diesem Absatz geht hervor, dass die Gruppe den Wörtern entspricht
([-][-]{2,7})
. Und die Namen
([-][-]{2,7}(?:[- ][-][-]{2,7}))
.
Die Bewohner haben 6 Finger in der Hand, im Alltag das Duodezimalsystem. Die Zahlen 0â9 werden unverĂ€ndert verwendet, und 10 und 11 werden durch die Zeichen ~
und â
angezeigt.
Hier erfahren wir, dass es nicht ausreicht,
\d
fĂŒr Zahlen zu verwenden - Sie mĂŒssen
[0-9~â]
.
Die Postnummer in der Komposition besteht entweder aus 3 Ziffern in einer Reihe oder aus 4 Ziffern, die in 2 Gruppen zu je 2 Ziffern mit einem Minuszeichen unterteilt sind. Beispiele: 300
, 44-99
.
Somit entspricht die Gruppe den Postnummern
([0-9~â]{3}|[0-9~â]{2}-[0-9~â]{2})
.
Manchmal schicken die Bewohner auf Anfrage Briefe an die Gemeinde. In diesem Fall gibt es keine Adresse: nur die Gemeinde und den Namen des EmpfÀngers.
Wir verstehen, erinnern uns und berĂŒcksichtigen bei der Versammlung der endgĂŒltigen Funktion, dass der Bezirk und das Postamt gleichzeitig abwesend sein können.
Es ist lustig, aber die Menschen auf dem Planeten heiĂen nur sechs Namen und neun Nachnamen. Namen: Shob, Ryob, Mob, Xiang, Ryan, Mans. Nachnamen: Sho, Ryo, Mo, Xia, Rya, Ma, Syu, Ryu, Mu. Die Bewohner dieses Planeten sind ĂŒberhaupt keine TrĂ€umer.
Dies ist der letzte Teil der Bedingung. Hier brauchten wir eine andere einfache Gruppe
(||||||||) (|||||)
oder ihr kĂŒrzeres GegenstĂŒck
([][]|[]) ([C]|[]||)
.
Der Einfachheit halber speichern wir die Gruppen in Variablen und verwenden sie im resultierenden regulÀren Ausdruck.
const w = '[-][-]{2,7}';
Aufgabe 2. Code, in dem ein Fehler vorliegt
Zustand
TagsĂŒber hat das Entwicklungsteam mehrere neue Funktionen entwickelt. Bei der Vorbereitung des Releases traten jedoch ZusammenfĂŒhrungskonflikte auf, und nachdem sie gelöst wurden, wurde das Layout getrennt. Es ist dringend erforderlich, Fehler aufgrund der minimalen Anzahl von Korrekturen im Code zu beseitigen, damit die Version Zeit hat, in Produktion zu gehen.
Sie erhalten eine HTML-Seite mit fehlerhaften Stilen und einem PNG-Format (mit dem erwarteten Ergebnis). Sie mĂŒssen die Fehler beheben, damit das Ergebnis beim Anzeigen in Chrome dem ursprĂŒnglichen Screenshot entspricht. Senden Sie die korrigierte Seite als Lösung fĂŒr das Problem.
HTML-Seite mit defekten Stilen. Layout:

Parsen
In dieser Aufgabe haben wir versucht, die Situation nachzuahmen, auf die Yandex-Entwickler regelmĂ€Ăig stoĂen: Finden Sie in der riesigen Codebasis die wenigen einfachen Codezeilen, deren Korrektur zum gewĂŒnschten Ergebnis fĂŒhrt. Das heiĂt, die Schwierigkeit bestand nicht darin, den Code zu schreiben, sondern zu verstehen, wo genau er geschrieben (oder gelöscht) werden soll.
Wir haben den echten Suchmaschinencode genommen und nur ein paar Korrekturen vorgenommen, die das Layout beschÀdigt haben. Die Teilnehmer des Wettbewerbs hatten weniger als eine Stunde Zeit, um mit ungefÀhr 250 Kilobyte Layout fertig zu werden und den Code in einen Zustand zu bringen, der dem Layout entspricht.
Es ist klar, dass das Zeitlimit fĂŒr den Wettbewerb nicht das Lesen des gesamten Codes erlaubte, daher sollten die Teilnehmer die Tools fĂŒr Entwickler im Browser verwenden.
Um eine der vier Aufgabenoptionen zu korrigieren, waren die folgenden Ănderungen ausreichend:
diff --git a / blitz.html b / blitz.html
Index 36b9af8..1e30545 100644
--- a / blitz.html
+++ b / blitz.html
@@ -531.10 +531.6 @@
iframe [src $ = 'ext-analysis.html'] {
Höhe: Auto;
}}
-.search2__button .suggest2-form__button: n-tes Kind (1) {
- Hintergrund: # ff0! wichtig;
-}
- -
/ * ../../blocks-desktop/input/__control/input__control.styl end * /
/ * ../../node_modules/islands/common.blocks/input/__clear/input__clear.css begin * /
/ * Positioniert relativ zu input__box.
-744.10 +740.6 @@
iframe [src $ = 'ext-analysis.html'] {
Hintergrundclip: Polsterbox;
}}
.input_theme_websearch .input__clear {
Hintergrundbild: URL ("/ static / web4 / node_modules / Islands / common.blocks / input / _theme / input_theme_websearch.assets / clear.svg");
HintergrundgröĂe: 16px;
@@ -857.6 +849.7 @@
iframe [src $ = 'ext-analysis.html'] {
Hintergrundfarbe: # f2cf46;
}}
.websearch-button__text: vor {
+ Position: absolut;
oben: -6px;
rechts: -9px;
Breite: 0;
@@ -866.8 +859.6 @@
iframe [src $ = 'ext-analysis.html'] {
Randstil: fest;
Randfarbe: rgba (255,219,76,0);
Rand-links-Farbe: erben;
- Position: relativ;
- Z-Index: -1000;
}}
/ * ../../blocks-deskpad/websearch-button/websearch-button.styl end * /
@@ -1349.6 +1340.7 @@
iframe [src $ = 'ext-analysis.html'] {
SchriftgröĂe: 14px;
Zeilenhöhe: 40px;
Position: relativ;
+ Anzeige: Inline-Block;
Höhe: Auto;
Polsterung: 0;
vertikal ausrichten: Mitte;
Aufgabe 3. Ein Bild mit einer bestimmten VariabilitÀt
Zustand

Der Designer hat das Logo entworfen. Es muss unter verschiedenen Bedingungen verwendet werden. Um es so bequem wie möglich zu gestalten, erstellen Sie ein HTML-Element in reinem CSS.
Sie können keine Bilder verwenden (auch nicht durch Daten: uri).
Parsen
Da die Aufgabe darin bestand, nur ein Div zu verwenden, haben wir nur das Div selbst und die Pseudoelemente :: vorher und :: nachher.
Das Bild auf dem Layout besteht glĂŒcklicherweise nur aus drei Bereichen unterschiedlicher Farbe. Wir erstellen unseren eigenen Hintergrund fĂŒr jedes zugĂ€ngliche Element, die richtige Position und die runden Ecken. Es gibt einen Schatten auf dem grauen Bereich des Layouts - wir machen es mit einem Farbverlauf.
div { background: #0C0C0C; border-radius: 10px; position: relative; } div:before { border-radius: 9px 9px 0 0; position: absolute; width: 100%; height: 50%; background: #F8E34B; content: ''; } div:after { content: ''; background: linear-gradient(178deg, #C8C8C8 0px , transparent 7px), #EEEDEF; position: absolute; width: 50%; height: 50%; bottom: 0; border-radius: 0 0 0 9px; }
Aufgabe 4
Zustand
Petja arbeitet als leitender Schriftsetzer in der Zeitung Moscow Frontender. FĂŒr das Layout der Zeitung verwendet Petya einen Stapel von Webtechnologien. Die schwierigste Aufgabe fĂŒr Petya war die Anordnung der Ăberschriften in Zeitungsartikeln: Die Zeitungsspalten in jeder Ausgabe haben unterschiedliche Breiten, und die Ăberschriften haben eine andere Schriftart und eine unterschiedliche Anzahl von Zeichen.
Petya muss fĂŒr jede Ăberschrift seine eigene SchriftgröĂe auswĂ€hlen, damit die SchriftgröĂe maximal ist. Gleichzeitig passt der gesamte Ăberschriftentext in den ihm zugewiesenen Bereich.
Hilfe Petya: Implementieren Sie die Funktion calcFontSize, mit der Sie den ĂŒbertragenen Text so in den Container eingeben können, dass er in den gesamten Container passt und die maximal mögliche GröĂe hat. Wenn dies fehlschlĂ€gt, sollte die Lösung null zurĂŒckgeben. Die maximale LĂ€nge der Eingabezeile betrĂ€gt 100 Zeichen. Die Eingabezeichenfolge darf nicht leer sein. Ihre Lösung sollte den gesamten Funktionscode enthalten und keine externen AbhĂ€ngigkeiten verwenden, damit Petja ihn in sein Projekt kopieren und in seinem Code aufrufen kann.
Wir werden prĂŒfen, wie optimal Ihre Lösung funktioniert, und sie optimieren, wenn sie zu viele DOM-Manipulationen hervorruft.
Parsen
Als Erstes mĂŒssen Sie lernen, wie Sie feststellen, ob der Text in den Container passt, da der Text im Container mehrere Zeilen umfassen kann. Der einfachste Weg, dies zu tun, ist die Verwendung der Funktion
element.getBoundingClientRect () , mit der Sie die Abmessungen des Elements
abrufen können.
Sie können Text mit einem separaten Span-Element im Container zeichnen und prĂŒfen, ob die Breite oder Höhe dieses Elements die GröĂe des Containers ĂŒberschreitet. Wenn ja, passt der Text nicht in den Container.
ĂberprĂŒfen Sie als nĂ€chstes die Randbedingungen. Wenn der Text trotz einer MindestschriftgröĂe nicht in den Container passt, kann er nicht eingegeben werden. Wenn der Text mit der maximal zulĂ€ssigen GröĂe unterbrochen wird, ist max die richtige Antwort. In anderen FĂ€llen liegt die gewĂŒnschte SchriftgröĂe irgendwo im Intervall [min, max].
Um eine Lösung zu finden, mĂŒssen Sie diese gesamte LĂŒcke sortieren und einen Wert fĂŒr die SchriftgröĂe finden, bei dem der Text in den Container eingefĂŒgt wird. Wenn Sie ihn jedoch um 1 ââ erhöhen, passt er nicht mehr.
Sie können dies mit einer einfachen for-Schleife ĂŒber den Bereich [min, max] tun, aber die Lösung fĂŒhrt viele ĂberprĂŒfungen und Neuzeichnungen der Seite durch - mindestens eine fĂŒr jeden Wert, der im Bereich ĂŒberprĂŒft werden soll. Die algorithmische KomplexitĂ€t einer solchen Lösung ist linear.
Um die Anzahl der ĂberprĂŒfungen zu minimieren und eine Lösung zu erhalten, die fĂŒr O (log n) funktioniert, mĂŒssen Sie den binĂ€ren Suchalgorithmus verwenden. Die Idee des Algorithmus ist, dass bei jeder Iteration der Suche der Wertebereich in zwei HĂ€lften geteilt wird und die Suche in der HĂ€lfte, in der sich die Lösung befindet, rekursiv fortgesetzt wird. Die Suche wird beendet, wenn der Bereich auf einen einzelnen Wert reduziert wird. Weitere
Informationen zum binÀren Suchalgorithmus finden Sie
im Wikipedia-Artikel .
Wir haben die algorithmische KomplexitÀt der Lösung mit
MutationObserver ĂŒberprĂŒft: Wir haben sie auf der Seite platziert und berechnet, wie viele Mutationen das DOM bei der Suche nach der Antwort trifft. Bei einigen Tests war die Anzahl der Mutationen von oben streng begrenzt, so dass nur eine auf binĂ€rer Suche basierende Lösung diese EinschrĂ€nkung bestehen konnte.
Um die volle Punktzahl fĂŒr die Aufgabe zu erhalten, mussten auch die verschiedenen Randbedingungen (Ăbereinstimmung von min und max, eine leere Textzeile an der Eingabe) sorgfĂ€ltig ĂŒberprĂŒft und verschiedene Umgebungsbedingungen angegeben werden, unter denen der Code ausgefĂŒhrt wird (z. B. behoben mit! Wichtige SchriftgröĂe auf der CSS-Seite )
Aufgabe 5. Kommunikationsschwierigkeiten
In jeder der Qualifizierungsoptionen gab es eine Aufgabe, bei der eine HTML-Seite mit einer Schnittstelle als Eingabe angeboten wurde. Die Aufgaben hatten eine andere Legende, aber sie alle liefen darauf hinaus, dass es notwendig war, einige Daten von der Seite zu extrahieren und mithilfe dieser Daten irgendwie mit der BenutzeroberflÀche zu interagieren.
Lassen Sie uns die Lösung fĂŒr eine der Aufgaben dieser Reihe analysieren, die als "Kommunikationsschwierigkeiten" bezeichnet wurde.
Zustand
Pferd Adolf liebt es, mit Freunden am Telefon zu sprechen. Dies ist leider selten. Jedes Mal, wenn Adolf anrufen möchte, benötigt er mehr als eine Stunde, um die Nummer eines Freundes zu wĂ€hlen. Dies liegt daran, dass es fĂŒr ihn sehr schwierig ist, mit seinen dicken Hufen die richtigen Tasten zu treffen.
GlĂŒcklicherweise unterstĂŒtzt Adolfs Handy JavaScript. Bitte schreiben Sie ein Programm, das Adolfs Freunde wĂ€hlt, indem Sie auf die Tastatur klicken. Alle notwendigen Nummern werden in einem Notizbuch festgehalten. Das unglĂŒckliche Pferd wird Ihnen sehr dankbar sein!
Parsen
Hier ist die Seite, die als Eingabe angeboten wurde:
Der erste Teil der Lösung ist das Abrufen von DatenJede der Ziffern der Telefonnummer wird durch ein Bild ĂŒber das Hintergrundbild gesetzt. Gleichzeitig werden die Zahlen wĂ€hrend der ĂberprĂŒfung dynamisch ersetzt. Wenn Sie den Debugger in einem Browser öffnen und den DOM-Baum der Seite anzeigen, werden Sie feststellen, dass jedes DOM-Element eindeutige Klassen verwendet:
<div class="game"> <div class="target"> <div class="line"> <div class="symbol nine"></div> <div class="symbol eight"></div> <div class="symbol five"></div> <div class="symbol separator"></div> <div class="symbol four"></div> <div class="symbol one"></div> <div class="symbol two"></div> <div class="symbol separator"></div> </div> </div> </div>
In diesem Fall reichte es aus, nur ein Wörterbuch zu erstellen, CSS-Klassen zu extrahieren und sie in eine Zeichenfolge mit einer dem Wörterbuch entsprechenden Zahl ohne Trennzeichen (CSS-Klassentrennzeichen) zu konvertieren. Zum Beispiel so:
const digits = document.querySelectorAll('.game .target .symbol:not(.separator)'); const dict = { 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7, 'eight': 8, 'nine': 9, 'zero': 0, }; const phoneNumber = Array.from(digits).reduce((res, elem) => { for (const className of elem.classList) { if (className in dict) { res.push(dict[className]); break; } } return res; }, []);
Als Ergebnis erhalten wir das folgende Array: [9, 8, 5, 4, 1, 2, 8, 0, 9, 0].
Der zweite Teil der Lösung besteht in der Interaktion mit der SchnittstelleZu diesem Zeitpunkt haben wir bereits ein Array mit allen Ziffern der Nummer, und wir mĂŒssen verstehen, welche Tasten auf der Tastatur welcher Ziffer entsprechen. Wenn wir uns den HTML-Code ansehen, werden wir feststellen, dass es keine speziellen Hinweisklassen gibt, die eine Zahl auf der Tastatur angeben:
<div class="keys"> <div class="key"></div> <div class="key"></div> </div>
Man könnte einfach manuell ein anderes Wörterbuch nach Index erstellen, aber es gibt einen einfacheren Weg. Wenn Sie sich die Stile im Webinspektor genau ansehen, werden Sie feststellen, dass die Nummer auf der SchaltflĂ€che ĂŒber den Inhalt der CSS-Eigenschaft fĂŒr das Pseudoelement: before festgelegt wird. FĂŒr die Taste â3â sehen die Stile beispielsweise folgendermaĂen aus:
.key:nth-child(3):before { content: '3'; }
Um den Wert dieser Eigenschaft abzurufen, verwenden wir die window.getComputedStyle-Methode:
const keys = Array.from(document.querySelectorAll('.game .key')).reduce((res, elem) => { const key = window
Als Ergebnis erhalten wir ein Objekt, bei dem die SchlĂŒsseltexte die Texte auf den SchaltflĂ€chen (Nummer oder "Anruf") sind und die Werte DOM-Knoten sind.
Es bleibt nur, die Werte aus dem ersten Array (phoneNumber) zu ĂŒbernehmen, sie durchzugehen und mit unserem SchlĂŒsselwörterbuch auf die entsprechenden SchaltflĂ€chen zu klicken:
phoneNumber.push('call'); const call = () => { const event = new Event('click'); const next = phoneNumber.shift(); keys[next].dispatchEvent(event); if (phoneNumber.length) { setTimeout(call, 100); } } call();
Bitte beachten Sie: Vor dem WĂ€hlen fĂŒgen wir am Ende des Arrays einen Aufrufwert hinzu. Dies ist fĂŒr die Bedingungen der Aufgabe erforderlich, da die Lösung den Test nicht besteht, wenn Sie die Nummer wĂ€hlen und nicht auf "Anrufen" klicken.
Ein weiteres Merkmal ist das asynchrone DrĂŒcken der Tastaturtasten. Wenn das Zeitintervall zwischen den TastenanschlĂ€gen beim WĂ€hlen weniger als 50 ms betrĂ€gt, besteht diese Lösung den Test ebenfalls nicht. Diese Funktion wurde in den Bedingungen des Problems nicht explizit beschrieben, und wir erwarteten, dass der Teilnehmer dies durch Lesen des Quellcodes der Seite herausfindet. Ăbrigens erwartete die Teilnehmer des Quellcodes eine kleine Ăberraschung. ;)
Aufgabe 6
Zustand
Fedor Rakushkin verwaltet das Task-Management-System in seinem Unternehmen. Der Server, auf dem sich das System befindet, ist ausgefallen (und Fedor hat keine Sicherungen durchgefĂŒhrt).
Im Speicher des Servers befand sich noch ein Cache mit Strukturen, die Aufgaben, AusfĂŒhrende und Beobachter sowie die Beziehungen zwischen diesen Strukturen beschreiben. AuĂerdem wurde die Cache-VerknĂŒpfung zur zuletzt geĂ€nderten Struktur beibehalten.
Helfen Sie Fedor, eine Funktion zu schreiben, mit der alle möglichen Verbindungen zwischen Aufgaben und Benutzern wiederhergestellt und in einem Dokument im Markdown-Format gespeichert werden können, von dem aus Fedor die Datenbank auf einem neuen Server wiederherstellt.
Das Abschriften-Dokument sollte das folgende Format haben:
## - % 1%, %username1%, : %username2% - % 2%, %username1%, : %username2%, %username3% - % 3%, %username1% // - % 4%, : %username1%, %username2% // - % 5% // - % 6%, : %username1% - % 1%, %username1% // - % 2% - % 3%, : %username1% ## - %username1% * % 1% // , * % 2% * % 3% * % 1% - %username2% * % 3%
Die Liste der Aufgaben, die Liste der AusfĂŒhrenden in der Aufgabe, die Liste der Beobachter in der Aufgabe, die Liste der Benutzer und die Liste der Aufgaben, die der Benutzer ausfĂŒhrt, sollten lexikografisch sortiert werden.
Beschreibung der Strukturen im Cache
Benutzer werden im Cache als Struktur vom Typ "Benutzer" gespeichert:
type User = { login: string; tasks: Task[]; spectating: Task[]; };
Aufgaben werden im Cache als Struktur vom Typ "Aufgabe" gespeichert:
type Task = { title: string; assignee: User; spectators: User[]; subtasks: Task[]; parent: Task | null; };
Lösungsvorlage
Ihre Lösung sollte ein CommonJS-Modul enthalten, das eine Funktion exportiert, die der folgenden Signatur entspricht:
module.exports = function (data) {
Beispiele
Wenn Sie einen Link zu "lastEdited" anzeigen, erhalten Sie die folgende Struktur:
{ type: 'task', title: 'Sub task', assignee: null, spectators: [], subtasks: [], parent: { type: 'task', title: 'Something else', assignee: null, spectators: [ { type: 'user', login: 'fedor', tasks: [ { type: 'task', title: 'Do something', assignee: [Circular], spectators: [ { type: 'user', login: 'arkady', tasks: [], spectating: [ [Circular] ] } ], subtasks: [], parent: null } ], spectating: [ [Circular] ] } ], subtasks: [ [Circular] ], parent: null } }
Markdown , :
## - Do something, fedor, : arkady - Something else, : fedor - Sub task ## - arkady - fedor * Do something
, .
, , . , :
function traverse(ctx, handlers) {
. , , âŠ
users.sort((u1, u2) => u1.login < u2.login ? -1 : (u1.login > u2.login ? 1 : 0)); tasks.sort((t1, t2) => t1.title < t2.title ? -1 : (t1.title > t2.title ? 1 : 0));
⊠â :
7.
, .. :

, . , «».

.

, . , . . . HTML CSS. JavaScript .
, , - . , .
HTML/CSS, , , - .
CSS, , â float, . float , , .
â , . (
jsfiddle.net .)
â padding-top, margin-top ( ). DOM- (). (
.)
8.
HTML-. , . , . .
(pixel perfect). .
. .
, , . , . , : - , , .
, . , , .
, â . â CSS- border ( ), background ( ) box-shadow ( ).
- « » ( ) . , , .
â , 70 70 . : , . CSS- , CSS- , .

â 210 210 , 70 70 .

9.
â , -. JavaScript, . , .
: . , , . .
, â . â . , JavaScript API . , -, , . 10 , HTTP- .
â . , , , .
-.
Anmerkungen:
â API npm- @yandex-blitz/phone.
â
API .
â -, :
task.js .
â -
runkit- .
-, .
â GET- return connect.
: - . , , . .
, : , . :
const writeQueue = []; const processQueue = () => { if (writeQueue.length) { const fn = writeQueue.shift(); fn().then(() => { processQueue(); }); } }
, « ».
const writeQueue = []; let isWriteInProgress = false; const processQueue = () => { if (isWriteInProgress) { return; } if (writeQueue.length) { isWriteInProgress = true; const fn = writeQueue.shift(); fn().then(() => { isWriteInProgress = false; processQueue(); }); } }
, POST- . , , .
app.post("/speeddial/:digit/:phonenumber", (req, res) => { writeQueue.push(makeWriteJob(phone, req, res)); processQueue(); });
:
const express = require('express'); const { BEEP_CODES } = require('@yandex-blitz/phone'); const writeQueue = []; let isWriteInProgress = false; const processQueue = () => { if (isWriteInProgress) { return; } if (writeQueue.length) { isWriteInProgress = true; const fn = writeQueue.shift(); fn().then(() => { isWriteInProgress = false; processQueue(); }); } } const makeWriteJob = (phone, req, res) => { return () => { return phone.getData() .then(value => { const speeddialDict = JSON.parse(value); speeddialDict[req.params.digit] = Number(req.params.phonenumber); return phone .setData(JSON.stringify(speeddialDict)) .then(() => phone.beep(BEEP_CODES.SUCCESS)) .then(() => { res.sendStatus(200); }) }) .catch(e => { phone.beep(BEEP_CODES.ERROR).then(() => { res.sendStatus(500); }) }) } }; const createApp = ({ phone }) => { const app = express();
10.
. , «».
:
â , .
â , , .
â , , .
â , : â â â â.
â â , .
, , , , .
.
.
HTML- ( ).
, window.onMazeReady(). . 2 , .
CSS- map.
click CSS-:
â â control_direction_left,
â â control_direction_down,
â â control_direction_up,
â â control_direction_right.
CSS- :
background: radial-gradient(circle at 5px 5px, #eee, #000);
25 , 500 . Zum Beispiel:




window.map String. :
# â
. â
o â
x â
, , , â . .
,
window.map = ` ##### #o#x# #.#.# #...# ##### `;
:

:
<!DOCTYPE html> <html lang=ru/> <head> <style> body { padding: 100px 0 0 100px; } .game-box { perspective: 500px; perspective-origin: center; } .map { transform-style: preserve-3d; } .map__tilt_left { transform: rotateY(-25deg); } .map__tilt_down { transform: rotateX(-25deg); } .map__tilt_up { transform: rotateX(25deg); } .map__tilt_right { transform: rotateY(25deg); } </style> <title></title> </head> <body> <div class="game-box"> <div class="map"> </div> </div> <script> </script> </body> </html>
(HTML, CSS, JS). , «» .
. ( ), , .
, , .
, :
<table class="map map__tilt_none"> <tr> <td class="map__cell map__cell_content_wall"></td> <td class="map__cell map__cell_content_empty"></td> <td class="map__cell map__cell_content_ball"></td> <td class="map__cell map__cell_content_exit"></td> <td class="map__cell map__cell_content_wall"></td> </tr> </table>
:
.map { border: 0; border-spacing: 0; border-collapse: separate; background-color: #ccc; transform-style: preserve-3d; } .map__cell { box-sizing: border-box; border: 1px solid; border-color: #9b9b9b #575757 #575757 #9b9b9b; width: 30px; height: 30px; text-align: center; vertical-align: middle; font-size: 0; line-height: 0; background-color: #707070; } .map__cell_content_ball:after { content: ''; display: inline-block; width: 20px; height: 20px; border-radius: 50%; background: radial-gradient(circle at 5px 5px, #eee, #000); } .map__cell_content_wall { border-width: 4px; } .map__cell_content_exit { background-color: #000; border: 5px solid; border-color: #222 #555 #555 #222; }
â â .
, .
«» , . , . , :
window.map = ` ####### ##.#### #..o..# ##x.#.# ###...# ####### `;
:
function convertMap(mapInput) { return mapInput .trim() .split(/\n\s*/) .map(row => row.split('')); }
, HTML :
const CELL_CONTENT = { '#': 'wall', 'o': 'ball', '.': 'empty', 'x': 'exit' }; function buildGameBoxHtml(map) { return ` <div class="game-box"> <table class="map map__tilt_none"> ${map.map(row => ` <tr> ${row.map(cell => ` <td class="map__cell map__cell_content_${CELL_CONTENT[cell]}"></td> `).join('')} </tr> `).join('')} </table> <!-- --> <div class="controls"> <button class="control control_direction_left">â</button> <button class="control control_direction_down">â</button> <button class="control control_direction_up">â</button> <button class="control control_direction_right">â</button> </div> </div> `; }
, :
let gameBox = document.querySelector('.game-box'); let map = gameBox.querySelector('.map');
â callback : window.onMazeReady(). .
, . , , . HTML, CSS JS â , .
, :
â ,
â , ,
â , ,
â , ,
â , ,
â , .
, :
â ,
â ,
â DOM API ,
â .
11.
, , . , . , .
, . , . . .js, :
module.exports = function solveCaptcha(captcha) {
. .
:
captcha = ' TRABWARH THSCAHAW WWBSCWAA CACACHCR '
:
â S â (sign)
â T â (tree)
â R â (road)
ââŠ
:
[ 'TRABWARH THSCAHAW' , 'WWBSCWAA CACACHCR' ]
:
â 1 10.
â .
â , .
â ( ).
â , , .
â , .
Cut the cake codewars.com.
, 10. , . , . :
â ;
â , .
, . : «⊠, ». . .

. . , . .
. «», .
module.exports = function solveCaptcha(captcha) { const n =
12. -
X . VCS , .
, -. â , .
, . , , . .
, . ( ) .
.
. â 1000, - â 20.
:
type PullRequest = { files: string[], id: string, created: number, }
(created) (id) â .
CommonJS- :
module.exports = function (pullRequests) {
AnmerkungenNodeJS v9.11.2. .
function mergeAllPRs(prs) { } console.assert( mergeAllPRs([ { id: '#1', created: 1536077100, files: ['.gitignore', 'README.md'] }, { id: '#2', created: 1536077700, files: ['index.js', 'package-lock.json', 'package.json'] }, { id: '#3', created: 1536077800, files: ['.pnp.js', 'yarn.lock'] } ]) .join(',') === [ "#1", "#2", "#3" ].join(',') ); console.assert( mergeAllPRs([ { id: '#1', created: 1536074100, files: ['README.md'] }, { id: '#2', created: 1536078700, files: ['README.md'] }, { id: '#3', created: 1536097800, files: ['README.md'] } ]).join(',') === [ "#1" ].join(',') ); console.assert( mergeAllPRs([ { id: '#1', created: 1536077100, files: ['.gitignore', 'README.md'] }, { id: '#2', created: 1536077700, files: ['index.js', 'package-lock.json', 'package.json'] }, { id: '#3', created: 1536077800, files: ['.pnp.js', 'package-lock.json', 'yarn.lock'] }, { id: '#4', created: 1536077900, files: ['index.spec.js', 'index.spec.ts', 'index.ts'] } ]) .join(',') === [ "#1", "#2", "#4" ].join(',') );
â ,
.
, « » (, ).
, , , . ( some includes). â O(n
2 ).
, , ( .
). â O(n).
:
function conflicts(a, b) { let i = 0; let j = 0; while (i < a.length && j < b.length) { if (a[i] === b[j]) { return true; } else if (a[i] > b[j]) { j++; } else { i++; } } return false; } function mergeAllPrs (input) { let i = 0; const mergedFiles = []; const mergedPrs = []; while (i < input.length) { const pr = input[i]; if (!conflicts(mergedFiles, pr.files)) { mergedPrs.push(pr); mergedFiles.push(...pr.files); } i++; } return mergedPrs.map(x => x.id); };
, , . , :
console.assert( mergeAllPrs([ { "id": "1", "created": 1538179200, "files": [ "a", "b", "c", "d" ] }, { "id": "2", "created": 1538189200, "files": [ "a", "x" ] }, { "id": "3", "created": 1538199200, "files": [ "b", "g" ] }, { "id": "4", "created": 1538209200, "files": [ "c", "f" ] }, { "id": "5", "created": 1538219200, "files": [ "d", "w" ] } ]) .join(',') === ['2', '3', '4', '5'].join(',') );
, ( , ).
: «» -, «» â ( ). ( ).
:
[ { "id": "#1", "created": 1536077100, "files": [ ".gitignore", "README.md" ] }, { "id": "#2", "created": 1536077700, "files": [ "index.js", "package-lock.json", "package.json" ] }, { "id": "#3", "created": 1536077800, "files": [ "index.js" ] } ]
#2 #3 , ["#1", "#2"]. .

, .
, â O(n
2 ), . .
, , . , , .
conflicts , . :
const conflictMatrix = new Uint8Array(prs.length ** 2); const prToIndex = new WeakMap(); for (let i = 0; i < prs.length; i++) { const pr1 = prs[i]; prToIndex.set(pr1, i); conflictMatrix[i * prs.length + i] = 0; for (let j = i + 1; j < prs.length; j++) { const pr2 = prs[j]; conflictMatrix[i * prs.length + j] = conflictMatrix[j * prs.length + i] = conflicts(pr1.files, pr2.files); } } function doPRsConflict(pr1, pr2) { const i = prToIndex.get(pr1); const j = prToIndex.get(pr2); return conflictMatrix[i * prs.length + j] === 1; }
«» , . ( ) , . , , - .
, , .
function getNonConflictingPRs (prsSet, mergedPrs) { const result = []; const prsToTest = [...prsSet, ...mergedPrs]; prsSet.forEach((pr) => { if (!conflictsWithSomePR(pr, prsToTest)) { result.push(pr); } }); return result; }
.
const fullSearch = (prsSet, mergedPrs = [], mergedFilesCount = 0) => { hits++;
.