Практические уроки по программированию
65,54
рейтинг
27 октября 2015 в 11:52

Разработка → Обновленный Codebattle: игра для программистов

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



Три недели назад мы (дружная команда образовательного проекта Хекслет) опубликовали пост про наш новый проект — игру для программистов 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, где можно обсудить игры, проблемы и идеи.
Автор: @freetonik
Hexlet
рейтинг 65,54
Практические уроки по программированию
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

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

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

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

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

      • +2
        вы про это?)
        image
        • +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'];
                  }
              }
          }
          
          • 0
            Закончилась халява)
  • +2
    Добавьте нотификации перед началом игры
    Ждешь игру, перключаешься на другую вкладку, возвращаешься — уже проиграл
    • +2
      Звуковую?
      • +5
        + можно мигающую фавиконку
        • 0
          или alert
      • 0
        Да
  • +3
    Было бы неплохо, чтобы работал ES6 синтаксис в JavaScript — он более короткий и если кодить на время, то это важно :)
    • 0
      Он частично работает, там node 4.2.1
  • +1
    Добавьте haskell. Кстати, кому жалко тратить по 10$ в месяц хочу посоветовать stepic.org.
    • 0
      Хаскель в процессе. У степика нет такой практики как у нас ;)
  • 0
    У вас тесты на некоторых задачах неверные. Из-за них верные решения не проходят. Нужно добавить кнопку, что-то типа «Пожаловаться».

    Например:

    У меня ответ [-1, -1]
    А ваш assert ожидает [1, 1]
    Хотя и то и то выдают одинаковое произведение
    • 0
      Ага спасибо, посмотрим.
      • 0
        или у меня ответ [7, 6], а ваш assert ждет [6, 7]
      • 0
        Задача 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}]

        Два элемента с одинаковым минимальным значением, один почему-то «неправильный»
      • 0
        Те же проблема со списком анаграм.
  • +2
    У вас в задачах есть ссылка на github, и там выложено решение на Clojure внизу… не дает ли это преимущество тем кто выбирает Clojure и затем копипастит решение?
    • +1
      Мы таким образом хотим познакомить программистов с кложей. Реально на ней играет пока очень мало игроков.
  • 0
    Bugreport: en.hexlet.io/users/new — last surname
    • 0
      Поправили, спасибо, в следующем деплое обновится на сайте.
  • 0
    Предлагаю сделать так:
    Решил первым: 3 очка
    Решил вторым: 2 очка
    Решил позже таймаута: 1 очко.
    (очки накапливаются, рейтинги там и все дела, но потихоньку тают, дабы лидерство поддерживать)
  • +1
    Открытые игры быстро «дёргаются», красивее было бы сделать fadein(out)
    • 0
      Сделаем
  • 0
    Пользователи частенько «отваливаются». Может добавить возможность «подхватить упавшее знамя»?
  • 0
    Имхо, нужно давать возможность проверять свое решение даже после поражения (как бы, вне игры).
    Даже если я проиграл, я привык доходить до конца. И, интересно, справился ли я в итоге.
    • 0
      Так и работает же. Всегда можно доиграть.
      • 0
        Не срабатывает кнопка «проверить», пишет, что игра закончена уже
        • 0
          Вы уверены что под этой надписью не появляется новый вывод? Просто эта надпись всегда сверху висит. Ну и нет там ограничений в коде да и по играм видно что оба соперника доигрывают.
  • +2
    > Хабраэффект нам не страшен (тьфу-тьфу-тьфу)
    Вроде как баттл лежит
    • 0
      Не лежит, но заметно лагал. Щас все снова в норме.
      • 0
        нет, не в норме.. Хотя возможно это относится только к ruby vm…
        • 0
          Да(, исполнение кода это таки не странички грузить.
          • 0
            Ну вот я щас сижу с человеком. У него PHP, у меня Ruby. У него PHPUnit работает, а у меня сплошные таймауты.
            К слову о нагрузке… тот же codingame.com держит тысячи пользователей онлайн без проблем… наверное у вас архитектурно что-то неправильно сделано.
            • +1
              Круто вы конечно сравниваете кто сколько держит. У них это весь сервис под которым целая инфраструктура, у нас виртуалка с одним ядром.
  • +1
    А нормально так, парень скопипастил решение откуда-то за 10 секунд и был таков…
    • 0
      Тоже самое только что было. Сейчас думаю покодю — опа и вы проиграли! :)
  • 0
    универсальная решалка для пыха:
    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'];
                
            }
        }
    }
    
    • 0
      хм, выше было уже оказывается
      • 0
        Все, нету больше чтений data.json
  • +1
    build_hash_with_default на пхп решается одной array_fill_keys
    • 0
      Эта задача много где решается в одну строку. На питоне:

      return {k:d for k in a}
      

      (d — default, a — array)
  • 0
    Я попробовал было. Задачка «транспонируйте матрицу». Я пишу себе на питоне. Потом замечаю что опонент на руби написал что-то типа m.transpose() за первые 3 секунды. Ну ок. Я вообще не понимаю зачем эти все соривнования, опоненты, потому больше не играл.
    • +2
      Инструмент надо выбирать под задачу. А на питоне меньше букв :)
      zip(*m)
      
      ( stackoverflow.com/a/4937526 )
  • 0
    Жаль что нет ни C++ ни C#.
    Интересно, и дальше не будет?
  • +1
    Несколько пожеланий:
    — мало задачек, часто повторяются, для одного ЯП — на вечер развлечение, потом неинтересно;
    — как-то надо учитывать фичи ЯП, например для «separate_with_comma» на питоне решение «return '{0:,}'.format(arg)», задание, думаю, подразумевает более низкоуровневое решение;
    — и да, как писали выше, есть задачки с несколькими возможными решениями, и как я понимаю верным считается первое, а у питона, так как нет порядка в ключах словаря, может быть ответ удовлетворяющий условию, т.е. верный, но не совпадающий с тестовым и приходится тыкать на «проверить» пока тест подходящий не сработает.
    • 0
      О, спасибо за крутое решение 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]
      
      • 0
        Там таких задачек много, есть что на php в одну строку.

        Опять же на питоне подсчет количества вхождений элементов массива сводится к банальному:
        def solution(arg):
          from collections import Counter
          return Counter(arg)
        


        Это несколько нечестно.
        • +1
          def solution(a):
            return {k: a.count(k) for k in a}
          

          Даже меньше на строчку. и вполне честно.
    • +2
      Задач будет больше. Их решают с такой скоростью что мы добавлять не успеваем)
      • 0
        Задачки новые сть — это хорошо. А вот «Timeout error has occurred» замучил сосвсем. Побеждает не тот, кто первый, а кому повезло что тесты отработали.
  • 0
    Непонятно, когда отправил задачку на проверку — сработала кнопка или нет. Надо какую-то обратную связь, чтоб не слать по несколько раз. Болеетого, если отправил, потом дописал, потом снова отправил, потом снова дописал — если приходит ответ с первой отправки — может откатить код на несколько шагов назад. Это неприятно.

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

    Safari, Mac os x 10.11

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

    Ещё было бы хорошо, если бы вы учитывали не только скорость написания, но и производительность написанной программы. Не знаю, какой-нибудь лидерборд на самый быстрый алгоритм или что-то подобное, подумайте. Написать по-другому — это по-крайней мере хоть какой-то стимул проходить уже пройденное задание снова.
    • 0
      + Сделайте ещё какое-нибудь пенальти за брошенные игры. Типа 10 минут в рид-онли не можешь создать или присоединиться к игре.
  • +1
    Спасибо за python, php. Особенно за добавление php, т.к. для python я находила игры. Для php встречаю в первый раз.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка