📜 ⬆️ ⬇️

Battle for Klozhuru or operation "Battle Magnet"

Participated in the Clojure Cup 2013 along with Sanya ingspree , Sergey Joes and Roma rofh . Probably, you have seen the cutting , and maybe the full presentation of Sanya on the Klozhurskripte and reactive programming. That turned up the opportunity to try these technologies in battle.

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

Without thinking twice, we decided to write both the Clojure server and the ClojureScript client using common code. Of the four participants, only Sani had at least some experience of writing on a slope, while the rest had only the experience of solving dozens of tasks for 4clojure .

Server


I will not tell a lot about the server part - for the entire time of the competition I have never touched the server part. As a database, we chose from two options:
')
The first thing that comes to mind when combining the words Clojure and "database" - Datomic . It is very interesting to try, but none of us had absolutely no experience with a datomic, even a couple of hours, and we were afraid that another completely new concept in the project could ruin the initiative and we will not have time.

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

We tried to use clony from si14 as a blank, but it turned out to be too difficult for us, it was very incomprehensible how to expand with our own things. Half a day after the start, they made a new repository without clony, and quickly transferred all the developments there.

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

Customer


We chose which technologies to use on the client. There is a new-fashioned core.async , but it was dropped due to the fact that all the tutorials and manuals write about how to manage the data nicely, and then nailed to DOM with selectors . There is an opinion that this is a flawed concept, and you just need to accept that HTML is how we build interfaces. And if the selectors join - then we sort of work on the side of it, and because of any change in the interface structure, we have to very carefully and tediously saw through these damn selectors.

There are good looking reactive libraries for ClojureScript - a bunch of javelin and hoplon . Though they are good from a conceptual point of view, no one has yet done optimizations there, and therefore they are very slow - the simplest Todo example noticeably slows down even on the desktop firefox. Development of a more complex application would turn into pain due to constant interface brakes, they decided to refuse.

In the last project for the work, Sanya and Roma are using Facebook React in conjunction with the censcript, and they like it very much. So they took him. On Friday, before the start of the competition, I began to sort out React a bit and began to write a binding library for him. We finished the first version on Saturday at one o'clock.

This is what React looks like:

var HelloMessage = React.createClass({ displayName: 'HelloMessage', componentWillMount: function() { ... }, render: function() { return <div class="smth">{'Hello ' + this.props.name}</div>; } }); 


This XML-like syntax is convenient and clear in javascript. But to integrate it into Clojure, even the thought did not arise: inspired by the syntax hiccup , we made this option:

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

Some problems have arisen at the junction of the cladorcript and the reactor. For example, we first tried to directly use ClojureScript data structures as a state, but the reactor inside it makes a shallow copy without transferring the prototype, and everything broke. As a crutch, they began to put our state in the field state.state , and get it out.

Of course, it is hidden in the library, but because of this, we had to do assoc-state and assoc-in-state helpers, which must be used to change the state. One of the developers of the React - Pete Hunt - in irc offered the same workaround. Maybe somehow it will be possible to make friends with them in a more adequate way.

In general, at the client, we store all the state in one atom, in one data structure, which we transfer to the controllers deeper and deeper by the necessary parts. We would have a local apocalypse with javascript, but the data structures in the clit are unmucable, so there were no problems.

To communicate between the server and the client, we used json, because the cool edn format on the client is deserialized slower by about an order of magnitude, and cljson , which maintains the structure of the templates, seemed ridiculous and incomprehensible. And this turned out to be a mistake!

Then there were problems with the fact that :keyword serialized as a “keyword”. Klodure can be said :keywordize-keys - make keywords in dictionaries from keys. But this does not solve all the problems - not all keywords were keys in dictionaries, and it creates others - not all keys in dictionaries were keywords. It turned out to be especially unpleasant with numbers — the server-side Clojure cannot do at all (keyword 1) and returns nil , and ClojureScript will do :1 , but then it turns out that json's keys deserialized with special options contain a string inside, not a number, ie (keyword "1") .

From the second half of Sunday we lost at least an hour and a half on this problem, and now the crutches are set up along the code here and there. It was necessary to initially use cljson, and, probably, we will remake it to 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 [xy] (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 understandable. And if someone is straining the number of brackets here, so remember that in real HTML there are two more of them: <div></div> - four, [:div] - two. Plus, when editing, paredit helps a lot - with it in brackets you don’t get confused at all.

In general, the impression was that ClojureScript and React are a pair of bundles, you can and should use it!

Competition


Sanya threw a cry about participation in the Clojure Cup two weeks before, at the same time we agreed on what to do and approximately what technologies to use. But, as usual, almost no one prepares for such competitions, despite any promises to himself and his comrades, and we are no exception. Although we started the library on Friday night (this is allowed by the rules)!

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

Having gathered in the office since 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 via Mozilla's Persona (cool thing!), Sending messages between the client and the server via web sockets, a little bit of the common code between the client and the server, and in the database - tablets with users, games and the event log. The classic Risk-card with the territories started to be drawn on the client and somehow highlighted on hover. Last commit at 22:30.

In the morning of Sunday I drew us a nice logo, and then I had all Sunday blurred into one continuous pedal of the code. The binding of the game card and the 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 are in time to finish the game to the minimum working condition, we decided to continue until we see a chance and a desire / opportunity to do something.

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

A commit with a working game and a button "end the move":

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



Unfortunately, in our production version, a bug has crept in with loading map data from a file. Locally it works, but when packed in a uberjar it does not, it needs to be loaded from resources. A commit with fixing this was 5 minutes before the finish, but it turned out to be unsuccessful, and we have a version that we can’t play. Lacked literally fifteen minutes.

According to the rules, we do not have the right to fix anything or post 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.

There were more than 90 teams registered. We got 6.5% commits of the total number of commits of all teams, and there is at least one team that has turned out even more, it seems 9.15%. 42 teams selected for voting. For some reason, their website doesn’t really work for me in firefox. In chrome works.

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

Source: https://habr.com/ru/post/195930/


All Articles