skiminok: (Default)
Ским окончательно решил, что пора бы перебираться на DreamWidth, когда потихоньку пошли слухи, что ЖЖ якобы отныне ограничивает длину поста до 3000 с чем-то символов. Не хочу разбираться, правда это или нет: раз у кого-то не получается (по любым причинам), то есть шанс, что не получится и у меня. Ну на фиг.

Зато теперь возник вопрос. Френдз, кто-то может помочь мне адаптировать стиль журнала к тому, который у меня сейчас в ЖЖ? Все возможные туторилы по всем этим слоям, темам, S2 и прочей ереси я перерыл, однако, в силу того что в CSS я ни в зуб ногой (в чем мне нисколечко не стыдно) — вышло чуть более чем ни черта. Взываю о помощи дизайнера :)

(no subject)

Saturday, 25 June 2011 02:06
skiminok: (Compas)
The programming article every one should read:
Joel Spolsky, "The Perils of Java Schools"

eng: http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html
rus: http://habrahabr.ru/blogs/java/122665/
skiminok: (xkcd)

Предыстория


Поучаствовать в самом знаменитом нестандартном ежегодном контесте по программированию ICFPC я лелеял мечту последние три года, с тех пор как узнал про его существование. К сожалению, его традиционно проводят в июне, и все прошлые годы он попадал мне аккурат на сессию — причем проблемную, к которой приходилось готовиться. Теперь же, в 2011 г., с сессией абсолютно никаких проблем у меня не наблюдалось, и я решил-таки собрать команду.

С командой вышел пробой (забегая наперед, он и определил наши результаты). Я очень хотел, чтобы все тиммейты были в Киеве, чтобы мы могли лично собраться и устроить брейнсторм с ноутами на три дня :) Среди моих знакомых умных людей с разными знаниями и умениями было дофига (а как показывает ICFPC, никогда не знаешь, кто именно тебе понадобится — физик, математик или биолог, так что я решил собрать всех). Как выяснилось в процессе поисков за 2 месяца до контеста, у одного умного человека — диплом, у другого — сессия, у третьего — работа, у четвертого — отъезд в США на интерншип на неделю раньше меня... так что в итоге осталось 5 человек, включая меня. В отчете остальные фигурируют как Саня, Сергей, Андрей и Виталик.

Буквально вечером в день перед контестом мне в аську постучался [livejournal.com profile] sharpc и спросил, не нужны ли нам вольные стрелки. Вольные стрелки нам, конечно же, были нужны :) Правда, на следующий день [livejournal.com profile] sharpc прочел задание, сказал, что он предпочитает раскапывание и реверс-инжиниринг (а-ля ICFPC 2010), а задача этого года его как-то не прет. Так мы и остались впятером.

Названий почти никто не генерировал, так что мы остановились на предварительном Code Cercopithecoids (это обезьяны, если что :) ). Правда, потом название вынужденно сменилось в процессе контеста на Bastard, см. далее ;) А пока что мы создали репозитарий на гитхабе, документ в sync.in и конференцию на jabber.ru. Было принято решение название нигде не светить, чтобы наш репозитарий не нагуглили, а то про-аккаунта на гитхабе ни у кого нет. Всеобщий язык - C#.

Задание


Вкратце, для тех, кто не читал: организаторы смешали M:tG с функциональным программированием и назвали то, что получилось, Lambda: The Gathering. Есть по 256 ячеек у каждого игрока, в каждой лежит значение (либо функция, либо число — которое, в принципе, есть нуль-арная функция) у каждой есть хп (от -1 до 65535, изначально 10000, ячейка с хп <= 0 считается мертвой). И есть набор из 15 видов карточек с функциями. За один ход можно выбрать свою ячейку, выбрать любую карту, и применить либо ячейку к карте, либо карту к ячейке, положив результат в ячейку (если действие занимает больше 1000 применений функций, оно обрывается). Выигрывает тот, кто либо вынес все ячейки противника, либо после 100000 ходов имеет больше живых. Карты можно кратко описать так:

Стандартные комбинаторы Шейнфинкеля для тьюринг-полноты:
 - I x = x
 - K x y = x
 - S f g x =
     h <- f x
     y <- g x
     return h y

Функции работы с числами:
 - zero = 0
 - succ x = x + 1
 - dbl x = x * 2

Функции работы с данными ячеек — своими и вражескими. Стоит заметить, что, чтобы передать индекс ячейки карте, его нужно сначала вычислить где-нибудь с помощью succ и dbl, так что ячейки с маленькими номерами становятся критично важными для жизни: вся существенная работа идет с их использованием. Чтобы жизнь малиной не казалась, организаторы сделали так, что обращение к вражеским ячейкам происходит не по номеру j, а по номеру 255-j, таким образом, чтобы добраться до жизненно важной для противника ячейки 0, нужно сначала набрать у себя число 255, а на это уходит время. На практике это ограничение мы потом хитро обошли :)
 - get i = f[i] // получить данные из своей ячейки i
 - put x = I // не использует аргумент. Используется для быстрой очистки своих ячеек.
 - inc i =
     ++v[i]; // увеличить хп своей ячейки на 1
     return I
 - dec j =
     --v'[255-j]; // уменьшить хп чужой ячейки на 1
     return I
 - copy j = f'[j] // получить данные из чужой ячейки j.
                  // Единственная карта, нарушающая правило "255-j"
 - revive i =
     v[i] <- 1 // оживить свою мертвую ячейку, установить хп в 1
     return I

Функции взаимодействия ячеек:
 - help i j n = // помочь одной своей ячейкой другой
     v[i] -= n;
     v[j] += 11*n/10;
     return I
 - attack i j n = // атаковать чужую ячейку за счет своего хп
     v[i] -= n;
     v'[255-j] -= 9*n/10;
     return I
 - zombie j x = // превратить чужую мертвую ячейку в зомби
     v'[255-j] <- -1
     f'[255-j] <- x
     return I

Карта zombie стоит особняком. Семантика её работы такова: если чужая ячейка уже мертва, то мы можем превратить её в одноразового зомби, записав в неё свой код. В начале каждого хода каждого игрока движок игры проходится по всем его картам, если находит зомби — выполняет его код на аргументе I, и после этого превращает карту в обычную мертвую (хп = 0, значение = I). Самое интересное то, что во время зомби-фазы семантика функций в коде зомби меняется — они начинают вредить противнику. А именно: inc не добавляет ему хп, а отнимает, dec не отнимает у нас хп, а добавляет, help отнимает у одной карты n хп, а у второй отнимает 11*n/10, attack отнимает у его карты n хп, а нашей прибавляет 9*n/10. Весело, правда? :)

Полная версия задания лежит на официальном сайте, если кому интересно. Там, кстати, умилительные картинки карт :) Организаторы постарались на славу, стиль M:tG выдержан.

Пятница, 17 июня


В период с 9 до 11 утра все, кроме Андрея, собирались у меня в квартире. Андрей мог подъехать только к часу. Впрочем, это было не очень существенно, поскольку мы все время тратили на разборки с заданием и принципами обращения с этой системой вообще. Обычно я сидел и объяснял суть карт одному человеку, потом в квартиру являлся еще один, все объяснения начинались сначала... и так до 12 дня. К полудню из чтения примеров и игр с эмулятором стало ясно следующее:
- нам нужно, чтобы кто-то сел и закодил движок-эмулятор правил игры, чтобы проигрывать в нем ходы свои и вражеские, и всегда знать, что творится на поле.
- разработать стандартные конструкции-комбинаторы в этой системе (типа "вечный цикл с непустым телом и полезными действиями", "применение слота к слоту" и пр.)
- разработать стратегию поведения бота.

Сергей с Виталиком сели за стратегию. Мы с Саней — за комбинаторы на бумажке. Движок писать никто не хотел, но он нам пока был не критичен, поэтому его отложили до приезда Андрея. Как я сейчас понимаю, это была стратегическая ошибка: из всей команды мы с Саней лучше всех понимали семантику поведения функций, а я — их ФП-подноготную, так что движок надо было сесть мне и закодить за 2 часа. Саня, как самый опытный геймер из всех, бегал периодически к Сергею, и они вместе спорили над стратегией, я, как не имеющий особого опыта в играх вообще, не вмешивался :0

Примерно два-три дня. Приезжает Андрей, мы в очередной раз объясняем ему тонкие моменты в правилах и садим за движок, предварительно согласовав типы данных. К этому моменту у нас готово следующее:

Вечный цикл, хранящийся по номеру index:
S (get) (S f I) index
f - по вызову тождественна с I

Чтобы предварительно получить данные из другой ячейки, нужно набрать ее номер и применить get — это операция копирования. Всунуть эту конструкцию в код так, чтобы она выполнялась при вызове, постоянно выбирая свежие данные ячейки, нереально: когда строится выражение, как только мы получаем закрытый терм, он тут же, собака, выполняется, и заменяется на результат. Так что обращаться к переменным "по ссылкам" мы так и не научились.

Вместо этого разработали так называемую "регистровую арифметику": ячейки 0, 1 отводятся под регистры, в них кладутся ценные данные (обычно — большие константы для help/attack, а также адреса других ячеек — двойная адресация), а потом при наборе выражения просто делается get 0 или get 1, и получаем кусочек формулы. С помощью этого подхода была разработана процедура Apply: применение слота к слоту:
APPLY i j:
* j -> #0
* get -> #0
* K -> #i
* S -> #i
* #i -> get
* #i -> zero


Вообще, конструкция "окружить что-то в К, а потом в S", используемая в комбинаторном исчислении, отлично помогала при реализации отложенных вычислений в цикле.

К этому моменту Сергей с Саней, долго ругаясь по поводу того, что и когда нам выгоднее делать, поняли, что можно делать help i i, что делает прирост хп на 1.1*n. При достаточно большом n, если засунуть это в цикл, можно получить в нулевой ячейке 65535 за один ход, если подготовить константу n, например, в #1.
S(K(S(K(help(zero)(zero)))(get)))(succ) zero — одноразовый вариант
S(K(S(K(help(zero)(zero)))(get)))(get) zero — в #1 индекс ячейки с числом, формула копируется get`ом.
Они планировали использовать это в защитной стратегии (или нападающей с помощью attack), если набрать сначала 65535 в #0, а потом циклическим help распространить хп от #0 до #255. После этого за счет своего хп можно и атаковать. Весь вопрос был в том, как разработать универсальный цикл.

Универсальный цикл у нас был готов только к 6 вечера, и выглядел он стремно:
#ind: S (S (K(get)) (K(ind))) (S K f) t
f — функция от одного аргумента (возможно, каррированная). Этот последний аргумент t передается циклу и запускает его на выполнение. Цикл хранится по индексу ind, и этот индекс намертво забит в теле цикла.
Мы думали пойти дальше, придумать цикл с фиксированным кол-вом итераций и еще более сложные конструкции, но потом оказалось, что это никому не нужно: решили более сложные конструкции реализовывать в шарповом коде за несколько ходов игры. Выводящий и оптимизирующий компилятор на Haskell писать мне дико не хотелось :)

Кстати, о движке. Процесс его написания выглядел следующим образом:
- Андрей что-то кодит некоторое время
- Андрей вскакивает и бежит задавать вопрос
- Андрей получает ответ, задумывается, ходит по комнате, потом замирает, орет "Бля! Я понял!" и бежит обратно за ноут :)
Андрей уехал в 7 вечера вместе со всеми, реализовав (с багами) около 80% эмулятора. Остаток я потом писал и исправлял сам в следующие 2 дня.

Стратегии и методы, которые сообща рождала фантазия Сергея, Сани и Виталика, к вечеру становились все изощреннее :) Я думаю, если бы все это реализовать в многокритериальной системе принятия решений (как одно время предлагал Сергей — все угукнули и решили оставить на фиг мечту закодить ее за трое суток), бота можно было бы выпускать на чемпионат мира по LtG.

Например, они предлагали следить за константами, которые строит противник. Если они нам нужны — копировать их через copy, если нет — грохать ячейки с константами, а то мало ли что :) Если бы написать анализатор формул противника, можно тырить и чужой код тоже. Если нет анализатора — можно грохать и зомбировать любую ячейку с длинным кодом.

Многокритериалка разрабатывалась долго и мучительно, в синке от нее остался длинный текст, описывающий разные критерии успешности и планы выполнения. Вплоть до субботы предполагалось, что мы реализуем сложное переключение с плана на план. Но основной стратегией еще с пятницы оставался zombie rush. В какой-то момент, когда мы полностью представили себе мощь зомби, Саня написал большими буквами на листе бумаги "ZOMBIE RUSH", и мы прикололи его на стену, это стало движущим моментом стратегии =) В какой-то момент предполагался zerg rush (нахеллять себе ячейки, а потом в цикле бить противника), но все-таки решили остановиться на зомби.



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

Суббота, 18 июня


Начинаются фейлы. Во-первых, собраться вместе уже не получается: негде. Во-вторых, Виталик и Андрей отпочковались от команды: один по причине сессии (и потере надежды разобраться в последующем коде), второй по причине работы. Движок дописывать приходится мне, но я пока трачу день субботы на то, чтобы реализовать в коде все придуманные комбинаторы и планы, в т.ч. разнообразные циклы. На данный момент (середина дня субботы) я единственный в команде, который ясно представляет, что за хрень там валяется в гитхабе :)

Мы решили-таки зарегистрироваться на неофициальном сервере дуэлей, чтобы тестить наших ботов. Проблема была в том, что истинное название команды светить не хотелось, так что регаться надо было под левым ником. Первым, что всплыло у меня в голове, оказался почему-то фильм Тарантино, так что я вбил в поле регистрации "Bastard", и так было создано новое имя команде :)

Становилось ясно, что втроем ничего сложного в качестве плана работы мы за два дня не реализуем. Первоначальным вариантом тупой стратегии Саня предложил тупой zombie rush без лечения, который выглядел приблизительно так:
- набиваем константу 4608 в #0.
- атакуем чужую #255 этой константой с помощью какой-нибудь своей бесполезной ячейки (выбрали #8).
- увеличиваем константу вдвое до 9216.
- атакуем еще раз из другой ячейки (#9).
- теперь чужая #255 мертва. Туда очень быстро начинают засылаться зомби, которые выполняют код типа help i j 9216. Это убивает ячейку j и сильно ослабляет ячейку i.
- повторять засылку зомби до наступления коммунизма.

(Как выяснилось после контеста, мы почти приблизились к идеалу. Единственный шаг вперед, до которого никто не додумался — сделать цикл зомби низкоуровневым, а не высокоуровневым: засунуть в код зомби цикл хелпов со счетчиком. Это была основная стратегия работы сильных команд, которая делала полный экстерминатус менее чем за 1000 ходов: кто-то за 200, кто-то за 300-500.)

Эту стратегию в самом тупом варианте я закодил к 11 вечера субботы. Дальше следовал час игрищ с установленным в виртуалке Дебианом. Организаторы в этом году оказались ленивыми, подготовить даже Live CD им оказалось впадлу, так что они отделались сообщением "Мы надеемся, что все участники в состоянии установить себе Debian squeeze x64". В общем и целом установить Дебиан не так-то и сложно, а вот собрать на Mono 2.6.7 код из нашего солюшена уже гораздо веселее, особенно учитывая, что о "Unit Test Project" монодевелоп представления не имеет, .NET 4 версия в репозитарие Дебиана еще не умеет, Generational GC в ней тоже не реализован, память эта архаика жрет что твой фаерфокс, и запускать полученный файл нужно через шелл-скрипт вида mono Game.exe. Заскриптовать весь этот процесс мне было лень (в идеале надо было это делать до контеста, а не тратить время в нем — но до контеста никто не знал структуру сабмита решения и его вид), так что процесс подготовки пакета для сабмита каждый раз занимал 20 минут. В первом же сабмите на сервер я забыл поставить восклицательный знак в инсталл-скрипте #!/bin/sh, так что сабмит падал с ошибкой «Невозможно установить бота, насяльника». К счастью, это быстро обнаружили и исправили.

Тупой бот, к удивлению Сергея (ратовавшего за долгое стратегическое лечение, а не rush) оказался поразительно продуктивен. За два часа он прошел 14 дуэлей, в которых победил в 11, а в остальных трех вылетел с эксепшном в стандартный вывод, после чего был забракован сервером :) Слабые команды эта стратегия выносила подчистую за 10076 или что-то около того ходов. Сильные, естественно, выносили нас.

Через два часа было принято решение поднять константы. Теперь первая атака делалась в размере 1250, а вторая — 10000, и код зомби help i j 10000 убивал как #i, так и #j. Это увеличило скорость работы в два раза: новая версия бота выносила все ячейки противника за 5495 ходов. На этом я обернул весь код бота в try с пустым catch, описал результаты в синке, и ушел спать. На часах было 3 ночи.

Воскресенье, 19 июня


Проснувшись к полудню, первым делом я бросился смотреть скорборд дуэльного сервера. Был приятно удивлен: подавляющая масса зарегистрированных команд была прощелкана нашими тупыми зомбаками как орешки :) Общий счет был 42 выигрыша из 62 партий, после чего сервер опять забраковал сабмит: какие-то три эксепшена все-таки просочились наружу. Но за счет этого тотального выноса слабых команд наш бот стабильно тусовался в топ-30, изредка опускаясь до 50го места или подымаясь до 10го. Был момент, когда мы даже занимали 7ю строчку таблицы — это был наш абсолютный рекорд за три дня контеста.

Сергей открыл код в гитхабе и ужаснулся. Там творился адовый кромешный северный пушной зверек, ибо тупую стратегию я кодил очень-очень быстро вечером субботы, а времени уже не было. Я честно попытался объяснить ему в джаббере, какая кошмарная архитектура там реализована мною поверх не менее кошмарного недописанного движка. Лог этого разговора лежит в http://code-cercopithecoids.sync.in/documentation, Сергею он в итоге не помог, зато к работе над кодом подключился Саня. Итак, из команды осталось 2 человека.

Как показала ночь с субботы на воскресенье, у бота есть несколько проблем. Во-первых, мы никак не защищаем свои критически важные ячейки: если противник исхитрится и успеет убить нам #0, вся череда зомбостроя прервется навсегда. Во-вторых, часто обе команды что-то делали друг с другом, нарушали нормальное функционирование планов друг друга и впадали в ступор: игра в итоге заканчивалась на 100000 ходу по очкам, то есть по количеству живых ячеек. Конечно, больше их обычно было у нас, но все-таки это 2 очка за партию, а не 6 — если следовать правилам официальных дуэлей, которые будут проводиться между отправленными ботами после контеста.
Строчка "player 1 wins by 1:252 after turn 100000" повторялась в логах дуэлей все чаще и заметно меня нервировала, хотя это, казалось бы, и была победа, но победа в заступоренном дефе :) Для решения проблемы были предложены 2 новых модуля. Копирую текст из синка.

Revival system
1. В каких-то далеких номерах (мб 30, мб 170) мы держим константы с номерами нужных ячеек - emergency array.
2. В случае гибели нужной ячейки - восстанавливаем командой revive і, где і содержится в emergency array.
3. В случае повреждения (гибели ячейки) из emergency array под функционал погибшей ячейки выделяется другая. Таким образом revival system работает до уничтожения почти всех наших ячеек без замедления в работе.

Targeting system
1. При отсутсвии у противника ячеек с хп >=10000 в 0ю ячейку помещается максимальное хп из его ячеек.
2. Нужно поддерживать зону высадки
а) Константу 0 нужно сделать в какой-то ячейке сразу
б) Если противник возрождает свою 255ю ячейку в ближайший свой ход убиваем её командой dec 0
3. Также можно рассмотреть противодейтсвие активному отжеру: если у противника достаточно ячеек с высоким хп - увеличиваем константу в 0
тут сложно подобрать критерии когда стоит менять константу
4. Можно подумать над обдуманным выбором целей атаки, но это в последнюю очередь.

Revival system писал уже Саня. К этому моменту, поскольку нас осталось двое, мы уже тупо сидели и общались вслух по скайпу, передавая код друг другу коммитами через гитхаб или, в некоторых случаях, тупо копируя пару строк в чат скайпа. Получался такой очень распределенный репозитарий :)

3-6 часов дня. Зомби-бот с Revival system прочно держится в топ-30, выигрывая 23 партии из 31. К вечеру мы начинаем работать над таргетингом... и обнаруживаем, что баги в движке не всегда правильно определяют состояние поля. Это был трындец: с одной стороны Саня пытается найти, какая ошибка в таргетинге заставляет партию все равно зацикливаться до 100000 хода, с другой стороны я лихорадочно исправляю движок, подгоняя его под правильную семантику тех или иных мелких моментов правил (типа "нельзя inc`ать мертвую ячейку"). Вечер воскресенья прошел в сумасшедшем режиме write-only: в общем итоге на сервер было сабмитнуто 5 разных версий бота с теми или иными версиями выбора целей для атаки зомби-хелпом. Что самое смешное, по производительности они не особо отличались: обозначилась некая стабильность, то бишь список команд, которых мы выносили регулярно (Wile E., ciklum, notogawa, Ria Mitsuru, coding_monkeys), и команды, которые регулярно выносили нас (Joho, Platinum Anger, atomic do $ save Madoka, Punch Jordan in the Face). Один раз мы даже выиграли у shinh3, но это, наверное, была какая-то ошибка :) В 6 часов вечера организаторы заморозили скорборд. Для истории остался скриншот с нашей командой на 7м месте:



К 12 ночи создалась патовая ситуация: есть две версии бота, со старой и новой targeting system. Новая теоретически работает лучше (так, слабые команды она выносит за 4900 ходов, а не за 5025), однако в ней, судя по всему, еще остались баги: когда бота запустили на сервер, он первые четыре дуэли со средними командами продул вчистую. И старая, которая работает средненько, но надежно. Саня после вечера дебага и кода сказал, что тут уже без разницы, оставил на меня выбор, и свалил спать. Дуэли проводятся раз в 10-15 минут, так что мне оставалось подобрать момент, когда сабмитнуть на сервер дуэлей старого бота и просидеть полтора часа, держа мышку на кнопке отправки в гугл-форме официального сабмита, чтобы, возможно, заменить официальный сабмит с нового на старый. В 2:45 (15 минут до конца контеста) я принял решение, что старая версия выиграла по количеству побед у новой, нажал "Отправить" и упал спать. Через четыре часа приходилось вставать и переться в универ на экзамен :)

Заключение


Официальное тестирование будет длиться всю эту неделю. В официальной форме отправке мы фигурируем как Bastard (aka Code Cercopithecoids). Лично я рассчитываю на топ-30 и второй раунд, а дальше уже как повезет.

В 2012 году надо будет собрать надежную крепкую классную команду и всех порвать :) Киевляне, кто за?

P.S. Да, а еще я забил на раунды TCO и Russian Code Cup. Писать их все равно было бессмысленно, ибо следующий раунд TCO через неделю я должен был бы писать в самолете, а финал RCC в сентябре, в случае прохода, — в другом самолете :) А на ICFPC больше фана, чем в них двоих, вместе взятых.

(no subject)

Tuesday, 14 June 2011 23:39
skiminok: (Ы)
Воффко Быдломытцев: Перефразируя Канта, в изумление меня приводят две вещи: звездное небо у нас над головами и ёбаный пиздец вокруг нас.

© BOR

(no subject)

Saturday, 11 June 2011 23:19
skiminok: (!int)
Не везет мне с ICFPC.
Мало того что в прошлые годы он попадал исключительно на сессию, причем серьезную - так что написать его при всем желании у меня не было ни единого шанса.

Так теперь в этом году ничего особо проблемного с моей сессией нет — зато команду нормально собрать ни хрена не получается. У одного диплом, у другого экзамен, у третьего другой экзамен, у четвертого ничего нет, но он боится будущих экзаменов... еле набрал вместе с собой 6 человек, причем не всегда тех, которых хотелось бы изначально. Команда получилась странная.

Но ничего-ничего, все равно всех порвем :)
skiminok: (Ы)
Помещение грядущего WWDC 2011:
skiminok: (xkcd)
Пакет инструментов gtools, входящий в состав nauty, отлично подходит для генерирования графов и различных их характеристик, а также манипулирования графами тучей разных способов. Собирается на Cygwin без малейших затруднений.

Пример: сгенерировать все связные графы из 5 вершин с максимальной степенью вершины 4 и диаметром 2, записать в файл в формате g6.

$ ./geng -cD4 5 | ./pickg --e -Z2 - "output.g6"
>A ./geng -cd1D4 n=5 e=4-10
>Z 21 graphs generated in 0.00 sec
>A ./pickg --e -Z2 - output.g6
1 graphs : e=4
2 graphs : e=5
4 graphs : e=6
4 graphs : e=7
2 graphs : e=8
1 graphs : e=9
>Z 21 graphs read from stdin; 14 written to output.g6; 0.000 sec


После чего полученный файл можно загрузить в Wolfram Mathematica и любоваться результатами.

Import["output.g6"]


С этим дальше можно играться. Например, получить представление в виде списка ребер для всех графов с 7 ребрами из этого файла.

EdgeRules /@ Select[Import["output.g6"], EdgeCount[#] == 7 &] // Column
{1->4,1->5,2->4,2->5,3->4,3->5,4->5}
{1->3,1->4,1->5,2->4,2->5,3->5,4->5}
{1->3,1->4,1->5,2->5,3->4,3->5,4->5}
{1->3,1->4,1->5,2->3,2->4,2->5,3->5}

(no subject)

Wednesday, 25 May 2011 20:37
skiminok: (xkcd)
Мы находимся в экспериментальном первом классе; в нём всего 18 учеников.

Я насыпал в бутылку из-под молока некоторое количество бобов и предлагаю ребятам угадать, сколько их. Весь класс кричит «Сто!». Я тогда говорю, что так неинтересно: никто не будет победителем.

— Давайте, — говорю я, — каждый назовёт своё число, но только чтобы все числа были разными. Мы их запишем на доске, а потом проверим.

Записываем имена и числа на доске. Потом всем классом хором вслух считаем бобы: это не вредно — лишний раз повторить последовательный счёт. Бобов оказывается 49.

— Ну что, кто победил? — спрашиваю я.

— Никто не победил.

Дети имеют в виду, что никто не угадал точного количества.

— Ну, хорошо, а кто всё-таки оказался ближе всех к правильному ответу?

— Таня ближе всех.
(Она назвала 52.)

— А на сколько она ошиблась?

— Она ошиблась на три боба, — отвечает класс.

Всё хорошо. С этой задачей покончено, и мы переходим к другим делам. Примерно через четверть часа я задаю детям «пример на вычитание»: из 52 вычесть 49. Результат ошеломляющий: с этой задачей не справился ни один человек. Ни один!

Я оставляю читателям полную свободу для интерпретаций.

А. К. Звонкин, «Малыши и математика».



Пользуясь случаем: кто может посоветовать еще интересную литературу о педагогике вообще и преподавании математики (точных наук) в частности?
*ох, чую, скоро придется мне новый тэг вводить: "преподавание"*

(no subject)

Friday, 20 May 2011 21:33
skiminok: (Default)
В коллекцию эпичных ошибок на контестах:
#define INF 10^9

Слава Богу, не моё :)
skiminok: (!int)
«Оно разродилось»: последний билд Nemerle торжественно удостоился названия 1.0. В комплект входят плагин для 2008 Студии, среда на основе Шелл-Студии для неимущих, плюшки а-ля "batteries included" в ассортименте и все необходимое для жизни. Долгих лет счастья им. Забирайте.
Пока у меня гребаная сессия разгрузится, я с этой игрушкой позабавиться смогу не раньше как к середине июня :(

«Старый конь борозды не испортит. Старый конь борозды не видит». Слухи о грядущей свежей Студии. К вашим услугам тысяча и одна красивая табличка для проджект-менеджеров, умные методологии разработки ПО в комплекте, SketchFlow мертв и предан забвению, его младший брат спешит на помощь в костюмчике от ПаверПойнта (убейте меня веником).
Из позитивного пока что — все популярные юнит-тест фреймворки из коробки, и — о диво! — снова здравствуй, добрый дружище C++! Я имею против тебя много всего замечательного, но, должен признать, ребята из Редмонда делают все возможное, чтобы программирование на тебе хоть иногда текло плавно и с улыбкой, а не с ломом и такой-то матерью.
О F# ни словечка с самого PDC. Ну-ну.

Microsoft Skype

Tuesday, 10 May 2011 18:51
skiminok: (Default)
Когда я в феврале собеседовался на интерна в Майкрософт, один из тимлидов, с которым я разговаривал, был из команды Lync. После того как я его поразил своими умениями обращаться с деревьями :) он рассказывал мне о альфа-версии продукта. На ноутбуке крутился стандартный видео-аудио клиент, эта прелесть работала со списком контактов, слала текст, видеоизображение, красиво драг-н-дропала контакты, при мне тимлид совершил звонок на мобильник... оно задумалось на несколько секунд, но в итоге мобильник среагировал и выдал сигнал. В общем, типичный бизнес-клиент для переговоров, просто тебе копия скайпа — в этом прототипе интерфейса даже цветовая гамма была почти такой же. Когда я спросил, что они будут делать с Windows Live Messenger, ответ был "Они для разных людей и целей". Ну, в общем то, правда.

В Lync я в итоге не поехал, попал в Office Labs. Не важно. Мне просто интересно, нахрена тратить сотни нефти и человеко-месяцы на разработку дубля популярного продукта, если у тебя в планах покупка оригинала? Странно все это.

P.S. Желаю скайпу долгих лет жизни и возрождения из состояния городской канализации, в котором он пребывает сейчас. Мне честно верится, что МС сумеет вернуть в него какую-то органичность, стабильность и заставит быть хотя бы юзабельным "из коробки". Посмотрим.
skiminok: (Compas)
Гугл заявляет, что московская команда внедрила в google.ru новую фичу. Вкратце — поисковые подсказки-термины, бликие семантически к ищущемуся вами в данный момент.

Интересно, там Петя нигде руку не приложил? :)

ICFPC

Monday, 2 May 2011 16:44
skiminok: (Default)
Объявили даты ICFPC 2011: 17-20 июня.
Опять во время сессии, но я надеюсь, что в этом году это мне не станет большой проблемой. Теперь главное команду собрать.

UPD: А еще 5 июня IPSC.
skiminok: (Compas)
Итак, к нам приближается:

Google Code Jam


6 мая 23:00 UTC — Qualification
21 мая 01:00 UTC — Online Round 1: Sub-Round A
21 мая 16:00 UTC — Online Round 1: Sub-Round B
22 мая 09:00 UTC — Online Round 1: Sub-Round C
4 июня 14:00 UTC — Online Round 2
11 июня 14:00 UTC — Online Round 3
29 июля — Onsite Finals

TopCoder Open


14 мая 12:00 UTC-4 — Qualification Round 1
19 мая 07:00 UTC-4 — Qualification Round 2
24 мая 21:00 UTC-4 — Qualification Round 3
18 июня 12:00 UTC-4 — Round 1
25 июня 12:00 UTC-4 — Round 2
9 июля 12:00 UTC-4 — Round 3
23 июля 12:00 UTC-4 — Round 4
6 августа 12:00 UTC-4 — Round 5
27 сентября 08:30 UTC-4 — Semifinal 1
27 сентября 12:30 UTC-4 — Semifinal 2
27 сентября 17:30 UTC-4 — Wildcard Round
28 сентября 10:00 UTC-4 — Championship

Russian Code Cup


8 мая 11:00 UTC+4 — 1 квалификационный тур
14 мая 11:00 UTC+4 — 2 квалификационный тур
15 мая 17:00 UTC+4 — 3 квалификационный тур
19 июня 11:00 UTC+4 — Отборочный тур
17 сентября — Финал

Открытый турнир Яндекса


4 мая, 9:00 UTC+4 — 1 квалификационный раунд
6 мая, 19:00 UTC+4 — 2 квалификационный раунд
20 мая, 19:00 UTC+4 — 1 отборочный раунд
22 мая, 19:00 UTC+4 — 2 отборочный раунд
12-18 июля — Финальный раунд

Python + pipes

Monday, 18 April 2011 03:29
skiminok: (xkcd)
Вот что значит гибкий язык.

Чтобы добавить в питон пайплайны, достаточно определить элементарный декоратор над iterables, переопределив в нем оператор "|". Взамен получаем просто-таки F#:
[1,2,3,4] | where(lambda x: x<=2) | as_list


При этом исходный код этого катастрофически сложного нововведения занимает 7 строчек. Не считая библиотеки стандартных комбинаторов.
class Pipe:
    def __init__(self, function):
        self.function = function

    def __ror__(self, other):
        return self.function(other)

    def __call__(self, *args, **kwargs):
        return Pipe(lambda x: self.function(x, *args, **kwargs))


Вообще, если бы в питоне была статическая типизация, я бы из него до конца жизни не вылезал.