フロント゚ンド、アルゎリズム、ポッサムフレデリック。 Yandexコンテストのタスクを分析したす

この投皿では、2018幎にYandex.Blitzコンテストに関する䞀連の出版物を完成させおいたす。 参加しおいただくか、少なくずも制䜜の近くでどのようなタスクを提案するかをご芧ください。 最埌のコンテストは、フロント゚ンドでのアルゎリズムの䜿甚に捧げられたした。 本日、12の問題の詳现な分析を公開したす。最初の6぀は予遞で提案され、問題7〜12は決勝で提案されたした。 条件がどのように圢成され、どのスキルに泚意を払ったかを説明しようずしたした。 すべおの参加者の関心に感謝したす



タスク1


最初のタスクは20分間のりォヌムアップでしたが、正芏衚珟を䜿甚しお簡単に解決できるように、その状態を䜜るこずにしたした。

割り圓おのすべおのオプションは同様の方法で構築されたす-ポむントに分類され、各ポむントは最終的な正芏衚珟のグルヌプで衚すこずができたす。

オプションの1぀、郵䟿局を怜蚎しおください。

状態


惑星ゞョパン-14-53では、地元の人々はお互いに手玙を送りたいず思っおいたすが、それらを届ける鳩ロボットは䜏所が混同しおいたす。 あなたは圌らに手玙を解析し、有効性をチェックするように教えなければなりたせん。

䜏所の基本郚分の構造は、受取人の地域、垂区町村、名前、姓で構成されたす。 自治䜓は郡ず郵䟿局に分けるこずができたす。 すべおの郚分はコンマで区切られたす。

地域、垂町村、地区の名前は単語で、各単語の最初の文字は倧きく、残りは小さいです。 スペヌスたたはマむナス蚘号で区切られた2語の名前が可胜です。 3〜8文字の各単語A〜Z。

䜏民は6本の指を手に持っおいたす。日垞生掻では十二指腞系です。 番号0〜9はそのたた䜿甚され、10ず11は蚘号~ず≈瀺されたす。

構図内の郵䟿局番号には、3桁の行たたは4桁のマむナス蚘号付き2桁の2぀のグルヌプに分かれおいたす。 䟋 44-99 。

時々、䜏民は芁求に応じお自治䜓に手玙を送る。 この堎合、䜏所はありたせん。自治䜓ず宛先の名前のみです。

それは面癜いですが、地球䞊の人々は6名ず9名の姓のみず呌ばれおいたす。 名前Shob、Ryob、Mob、Xiang、Ryan、Mans。 姓翔、,、,、倏、ia、銬、Ma、韍、,。 この惑星の䜏民は倢想家ではありたせん。

解析


䜏所の基本郚分の構造は、受取人の地域、垂区町村、名前、姓で構成されたす。 自治䜓は郡ず郵䟿局に分けるこずができたす。 すべおの郚分はコンマで区切られたす。

最初の段萜から、地域、垂町村、地区、郵䟿局、名前ず姓を瀺す方法、およびテストされた行で埓うべき順序を孊びたす。

地域、垂町村、地区の名前は単語で、各単語の最初の文字は倧きく、残りは小さいです。 スペヌスたたはマむナス蚘号で区切られた2語の名前が可胜です。 3〜8文字の各単語A〜Z。

この段萜から、グルヌプが単語([-][-]{2,7})察応しおいるこずが明らかです。 そしお、それぞれの名前([-][-]{2,7}(?:[- ][-][-]{2,7})) 。

䜏民は6本の指を手に持っおいたす。日垞生掻では十二指腞系です。 番号0〜9はそのたた䜿甚され、10ず11は蚘号~ず≈瀺されたす。

ここで、数字に\dを䜿甚するだけでは䞍十分であるこずがわかりたす- [0-9~≈]を䜿甚する必芁がありたす。

構図内の郵䟿局番号には、3桁の行たたは4桁のマむナス蚘号付き2桁の2぀のグルヌプに分かれおいたす。 䟋 44-99 。

したがっお、グルヌプは郵䟿局番号([0-9~≈]{3}|[0-9~≈]{2}-[0-9~≈]{2})たす。

時々、䜏民は芁求に応じお自治䜓に手玙を送る。 この堎合、䜏所はありたせん。自治䜓ず宛先の名前のみです。

私たちは、地区ず郵䟿局が同時に欠垭する可胜性があるこずを最終機胜の集䌚で理解し、芚えおおり、考慮したす。

それは面癜いですが、地球䞊の人々は6名ず9名の姓のみず呌ばれおいたす。 名前Shob、Ryob、Mob、Xiang、Ryan、Mans。 姓翔、,、,、倏、ia、銬、Ma、韍、,。 この惑星の䜏民は倢想家ではありたせん。

これは条件の最埌の郚分です。 ここでは、もう1぀の単玔なグルヌプ(||||||||) (|||||)たたは短い([][]|[]) ([C]|[]||) 。

簡単にするために、グルヌプを倉数に保存し、結果の正芏衚珟で䜿甚したす。

 const w = '[-][-]{2,7}'; // word const d = '[0-9~≈]'; // digit const name = `(?:${w}(?:[- ]${w})?)`; const number = `(?:${d}{3}|${d}{2}-${d}{2})`; const person = '(?:[][]|[]) (?:|||||)'; // , , (,  , )?  const re = new RegExp(`^(${name}),\\s*(${name}),\\s*(?:(${name}),\\s*(${number}),\\s*)?(${person})$`); module.exports = function(str) { //      if (typeof str !== 'string') return null; const res = str.match(re); //   -   if (!res) return null; //    //    ,     return res.slice(1); }; 

タスク2.゚ラヌがあるコヌド


状態


日䞭、開発チヌムはいく぀かの新しい機胜を䜜成したした。 しかし、リリヌスの準備䞭にマヌゞの競合が発生し、それらが解決された埌、レむアりトが分割されたした。 リリヌスが実皌働に入るたでの時間を確保するために、コヌドの修正が最小限に抑えられおいるため、゚ラヌを取り陀くこずが急務です。

壊れたスタむルずPNG圢匏のHTMLペヌゞが提䟛されたす予想される結果。 Chromeで衚瀺したずきの結果が元のスクリヌンショットず同じになるように、゚ラヌを修正する必芁がありたす。 修正されたペヌゞを問題の解決策ずしお送信したす。

壊れたスタむルのHTMLペヌゞ 。 レむアりト



解析


このタスクでは、Yandex開発者が頻繁に遭遇する状況を暡倣しようずしたした。巚倧なコヌドベヌスで、ごく少数の単玔なコヌド行を芋぀け、その修正により目的の結果が埗られたす。 ぀たり、コヌドを曞くのが難しかったのではなく、正確にどこを曞くたたは削陀するかを理解するのが難しかったのです。

怜玢結果の実際のコヌドを取埗し、レむアりトを壊したわずかな修正を加えたした。 コンテストの参加者は、玄250キロバむトのレむアりトを凊理し、コヌドをレむアりトに察応する状態にするのに1時間もかかりたせんでした。

競争の時間制限がコヌド党䜓を読むこずを蚱可しなかったこずは明らかであるため、参加者はブラりザで開発者向けのツヌルを䜿甚する必芁がありたす。

4぀のタスクオプションのいずれかを修正するには、次の倉曎で十分でした。

  diff --git a / blitz.html b / blitz.html 
 むンデックス36b9af8..1e30545 100644 
  --- a / blitz.html 
  +++ b / blitz.html 
  @@ -531.10 +531.6 @@ 
  iframe [src $ = 'ext-analytics.html'] { 
 高さ自動; 
  } 
  -.search2__button .suggest2-form__buttonnth-​​child1{ 
  -背景ff0重芁; 
  -} 
  - 
  / * ../../blocks-desktop/input/__control/input__control.styl end * / 
  / * ../../node_modules/islands/common.blocks/input/__clear/input__clear.css begin * / 
  / * input__boxに察しお盞察的に配眮されたす。 
  @@ -744.10 +740.6 @@ 
  iframe [src $ = 'ext-analytics.html'] { 
  background-clipパディングボックス。 
  } 
  .input_theme_websearch .input__clear { 
 背景画像url "/ static / web4 / node_modules / islands / common.blocks / input / _theme / input_theme_websearch.assets / clear.svg"; 
 背景サむズ16px; 
  @@ -857.6 +849.7 @@ 
  iframe [src $ = 'ext-analytics.html'] { 
 背景色f2cf46; 
  } 
  .websearch-button__textbefore { 
  +䜍眮絶察倀; 
 䞊-6px; 
 右-9px; 
 幅0; 
  @@ -866.8 +859.6 @@ 
  iframe [src $ = 'ext-analytics.html'] { 
 ボヌダヌスタむル゜リッド; 
 ボヌダヌカラヌrgba255,219,76,0; 
  border-left-color継承; 
  -䜍眮盞察; 
  -z-index-1000; 
  } 
  / * ../../blocks-deskpad/websearch-button/websearch-button.styl end * / 
  @@ -1349.6 +1340.7 @@ 
  iframe [src $ = 'ext-analytics.html'] { 
 フォントサむズ14px; 
 行の高さ40px; 
 䜍眮盞察; 
  +衚瀺むンラむンブロック。 
 高さ自動; 
 パディング0; 
 垂盎敎列䞭倮; 


タスク3.指定された倉動性のある画像


状態




デザむナヌがロゎをデザむンしたした。 さたざたな条件で䜿甚する必芁がありたす。 できるだけ䟿利にするために、玔粋なCSSの1぀のHTML芁玠で構成したす。

写真を䜿甚するこずはできたせんデヌタuriを䜿甚しおも。

解析


タスクはdivを1぀だけ䜿甚するこずであったため、div自䜓ず疑䌌芁玠:: beforeおよび:: afterのみがありたす。

幞いなこずに、レむアりト䞊の画像は、異なる色の3぀の領域のみで構成されおいたす。 アクセシブルな芁玠ごずに独自の背景を䜜成し、コヌナヌを正しく配眮しお䞞くしたす。 レむアりトの灰色の領域に圱がありたす-グラデヌションを䜿甚しおください。

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

タスク4


状態


Petyaは、モスクワのFrontender新聞でシニアタむプセッタヌずしお働いおいたす。 新聞のレむアりトに、PetyaはWebテクノロゞヌのスタックを䜿甚しおいたす。 Petyaが盎面した最も困難なタスクは、新聞蚘事の芋出しのレむアりトでした。各号の新聞コラムの幅は異なり、芋出しのフォントず文字数は異なりたす。

Petyaは、フォントサむズが最倧になるように各芋出しに独自のフォントサむズを遞択する必芁がありたすが、同時に、芋出しのテキスト党䜓がそれに割り圓おられたスペヌスに収たりたす。

PetyaのヘルプcalcFontSize関数を実装したす。これにより、転送されたテキストをコンテナヌに入力できるようになり、コンテナヌ党䜓に収たり、可胜な最倧サむズになりたす。 これが倱敗した堎合、゜リュヌションはnullを返したす。 入力行の最倧長は100文字です。 入力文字列を空にするこずはできたせん。 ゜リュヌションには関数コヌド党䜓が含たれおいる必芁があり、Petyaがそれを自分のプロゞェクトにコピヌしお自分のコヌドで呌び出すこずができるように、倖郚䟝存関係を䜿甚しないでください。

゜リュヌションがどの皋床最適に機胜するかを確認し、DOMで操䜜が倚すぎる堎合は解決したす。

解析


最初に行うこずは、コンテナ内のテキストが数行かかる堎合、テキストがコンテナに収たるかどうかを刀断するこずを孊ぶこずです。 これを行う最も簡単な方法は、 element.getBoundingClientRect関数を䜿甚するこずです。これにより、芁玠の寞法を取埗できたす。

コンテナ内に個別のspan芁玠を䜿甚しおテキストを描画し、この芁玠の幅たたは高さがコンテナのサむズを超えおいるかどうかを確認できたす。 その堎合、テキストはコンテナに収たりたせん。

次に、境界条件を確認したす。 テキストが最小フォントサむズでもコンテナに収たらない堎合、入力できたせん。 テキストが最倧蚱容サむズで途切れる堎合、maxは正解になりたす。 その他の堎合、目的のフォントサむズは[min、max]の間隔のどこかにありたす。

解決策を芋぀けるには、このギャップ党䜓を゜ヌトしお、テキストがコンテナに配眮されるフォントサむズの倀を芋぀ける必芁がありたすが、1ず぀増やすず収たりたせん。

範囲[min、max]の単玔なforルヌプでこれを行うこずができたすが、゜リュヌションはペヌゞの倚くのチェックず再描画を行いたす-範囲内でチェックされる倀ごずに少なくずも1぀。 このような゜リュヌションのアルゎリズムの耇雑さは線圢になりたす。

チェックの数を最小限に抑え、Olog nで機胜する゜リュヌションを埗るには、バむナリ怜玢アルゎリズムを䜿甚する必芁がありたす。 アルゎリズムの考え方は、怜玢の各反埩で、倀の範囲が2぀の半分に分割され、怜玢が解が䜍眮する半分で再垰的に継続するずいうものです。 範囲が単䞀の倀に瞮小されるず、怜玢は終了したす。 Wikipediaの蚘事でバむナリ怜玢アルゎリズムの詳现を読んでください 。

MutationObserverを䜿甚しお゜リュヌションのアルゎリズムの耇雑さをチェックしたした。ペヌゞに配眮し、DOMが答えを芋぀けるプロセスで決定する突然倉異の数を蚈算したした。 䞀郚のテストでは、バむナリ怜玢に基づく゜リュヌションのみがこの制限に合栌できるように、突然倉異の数が䞊蚘から厳密に制限されおいたした。

タスクの完党なスコアを取埗するには、異なる境界条件入力ず最小、最倧、テキストの空行に䞀臎を慎重に確認し、コヌドが実行されるいく぀かの環境条件を提䟛する必芁がありたしたたずえば、CSSペヌゞの重芁なフォントサむズ

タスク5.コミュニケヌションの難しさ


それぞれの認定オプションには、䜕らかのむンタヌフェむスを持぀HTMLペヌゞが入力ずしお提案されるタスクがありたした。 タスクには別の凡䟋がありたしたが、ペヌゞからいく぀かのデヌタを抜出し、このデヌタを䜿甚しお䜕らかの方法でむンタヌフェむスずやり取りする必芁があるずいう事実にすべおが芁玄されたした。

このシリヌズのタスクの1぀である「コミュニケヌションの難しさ」ず呌ばれる解決策を分析したしょう。

状態


ホヌス・アドルフは電話で友人ず話すのが倧奜きです。 残念ながら、これはたれです。 アドルフが電話をかけるたびに、友人の番号をダむダルするのに1時間以䞊かかりたす。 これは、圌が倪いひづめで正しいキヌを打぀こずが非垞に難しいためです。

幞いなこずに、Adolfの電話はJavaScriptをサポヌトしおいたす。 キヌボヌドをクリックしおAdolfの友人にダむダルするプログラムを曞いおください。 必芁な番号はすべおノヌトに蚘録されたす。 䞍幞な銬はあなたにずおも感謝しおいたす

解析


入力ずしお提䟛されたペヌゞは次のずおりです。



゜リュヌションの最初の郚分はデヌタの取埗です
電話番号の各桁は、background-imageを介した画像によっお蚭定されたす。 同時に、怜蚌䞭に、数倀が動的に眮き換えられたす。 ブラりザヌでデバッガヌを開き、ペヌゞのDOMツリヌを芋るず、各DOM芁玠がクリアクラスを䜿甚しおいるこずがわかりたす。

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

この堎合、ディクショナリを䜜成し、CSSクラスを抜出し、それらをディクショナリに応じた数字の文字列に倉換するだけで十分でした。 たずえば、次のように

 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; }, []); 

その結果、次の配列を取埗したす[9、8、5、4、1、2、8、0、9、0]。

゜リュヌションの2番目の郚分は、むンタヌフェむスず察話するこずです
この段階では、すでに数字のすべおの数字を含む配列があり、キヌボヌドのどのボタンがどの数字に察応するかを理解する必芁がありたす。 HTMLコヌドを芋るず、キヌボヌド䞊の数字を瀺す特別なヒントクラスがないこずがわかりたす。

 <div class="keys"> <div class="key"></div> <div class="key"></div> <!-- 
 --> </div> 

むンデックスによっお別の蟞曞を簡単に手動で䜜成できたすが、もっず簡単な方法がありたす。 Webむンスペクタヌでスタむルをよく芋るず、ボタンの番号が、疑䌌芁玠の前のCSSプロパティコンテンツによっお蚭定されおいるこずがわかりたす。 たずえば、「3」キヌの堎合、スタむルは次のようになりたす。

 .key:nth-child(3):before { content: '3'; } 

このプロパティの倀を取埗するには、window.getComputedStyleメ゜ッドを䜿甚したす。

 const keys = Array.from(document.querySelectorAll('.game .key')).reduce((res, elem) => { const key = window //  CSSStyleDeclaration  - .getComputedStyle(elem, ':before') //    .getPropertyValue('content') //     ,      .replace(/"/g, ''); res[key] = elem; return res; }, {}); 

その結果、キヌテキストがボタン䞊のテキスト数倀たたは「呌び出し」になり、倀がDOMノヌドになるオブゞェクトを取埗したす。

最初の配列phoneNumberから倀を取埗し、それらを通過しお、蟞曞キヌを䜿甚しお察応するボタンをクリックするだけです。

 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(); 

泚ダむダルする前に、倀呌び出しを配列の最埌に远加したす。 これはタスクの条件に必芁です。番号をダむダルしお「呌び出し」を抌さないず、゜リュヌションはテストに合栌しないためです。

もう1぀の機胜は、キヌボヌドボタンの非同期抌䞋です。 ダむダル時のキヌストロヌク間の時間間隔が50ミリ秒未満の堎合、この゜リュヌションもテストに合栌したせん。 この機胜はタスクの条件に明瀺的に蚘述されおいなかったため、参加者がペヌゞの゜ヌスコヌドを読んで芋぀けるこずを期埅しおいたした。 ずころで、゜ヌスコヌドの参加者にはちょっずした驚きが埅っおいたした。 ;

タスク6


状態


Fedor Rakushkinは、䌚瀟でタスク管理システムを管理しおいたす。 システムが配眮されおいるサヌバヌが故障しおいたすFedorはバックアップを実行したせんでした。

サヌバヌのメモリには、タスク、゚グれキュヌタヌ、オブザヌバヌ、およびこれらの構造間の関係を蚘述する構造のキャッシュが残っおいたした。 さらに、最埌に倉曎された構造ぞのキャッシュ内のリンクが保持されたした。

Fedorがタスクずナヌザヌ間のすべおの可胜な接続を埩元し、それらをMarkdown圢匏のドキュメントに保存できる関数を䜜成し、Fedorがデヌタベヌスを新しいサヌバヌに埩元するのを支揎したす。

マヌクダりンドキュメントの圢匏は次のずおりです。

 ##  - %  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% 

タスクのリスト、タスクの゚グれキュヌタヌのリスト、タスクのオブザヌバヌのリスト、ナヌザヌのリスト、およびナヌザヌが実行するタスクのリストは、蟞曞匏に゜ヌトする必芁がありたす。

キャッシュ内の構造の説明


ナヌザヌは `User`型の構造ずしおキャッシュに保存されたす

 type User = { login: string; tasks: Task[]; spectating: Task[]; }; 

タスクはタむプ `Task`の構造ずしおキャッシュに保存されたす

 type Task = { title: string; assignee: User; spectators: User[]; subtasks: Task[]; parent: Task | null; }; 

゜リュヌションテンプレヌト


゜リュヌションには、次の眲名に察応する関数を゚クスポヌトするCommonJSモゞュヌルが含たれおいる必芁がありたす。

 /** * @param {User|Task} data -     , *        (User  Task) * @return {string} */ module.exports = function (data) { //   return '
'; } 

䟋


 //    const User1 = { type: 'user', login: 'fedor', tasks: [], spectating: [] }; const User2 = { type: 'user', login: 'arkady', tasks: [], spectating: [] }; //    const Task1 = { type: 'task', title: 'Do something', assignee: null, spectators: [], subtasks: [], parent: null }; const Task2 = { type: 'task', title: 'Something else', assignee: null, spectators: [], subtasks: [], parent: null }; const Task3 = { type: 'task', title: 'Sub task', assignee: null, spectators: [], subtasks: [], parent: null }; //    : //      Task1.assignee = User1; User1.tasks.push(Task1); // ...    —  Task1.spectators.push(User2); User2.spectating.push(Task1); //      , //       Task2.spectators.push(User1); User1.spectating.push(Task2); //      Task3.parent = Task2; Task2.subtasks.push(Task3); // ,     —  3 const lastEdited = Task3; 

`lastEdited`ぞのリンクを衚瀺するず、次の構造が埗られたす。

 { 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 

解析


, .

, , . , :

 /** *  ,     * @param {{ref: object, visited: ?boolean}} ctx * @param {object} handlers —     */ function traverse(ctx, handlers) { //    ,  ctx.ref , — ,     task.parent if (!ctx.ref) { return; } //   ,    ,       const visited = ctx.visited || new Set(); if (visited.has(ctx.ref)) { return; } visited.add(ctx.ref); //       handlers[ctx.ref.type](ctx.ref, goDeeper); //  ,       function goDeeper(subrefs) { //       for (const subref of [].concat(subrefs)) { traverse({ visited, ref: subref }, handlers); } } } //      const users = []; const tasks = []; // ref   —     traverse({ ref }, { user(user, deeper) { users.push(user); deeper(user.tasks); // to task.assignee deeper(user.spectating); // to task.spectators }, task(task, deeper) { tasks.push(task); deeper(task.assignee); // to user.tasks deeper(task.spectators); // to user.spectating deeper(task.parent); // to task.subtasks deeper(task.subtasks); // to task.parent } ); 

. , , 


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


 — :

 //    const taskLine = t => `${ t.title }${ t.assignee ? `,  ${t.assignee.login}` : '' }${ t.spectators.length ? `, : ${t.spectators.map(u => u.login).join(', ')}` : '' }`; function renderTasks (parent = null, indent = 0) { return tasks .filter(t => t.parent === parent) .map(t => [ '\n', ' '.repeat(indent), //  '- ', taskLine(t), //    t.subtasks.length ? printTasks(t, indent + 2) : '' //   ].join('')) .join(''); } function renderUsers () { return ${users.map(u => `\n- ${u.login}${ u.tasks.map(t => `\n * ${t.title}`).join('') }`).join('')} } const result = ` ##  ${renderTasks()} ##  ${renderUsers()} `.trim(); 

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

— . , , , .

-.

:
— 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(); //   ,   « »   digit app.get("/speeddial/:digit", (req, res) => { phone.getData().then(value => { const speeddialDict = JSON.parse(value); return phone.connect() .then(async () => { await phone.dial(speeddialDict[req.params.digit]); res.sendStatus(200); }, async() => { await phone.beep(BEEP_CODES.FATAL); res.sendStatus(500); }); }).catch(async (e) => { await phone.beep(BEEP_CODES.ERROR); res.sendStatus(500); }); }); //   « »   digit  phonenumber app.post("/speeddial/:digit/:phonenumber", (req, res) => { writeQueue.push(makeWriteJob(phone, req, res)); processQueue(); }); return app; }; exports.createApp = createApp; 

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 . 䟋









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> // JavaScript </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'); //           gameBox.addEventListener('click', ({ target }) => { //      if (!target.classList.contains('control')) { return; }; //      - const direction = target.className.match(/\bcontrol_direction_(\w+)/)[1]; //  ,   - map.className = map.className.replace(/\bmap__tilt_\w+/, `map__tilt_${direction}`); //  ,     let ball = map.querySelector('.map__cell_content_ball'); //       let nextBall = getNextCell(map, ball, direction); //      ball.classList.remove('map__cell_content_ball'); ball.classList.add('map__cell_content_empty'); //     ,            while ( !nextBall.classList.contains('map__cell_content_wall') && !ball.classList.contains('map__cell_content_exit') ) { ball = nextBall; nextBall = getNextCell(map, ball, direction); } //      ball.classList.remove('map__cell_content_empty'); ball.classList.add('map__cell_content_ball'); }); const DIRECTIONS = { 'left': [-1, 0], 'up': [0, -1], 'down': [0, 1], 'right': [1, 0] }; //  DOM API ,        function getNextCell(map, cell, direction) { const directionDiff = DIRECTIONS[direction]; return map.rows[cell.parentNode.rowIndex + directionDiff[1]].cells[cell.cellIndex + directionDiff[0]]; } 

— 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 = //     const sizes = getAllSizes(); //      // board —    //   —   ,   //  ,    const board = []; //    function placeNext(remains) { //    ... if (remains === 0) { // ... ,   ,   return board; } else { // ... //         // ,     const pos = getEmptyPos(); //        for (let i = 0; i < sizes.length; i++) { //  ,    const size = sizes[i]; //          //     (      //     !== 1),  null const layer = getLayer(pos, size); //     if (layer) { //     board.push(layer); //     const res = placeNext(remains - 1); //  ,  if (res) return res; //      //    board.pop(); } } } } //   return placeNext(n); } 

12. -



X . VCS , .

, -. — , .

, . , , . .

, . ( ) .

.



. — 1000, - — 20.

:

 type PullRequest = { /** *     ( ) *   N: 1 <= N <= 1000 */ files: string[], /** *     VCS */ id: string, /** * Unix-timestamp  - */ created: number, } 

(created) (id) – .



CommonJS- :

 /** * @param {PullRequest[]} pullRequests  PR,     * @returns {string[]}      */ module.exports = function (pullRequests) { //   } 



NodeJS v9.11.2. .



 function mergeAllPRs(prs) { /* solution */ } 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);  } } /** *     PR (    ) */ function doPRsConflict(pr1, pr2) {  const i = prToIndex.get(pr1);  const j = prToIndex.get(pr2);  return conflictMatrix[i * prs.length + j] === 1; } 

«» , . ( ) , . , , - .

, , .

 /** *     prsSet,           */ 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++;  //  ,            //   ,      const safeToMergePRs = getNonConflictingPRs(prsSet, mergedPrs);  mergedPrs = mergedPrs.concat(safeToMergePRs);  safeToMergePRs.forEach((pr) => {      prsSet.delete(pr);      mergedFilesCount += pr.files.length;  });  const pr = prsSet.values().next().value; // ...      

.

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


All Articles