В минувшие выходные Clojure-сообщество испытывало… Ой, так начинался прошлогодний пост с отчётом о прошедшем ClojureCup 2013.
ClojureCup — это 48-часовой онлайн-хакатон, обязательным условием которого является написание приложений на языках Clojure/ClojureScript. В этом году в хакатоне участвовало около 50 команд (в каждой по 1-4 человека), которые сделали множество классных приложений.
Приложения оценивают как жюри (известные в Clojure-сообществе люди), так и обычные пользователи: зайти с помощью twitter-а и проголосовать за понравившиеся вам приложения можно на странице приложений.
А попробовать, что у нас получилось, можно вот тут.

Осторожно: под катом много картинок.
Идея поучаствовать в ClojureCup появилась у меня достаточно давно, но лень сделала свое дело, и спохватился я о сборе команды лишь за три дня до начала хакатона (как оказалось, за два дня до хакатона нужно б��ло сформировать команды). Однако этот мир оказался не без программистов, желающих потерять свои выходные, для того чтобы написать что-нибудь на странном языке со скобочками. Тут нужно дополнительно упомянуть, что никто из нас сколько-нибудь серьёзно на Сlojure не писал, а двое и вовсе не писали. И да, все четверо были программистами.
Изначально в качестве проекта было желание делать странную-штуку-для-статических-сайтов (одна из команд сделала что-то немного похожее в итоге), но возникли большие вопросы в нужности результата.
В итоге мы сформировали критерии выбора идеи: что-нибудь понятное сразу и для простого юзера.
От таких критериев мы быстро пришли к идее сделать какую-нибудь игрушку. И тут вспомнилась прекрасная Puzzle Bobble:

Это пазл с очень озорным режимом P2P: чем больший кусок шариков ты собьёшь группой, тем больше этих самых шариков придёт ко врагу. Так мы сосредоточились на поиске игры, к которой можно было бы прикрутить механику “сделать противнику неприятно”.
Думали и о тетрисе, и о 2048, но потом вспомнили Rocket Mania!

Так, ночью, за два дня до начала, родилась идея приложения: двое игроков будут играть в упрощённый вариант Rocket Mania!, но количество ракет будет ограничено и они будут улетать к врагу, а чтобы игра точно когда-нибудь закончилась, у них будет топливо, которое при каждом перелёте уменьшается, а в конце ракета будет улетать в вечность космос.
На тот момент игра должна была быть с классной графикой и возможностью играть по сети, но когда ночью в пятницу мы начали прорабатывать идею, внеслось два изменения: решили выкинуть игру по сети и поддержать только режим hotseat (помните, был такой в героях, когда два игрока за одним компьютером), а также использовать flat design, потому что так проще, а среди нас одни программисты =)
Это были очень правильные решения: нужно выбирать целями то, чего реально можно достичь. Упрощать-упрощать-упрощать.

Как и опыта в Clojure, опыта в функциональном стиле программирования у нас не было. Но тут мы решили брать функциональную парадигму по полной: каждое наблюдение мира — неизменяемо, функции чистые, и т.д. и т.п. В итоге в нашей архитектуре был один “атом мира”, состояние которого однозначно задавало то, что на экране, и изменением которого мир менялся. Также мы решили использовать react.js (это было очень правильное решение) и обертку над ним quiescent (а это неправильное, но терпимое). Сейчас мне кажется, что нужно было глядеть на phaser, но тогда такой мысли не возникло. Кстати, на phaser-е другая команда сделала весёлую игру с котами!
Тем временем время подошло к 4 утра, начался ClojureCup, а мы пошли спать. Один из членов команды (живущий в другом часовом поясе) в это время поднял сервер и запустил туда заглушку с ракетой Элона Маска:

Приложение очень хорошо разделилось на части: кто-то пилил чисто игровую логику, кто-то отображение игрового поля, кто-то занимался обработкой клавиатуры. Почему-то активного использования терминального REPL-а у нас не случилось, а вот figwheel — решение для автоматической перекомпиляции ClojureScript кода с hot reload-ом кода в браузере — был очень кстати и очень-очень упрощал нам жизнь. Тут надо заметить еще один интересный момент: состояние мира хранилось в неперезагружаемом атоме, поэтому даже после перекомпиляции приложение оставалось в том же состоянии, что и было до. Из минусов можно отметить то, что у нас время от времени слетала инкрементальная компиляция, приходилось делать полный ребилд.
Вначал�� мы хранили сгенеренный код в репозитории, поэтому у нас получились волшебные числа в десятки тысяч изменённых строк кода, при итоговом размере порядка 1К строк кода.
Из неприятных запар у нас случилось две:
Во время реализации игровой логики перед нами встала задача масштабного глубокого изменения immutable состояния мира. В итоге было придумано решение, как потом оказалось, с точностью до сигнатуры совпадающее с решением из стандартной библиотеки =) Мораль: изучайте хоть немного инструменты, которые будете использовать. И перед тем, как делать что-то своё, посмотрите: не сделал ли это кто-нибудь другой?
А ещё мы не знали, как сделать изменяемое состояние в Clojure (функциональщики поневоле), это родило такой монолог:
В итоге изменяемым состоянием мы пользоваться так и не научились, но заиспользовали loop.
В конце первого дня у нас уже была что-то показывающая версия приложения, а за 3 часа до конца хакатона — уже более-менее полноценная работающая версия игры. В последние 3 часа мы добавили в игру звуки (тут от функциональной парадигмы пришлось отказаться и появились методы вида play!) и очень сильно отполировали UI.
За часа 2 до конца у нас упал сервер, и оказалось, что игра раздавалась встроенным python-сервером из коробки. Быстро подняли nginx и были рады.
За полчаса до конца я открыл приложение в Сафари, и оказалось, что оно там вообще неработоспособно (поворот блоков не работал).
За 15 минут до конца у нас никак не хотел работать звук для победы (видимо, снова была проблема с компилятором ClojureScript-а), это всё заметно добавило адреналина.
Так что даже имея за 3 часа более-менее работающую версию, последние минуты мы провели в запаре.
А вот и этот самый момент, когда мы затегали релизную версию и получили три зелёных галочки, означающие, что всё необходимое мы сделали =)


У нас получилась, хотелось бы верить, весёлая игра на двоих. А даже если и не получилась — мы получили огромное количество веселья и не меньшее количество опыта. Вот тут можно смотреть исходники проекта, а тут при желании проголосовать.
Список проектов, упорядоченных по текущему положению в публичном голосовании, можно посмотреть здесь и здесь. Советую посмотреть на эти списки: среди проектов есть очень и очень приличные, весёлые и полезные.
За этот год уже можно почитать отчёты Александра Соловьёва (вы его можете знать по этому замечательному выступлению про FRP и Clojure), команды проекта Funstructor, а также Milestones.
Отдельно хочется сказать спасибо Александру Соловьёву ingspree и Никите Прокопову tonsky за поддержание интереса к Clojure на высоком уровне, а также Дмитрию Грошеву si14, Николаю Рыжикову и Михаилу Лапшину за проведенный workshop по clojure в компании JetBrains.
Также огромное спасибо Юле Беляевой juliette, Серёже Серебрякову megaserg и Андрею Сиунову fandes за прекрасную компанию на этом хакатоне. Хакатонить в прекрасной компании — прекрасно!
ClojureCup — это 48-часовой онлайн-хакатон, обязательным условием которого является написание приложений на языках Clojure/ClojureScript. В этом году в хакатоне участвовало около 50 команд (в каждой по 1-4 человека), которые сделали множество классных приложений.
Приложения оценивают как жюри (известные в Clojure-сообществе люди), так и обычные пользователи: зайти с помощью twitter-а и проголосовать за понравившиеся вам приложения можно на странице приложений.
А попробовать, что у нас получилось, можно вот тут.

Осторожно: под катом много картинок.
Идея
Идея поучаствовать в ClojureCup появилась у меня достаточно давно, но лень сделала свое дело, и спохватился я о сборе команды лишь за три дня до начала хакатона (как оказалось, за два дня до хакатона нужно б��ло сформировать команды). Однако этот мир оказался не без программистов, желающих потерять свои выходные, для того чтобы написать что-нибудь на странном языке со скобочками. Тут нужно дополнительно упомянуть, что никто из нас сколько-нибудь серьёзно на Сlojure не писал, а двое и вовсе не писали. И да, все четверо были программистами.
Изначально в качестве проекта было желание делать странную-штуку-для-статических-сайтов (одна из команд сделала что-то немного похожее в итоге), но возникли большие вопросы в нужности результата.
В итоге мы сформировали критерии выбора идеи: что-нибудь понятное сразу и для простого юзера.
От таких критериев мы быстро пришли к идее сделать какую-нибудь игрушку. И тут вспомнилась прекрасная Puzzle Bobble:

Это пазл с очень озорным режимом P2P: чем больший кусок шариков ты собьёшь группой, тем больше этих самых шариков придёт ко врагу. Так мы сосредоточились на поиске игры, к которой можно было бы прикрутить механику “сделать противнику неприятно”.
Думали и о тетрисе, и о 2048, но потом вспомнили Rocket Mania!

Так, ночью, за два дня до начала, родилась идея приложения: двое игроков будут играть в упрощённый вариант Rocket Mania!, но количество ракет будет ограничено и они будут улетать к врагу, а чтобы игра точно когда-нибудь закончилась, у них будет топливо, которое при каждом перелёте уменьшается, а в конце ракета будет улетать в вечность космос.
На тот момент игра должна была быть с классной графикой и возможностью играть по сети, но когда ночью в пятницу мы начали прорабатывать идею, внеслось два изменения: решили выкинуть игру по сети и поддержать только режим hotseat (помните, был такой в героях, когда два игрока за одним компьютером), а также использовать flat design, потому что так проще, а среди нас одни программисты =)
Это были очень правильные решения: нужно выбирать целями то, чего реально можно достичь. Упрощать-упрощать-упрощать.

Как и опыта в Clojure, опыта в функциональном стиле программирования у нас не было. Но тут мы решили брать функциональную парадигму по полной: каждое наблюдение мира — неизменяемо, функции чистые, и т.д. и т.п. В итоге в нашей архитектуре был один “атом мира”, состояние которого однозначно задавало то, что на экране, и изменением которого мир менялся. Также мы решили использовать react.js (это было очень правильное решение) и обертку над ним quiescent (а это неправильное, но терпимое). Сейчас мне кажется, что нужно было глядеть на phaser, но тогда такой мысли не возникло. Кстати, на phaser-е другая команда сделала весёлую игру с котами!
Тем временем время подошло к 4 утра, начался ClojureCup, а мы пошли спать. Один из членов команды (живущий в другом часовом поясе) в это время поднял сервер и запустил туда заглушку с ракетой Элона Маска:

Реализация
Приложение очень хорошо разделилось на части: кто-то пилил чисто игровую логику, кто-то отображение игрового поля, кто-то занимался обработкой клавиатуры. Почему-то активного использования терминального REPL-а у нас не случилось, а вот figwheel — решение для автоматической перекомпиляции ClojureScript кода с hot reload-ом кода в браузере — был очень кстати и очень-очень упрощал нам жизнь. Тут надо заметить еще один интересный момент: состояние мира хранилось в неперезагружаемом атоме, поэтому даже после перекомпиляции приложение оставалось в том же состоянии, что и было до. Из минусов можно отметить то, что у нас время от времени слетала инкрементальная компиляция, приходилось делать полный ребилд.
Вначал�� мы хранили сгенеренный код в репозитории, поэтому у нас получились волшебные числа в десятки тысяч изменённых строк кода, при итоговом размере порядка 1К строк кода.
Из неприятных запар у нас случилось две:
- первая была связана с тем, что quiescent очень странно решал, какие части страницы нужно обновлять, а какие нет; в тот момент я научился вставлять отладочный вывод в сгенеренный JS-код и даже понимать что там нагенерено,
- вторая была по всей видимости багой ClojureScript компилятора: правильный код компилировался, но потом не работал.
Во время реализации игровой логики перед нами встала задача масштабного глубокого изменения immutable состояния мира. В итоге было придумано решение, как потом оказалось, с точностью до сигнатуры совпадающее с решением из стандартной библиотеки =) Мораль: изучайте хоть немного инструменты, которые будете использовать. И перед тем, как делать что-то своё, посмотрите: не сделал ли это кто-нибудь другой?
А ещё мы не знали, как сделать изменяемое состояние в Clojure (функциональщики поневоле), это родило такой монолог:
жутко бесит меня кложур, чтоб я ещё раз на нём кодил; зачем нужен этот язык, если его невозможно читать, а чтобы что-то нормальное писать, нужно всё равно выходить из концепций языка.
В итоге изменяемым состоянием мы пользоваться так и не научились, но заиспользовали loop.
В конце первого дня у нас уже была что-то показывающая версия приложения, а за 3 часа до конца хакатона — уже более-менее полноценная работающая версия игры. В последние 3 часа мы добавили в игру звуки (тут от функциональной парадигмы пришлось отказаться и появились методы вида play!) и очень сильно отполировали UI.
За часа 2 до конца у нас упал сервер, и оказалось, что игра раздавалась встроенным python-сервером из коробки. Быстро подняли nginx и были рады.
За полчаса до конца я открыл приложение в Сафари, и оказалось, что оно там вообще неработоспособно (поворот блоков не работал).
За 15 минут до конца у нас никак не хотел работать звук для победы (видимо, снова была проблема с компилятором ClojureScript-а), это всё заметно добавило адреналина.
Так что даже имея за 3 часа более-менее работающую версию, последние минуты мы провели в запаре.
А вот и этот самый момент, когда мы затегали релизную версию и получили три зелёных галочки, означающие, что всё необходимое мы сделали =)

Вместо заключения

У нас получилась, хотелось бы верить, весёлая игра на двоих. А даже если и не получилась — мы получили огромное количество веселья и не меньшее количество опыта. Вот тут можно смотреть исходники проекта, а тут при желании проголосовать.
Список проектов, упорядоченных по текущему положению в публичном голосовании, можно посмотреть здесь и здесь. Советую посмотреть на эти списки: среди проектов есть очень и очень приличные, весёлые и полезные.
За этот год уже можно почитать отчёты Александра Соловьёва (вы его можете знать по этому замечательному выступлению про FRP и Clojure), команды проекта Funstructor, а также Milestones.
Отдельно хочется сказать спасибо Александру Соловьёву ingspree и Никите Прокопову tonsky за поддержание интереса к Clojure на высоком уровне, а также Дмитрию Грошеву si14, Николаю Рыжикову и Михаилу Лапшину за проведенный workshop по clojure в компании JetBrains.
Также огромное спасибо Юле Беляевой juliette, Серёже Серебрякову megaserg и Андрею Сиунову fandes за прекрасную компанию на этом хакатоне. Хакатонить в прекрасной компании — прекрасно!
