compilation fixes
[intricacy.git] / notes / game
blobd606f756c98060257bd2984f8ad1e7d7e1bb1ed9
1 Wrench *
2 Hook @ -
3 Sprung block S S S S # # #
4 Wound sping  $ $ # # #
5 Block with gap  # # _ # #
6 Pivot  o -
7 Ball  O
9 Ratchet:
10    S S # _ # _ #           
12    $ # \ # _ #
13         o -
14        /                
16    S S # / # _ #
17       - o  
18          \
20                         \ /
21                        - @ -
22      /      \           / \
23   - o        o -
24    \ \      / /
25     o -    - o
26    /          \
27 #    
28  _    
29 # # # #
30    #  
34 Physics:
36 Forces try to push entities in hexdirs. Forces can necessitate other forces.
37 Resolution involves moving each entity one hex in the given hexdir. These
38 forces can be inconsistent, resulting in nullification or transferral of the
39 forces. Note that inconsistency is a complicated business, pairwise
40 consistency not being enough.
42 Furthermore, we need some notion of equilibrium: a block with springs on both
43 ends should settle with equal numbers wound on each end. The wrench,
44 meanwhile, should be forceful enough to wind arbitrarily many springs.
46 Importantly, forces are always about something moving into a particular hex.
47 This means that multiple forces acting in different directions must be
48 inconsistent and will all fail. So we only need to add when there are forces
49 acting in diametrically opposite directions.
51 Adjacent tiles can be connected together into 'entities'. This can be
52 represented graphically drawing a connected entity in one colour, and ensuring
53 adjacent distinct entities are always drawn in different colours (cf 4-colour
54 theorem; or maybe 5-colour theorem - see linear time algorithm here:
55 https://en.wikipedia.org/wiki/Five_color_theorem ). The physics could also
56 allow non-connected entities, we can't really draw them (requires
57 arbitrarily many colours) so let's disallow that for now (except for the
58 uncoloured fixed frame).
60 Entities which are connected to no spring are fixed in place, and needn't be
61 coloured; they are considered to form a single "lock body" entity.
63 One of the tile types which can form part of an entity is the pivot. Pivots
64 have one-hex arms emanating from them (with colours indicating the
65 connection). Forces on arms translate to torque on the pivot.
67 The hook pick acts as a pivot with one arm and user-applied torque. It can
68 move laterally, but can't apply lateral force.
70 The wrench is always pushing in some direction. You can reset the direction
71 only when it's stopped.
73 Balls are special one-hex pieces which don't experience friction so can
74 transfer momentum - once pushed, they continue moving in the direction until
75 they hit something, then push that.
77 Springs connect two entities in a principal direction. One entity is the root.
78 The spring never given rise to a force on the root, except when it's fully
79 wound and is pushed on rootwards. To avoid unpleasantness, a sprung entity
80 must be rooted by a single spring - although it can act as the root for
81 arbitrarily many other sprung entities.
83 Graphical representation of springs: '|' for the root in the root's colour,
84 '=' for the connection to the sprung entity in its colour, '$' for wound
85 spring, 'S' for unwound (each '$' unwinds to two 'S's).
87    #                     
88     = # | $ $ = #      #               
89      S         #        = # | S S S S = #
90       S                  $             #
91        |                  |          
93 We can also have unsprung yet moveable entities, by having them rooted to
94 points on linear tracks (which can be part of other entities), represented by
95 '| - = - |' etc.
96                                      
97 Origins of forces:
98     wrench
99     turning hook
100     wound springs
101     ball collisions
103 Force resolution:
104     A force is always actually just of the form "entity E wants to move in
105     hexdir D", and a torque is of the form "pivot P wants to turn +/-". If
106     there's something in the way, the force is transferred to the blocking
107     entity in the same direction. Forces on an arm are converted to torques on
108     the pivot.
110     We could use something more confusingly ornate, but I suggest the
111     following very simple resolution: as soon as two forces act in different
112     directions on the same entity, even if the directions are diametrically
113     opposed, the piece gets 'stuck' and no motion results.
115     So for each top-level force, we cascade down to determine resulting
116     forces, watching out that we don't get lost in a loop. The force is
117     self-consistent if no entity gets two different forces, and no entity gets
118     a force in a direction in which it is fixed. Two forces are mutually
119     consistent if the union has this property. With these rules, pairwise
120     consistency is enough - so after throwing away first the self-inconsistent
121     top-level forces which are inconsistent with themselves and then those
122     which are inconsistent with some other remaining top-level force, we're
123     left with a consistent set of forces.
125     (note we do need to throw away self-inconsistent forces first, or we have
126     situations where a silly force can block a non-silly one)
128     Problem: this doesn't handle the case of winding springs, where we really
129     do want opposing forces not to lead to stuckness.
131     Solution: there are two levels of force - player-instigated, and
132     environmental. The former overrules the latter.
134     So force resolution works as follows: first remove self-inconsistent
135     forces; then look for pairwise inconsistencies, and remove any force which
136     is inconsistent with a force of equal or greater strength.
138     Problem: we haven't handled moving roots.
139     Solution: have the strength of a spring decrease with depth in the
140     tree of rootedness - which we do require to be a tree.
142     Winding springs: during force propagation, a spring of equal or greater
143     strength will always pass the force through, while one of lesser strength
144     will pass the force through iff it is already fully wound.
146     Ball collisions can be the weakest level of force, so they will never wind
147     springs but can push other balls and tracked entities.
149     A spring-force which is inconsistent should be replaced with a reaction
150     force on the root entity, with the same strength, with this then being
151     checked for self- and (from the top) pairwise-consistency.
153     Hmm. Would be much neater to not have different strengths for different
154     springs. Can we avoid it?
156     Yes:
157         A spring will resist a force on the sprung entity which tends to
158         extend/compress the spring, unless the force is player-strength.
160         For remaining forces: a push/pull is transferred through a spring iff
161         the spring is at minimal/maximal extension or the spring is at an
162         angle to the force.
164         A spring not at its rest-length will generate a force on the sprung
165         entity. This never yields a force on the root.
167     Problem: pairwise consistency *isn't* enough.
168         e.g. consider a line of interlocked sprung entities with compressed
169         springs, such that the springs must decompress together.       
171         Consider this as a SAT problem, where we favour certain answers?
172         No.
174     Another problem: colliding entities.
175         If two entities are forced to move into the same hex by forces of the
176         same strength, we need to decide what to do. Currently I think it
177         would be better to block them both, rather than to introduce some
178         arbitrary rule to decide which gets precedence. The idea would be that
179         the gridded nature of the lock isn't just an artifact of our discrete
180         physics simulation, it's part of the underlying architecture of the
181         lock itself.
183         However, this does mean that checking for consistency is *not* simply
184         a matter of inducing forces and checking for multiple different forces
185         induced on the same entity. Actually it wasn't anyway; pivots can
186         break that too.
188 Reworking:
189     Forget "strengths". Instead, we split into two phases: player-generated
190     movement, then environment-generated movement.
192     Player-generated:
193         Hook and wrench forces are considered equal, and can block each other;
194         the hook never gets pushed around by the wrench.
195         Springs can be stretched and compressed freely within their limits.
197     Environment-generated:
198         A force on a sprung block is blocked if the spring's length isn't such
199         as to be generating the same force.
201         Allowing moveable pivots other than the hook results in unpleasantness
202         when forces and torques combine. So only fixed pivots are allowed.
204         (alternative would be to allow pivots on entities, but consider them
205         'locked' when there's a force on the entity, so any torque is blocked)
206     
207     Blocking:
208         note we still need to have forces propogating beyond a blockage to
209         block other forces - otherwise we easily get strange loops of the
210         "blocked iff not blocked" sort, leading to unintuitive complexity.
212 Detailed force resolution algorithm:
213     (to be run first for player forces, then for environmental forces)
215     Unoptimised version:
216         For each source force, propagate:
217             neighbouring entities get pushed,
218             neighbouring pushed arms induce torque on the pivot,
219             springs transmit forces where they can't absorb them.
220         
221         Record with the propagated force the sources which gave rise to it.
223         Look for blockages:
224             forces on fixed objects
225                 (player objects are fixed during environmental stage),
226             environmental forces on sprung entities not according with the
227                 spring length,
228             multiple forces in different directions on the same entity,
229             collisions;
230         in each case, mark the source forces involved as blocked.
231         Blockedness of a force is irrelevant when determining blockages.
233         Enact all forces with at least one unblocked source.
235         A ball which pushed something loses its momentum; a ball with a
236         resulting force gets momentum as well as moving.
238         A wrench whose force is blocked loses its momentum.
240     Optimising:
241         Just check for blockages while propagating.
242             
243         Could try to avoid propagating the same force twice from different
244         sources, but it would be more complicated than it's worth.
246 Problem:
247     This algorithm doesn't allow for gearing of the kind I'd envisioned.
249     Idea: split the concept of blocking into two: blocks and locks. A blocked
250         force is one which is "immediately self-inconsistent", e.g. which
251         pushes an entity against a fixed object or in a direction in which it
252         is fixed. A lock involves another object or another force or spring
253         tension. The idea then is that when a pivot turns, its arms try to
254         push against things in the obvious direction, but if that's blocked
255         (as opposed to locked) they try in the next-to-obvious direction
256         instead.
258         Quite intuitive, and shouldn't be hard to implement.
260         DONE
262     
264 Architecture:
265     Board state consists of:
266         list of entities:
267             type (hook, wrench, pivot, block, ball, frame)
268             shape
269             position
270             springs:
271                 root entity and position
272                 direction and natural length of spring
273                 position on current entity of connection
274                     (redundant, but included for debugging)
275             momentum
276     
277     The force resolution algorithm above simply maps states to states.
279 Remarks on unintuitive results of our algorithm:
280     Forces which seem like they should just "cancel out" can nonetheless block
281     third forces due to collisions. e.g. all three forces are blocked in:
282       # $ # /
283            o   # $ #
284       # $ # \
285     . One could imagine trying to delay or renege on the blocking of the
286     rightmost force. But this isn't straightforward; e.g. consider a setup like
287     the above whereby the pivot turning clockwise propagates to another
288     mismatch which should cancel out the bottom force, while similarly
289     anticlockwise should cancel the top force.
291     Generally, having forces block forces from blocking other forces opens a
292     can of knotted ouroboroses.
294     Concrete example: consider three pivots and a force on each producing
295     anticlockwise torque, and set things up such that an anticlockwise torque
296     on a pivot propagates to clockwise torque on the next. Then if we want the
297     rule to be that balanced forces don't propagate, we find that the forces
298     are balanced iff they're not.
300     So balanced forces must propagate - which means we're bound to get the
301     kinds of unintuitive nastiness exemplified in the diagram above.
303     So I should switch to trying to make it seem intuitive.
305     Actually, there may be another way, which is to take a hint from CD
306     as to how to cook those ouroboroses: when we get cycles as in the above
307     example, consider something balanced if it's *ever* balanced as we go
308     through the cycle. Would this lead us to surprising consequences with
309     pulses and latches?
311     Yes, it really would, just like in CD, only more surprising and harder to
312     think through. This can't be a good idea, can it?
314     Alternatively: work monotonically - once something is balanced, it remains
315     balanced.
317     So in other words: we use the current rules, but mismatches don't block.
318     once complete, we look for mismatches - multiple forces on a piece. If
319     there are any, we mark those pieces as 'balanced' (better term?), and run
320     the algorithm again; balanced pieces just resist any force. Iterate, only
321     adding to the list of balanced pieces, until we settle.
323     One thing to consider: what should happen when a force on a piece
324     propagates to some forces, one of which balances out the first force?
325     Wouldn't it be perverse to have that propagation nullified? So we should
326     say that it takes two differently sourced forces to cause a balance, and
327     one-source mismatches just lead to blocking as currently.
329     Simple enough to implement, and fairly intuitive in conception. Does it
330     lead to any pathological results? How unintuitive is a force which is
331     balanced out nonetheless having its propagands balance other forces?
332     Answer: at least as unintuitive as the behaviour which motivated all this.
333     Hrm.
335     So a CD-style "iterate until we loop" algorithm is the only one I
336     can see which would lead to reasonable behaviour - the problem being that
337     it also leads to a kind of complexity I was hoping to avoid. Hrm.
339     Not only that: at least in any way I can see it working, it would *still*
340     involve forces propagating beyond a balance having an effect, due to the
341     latch effect.
343     Note that the results of actual physics in these sorts of situations would
344     involve the finite speed of force transferal - which is something I
345     *really* don't want to be part of the game physics.
347     OK, so the current "quantum" physics stays.
349 Thoughts on representing blocks in the SDL client:
350     Draw every blocked (as opposed to instantly resisted) force. For
351     pivots/hooks: draw dimly arms in partially-rotated positions. For
352     blocks/wrenches: draw dimly shifted to the edge of their hex (when not
353     already drawing to the edge).
355 Tool resistance:
356                                                  /
357     Currently, the wrench can't move east in: - o
358                                                * \
359     This is kind of counterintuitive. Similarly with a pushing hook.
360     But if we allowed wrenches to turn cogs like this, while keeping with our
361     simple 'momentum' setup for wrenches and our current resistance mechanics,
362     it seems we would have to have a wrench resist all pushes not in its
363     current direction of motion, which means we lose the neat current mechanic
364     of being able to stop a wrench by pushing it to one side.
366     But on consideration, this is the lesser evil.
368 Spring oddity:
369     Following are stable:
370         # $ $ # s s %
371              #   % %
373         # $ $ # s s %
374                # % %
376     and so on. Complicated solution: bring back spring strengths.
378     Simpler solution desired!
380     Maybe not simpler, but neater: work directly with the porder of the DAG of
381     connections. A spring source-force dominates any source-force on an
382     ancestor of its root. Say "latter force *> former force" in this case (not
383     that it's transitive) we should see that we can't have x*>y*>x.
385     Indeed: if F:r->e and F':r'->e' are spring forces and F*>F' and F'*>F, then 
386     e' >= r > e >= r' > e' (porder of connectivity), which is a contradiction.
388     A sforce with source F can't block any sforce whose source dominates F.
390     Note we're left with some arguably surprising cases, e.g.
391          # $ $ $ #   #
392         #           #
393                % # # 
394         # $ $ % s s #
396     But that makes a kind of sense if we think of the right piece wobbling
397     leftwards... I think it's fine.
399     Implemented.
402 Rethinking the physics:
403     I think we took a wrong turning in trying to work one force at a time.
405     Rather, we should fully propagate each source force, and then look for
406     conflicts between the forcegroups. This seems to simplify many things,
407     and make multispring less problematic.
409     Propagation: sources and propagands are allowed to be ordered lists of
410     forces; if one fails, the next is tried; if all fail, the source/propagand
411     fails. Recursively propagate out; a force fails if it is resisted or if
412     one of its propagands fails. No need for special 'mediated resistance',
413     nor for partially compressed springs to convey forces.
415     Then check for pairwise conflicts between forcegroups:
416         mismatches (different forces on the same piece)
417         pairwise collisions (applying each forcegroup to the initial state)
418     using domination rules to determine resulting blocks.
420     Implemented.