Playing Kingdomino with a 3 year old

What is Kingdomino?

I have recently discovered a really nice board-game. Well, discovered might be a strong word, it has won Spiel des Yahres for 2017, so it is pretty well known by now. It is a simple game, might take me and my wife around half an hour to play a round and we can manage the rules even when exhausted after the day is over and before we go to bed.

/images/elen_kingdomino2.jpg

I think a professional might do a better job of explaining the rules than me, but for the purposes of this, lets summarize the two-player rule-set:

  • you have 24 rectangle tiles, resembling dominoes, split in half to two square regions
  • on the back of the tiles you have a number, higher the number implies more valuable tile
  • region might be a field, a pasture, a forest, a lake, a swamp or a gold mine
  • in the region, there might be a house, containing 1, 2 or 3 crowns.
  • Each player has a square tile with a castle and two king-shaped meeples
  • play consists of 6 rounds where each player has two turns to choose new tiles for his kingdom
  • before you begin you choose first 4 tiles, sort them from highest to lowest number and then each player alternates and chooses a tile where he puts his king, until there are 4 kings (of two colors) on 4 tiles
  • then round begins, where + you draw 4 new tiles and sort them besides the tiles your kings are standing + player who has king on the first tile will move him to claim a new tile + this player then takes the vacated tile and puts in his kingdom - either besides the castle, or besides a matching tile (dominoes style). All the tiles of your kingdom should fit in 5x5 region. + then player with king on the second tile does the same, e.t.c until there are only 4 newly claimed tiles + then next round starts

How does it look like when we are playing?

After I finished my first game, I thought to myself, "These rules are simple enough to play with our little Elen", the two-player game is quite quick and playing the 6 rounds should fit within 3-year-old attention span.

So before you try this at home, beware, that we have quite patient and calm kid, and even then I can manage a 20 minute session at best. The session might end abruptly for any of following reasons:

  • Elen no longer feels like playing
  • Two kings šŸ‘‘ now have to live inside of the little castle šŸ° tile, and don't want to go do their job of claiming the new tiles anymore. This can still sometimes be saved if I add two more meeples, so that two meeples can rest at their castle and the other two are working. Unless the four kings start to argue, whose turn it is to stay in the castle šŸ˜ƒ
  • The tile placement doesn't go according to her plan, for example she now has to have two distinct lakes in her kingdom instead of a single large one and she concedes šŸ³ I usually just let her re-organize her board.
  • The tile with a image of a dragon silhouette šŸ‰ or lochness monster appears and suddenly it is much more important to figure out if the two kings, living in hte castle, will be safe from the monster, than playing the game by the rules. Alternatively, dragon eats the monster.
/images/elen_kingdomino.jpg

We do actually finish more games than not, and even those times when we don't finish properly, it usually morph into something similarly interesting, like free-form kingdom building, or funny discussions about kingdom's food chain, that probably will include both dragon, two types of water-monster and a man-sized spider.

As long as I keep in mind, that the reason for playing is having fun and killing 20 minutes of time, it is always a success.

Modding the rules

So, how do we actually play? Mostly by me prompting her to do stuff she should on her turn:

  • Choose the new tile you like with your king
  • Now place the old tile somewhere in your kingdom

With these two prompts we are able to play fairly well. I explained the importance of crowns on tiles to her, and that creating large continuous areas is good, and even though she doesn't really play with much foresight, I would say that her tile-placement is anything but random :-) She even beat me on one occasion. Well, the final counting of points is not something, that really interests her, even though she really likes the idea of having lots of points šŸ˜ƒ

I relaxed the rule about keeping the kingdom in 5x5 grid, because I figured that it would take too long to explain, and I didn't want to just forbid her placing a tile, if she wouldn't understand why she shouldn't do that. I understand the reason for the 5x5 rule, but from a perspective of a 3-year old, it sounds arbitrary.

To conclude,

this is how we do it, and it saved us few afternoons, especially when Elen was sick and we couldn't go play outside. If you will try this as well, just keep in mind, that the goal here isn't to win, and that sometimes it is more important to have the dragon in your kingdom than having the most points.

Creating a Monoid instance for Purescript Record

A rationale behind this attempt

Why would I want to attempt to create a monoid-like thing for records? Well, the reasoning is three-fold. First, when playing around with Purescript, I often have these large record definitions and then I have to initialize them. I would much rather just say "This record has all fields empty, figure it out." Second, these large records are often the heart of some state, that I update all the time, and I would much rather just do

updateFunction state update = appendRecord state update

than

updateFunction state update = state {a = state.a <> update.a ...}

Third, because Record is kind-of like a row-polymorphic Tuple, and Tuple has a Monoid instance, it should mean that creating a something similar for Records should be quite an easy puzzle to solve :-)

First the memptyRecord

Because I wouldn't be implementing a proper instance (still not entirely sure that is possible just yet in Purescript, would I need overlapping typeclass instance support to do that?), and just the two hepler functions, that more or less try to convey the implementation of the instance in spirit, I start with the memptyRecord implementation.

It turns out it is easier than the appendRecord one.

For the class itself, I just need the RowList and output row.

class MemptyRecord rl row | rl -> row
  where
    memptyRecordImpl :: RLProxy rl -> Record row

And Nil case will be just an empty record.

instance memptyRecordNil :: MemptyRecord Nil () where
  memptyRecordImpl _ = {}

In the Cons instance, I just iterate over the RowList, on constraint that every there is a Monoid in every row and insert a mempty for that row.

instance memptyRecordCons ::
  ( IsSymbol name
  , Monoid t
  , MemptyRecord tail tailRow
  , RowLacks name tailRow
  , RowCons name t tailRow row
  ) => MemptyRecord (Cons name t tail) row where
  memptyRecordImpl _ =
     insert namep mempty rest
   where
     namep = SProxy :: SProxy name
     tailp = RLProxy :: RLProxy tail
     rest = memptyRecordImpl tailp

To tie it all together, I wrap the memptyRecordImpl in memptyRecord function, that makes the compiler to figure out the row-list for me.

memptyRecord :: forall rl row . RowToList row rl
   => MemptyRecord rl row
   => Record row
memptyRecord = memptyRecordImpl (RLProxy :: RLProxy rl)

In the end, this was fairly simple and I managed to solve the first problem, because now

{a : "", b : []}  == memptyRecord :: {a :: String, b :: Array Int}

This of-course doesn't really work on nested records, but I think it is still an improvement.

Now, the appendRecord

For the sake of simplicity I will just dump the entirety of my first attempt at this. It actually worked and

~appendRecord {a: "1", b: [2], c: "3"} {a: "a", b: [4], c: "c"} == {a: "1a", b: [2,4], c: "3c"}~

It looked like this:

+class SemigroupRecord rl row row'
  | rl -> row row'
  where
    appendRecordImpl :: RLProxy rl -> Record row -> Record row -> Record row'

instance appendRecordCons ::
  ( IsSymbol name
  , Semigroup ty
  , RowCons name ty trash row
  , SemigroupRecord tail row tailRow'
  , RowLacks name tailRow'
  , RowCons name ty tailRow' row'
  ) => SemigroupRecord (Cons name ty tail) row row' where
  appendRecordImpl _ a b =
      insert namep (valA <> valB) rest
    where
      namep = SProxy :: SProxy name
      valA = get namep a
      valB = get namep b
      rest = appendRecordImpl (RLProxy :: RLProxy tail) a b

 instance appendRecordNil :: SemigroupRecord Nil row () where
   appendRecordImpl _ _ _ = {}

This implementation required, that when I use appendRecord, both records have the same keys, and that didn't really fit the record-updating semantics I had in mind. If I had a large record to update, I don't really want to specify all of the keys, I just want to specify the keys that I want to update.

With this in mind, I went to #purescript on functionalprogramming.slack.com and started asking, if anybody knows how to implement outer join of two records. If I knew, that you could somehow merge {a: 1, b:"b"} with {b:"b", c:"c"} to get {a:1, b:"b", c:"c" }, changing it do {a:1, b:"bb", c:"c" } should be simple, right?

Unfortunately, it turns out, doing outer-join with RowLists seems to be hard, mostly ending the conversation about my outer-join ideas with the question "So, why do you actually want to do that?" After I described the use-case for updating records, @monoidmusician suggested to change the appendRecord function to allow the second record be a subset of first one. This means that the appendRecordImpl type becomes

RLProxy rl -> Record big -> Record small -> Record big

This makes everything simpler, and @paluh even sent me an implementation in gist.

First interesting thing I have noticed, that with the new class definition, I don't actually need any functional dependencies.

class AppendSubrecordImpl rl bigger smaller where
  appendSubrecordImpl :: RLProxy rl -> Record bigger -> Record smaller -> Record bigger

Because the iteration happens over the smaller record, in nil case I just return the bigger record.

instance appendSubrecordNil :: AppendSubrecordImpl Nil bigger smaller where
  appendSubrecordImpl _ b s = b

And because I know that the result will be the Row bigger, I don't actually need the machinery to build up the output row.

instance appendSubrecordCons ::
  ( IsSymbol name
  , RowCons name t trash smaller
  , RowCons name t trash' bigger
  , Semigroup t
  , AppendSubrecordImpl tail bigger smaller
  ) => AppendSubrecordImpl (Cons name t tail) bigger smaller where
    appendSubrecordImpl _ bigger smaller = modify key modifier rest
      where
	key = SProxy :: SProxy name
	modifier v = v <> get key smaller
	rest = appendSubrecordImpl (RLProxy āˆ· RLProxy tail) bigger smaller

One day, I maybe will be bugging @jusrin00 to merge this PR as well :-)

Could this still be a monoid?

Now that I have what I wanted, I started thinking if this still can be considered a monoid. If we would have one well defined type, that would go through all of our functions, a single closed row-type, I don't think there would be a problem with any of the monoid laws and fairly easily we could do:

newtype User = User {name: Maybe String, surname: Maybe String}

instance semigroupUser :: Semigroup User where
  append (User a) (User b) = User (appendSubrecord a b)

instance monoidUser :: Monoid User where
  mempty = User {name : Nothing, surname: Nothing}

But I am not really sure what does the fact, that we relaxed the second row to be a sub-set of the first actually mean. It seems that there might be some foot-guns hidden in there. For example, because I only iterate through the smaller row RowList, the first row could contain keys that aren't Semigroups. Is that a bug, or a feature? I am not sure yet, but I definitely can't claim that {a:String, b:MyNotASemigroup} is a Semigroup, even though I could appendSubrecord {a:"Adam", b:Test} {a: " Saleh"}. The compiler would still catch, if I try to append the wrong key. On the whole, this starts to resemble more of a relational algebra, than the group-hierarchy. I wonder how would rest of the instances from Tuple look like, if I ported them over to Record. Maybe I will try to take a stab at Row-polymorphic curry and uncurry, that might be fun.

Experimenting with Purescript's RowToList metaprogramming

Experimenting with Purescript's meta-programming

After a few stabs at this, I have decided I should write about my experience with Purescript's meta-programming capabilities. Purescript is a nice small language, inspired by Haskell, that mostly compiles to Javascript. I know there are some other backends, like Erlang, or C++, but I haven't tried those yet.

It has a really nice book to guide you through the language, given you have at least some limited Haskell experience. My experience with Haskell so far is roughly a half-semester at a university-course (where the other half was concerned with Prolog), and a few never-published toy-projects.

It even has several interesting features that you wouldn't find in Haskell (unless you enabled some extensions). The feature I have mostly played around is concerned with row-types.

Read moreā€¦

Debugging Jenkins pipeline

Few notes on debugging a Jenkinsfile

For past few months I have been using Jenkinsfiles and Jenkins pipelines more and more. They have many advantages to our previous setup, that over-relied on storing as much as possible in jenkins-job-builder yaml configs and chaining builds together with post-build triggers.

With jenkins-files, we can use Groovy to script everything and that is much nicer than the combination of bash and yaml. You can do code-sharing with pipeline-libraries. And all of this really nicely integrates with Github, so we now roll our own travis-like ci in our own infrastructure.

On the other hand, if there is a problem, especially in longer running pipeline, it can be quite painful to debug.

Stage one: thorough debug logs

If the problem that I encounter is simple enough, it might be solved just by adding a few print statements here and there, and re-running the job. This usually works as long as the run is shorter than ~5 minutes. In my case, around the 5 minute mark, the "Read logs"-"Change code"-"Rerun" cycle starts being unbearably slow.

Stage two: improvising a break-point

In our infrastructure, we are using docker slaves for most things. On one hand, this means that job usually run in clean environment. Unfortunately, this also means that if something goes wrong, before I start investigating, the container my job was running on is long gone.

One trick that usually helped me here, was adding an "input" prompt into my jenkins file in the place I want to investigate. This way I was able to pause the execution of the pipeline, ssh into the container and investigate further.

Improvised breakpoint

Doing a naive ssh into the container can lead to one more problem. The running job often changes the environment for the commands it would run, which means that the external connection wouldn't have access to the same environmental variables, e.t.c.

To mitigate this, I first try to start a tmux session in the jenkins job. Then I attach inside of it.

Stage three: improvising a repl

Sometimes I would need to investigate reason for failure of a pipeline-command itself. In this case I would create a weird little repl-like environment:

while (true) {
    def cmd = input message: 'What to run:', parameters: [string(defaultValue: '', description: '', name: 'cmd')]
    try {
        print Eval.x(this,cmd)
    } catch (e) {
        print e
    }
   }

You can then input code to execute in this case, something like:

x.binding.steps.invokeMethod("sh","node --version")

The nice thing about this is, that now I have limitted access to the rest of the jenkins machinery, while in shell I'd only have access to shell commands. On the other hand, it is really crude. Especially passing in other variables is tricky, and it is even harder to save the results of execution anywhere else. As you can see, I had to use the dsl object methods directly, instead of writing in the same style as in the Jenkinsfile.

To conclude, debugging Jenkinsfiles is ugly

In the future I will probably invest more into the testing side of our pipelines. I know there is a unit-testing library. I should make sure that thing I actually run in the pipelines is reproducible more easily on my machine as well.

On the other hand, I could invest a significant ammount of time to develop a true Jenkinsfile repl. Unfortunately it seems that ain't nobody got time for that. But I would be quite interested in other solutions to this problem.

Applicative pattern for reactive programming

Reactive programming with applicatives!Ā¶

One interesting thing we could do with applicatives, is to experiment with gui programming. I have been playing around with excelent flare library in purescript and it heavily uses applicative style to achieve a style of code that is resembling working with spreadsheets.

Read moreā€¦

Monoid for specifying configuration

Monoid to make a configuration dslĀ¶

If you have read Gabriels presentation, you might think, that I would be trying to create monoid for combining event streams. Unfortunately, I don't think I am hard-core enough to do that in Python.

On the other hand, writing a tiny config library sounds like a bit of harmless fun.

Read moreā€¦

Applicative pattern in Python

Applicatives, or functors for arbitrary functionsĀ¶

Mapping only single param functions can be limiting. It would be cool if lift would could work on function with arbitrary many parameters.

Read moreā€¦

Monoid pattern in Python

Monoids are coolĀ¶

One useful concept to investigate here is the one of a monoid. If we think of this as an interface that is implemented by a type T, it needs two things:

  • the empty element, usually called mempty
  • the append operation, that takes any two T's and returns a new T

There are three laws:

  • operation is closed over T: for any two objects of type T it will really return new T, no exceptions.
  • identity: for any a of type T: append(a,empty) == append(empty,a) == a
  • associativity: for any a,b,c of type T: append(a,append(b,c)) == append(append(a,b),c)

Now we have this interface, we can do cool things with this :)

Read moreā€¦

Functor pattern in Python

Introducing FunctorĀ¶

In programing, you often embed some domain specific language in a host language. This might be for working with databases, or working with configuration. And when you have such DSL, you will probably want to use functions from your host-language on values from your dsl. Some database libraries are quite bad in this regard, where you set your queries as literal strings:

session.execute("select text,priority from memos where priority>9;")

On the other hand, if you use library, such as sql-alchemy, the interoperabilty with the host-language is much more seamless:

session.query(Memos).filter(Memos.priority > 9)

When you look at the code, it is apparent that some translation needs to happen that would convert the python expression Memos.priority > 9 to the SQL equivalent.

Read moreā€¦

Algebraic patterns in Python

What do I mean by algebra?Ā¶

When I say algebra, I don't think about implementing i.e. DSL for linear algebra in Python. I think about algebra in general as in "a set of objects and a collection of operations on them".

I like algebras, when they are used in programming. Probably the most well-known is Relational Algebra. The object in Relational Algebra is a relation, with operations like join, projection and union. The nice thing about having something that works like an algebra is, that you always work with the same type of object, reusing the box of tools without breaking the flow.

Read moreā€¦