intricacy: A game of competitive puzzle-design

[ game, gpl ] [ Propose Tags ]

A lockpicking-themed turn-based puzzle game on a hex grid. A series of preset puzzles serves as an extended single-player introduction, after which players enter a multi-player game with a client-server architecture, in which players design puzzles (locks) and solve those designed by others. A metagame encourages the design of maximally difficult puzzles, within tight size constraints. The client supports Curses and SDL, with all graphics in SDL mode drawn by code using SDL-gfx. The network protocol is based on the binary package, and is intended to be reasonably efficient. TVars are used to give transparent local caching and background network operations. Also incorporates an implementation of a graph 5-colouring algorithm (see GraphColouring.hs).

[Skip to Readme]


Manual Flags


Build game


Build server


Include support for sending mail

Automatic Flags

Enable SDL UI


Enable sound


Enable Curses UI


Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info


Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS] 0.3, 0.3.1, 0.3.3, 0.3.8, 0.4.1, 0.4.3, 0.5, 0.5.5, 0.5.7,,, 0.6, 0.6.1, 0.6.2, 0.7,, 0.7.1,, 0.7.2,,,, 0.8.0,, 0.8.1,, 0.8.2,
Change log NEWS
Dependencies array (>=0.3 && <0.6), base (>=4.3 && <5), binary (>=0.5 && <0.11), bytestring (>=0.10 && <0.13), containers (>=0.4 && <0.8), cryptonite (>=0.16 && <0.31), directory (>=1.0 && <1.4), exceptions (>=0.8.3 && <0.11), filepath (>=1.0 && <1.6), hscurses (>=1.4 && <1.5), memory (>=0.11 && <0.19), mtl (>= && <2.4), network-simple (>=0.3 && <0.5), random (>=1.0 && <1.4), safe (>=0.3.18 && <0.4), SDL (>=0.6.5 && <0.7), SDL-gfx (>=0.6 && <0.8), SDL-mixer (>=0.6 && <0.7), SDL-ttf (>=0.6 && <0.7), semigroups (>=0.18 && <0.19), stm (>=2.1 && <2.6), time (>=1.2 && <1.14), transformers (>= && <0.8), vector (>=0.9 && <0.14) [details]
License GPL-3.0-only
Author Martin Bays
Category Game
Home page
Source repo head: git clone
Uploaded by mbays at 2024-05-01T10:38:30Z
Reverse Dependencies 1 direct, 0 indirect [details]
Executables intricacy-server, intricacy
Downloads 13727 total (59 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs pending
Build status unknown [no reports yet]

Readme for intricacy-

[back to package description]
# Intricacy
A game of competitive puzzle-design.

## Configuration
You can change keybindings in SDL mode by right-clicking on the corresponding
button and pressing a key.

Config files are saved in `~/.intricacy/` on unixoids, or something like
`...\Application Data\intricacy\` on windows. In particular, the locks you
create in the lock editor are saved in there (and you can organise them into
directories by including (back)slashs in the lock name). You can edit them in
a text editor if you want - see AsciiLock.hs for what the characters mean.

If you want to cheat your way past initiation, change "False" to "True" in

## Rough description of the game mechanics
* Locks consist of blocks, pivots and balls. Blocks can be connected to other
    blocks by springs. Pivots have arms. When picking a lock, you control two
    tools - a hook, which acts as a mobile one-armed pivot, and a wrench,
    which acts as a mobile block with momentum.
* To open a lock, the "bolthole" (the area in the top-right) must be empty.
* Each turn, the tools push and rotate according to the player's commands, and
    then each spring which is compressed or extended beyond its natural length
    pushes/pulls on the block at its end.
* If two forces try to move a piece in two different directions, or try to
    move two pieces into the same hex, one or both is blocked.
* If a pivot/hook is trying to turn and there's a piece in the way of one of
    its arms, it will at first try to push the piece away - but if that force
    is blocked, it will try instead to pull the piece round with the arm as it
* If two springs are trying to push two blocks into the same hex, they will
    generally both be blocked. However, if one of the forces is also pushing
    against a fixed piece such as a tool, it won't block the other force.

## Full details of the metagame mechanics
(Where by the 'game' I mean the lock-picking bit, and by the 'metagame' I mean
the bit with the 3-letter codenames and the three lock slots and notes and so
on. If you haven't seen anything of the kind... keep solving those locks!)

Scoring is always relative - each player has a score relative to each other
player. That score is the number of the second player's lock slots to which
the first player has access, minus the number of the first player's lock slots
to which the second player has access.

A player accesses a lock slot when one of the following holds
    * the player has solved the lock in the slot, and declared the solution;
    * the player has read three notes on the lock in the slot;
    * there's no lock in the slot, and the player has accessed all the slots
	which do have locks in them.

A player reads every note in every lock the player accesses. When a lock is
replaced ('retired'), each note secured by the lock becomes 'public', and is
read by every player.

Note this means that once three notes on a lock become public, every player
accesses the lock. The lock is then 'public' (and its owner should replace

To declare a solution, you must secure a note on it behind a non-public lock
in one of your three lock slots. Once the note is placed, it can't be moved.

## Full details of the game mechanics
Springs are directed; one of the blocks it is connected to is the 'root', the
other the 'end'. A block is stationary if it is not the end of any spring. The
directed graph whose nodes are the blocks and whose edges are the springs is
required to be acyclic. A spring may also be rooted in a pivot.

I will now attempt to describe in full excruciating detail the game physics,
i.e. the algorithm used to determine what happens on a turn. You shouldn't
need to read this to play the game! Experimenting and turning on blockage
annotations should be enough.

The following description corresponds to the code in Physics.hs. The algorithm
is the result of an extended process of experiment and iterated
simplification; see notes/game in the source distribution if you're perverse
enough to want to read a scattered stream-of-consciousness account of the

Each turn is separated into two phases. In the first phase, the forces arising
from the player's move are 'resolved', possibly resulting in some movement. In
the second phase, forces arising from stretched or compressed springs are
resolved, possibly resulting in some movement.

Here we abuse physics terminology, and use the term 'force' to refer to either
a directional force in the usual sense, which we call a 'push', or a
rotational force, which we call a 'torque'. A force is always on a piece, in
some direction. Only the obvious six hex directions and the two obvious
rotational directions occur; the magnitude is always 1. 

Player moves result in forces on the tools in the obvious way. A wrench which
is moving gets a push in that direction each turn (sorry, Newton!). A spring
which is not at its relaxed length results in a push on the end piece.

To _resolve_ these initial forces:
    * each initial force is 'propagated to a force group'
    * if a group is 'inconsistent' with another group: if one of the group is
	'dominated' by the other, it is 'blocked'; else both are blocked
    * each force in each unblocked force group is 'applied'

To _apply_ a push: move the piece in the given direction.
To _apply_ a torque: rotate the piece in the given direction.

To _propagate_ a force:
    * If applying the force would result in an overlap with another piece,
	push that piece - but if it's an arm, twist the pivot instead when
	that makes sense.
    * If the force is a torque and an arm is pushing on another piece, we have
	a special rule: if the obvious push ends up propagating to a resisted
	force (see below), then it is replaced with a 'clawing' push. e.g. in
	the following situation:
	    \ O #
	if the pivot is twisted clockwise, the ball will first be pushed east,
	but when that force propagates to a push on the stationary block to
	its east which is resisted, processing will back up and a push on the
	ball southeast will be tried instead.
    * A spring connecting two blocks transmits a push on one block to the
	other unless the direction is such that the push is tending to
        compress resp. extend the spring and the spring is currently extended 
        resp. compressed, or the force is a player force and the spring isn't 
        already fully compressed resp. extended.

A force is _resisted_ if it is a push on a pivot or on a block which isn't the
    end of a spring, or it's a torque on a block, or it's a force on a hook
    which isn't already getting that force this phase as the result of a
    player move, or it's a force on a wrench which isn't a push in the
    direction it's already moving.

To _propagate an initial force to a force group_:
    * Propagate the force as above, then recursively propagate the resulting
	forces, checking for resistance as we go. The force group consists of
	all the resulting propagated forces.
    * On resistance: back up to try clawing as described above if appropriate,
	else consider the whole force group resisted - so the resulting force
	group is empty.

Two forces are _inconsistent_ when
    * they act on the same piece in different directions, or
    * applying both at once results in overlapping pieces, or
    * applying both at once results in an overextended/overcompressed spring.

Two force groups are _inconsistent_ iff there is some force from the first
    which is inconsistent with some force from the second.

A force group _dominates_ another iff their initial forces are spring forces,
    and the root of the first spring is an ancestor of the root of the second
    spring, where an ancestor of a block B is recursively defined to be a block
    which is the root of a spring which ends at B, or an ancestor of such a

-- 2013