الحاجة إلى السرعة: تحدي ترميز جافا سكريبت Toptal بأثر رجعي

نشرت: 2022-03-11

بدأ Toptal تطبيق الويب لتحدي ترميز JavaScript كوسيلة لجذب الأشخاص إلى أكشاك المؤتمرات الخاصة بنا. نظرًا لمدى نجاحها ، قررنا إنشاء إصدار تجريبي على الويب ، مفتوح للجميع في مجتمعنا وشبكاتهم.

لقطة شاشة لتحدي ترميز جافا سكريبت لسرعة جافا سكريبت من Toptal ، تعرض أحد الأسئلة الأولى. يتم توفير وظيفة تسمى "double" ، ويتكون جسمها من تعليق ، "إرجاع x مضاعف".

في وقت الإطلاق ، شجعنا المطورين المتحمسين على ابتكار طرق مبتكرة لتحقيق درجات عالية في تحدي ترميز JavaScript الشامل. تتمثل بعض عوامل النجاح الرئيسية لأصحاب العمل المستقلين العظماء في القدرة على التفكير خارج الصندوق وإيجاد طرق إبداعية للعمل ضمن مجموعة من القيود.

أسئلة تحدي ترميز جافا سكريبت

يتكون التحدي من عدد من أسئلة JavaScript - مشابهة لتلك التي يمكن استخدامها كأسئلة مقابلة - بدءًا من أسئلة تحدي JavaScript الأساسية حقًا:

 box.double = function double (x) { //return x doubled };

... إلى المتوسطة:

 box.dateRank = function dateRank (x) { // x is a date in 2019 as string (example: "06/30/2019") // return the rank of the day in 2019 (ie, "09/01/2019" translates to 244) };

أردنا للمطورين المبتدئين والمتقدمين الاستمتاع بدعوة أصدقائهم وزملائهم للتنافس معهم للحصول على أعلى الدرجات. تم فرز الأسئلة حسب النقاط ، بحيث يمكن حتى للمطورين المبتدئين تسجيل بعض النقاط. كان ترتيب الأسئلة التي تحتوي على نفس القدر من النقاط عشوائيًا ، لذا كانت التجربة مختلفة قليلاً في كل مرة ، بينما بقيت المجموعة الكاملة من الأسئلة كما هي طوال الأسبوع.

كان الهدف هو إكمال أكبر عدد ممكن من المهام في غضون ثلاث دقائق. في حالة ما إذا وجد شخص ما طريقة لإكمال جميع المهام في تحدي ترميز JavaScript ، فسيتم منح 10 نقاط لكل ثانية متبقية. سمحنا بمحاولات متعددة لإكمال التحدي.

أول شيء توقعناه سيحدث هو أن الناس سيستغرقون بعض الوقت لحل الأسئلة بوتيرة مهل ، ثم نسخ الإجابات ولصقها في تطبيق الويب.

بعد ساعة و 40 دقيقة من بدء التحدي ، اتبع الشخص الأول هذا النهج وحصل على 1445 نقطة كحد أقصى التي سمح بها التطبيق ، بالإضافة إلى بعض النقاط الإضافية التي منحناها لكل ثانية متبقية.

نقطة تحول في تحدي تشفير جافا سكريبت

باستخدام نهج النسخ واللصق ، لم يكن لدى كبار المتسابقين ما يكسبونه من التركيز على كتابة الإجابات بأنفسهم. وبدلاً من ذلك ، حولوا انتباههم إلى زيادة السرعة في مهاراتهم في الأتمتة.

كانت أسهل طريقة في هذه المرحلة هي كتابة بعض JavaScript التي من شأنها حل كل مهمة أثناء الانتظار في حلقة حتى تصبح الأزرار جاهزة ، ونسخها ولصقها في وحدة تحكم المتصفح:

 const solutions = { 'double': 'return x*2', 'numberToString': '...', 'square': '...', 'floatToInt': '...', 'isEven': '...', 'squareroot': '...', 'removeFirstFive': '...', // ... 'dateRank': '...', // ... }; const get_submit_button = () => document.querySelector('.task-buttons > .col > .btn'); const solve = () => { const ace_editor = ace.edit(document.querySelector('.ace_editor')) const submission = ace_editor.getValue() for (const key in solutions) { if (submission.includes('box.' + key + ' ')) { ace_editor.insert(solutions[key]) get_submit_button().click() setTimeout(() => { get_submit_button().click() setTimeout(() => { solve() }, 400) }, 900) return } } } solve()

يمكن أيضًا أتمتة هذا الجزء باستخدام أدوات أتمتة منتظمة مثل السيلينيوم. ولكن الطريقة الأسرع هي أتمتة استخدام واجهة برمجة التطبيقات ، وإرسال الحلول إلى المهام:

 const request = require('request'); const runTask = (data, entryId, callback) => { const tests = data.nextTask.tests_json; const results = Object.fromEntries( Object.entries(tests).map(([key, value]) => [key, value.result]) ); request.post(`https://speedcoding.toptal.com/webappApi/entry/${entryId}/attemptTask`, { form: { attempt_id: data.attemptId, tests_json: JSON.stringify(results), }, }, (error, res, body) => { if (error) throw error; const next = JSON.parse(body).data if (next.isChallengeEntryFinished) { callback(next) return } runTask(next, entryId, callback) }); } const runEntry = (callback) => { request.post('https://speedcoding.toptal.com/webappApi/entry', { form: { challengeSlug: 'toptal-speedcoding', email: ..., leaderboardName: ..., isConfirmedToBeContacted: ..., dateStop: ... }, }, (error, res, body) => { if (error) throw error; const { data } = JSON.parse(body); const entryId = data.entry.id runTask(data, entryId, callback) }); } runEntry(console.log)

شيء واحد يجب ملاحظته هو أنه في هذا الإصدار من تحدي التشفير السريع ، تم اختبار الكود من جانب العميل فقط. لهذا السبب ، كان من الممكن ببساطة إرسال الإجابات على حالات الاختبار بدلاً من الكود. سمح ذلك بالتحسين وقطع بعض المللي ثانية من جانب العميل.

بعد ثلاثة أيام ، بدأت المنافسة تحتدم بالفعل ، حيث ارتفع عدد المحاولات لكل دلو مدته ثلاث ساعات فجأة من أقل من 1000 إلى ما يقرب من 100000.

لمدة ثلاثة أيام ، ظلت الدرجات كما هي. كان بعض الأشخاص يكتبون تحسينات صغيرة لشفراتهم ، وكان العديد من الأشخاص يقدمون حلولهم في حلقة ، على أمل أن يصبح الخادم أقل ازدحامًا حتى يتمكنوا من الحصول على بضع نقاط. كنا مستحقين لمفاجأة كبيرة.

كسر الحد الأقصى من نقاط تحدي تشفير جافا سكريبت

أولاً ، لنقم ببعض العمليات الحسابية السريعة: لقد حصلنا على إجمالي 1445 نقطة لإكمال جميع المهام ، ومخصص زمني قدره 180 ثانية. إذا منحنا 10 نقاط في الثانية المتبقية في التطبيق ، فسيكون الحد الأقصى للنتيجة النظرية القابلة للتحقيق 3245 - في حالة إرسال جميع الإجابات على الفور.

حقق أحد مستخدمينا أكثر من 6000 درجة ، والتي استمرت في النمو بشكل مطرد بمرور الوقت.

كيف يمكن لشخص أن يحصل على هذه الدرجة العالية؟

بعد مراجعة موجزة ، وجدنا ما كان يحدث. وجد أفضل متسابق لدينا ، وهو مطور محترف كامل المكدس و Toptaler مع أكثر من 15 عامًا من الخبرة في البرمجة التنافسية ، ثغرة في إعداد تحدي البرمجة. لقد أنتج روبوتات متعددة ، مما أدى إلى إبطاء الخادم ؛ في غضون ذلك ، يمكنه إكمال نفس المهمة (المهمة التي منحت أكبر عدد من النقاط) عدة مرات قدر الإمكان وتعيين درجاتهم إلى إدخال واحد ، مع الإضافة باستمرار إلى درجة ذلك الإدخال.

لم يكن هذا مخالفًا للقواعد ، حيث سمحنا بالحلول الإبداعية ؛ ومع ذلك ، فإن الطريقة المحددة التي كان يستخدمها تسببت في أن يكون الخادم أكثر انشغالًا بشكل كبير ، مما يجعل طلبات الشبكة أبطأ لأي شخص آخر. أول شيء قمنا به هو زيادة قوة الخادم ، مما جعله ينتقل فقط من 56000 إلى 70000 نقطة ويبقى في المقام الأول.

على الرغم من أننا لم نرغب في التدخل في الطريقة التي يتفاعل بها الأشخاص مع التحدي ، فقد أدت هذه المحاولات إلى إبطاء الخادم إلى الحد الذي يصعب معه استخدام التحدي للمستخدمين الآخرين ، لذلك قررنا إصلاح الثغرة.

جعل الإصلاح من المستحيل على الأشخاص الآخرين تحقيق نفس النتيجة خلال اليوم الأخير من تحدي ترميز JavaScript. لهذا السبب ، قررنا زيادة عدد الجوائز الممنوحة لكبار المتسابقين. في الأصل ، كان من المفترض أن تذهب الجائزة الأولى - زوج من AirPods - إلى المتسابق الأول فقط. في النهاية ، تم منح AirPods لمن يحتلون المراكز الستة الأولى.

بدايات متواضعة ونهايات شرسة: بعض إحصائيات تحدي ترميز جافا سكريبت

حتى أعلى الهدافين لدينا واجهوا بعض الصعوبة في البدء. في الواقع ، من بين جميع الأشخاص الذين أجروا خمس محاولات أو أكثر ، كانت أعلى نتيجة لمحاولة أي شخص الأولى هي 645 ، وكان متوسط ​​النتيجة للمحاولات الأولى في تلك المجموعة 25 نقطة فقط.

بحلول نهاية المسابقة ، يمكن للمرء أن يخمن الاستراتيجية التي تتم تجربتها من إجمالي عدد المحاولات. في حين أن البعض كان أكثر كفاءة من البعض الآخر ، فإن المتسابق الأبرز بعيدًا وبعيدًا كان لديه أعلى عدد من المحاولات.

رسم بياني شريطي لوغاريتمي مُجمع يُظهر أعلى نقاط 20 متسابقًا وإجمالي عدد المحاولات. بينما ذهب عدد المحاولات الأعلى إلى الهداف ، ذهبت ثاني وثالث ورابع أعلى عدد من المحاولات إلى المتسابقين في المركز الثالث عشر والتاسع والثاني ، على التوالي. نصف أفضل 20 متسابقًا بالضبط ، بما في ذلك المتسابقون في المركزين الرابع والسادس ، كان لديهم أقل من 1000 محاولة. خاض متسابقان أقل من 100 محاولة ، وكان آخر أقل من 10 محاولات.

تحرك للأمام

ماذا يحمل المستقبل؟

كان هذا فقط أول تكرار لتحدي ترميز JS. نريد القيام بالعديد من تحديات برمجة JavaScript في المستقبل ، وتعلم الدروس من عمليات التشغيل السابقة ، وجعلها أكثر إثارة. أول شيء نريد القيام به هو تنفيذ اختناق المحاولة للحد من عدد عمليات الإرسال. نريد أيضًا التوسع إلى ما هو أبعد من تحديات الترميز في JavaScript ، وجعلها متاحة في مجموعة واسعة من لغات البرمجة.

أخيرًا ، بينما نتخذ تدابير لجعل تحدي ترميز JavaScript أكثر أمانًا ، نخطط للاستمرار في السماح باستخدام الروبوتات وغيرها من الأساليب الإبداعية للتحديات المستقبلية.


شكر خاص لكل من Pavel Vydra و Anton Andriievskyi و Tiago Chilanti و Matei Copot على مساهماتهم في التحدي وهذه المقالة ، وإلىZirak على المشروع المفتوح المصدر الذي شكل أساس تطبيق المسابقة. وبالمثل ، شكرًا لكل من شارك في المسابقة وأدارها.