
Show HN: A Clojure game library - gw
https://github.com/oakes/play-clj
======
Morgawr
Nice one, great job! I'll be checking out the code and see if I can "steal"
some nice ideas, really well done. At the moment I'm also working on a fully
concurrent game engine written in Clojure,
[https://github.com/Morgawr/Cloister-
engine](https://github.com/Morgawr/Cloister-engine) I'm wrapping around a few
lwjgl calls, it's definitely a fun and interesting project. (Still far far
from being done).

Kudos to you OP, good job.

~~~
willismichael
I'm really curious what you mean by "fully concurrent" and how you accomplish
it. I've been deliberating over how to write a game loop for my pet project
[1] that allows multiple threads to concurrently update the world state
without causing inconsistencies.

For example, let's take the age-old problem of two people in the game
(potentially both AI-controlled) both deciding to pick up an item. If two
separate threads are handling the logic, both having a view of the world with
the item on the ground, and both of them end up trying to update the world
state such that the item ends up in somebody's inventory, what is a clean and
elegant way to resolve that?

I've considered using STM, having a ref to each entity in the game, and
wrapping each bit of game logic in a transaction, thus rolling back entity
updates that conflict with previously committed entity updates. I'm concerned
about the performance implications of this, though. I keep imagining it
becoming way too contentious and having more rollbacks than completed
transactions.

[1] [https://github.com/willismichael/terra-
incognita](https://github.com/willismichael/terra-incognita)

~~~
Morgawr
I've been writing on and off about my engine design on my blog
[http://morgawr.github.io/](http://morgawr.github.io/) but it's mostly just
the introduction, I wanted to finish the actual engine (or, rather, make it
usable) and make a small game as a demo in it (so far I only have a pong
clone).

I don't know how feasible my design is, but my idea is to use Clojure's
principles of values/identities and reftypes, every entity in the game is an
agent with its own thread (scheduled in a thread pool), inside the agent there
are refs (I intended to use atom but for race conditions and synchronized
updating, the example you mentioned, I decided to go with refs). Entities can
query a global database of entities (tagged, component based) and retrieve
their state by dereferencing. If they want to update the state of other
entities they can either send a message via the agent (ordered asynchronous
update) or act directly onto the inner ref (via transaction) for a
synchronized/concurrent update.

It's tricky to explain, and it's probably going to give me a lot of troubles
along the way but it's mostly a fun experiment for a private project.

~~~
willismichael
Thanks! Same here in terms of "mostly a fun experiment". A while ago Brian
Carper blogged about early attempts at developing a little RPG in clojure:
[http://briancarper.net/blog/520/making-an-rpg-in-clojure-
par...](http://briancarper.net/blog/520/making-an-rpg-in-clojure-part-one-of-
many)

His post indicated that he initially tried the "every entity is an agent"
idea, and had performance problems with it - although maybe his real problem
was that the entire world was a single ref, and all of the agents would bash
on that ref.

Another question, do you have any kind of reverse-lookup table implementation?
I see that you're using component-based entities, do you have any way to
quickly query your world for entities that match some arbitrary criteria,
rather than iterate over all entities in the entire world? I ask because my
game idea would involve potentially tens of thousands of entities, and I'm
trying to figure out a way to build game logic mini-systems that can quickly
find a subset of game entities and perform some function on them. For example
"In the spring time find all fruit-bearing trees of a minimum age and spawn
unripe fruit on them", or "every 15 seconds find all poisoned characters and
deduct 1 health point"

~~~
Morgawr
_although maybe his real problem was that the entire world was a single ref,
and all of the agents would bash on that ref._

Having the whole world inside a ref sounds bad... I mean, I don't really know
but if you want to take the godmap approach you really need to pass around the
state and apply the parallelism somewhere else (aka in the way the state
transformations are applied, like pmap for example), not have entities as
external agents that hammer on the poor state.

 _do you have any kind of reverse-lookup table implementation?_

Yes and no. I don't have anything fancy at the moment, I can query the
"database" of entities (aka the individual game screens) for specific tags,
lookup operations are very quick in Clojure since the entity's state is a map
with tag -> value, the lookup is close to O(1). The downside is that I need to
query through all the entities contained in a specific screen (or all screens)
to look for the tags I'm looking for, but I don't see another feasible way to
do it and maintain concurrency. I could sort entities by tags (what I tried to
do on my old and half-failed attempt:
[https://github.com/Morgawr/CloisterJS](https://github.com/Morgawr/CloisterJS)
ClojureScript engine) but I would lose the advantage of concurrency. If the
performance impact gets too much, then I'll try to engineer something around
the problem but so far I have more important issues (like, having an engine at
all).

~~~
willismichael
One way that I prototyped a component-entity system that worked well for quick
lookups was more like this:

    
    
      {:health   {entity-id-1 32
                  entity-id-2 23}
       :location {entity-id-1 [32 16 7]
                  entity-id-2 [33 13 7]}
       ... more stuff ...}
    

So the entity data structure is organized first by component tag, then by
entity id. I've heard this referred to as "inside-out objects". That way
functions can quickly query things like "find all entities that have a
location" or "find all entities that are poisoned". The downside is that there
isn't a convenient way to atomically update multiple components of a single
entity.

~~~
Morgawr
Yes, that's exactly how my ClojureScript prototype worked, the problem is that
it wouldn't allow me to use concurrent entities. (Although you could use refs
for tag-types instead of individual entitites... an interesting approach
nonetheless)

------
jheriko
as much as i disagree with this approach it is actually quite fantastic to see
a cross platform solution that isn't either using native code directly or
Unity.

small tip: make the engine to fit a game, building an engine without a game is
often a one way trip to a programmer wankfest that results in lots of code and
cool tech but nothing usable

also interesting to see other approaches to the problem of games in general...
this is quite high level stuff and the choice of tech has cut a lot of the
implementation details out of the code (although imo still way to much in each
file, but there we go) - quite different to what I normally consider 'game' or
'engine' programming - although obviously it is.

will fav and try to actually use when i get home from work. :)

~~~
Morgawr
_small tip: make the engine to fit a game, building an engine without a game
is often a one way trip to a programmer wankfest that results in lots of code
and cool tech but nothing usable_

Good tip, but don't forget that some people enjoy making engines and new
technologies without having to make games.

~~~
golergka
Still, developing a "demo" game really helps building your engine.

------
coolsunglasses
I use Clojure at work and I'm an active participant in the Clojure open source
community.

The mention of Carmack is somewhat misplaced in that he decided languages like
Lisp and Scheme that didn't offer the leverage of types like Haskell.

So perhaps languages like ML/Haskell, maybe even Rust, are more to the point?

~~~
gw
He did indeed say he preferred static typing, but I wonder if his opinion
would change if he learned about core.typed. He would probably still prefer
Haskell, because he doesn't like things like that to be optional, but
nonetheless I mentioned him purely as an endorsement of functional game
programming, not of Clojure in particular.

~~~
coolsunglasses
> but I wonder if his opinion would change if he learned about core.typed.

As a user of Clojure and Haskell, no, not at all.

core.typed is unpleasant to use and not very sophisticated at all compared to
Haskell.

Also doesn't do anything to separate pure and impure functions.

~~~
gw
Fair enough; I haven't used Haskell but I know intuitively that a Clojure
library won't be able to replicate the sophistication of its type system. On
the other hand, the dynamic nature of Clojure is what makes REPL-driven game
development possible.

I'm willing to give up some type system sophistication in exchange for that,
along with access to the JVM, macros, and so on. Carmack may not agree with
that trade-off, but like I said, I mentioned him specifically for his
endorsement of functional game programming.

~~~
Mikera
I'm actually trying to prototype a language that gets you the best of both:

* Clojure style dynamic development * A Lisp (with proper macros, homoiconicity etc.) * Runs on JVM * Can use Clojure libraries unmodified * Static type system (similar to Typed Clojure, but as part of the compiler and driving real optimisations, not just as a separate static analysis tool)

Still an early stage experiment right now, but I think it is the right idea:
[https://github.com/mikera/kiss](https://github.com/mikera/kiss)

~~~
Flow
Nice work. Have you looked at Shen? It's highly praised by many.

------
overviewerror
Reminds me of LÖVE [https://love2d.org](https://love2d.org), Very nice.

------
jplur
Very exciting! libGDX is the best game library I've used, and I'm spending a
lot of time with clojure these days. I'm excited to see what this can enable
me to make.

------
steveolsen
I'd really love to see a library that handles wrapping libgdx without being
tied to a specific entity system.

------
woah
Aren't there issues with Clojure on Android?

~~~
Jach
The only real issues I'm aware of are slow startup times and a bigger app
distributable. (Try out Bounce Away:
[https://play.google.com/store/apps/details?id=com.friendlyvi...](https://play.google.com/store/apps/details?id=com.friendlyvillagers.ballz&hl=en))

------
bitwize
So it's basically LambdaNative for a lesser lisp.

Nice?

