The Battle of Kladoura or Operation War Magnet

    Participated in the Clojure Cup 2013 along with Sanya ingspree , Sergey Joes and Roma rofh . Probably, you saw a cut , or maybe a full presentation by Sani about kloskurscript and reactive programming. So the opportunity turned up to try these technologies in battle.

    The subject of the competition is to file something in 48 hours using Clojure or ClojureScript. Of the various options, it was decided to cut the browser Risk , in particular because all existing applications are wretched by the interface, either written in Flash, or, even worse - Silverlight, or in some other way spoil the pastime. Collectively came up with a good name - War Magnet .

    Without thinking twice, we decided to write both the server in Clojure and the client in ClojureScript, using common code. Of the four participants, only Sani had at least some experience of writing on a cross-section, while the rest had only experience in solving several dozen problems on 4clojure .

    Server


    On the server side, I won’t tell much - for the entire time of the competition, I never once touched the server side. As a database, we chose from two options:

    The first thing that comes to mind when combining the words Clojure and “database” is Datomic . It is very interesting to try, but none of us had absolutely any experience with datomics, not even a couple of hours, and we were afraid that another completely new concept in the project could ruin the initiative and we won’t succeed.

    Therefore, the choice fell on the most fashionable database in recent years - PostgreSQL.

    Tried to use clony from si14as a blank, but for us it was too difficult, it was very unclear how to expand with our things. Half a day after the start, they made a new repository without clony, and quickly transferred all the work there.

    I will list the libraries that we used on the server - compojure, ring, korma, friend, cheshire, http-kit. Out of the corner of my ear I heard unpleasant comments about feed, and Sergey promised to describe all other details on this subject in his blog .

    Client


    Choose which technology to use on the client. There is a newfangled core.async , but it disappeared due to the fact that all the tutorials and manuals write about how cool it is to manage data, and then they are beaten to the DOM by selectors . There is an opinion that this is a flawed concept, and you just have to accept that HTML is the way we build interfaces. And if we join with selectors, then we are working, as it were, on the side of it, and because of any change in the interface structure, these damn selectors have to be very carefully and boring.

    There are good looking react libraries for ClojureScript - a bunch from javelin and hoplon. Although they are good from a conceptual point of view, no one has been involved in optimizations there so they are very slow - the simplest Todo example noticeably slows down even on desktop firefox. The development of a more complex application would turn into a pain due to the constant brakes of the interface, they decided to refuse.

    In the latest project, Sanya and Roma use Facebook React in conjunction with coffee script, and they really like it. So they took him. On Friday, before the start of the competition, I started to deal with React little by little and started writing a binding library for him. We completed the first version on Saturday by one in the afternoon.

    This is what React looks like:

    var HelloMessage = React.createClass({
      displayName: 'HelloMessage',
      componentWillMount: function() {
        ...
      },
      render: function() {
        return 
    {'Hello ' + this.props.name}
    ; } });


    This XML-like syntax is convenient and understandable in javascript. But there wasn’t even a thought to integrate it into Clojure: inspired by the hiccup syntax , we made this option:

    (defr HelloMessage
      :component-will-mount (fn [] ...)
      [this props state]
      [:div.smth (str "Hello " (:name props))])

    Some problems arose at the intersection of the script and the react. For example, we first tried to directly use ClojureScript data structures as a state, but the reaction inside us makes a shallow copy without transferring the prototype, and everything broke. As a crutch, they began to lay down our fortune in the field state.state, and get it out.

    Of course, it is hidden in the library, but because of this I had to make helpers assoc-stateand assoc-in-statewhich need to be used to change the state. One of the React developers, Pete Hunt, at irc suggested the same workaround. Maybe somehow they will be able to make friends in a more adequate way.

    In general, on the client we store all the state in one atom, in one data structure, which we transfer to the controllers deeper and deeper in the necessary parts. With a javascript, we would have had a local apocalypse, but in the structure the data structures are not stable, so there were no problems.

    We used json to communicate between the server and the client, because the cool edn lay-out format on the client is deserialized slower by about an order of magnitude, and cljson , which saves the layered structures, seemed ridiculous and incomprehensible. And that turned out to be a mistake!

    Then problems with what is being :keywordserialized as a "keyword" got into . Klozhure can say:keywordize-keys- Make keywords in dictionaries from keys. But this does not solve all problems - not all keywords were keys in dictionaries, and creates others - not all keys in dictionaries were keywords. Particularly unpleasant it proved with numbers - server Clojure generally can not do (keyword 1), and returns nil, and ClojureScript do :1, but then it turns out that the deserialized with spetsoptsiey keys from json'a contained within a string, not a number, ie (keyword "1").

    Since the second half of Sunday, we have lost at least an hour and a half on this problem, and now crutches are placed here and there. It was necessary to use cljson initially, and, probably, we will redo it for its use.



    Here is the code for this window:

    (defr Attack
      [C {:keys [attacker attacking defender defending attack!]} S]
      (let [[aname {:keys [coordinates]}] attacker
            [dname dmap] defender
            [x y] (xy-for-popover coordinates)]
       [:div.popover {:style {:display "block" :left x :top y}}
        [:div.popover-content
         [:table
          [:thead [:tr [:th (name aname)] [:th (name dname)]]]
          [:tbody [:tr [:td attacking] [:td defending]]]]
         [:div.btn-group
          [:button.btn.btn-warning {:on-click #(attack! 1 aname dname)} "Attack"]
          [:button.btn.btn-danger  {:on-click #(attack! (dec attacking) aname dname)} "Blitz"]]]]))

    In my opinion, everything that happens here is pretty clear. And if someone is bothered by the number of brackets here, so remember that in real HTML there are twice as many:
    - four, [:div]- two. Plus, paredit helps a lot when editing - you don’t get confused with it in parentheses at all.

    In general, I got the feeling that ClojureScript and React are a umat bunch, you can and should use it!

    Competition


    Sanya threw a cry about participating in the Clojure Cup two weeks before, then they agreed on what we would do and approximately what technologies to use. But, as usual, almost no one is preparing for such competitions, despite any promises to ourselves and comrades, and we are no exception. Although we started to make the library on Friday night (this is allowed by the rules)!

    Clojurecup itself lasted exactly 48 hours of the weekend, from 00:00 UTC Saturday to 00:00 UTC Monday. In our time it is three in the morning.

    Gathering in the office on Saturday morning, we spent about half a day on completing the library, all sorts of setups, and other buildup to working condition.

    For Saturday we got authorization through Mozilla's Person(cool thing!), sending messages between the client and server via web sockets, a bit of common code between the client and server, in the database there are tablets with users, games and the event log. Even on the client, a classic Risk map with territories began to be drawn and somehow highlighted on hover. Last commit at 22:30.

    On Sunday morning, I drew us a nice logo, and then I have it all Sunday smeared into one continuous pedal code. Linking the game card and server, moves-attacks-replenishment, etc., actually remained at the end.



    By evening, it was still in a disassembled state, by eight o’clock we had slightly altered the map description format and loaded it on the server for the first time. Since it was still completely incomprehensible whether we had time to finish the game to the minimum working state, we decided to continue until the chance and desire / ability to do something were visible.

    Somewhere around eight or nine o’clock we moved to another room, where the lighting was much better, cool and closer to the corner with tea and coffee :) It turned out that there was always a chance to make a working version, there was enough energy and enthusiasm, and we sawed, sawed him right up to the deadline.

    Commit with a working game and the “end the move” button:

    Mon Sep 30 2013 02:59:54 GMT + 0300 (EEST)



    Unfortunately, in our production version a bug crept in with loading map data from a file. Locally it works, but when packaging in uberjar it doesn’t, it needs to be loaded from resources. A commit to fix this was 5 minutes before the finish, but it turned out to be unsuccessful, and we have posted a version that cannot be played. Not enough literally fifteen minutes.

    According to the rules, we have no right to fix anything or lay out another version somewhere. Now there is a vote, it will end on Thursday and on Friday it will be possible to update and show fully working.

    In total, more than 90 teams were registered. We got 6.5% of the commits of the total number of commits of all teams, and there is at least one team that got even more, it seems 9.15%. 42 teams selected for voting. For some reason, in Firefox, their site does not really work. It works in chrome.

    I promise that on Friday we will post a working version in any case, but for now on the page of our team you can vote for us !

    Also popular now: