Привет, Хабр!



Три недели назад мы (дружная команда образовательного проекта Хекслет) опубликовали пост про наш новый проект — игру для программистов Codebattle. Напомню, идея игры очень простая: вам и сопернику дается задача, вы решаете ее на выбранном вами языке. Вы видите код соперника в реальном времени, результаты запуска тестов и можете общаться с ним и зрителями в чате. Кто первый решит задачу (удовлетворит тестам) — тот победил.

После публикации статьи у нас был веселый день. Легли под хабраэффектом и обнаружили несколько багов. Когда буря успокоилась, на арене снова закипела жизнь. В течение следующих двух недель мы решали три главные проблемы: производительность, борьба с читерством и возможность быстрого добавления новых языков. И решили их!

Встречайте — обновленный Codebattle! Вкратце:

  1. Хабраэффект нам не страшен (тьфу-тьфу-тьфу)
  2. Читерить больше не получится (нельзя подогнать решение под тесты)
  3. Добавлять языки стало проще (сейчас уже есть clojure, ruby, js, python, php, java, erlang)

Подробности под катом >

Почему лежали и как решили


Мы работали через поллинг, что генерировало тысячи запросов в минуту. Теперь все переписали через websockets. Еще нашелся баг в библиотеке nkdocker.

Читерство и языки


В предыдущей версии была такая система:
  1. Пишем задание и тесты на Clojure
  2. Транслируем на целевые языки нашей библиотекой multicode.
  3. Показываем сгенерированные тесты игроку

Естественно, нашлись люди, которые просто подгоняли код под тесты не решая саму задачу. К тому же, такая схема усложняла добавление новых языков.

В обновленной версии другая схема: мы просто работаем через стандартные потоки ввода/вывода stdin/stdout, и не привязываемся к конкретному языку. Теперь мы генерируем тесты при проверке и не показываем их игроку. Система стала намного проще: мы просто подаем в вашу программу сгенерированные данные и смотрим на stdout. Это же позволило упростить добавление новых языков, мы успели добавить Clojure, Java и Erlang.

Из мелочей: добавили кучку новых заданий, обновили описание во всех заданиях, обновили расширение для Хрома.

Как добавить новое задание

Хотите добавить новое задание в базу и прокачаться в Clojure? ;-) В нашем репозитории с задачами есть подробное README и наглядные примеры.

В нашем Слак-чате есть специальный канал #codebattle, где можно обсудить игры, проблемы и идеи.

Комментарии (58)


  1. hedgehog
    27.10.2015 12:35
    +1

    Зря спрятали тесты совсем. Иногда они помогают лучше понять задание. Неплохо бы пару тестов показывать, но при проверке задания во внимание их не принимать.


    1. freetonik
      27.10.2015 12:46
      +4

      Мы показываем один пример из теста рядом с описанием задания:


    1. ev42
      27.10.2015 13:18

      Пока плохо спрятали, к вечеру обещают закрыть серьёзнее =)


      1. mekegi
        27.10.2015 14:15
        -1

        как то вот так

        1. read(x)
        2. sendToMyOwnServerViaInternet(x)
        3.…
        4. profit


      1. FallDi
        27.10.2015 22:06
        +2

        вы про это?)
        image


        1. FallDi
          27.10.2015 22:52
          +3

          Проверил на большинстве задач, уникальное решение для php примерно такое =)

          <?php
          
          function solution($a) {
              $tests = file_get_contents('data.jsons');
              echo $tests;
              $tests = explode("\n", $tests);
              foreach ($tests as $test) {
                  $test = json_decode($test, true);
                  $arg = $test['arguments'];
                  if (is_array($test['arguments'])) {
                  	$arg = $test['arguments'][0];
                  }
                  if ($arg == $a) {
          	    	return $test['expected'];
                  }
              }
          }
          


          1. toxicmt
            28.10.2015 06:17

            Закончилась халява)


  1. NeX
    27.10.2015 12:54
    +2

    Добавьте нотификации перед началом игры
    Ждешь игру, перключаешься на другую вкладку, возвращаешься — уже проиграл


    1. toxicmt
      27.10.2015 13:05
      +2

      Звуковую?


      1. freetonik
        27.10.2015 13:07
        +5

        + можно мигающую фавиконку


      1. NeX
        27.10.2015 13:15

        Да


  1. bogus92
    27.10.2015 13:51
    +3

    Было бы неплохо, чтобы работал ES6 синтаксис в JavaScript — он более короткий и если кодить на время, то это важно :)


    1. toxicmt
      27.10.2015 17:14

      Он частично работает, там node 4.2.1


  1. devpony
    27.10.2015 13:53
    +1

    Добавьте haskell. Кстати, кому жалко тратить по 10$ в месяц хочу посоветовать stepic.org.


    1. toxicmt
      27.10.2015 13:59

      Хаскель в процессе. У степика нет такой практики как у нас ;)


  1. xytop
    27.10.2015 13:56

    У вас тесты на некоторых задачах неверные. Из-за них верные решения не проходят. Нужно добавить кнопку, что-то типа «Пожаловаться».

    Например:

    У меня ответ [-1, -1]
    А ваш assert ожидает [1, 1]
    Хотя и то и то выдают одинаковое произведение


    1. toxicmt
      27.10.2015 13:57

      Ага спасибо, посмотрим.


      1. mannaro
        27.10.2015 14:33

        или у меня ответ [7, 6], а ваш assert ждет [6, 7]


      1. Core2Duo
        27.10.2015 20:32

        Задача key_for_min_value тоже видимо не слишком правильная.

        Given a hash map, return the key of the element with the smallest value.

        AssertionError: 'religion' != 'surprise'
        — religion
        + surprise
        : Arguments was: [{'surprise': 1, 'paper': 5, 'religion': 1, 'food': 2}]

        Два элемента с одинаковым минимальным значением, один почему-то «неправильный»


      1. ruikarikun
        27.10.2015 22:09

        Те же проблема со списком анаграм.


  1. xytop
    27.10.2015 14:27
    +2

    У вас в задачах есть ссылка на github, и там выложено решение на Clojure внизу… не дает ли это преимущество тем кто выбирает Clojure и затем копипастит решение?


    1. toxicmt
      27.10.2015 14:38
      +1

      Мы таким образом хотим познакомить программистов с кложей. Реально на ней играет пока очень мало игроков.


  1. ISanych
    27.10.2015 16:09

    Bugreport: en.hexlet.io/users/new — last surname


    1. freetonik
      27.10.2015 16:33

      Поправили, спасибо, в следующем деплое обновится на сайте.


  1. mihmig
    27.10.2015 17:15

    Предлагаю сделать так:
    Решил первым: 3 очка
    Решил вторым: 2 очка
    Решил позже таймаута: 1 очко.
    (очки накапливаются, рейтинги там и все дела, но потихоньку тают, дабы лидерство поддерживать)


  1. mihmig
    27.10.2015 17:16
    +1

    Открытые игры быстро «дёргаются», красивее было бы сделать fadein(out)


    1. toxicmt
      27.10.2015 20:19

      Сделаем


  1. mihmig
    27.10.2015 17:23

    Пользователи частенько «отваливаются». Может добавить возможность «подхватить упавшее знамя»?


  1. berezuev
    27.10.2015 17:49

    Имхо, нужно давать возможность проверять свое решение даже после поражения (как бы, вне игры).
    Даже если я проиграл, я привык доходить до конца. И, интересно, справился ли я в итоге.


    1. toxicmt
      27.10.2015 20:19

      Так и работает же. Всегда можно доиграть.


      1. berezuev
        28.10.2015 11:23

        Не срабатывает кнопка «проверить», пишет, что игра закончена уже


        1. toxicmt
          28.10.2015 20:45

          Вы уверены что под этой надписью не появляется новый вывод? Просто эта надпись всегда сверху висит. Ну и нет там ограничений в коде да и по играм видно что оба соперника доигрывают.


  1. xytop
    27.10.2015 21:27
    +2

    > Хабраэффект нам не страшен (тьфу-тьфу-тьфу)
    Вроде как баттл лежит


    1. toxicmt
      27.10.2015 22:14

      Не лежит, но заметно лагал. Щас все снова в норме.


      1. xytop
        27.10.2015 22:35

        нет, не в норме.. Хотя возможно это относится только к ruby vm…


        1. toxicmt
          27.10.2015 22:42

          Да(, исполнение кода это таки не странички грузить.


          1. xytop
            27.10.2015 23:07

            Ну вот я щас сижу с человеком. У него PHP, у меня Ruby. У него PHPUnit работает, а у меня сплошные таймауты.
            К слову о нагрузке… тот же codingame.com держит тысячи пользователей онлайн без проблем… наверное у вас архитектурно что-то неправильно сделано.


            1. toxicmt
              27.10.2015 23:16
              +1

              Круто вы конечно сравниваете кто сколько держит. У них это весь сервис под которым целая инфраструктура, у нас виртуалка с одним ядром.


  1. CharnaD
    27.10.2015 21:56
    +1

    А нормально так, парень скопипастил решение откуда-то за 10 секунд и был таков…


    1. romario13
      29.10.2015 15:58

      Тоже самое только что было. Сейчас думаю покодю — опа и вы проиграли! :)


  1. hedgehog
    27.10.2015 23:18

    универсальная решалка для пыха:

    function solution(){
        $f = function($json){return json_decode($json, true);};
        
        $data = array_map($f, file('data.jsons'));
        foreach($data as $row){
        	if ($row['arguments'] == func_get_args()){
                return $row['expected'];
                
            }
        }
    }
    


    1. hedgehog
      27.10.2015 23:36

      хм, выше было уже оказывается


      1. toxicmt
        28.10.2015 06:14

        Все, нету больше чтений data.json


  1. CharnaD
    28.10.2015 01:14
    +1

    build_hash_with_default на пхп решается одной array_fill_keys


    1. Core2Duo
      28.10.2015 12:46

      Эта задача много где решается в одну строку. На питоне:

      return {k:d for k in a}
      

      (d — default, a — array)


  1. Shchvova
    28.10.2015 02:35

    Я попробовал было. Задачка «транспонируйте матрицу». Я пишу себе на питоне. Потом замечаю что опонент на руби написал что-то типа m.transpose() за первые 3 секунды. Ну ок. Я вообще не понимаю зачем эти все соривнования, опоненты, потому больше не играл.


    1. funca
      28.10.2015 11:42
      +2

      Инструмент надо выбирать под задачу. А на питоне меньше букв :)

      zip(*m)
      
      ( stackoverflow.com/a/4937526 )


  1. Daniro_San
    28.10.2015 09:23

    Жаль что нет ни C++ ни C#.
    Интересно, и дальше не будет?


  1. tmnhy
    28.10.2015 11:26
    +1

    Несколько пожеланий:
    — мало задачек, часто повторяются, для одного ЯП — на вечер развлечение, потом неинтересно;
    — как-то надо учитывать фичи ЯП, например для «separate_with_comma» на питоне решение «return '{0:,}'.format(arg)», задание, думаю, подразумевает более низкоуровневое решение;
    — и да, как писали выше, есть задачки с несколькими возможными решениями, и как я понимаю верным считается первое, а у питона, так как нет порядка в ключах словаря, может быть ответ удовлетворяющий условию, т.е. верный, но не совпадающий с тестовым и приходится тыкать на «проверить» пока тест подходящий не сработает.


    1. Core2Duo
      28.10.2015 12:51

      О, спасибо за крутое решение separate_with_comma. Я как-то так извращался:

      def solution(s):
          s = s[::-1]
          p = [s[i:(i + 3)] for i in range(0, len(s), 3)]
          s = ','.join(p)
          return s[::-1]
      


      1. tmnhy
        28.10.2015 17:56

        Там таких задачек много, есть что на php в одну строку.

        Опять же на питоне подсчет количества вхождений элементов массива сводится к банальному:

        def solution(arg):
          from collections import Counter
          return Counter(arg)
        


        Это несколько нечестно.


        1. M_Muzafarov
          28.10.2015 18:40
          +1

          def solution(a):
            return {k: a.count(k) for k in a}
          

          Даже меньше на строчку. и вполне честно.


    1. toxicmt
      28.10.2015 20:47
      +2

      Задач будет больше. Их решают с такой скоростью что мы добавлять не успеваем)


      1. tmnhy
        29.10.2015 15:35

        Задачки новые сть — это хорошо. А вот «Timeout error has occurred» замучил сосвсем. Побеждает не тот, кто первый, а кому повезло что тесты отработали.


  1. M_Muzafarov
    28.10.2015 12:23

    Непонятно, когда отправил задачку на проверку — сработала кнопка или нет. Надо какую-то обратную связь, чтоб не слать по несколько раз. Болеетого, если отправил, потом дописал, потом снова отправил, потом снова дописал — если приходит ответ с первой отправки — может откатить код на несколько шагов назад. Это неприятно.

    Потом в какой-то момент после нескольких infinite loop/slow script — сервер вообще перестал что либо принимать. Потом в консоли браузера стало появляться много ошибок о дублирующемся ключе. Обновил вкладку, не с первого раза пустило, задачу на проверкуне принимало, потом вообще написали мне, что игра не существует.

    Safari, Mac os x 10.11

    А в целом идея интересная, если не превратится в очередной codeforces. пока задачки не академические — дух соперничества подстегивает играть))


  1. WingedBoar
    28.10.2015 15:47

    Сделайте, пожалуйста, чтобы окна с кодом изменяли размер, но всегда остовались бок о бок, а не выпихивали друг-друга. В данный момент окошки всегда по ширине текста и зачастую выпихивают друг-друга вниз экрана (на Огнелисе, на других не знаю).

    Ещё было бы хорошо, если бы вы учитывали не только скорость написания, но и производительность написанной программы. Не знаю, какой-нибудь лидерборд на самый быстрый алгоритм или что-то подобное, подумайте. Написать по-другому — это по-крайней мере хоть какой-то стимул проходить уже пройденное задание снова.


    1. WingedBoar
      28.10.2015 16:01

      + Сделайте ещё какое-нибудь пенальти за брошенные игры. Типа 10 минут в рид-онли не можешь создать или присоединиться к игре.


  1. ketrin7
    28.10.2015 21:28
    +1

    Спасибо за python, php. Особенно за добавление php, т.к. для python я находила игры. Для php встречаю в первый раз.