BF Joust strategies

BF Joust is more involved than its simple rules might suggest. This page details some of the major strategies which are used, and gives a short example for each. It then lists major programs and describes the strategies they use.

Rush
Rush-based programs focus entirely on attack, though they may use any attack strategy. They tend to be further divided into "slow rushes" that set large decoys, and "fast rushes" which try instead to reach the opponent before they can set up their own decoys.

The rule of nine
There is no purpose in attacking the first 9 cells, since the enemy's flag will never occupy one of them. For this reason, it is common to start fast rush programs with code similar to (>)*9 Slow rush programs will instead generally set up decoys and then move to the tenth square of the tape and start attacking from there, in the hope of avoiding decoys.

Many programs want to skip the first nine cells this way, and then do something (such as a clear) on each of the potential remaining 21 cells. In such cases, the simplest code puts 8 of the > commands into an initial repeat, and then the ninth inside a loop, in order to give nine > instructions but not run off the end of a length-30 tape if the "something" fails: (>)*8(>do something)*21

The only reason a rush program might want to not start at the tenth cell is if it's specifically trying to detect decoys, as in tripwire avoidance; even though the enemy flag cannot be in the first 9 cells, enemy decoys can be, and this can sometimes produce useful information.

Exploiting the fact that an opponent uses this tactic is difficult or impossible; in theory it can give away information about the tape length, but it's too easily confused by decoys and similar tactics.

Clear
The most obvious strategy is to look for any set cells and zero them as in Brainfuck. [-] The canonical example of this strategy is (>)*9([-]>)*21

This is known as a "two-cycle clear", and is fast and common, but can often be exploited by defence programs. A slower "three-cycle clear" looks like this: [-.] (or alternatively with the  and   reversed). Three-cycle clears are less generally exploitable by defence programs; sometimes programs use five-cycle clears to make absolutely sure. (Four-cycle clears are not useful as they are generally locked by the same code that locks two-cycle clears.)

The vast majority of programs use some kind of clear. A simple non-offset clear such as this one, though, is very vulnerable to decoys; a decoy with one polarity (positive or negative depending on whether the clear is based on  or  ) will have almost no effect, but a decoy with the other polarity will cause the clear to take much longer, as it has to cycle through all the possible values until it reaches 0 from the other direction. Another method of exploiting an enemy clear is to lock it; this requires knowing or guessing the cycle length of the clear, and thus two-cycle clears, being the most common, are also the most vulnerable to this.

Offset clear
It is common to set values in several cells as decoys. The values of these decoys are usually very close to zero, often 255 (-1) or 1. As such, using a clear such as [-] will result in the maximum cycles spent looping to clear a cell. This can be offset by first adding an expected decoy value. (+)*9[-] The canonical example of an offset clear program (with offset 9) is (>)*9([(+)*9[-]]>)*21

Offset clears are less prone to decoys than regular clears, but can still be defeated by decoys larger than the offset. They are slower on average than typical clears, too, and almost as vulnerable to locking (unless the offset is large enough to throw off the timing of the lock). Additionally, offset clears have a distinctive pattern of zeroing on a small tripwire of the right polarity (first zeroing for one cycle, and then later zeroing for two cycles), meaning that a carefully placed tripwire can detect the presence, polarity, and offset of an offset clear, making locking it very easy unless it varies from one tape position to the next.

Wiggle clear
A further extension of the offset clear concept is to have nested loops in opposite directions, to catch small values quickly. For instance, the clearing algorithm ([-{ ([+{ [-] }])%8 }])%4 will finish quickly with values from -4 to 4. The inner clear algorithm can be anything, including a larger offset clear. Combining wiggle clear with offset clear yields the canonical program (>)*9 ( ([-{ ([+{ (+)*9[-] }])%8 }])%4 >)*21

One downside of the canonical wiggle clear is the part where it closes a lot of brackets in a row before moving on to the next cell. This renders it vulnerable to triplocks, which begin rebuilding a just-cleared decoy by the time the third closing bracket is reached, preventing the algorithm from ever being able to leave the triplocked cell. The most straightforward way to avoid this is to add an extra clear before every closing bracket in this row. A canonical offset wiggle clear algorithm with triplock avoidance might look like: (+)*4 ([-{ [(-)*15 [+]] }[-]])%8 Note that this algorithm will be stuck clearing the triplocked cell of a good triplocker for nine consecutive triplock phases, and this may be enough for the triplocker to win on short tapes. However, it typically takes more than 9 triplock phases to full clear a long tape, at which point this clear algorithm will finish and the program using it will move on the next cell. If the triplocker uses the tripwire method to wait for its opponent to clear its triplock, it will sit waiting forever while the program using this clear moves on to take out its flag.

Here is a direct incorporation of the above clear algorithm into an otherwise simple program defeating a sophisticated triplocker on all tape lengths and polarities:

Careless clear
The careless clear, also known as the turtle strategy, is a fast way of clearing cells which are equal to the value 128, the initial flag value. Rather than looping, the attacker simply adds or subtracts 128 from the cell, then waits one cycle in case the flag is zero. This saves one cycle per iteration (since there are no true iterations), but is expensive on small values. (-)*128. This of course will only have the desired result if the flag is not changed by the opponent. The canonical example of this careless clear is (>)*9([(-)*128.>]>)*21

The careless clear can also be combined with the concept of the offset clear to support some range of values by waiting after a sequence of modifications rather than only the last one. For instance, to support values from 112 to 144: (-)*112(-.)*32

Although very fast against large decoys, careless clears can typically be tricked off the end of the tape simply by changing the value of your own flag (although the amount often has to be quite large); for much the same reason, they tend to be defeated by shudder-based programs, and often also by vibration-based programs. Setting a lot of decoys helps as well, as small and moderate-sized decoys can be set much faster than the careless clear can clear them.

Reverse offset clear
When you suspect you're on an opposing flag, but fear the opponent might have used anti-careless-clear tactics, it is possible to use an offset clear centred about 128, rather than 0; instead of a small offset then clearing in the opposite direction to the offset, it uses an offset just below 128 followed by a clear in the same direction. The following program does this, after first checking for decoys of size 1: >+>-(>)*6(>[-[++[(+)*120[+]]]])*21

The advantage of this over a careless clear that checks a range of values is that it's guaranteed to clear an undefended cell eventually even if it's been set to a very unusual value for a flag, rather than running off the tape like a careless clear would; and the advantage over a regular offset clear is that for flags near 128, it's almost twice as fast. The disadvantage is that if the cell isn't actually a flag, it can take much longer than a typical offset clear would, leaving it vulnerable to being outraced. It's thus mostly useful only if you have a good idea that the current cell is a flag.

Anti-shudder clear
Due to the danger of suiciding when the opponent changes their own flag to zero and back again, it is now necessary to make sure to close at least two brackets before moving to the next cell. Generally speaking, this consists of a simple clear like a two-cycle clear, followed by a clear with a much large cycle length in order to beat defenders and shudderers. This following example does this, with a two-cycle offset clear followed by an eleven-cycle clear, and in addition ensures that the canonical shudder program will die in as few cycles as possible: (>)*8(>[(+)*9[-].[.++---]])*21

Such clears are only slightly slower than typical clears (and likewise, an offset anti-shudder clear is comparable and slightly slower than a straight offset clear), and are hard to take advantage of via defensive programs. Lock-based programs can sometimes lock such clears on their first, simple clear, though, although generally only if they know their polarity (the lock will break eventually otherwise, but can sometimes take a very long time to do so, sometimes long enough for the lock-based program to clear three or four tape cells even via the slow full-tape clear method).

Timer clear
One interesting way to evade locks is to simply stop clearing after a while and move onto a different strategy; this allows fast two-cycle clears to be used to defeat rush programs, whilst still not being trivially locked by defence-based programs. Here's a simple example, showing a program that changes clear direction and period after 500 clear attempts, and running it against a trivial lock-based program that tries to lock the sort of clear that the program starts with into an infinite loop and a draw (in practice, lock-based programs would probably be trying to win, but the same principle applies): (>)*8(>([-{[+.]}>])%500)%21

The major issue with timer clears is their inflexibility; although it is relatively easy to make an offset and/or anti-shudder timer clear, most other modifications are difficult or impossible. (For instance, they cannot check to see if a cell is 0 before offsetting it, like most offset clear algorithms do.)

Flexible timer clear
The inflexibility of timer clears can be effectively worked around simply by embedding multiple timer clears inside each other, putting each inside timer clear just after the > of the one immediately afterwards. This allows each of the individual clears to be on a separate timer; it also allows pretty much arbitrary modifications to the clear algorithm, which can even be different for differently positioned decoys. Here's an example using offset clears with multiple different offsets: (>)*8(>(+)*24([-{[+.]}>(+)*23             ([-{[+.]}>(+)*22              ([-{[+.]}>(+)*21              ([-{[+.]}>(+)*20 (etc...)              )%500])%500])%500])%500)*-1 It works as a drop-in replacement for standard sorts of clear, as it works identically before the timer expires; the main disadvantage is its length, which can make it hard to fit into more control-heavy programs. Unlike regular timer clears, it cannot reasonably be nested, and so can be locked if the defending program finds a lock pattern that works on both the inside and the outside clear loops.

Full-tape clear
If the opponent does not do anything, it is possible to definitively clear the opposing flag in constant time by setting every element on the tape to each of its 256 possible values and waiting a turn. Naive code to do that looks something like this: (>)*8(>(+.)*255)*21

Such an approach is both incredibly slow and prone to locking, and thus rarely if ever seen in successful rush programs. However, its fixed-cycle and easily interruptible nature makes it incredibly valuable in programs that use locks themselves; if the opponent is successfully locked, a defensive program can alternate between maintaining the lock and continuing its full-tape clear. As such, this is considered a very defensive form of attack, generally used only by defense programs to finish off their opponent.

To beat a full-tape clear, all that is necessary is to be able to act freely, rather than stuck in a lock; almost any aggressive technique will outrace it, and many defensive techniques will beat it too, although often with a tape length dependency.

Poke
A slow rusher can usually win by having more decoys than its opponent, and there are two ways to do that: by making your opponent have fewer decoys, and by building yourself more decoys. Poking is a strategy that makes both of these things possible. The idea is to find out where your opponent is before building any of your own decoys, and then adjusting your decoy line forward so as to make room for more of your own decoys, and also possibly to identify a safe location behind your opponent's decoys, depending on how they build them. Here is an example: (>)*8(>[(<)*8(-)*33(>)*8([-[++[(+)*9[-]]]]>)*21])*21

Pokes can be made less effective by setting up advanced decoys early, in order to cause them to think the tape is shorter than it is; this is rarely fatal to the opposing program, but will often slow it down. Additionally, rushing forwards quickly gives a chance to get behind the opponent's decoys before they are set up, making the enemy flag very easy to find. Thus, poke-based programs normally need a separate countermeasure against fast rush programs.

Poke can be combined with the wiggle clear to produce a deep poke. This strategy was first used in Gregor_furry_furry_strapon_pegging_girls, see its behavior against Deewiant_pendolino for a good example:

Tripwire avoidance
Any program that assumes the first nonzero cell it sees is probably a decoy and keeps searching. Here is an example: (>)*8(>[(>[(+)*9[-]]>)*21])*21

Note that it will suicide against any opponent that doesn't make decoys. Or, that doesn't make them fast enough for it to see them. (Tripwire avoiders will sometimes wait deliberately, and change the Rule of Nine to a Rule of Seven or whatever, in order to make sure that if the opponent makes decoys, it spots them. However, this removes some of their speed advantage.)

Tripwire avoidance can be defeated by setting up no decoys, but this is often a bad idea due to the huge disadvantage it gives against other sorts of programs. Alternatively, setting up an extra small decoy is often all that is needed to neutralize the tactic.

Reverse tripwire avoidance
For small reverse tripwires, it is possible to avoid triggering them by restoring them to their original value before moving away from them, such as the following example that restores all size 1 decoys:

(>)*8((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]_>((>[-[{(++[(+)*9[-]]_> ((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]-> ((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]-> ((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]-> ((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]->((>[-[{(++[(+)*9[-]]-> ((>[-[{(++[(+)*9[-]]->[-]{}])}]]+)%1)*2{}])}]]+)%2)*2{}])}]]+)%3)*3{}])}]]+)%4)*4 {}])}]]+)%5)*5{}])}]]+)%6)*6{}])}]]+)%7)*7{}])}]]+)%8)*8{}])}]]+)%9)*9{}])}]]+)%10)*10{}])}]]+)%11)*11 {}])}]]+)%12)*12{}])}]]+)%13)*13{}])}]]+)%14)*14{}])}]]+)%15)*15{}])}]]+)%16)*16{}])}]]+)%17)*17 {}])}]]+)%18)*18{}])}]]+)%19)*19{}])}]]+)%20)*20{}])}]]+)%21)*21

As you can see, it is not very compact or flexible. Futhermore, this only works if the program using them does not check them constantly, as in this case, it would check them before you had a chance to restore them. However, the primary benefit of reverse tripwires is their ability to allow a program to go do other things before determining if the tripwire has been tripped, so most programs that use them will not check them immediately. However, in the age of larger reverse tripwires, it may become prohibitively expensive to restore their value.

In the case of programs that use small reverse tripwires and check them incessantly, just checking several times in a row if a cell is nonzero before attempting to modify it can avoid them in many timings/tape lengths (at the cost of slowing down your decoy clear by a few cycles):

(>)*8(>[[+[--[(+)*9[-++]-]]]])*21

Reverse decoy avoidance
If you assume that the opponent uses a reverse decoy setup, you can look for a nonzero cell, assume it's the first of a series of reverse decoys, then keep moving until you find an empty cell, then move again until you find a nonempty cell, asssume it's a flag, and clear it (e.g. with a reverse offset clear, wiggled to avoid small decoys, as shown in the example below):

>>>>(>>]((>++++[-[-[-[-[-[-[-[-[(-)*120[-]{}]]]]]]])%29)*-1])*-1

As written, the strategy is trivially defeated by a trail or forward decoy setup, making it a very fast risky rushing strategy. (Some protection against trails can be added either by looking for small values on cells, which has problems of its own, or more plausibly deciding that a reverse decoy setup is probably not in use after seeing a suspiciously large number of decoys.) It also tends to be quite sensitive to the details of the opposing programs (the details of decoy setups tend to vary wildly).

Interrupted rush
On short tape lengths, rushers benefit from rushing as quickly as possible to prevent the enemy taking a lot of time to build more and larger decoys, or at least to tear through their decoys nearly as fast as they can be built. However, since decoy building is always faster than decoy clearing, on longer tapes it benefits one to slow down the opponent by having a lot of decoys. Thus, a very effective strategy is to rush for a while, then go back and build more decoys when you believe your initial set of decoys has almost been destroyed, then go back and rush some more. Examine Deewiant_pendolino in the Major Programs section for an example of this strategy. One might also wish to interrupt a rush to do other things, like switch to a defense strategy, or repair your own flag a little bit. These sorts of ideas are advanced strategies that have not yet been thoroughly explored.

Defense
Note that no defense programs are simple (the very nature of a defensive strategy makes this so), and as such the examples used here are generally ineffectual on their own and sometimes not very short. They are presented simply to give the gist of a particular strategy, without being a good program in and of themselves. Defensive tactics can sometimes also be useful even for rush programs, in order to buy more time for their rush.

Decoy
The most fundamental defensive strategy is the decoy. Since it is impossible for an attacker to distinguish the flag from any other non-zero cell, filling a section of the tape with various non-zero values can slow down or even foil an attack. It is important to use both positive and negative decoys, so that your decoys slow down your opponent equally in either polarity. (>+>-)*4 Decoys can be further split into "small decoys" that are less than the range of a typical offset clear (or in the case of a wiggle clear, the offset of the wiggle) and serve only to trick the opposing program into thinking that something is there (in order to defeat tripwire-avoiders, confuse pokes, etc.); and "large decoys" that are designed to be large enough to force even offset clears with the wrong polarity to clear the cell in the wrong direction. It is also common to refer to "medium decoys", which are larger than the wiggle offset of a typical wiggle clear, but smaller than the much larger offset that follows the wiggle.

Since decoys are unlikely to cause an opponent to lose on their own, they are almost always combined with other strategies. They are common in all sorts of programs to buy time to perform their strategies (in rush programs, to try to buy time with an undefended flag in order to zero the enemy flag first; in defence programs, in order to set up tripwires and adjust cells on which locks will be performed to sane starting values).

A few programs may benefit from not setting up decoys. This normally happens with bad programs, where setting up decoys may not aid their strategy because it was a bad idea in the first place, whereas not setting up decoys can trick tripwire avoiders off the end of the tape; this is not particularly useful, though, as it won't correct the fundamental flaws in the original program. Alternatively, not using decoys can benefit defence programs that don't use tripwires either, in order to make the likely timings of the opponent a little more consistent, and aid them in setting up a successful lock or shudder.

Small decoys are typically avoided via offset clears. For large decoys, using a fast clear is one possible method to avoid them. However, techniques like tripwire avoidance and careless clears work better by entirely neutralising the decoy's effect, and defensive programs, especially those that use tripwires for synchronization, generally care nothing for opposing decoys, either due to not attacking at all, or due to using full-tape clears that are unaffected by decoy values.

Reverse decoy setup
When setting a large number of decoys, it is often a good idea to set the ones nearer the enemy flag first, in order that the first few decoys might buy time to set up the last few against faster rush programs. >>>>>(-)*64<(+)*64<(-)*64<(+)*64(>)*7(>[-])*21 Here's a comparison (decoy setups versus a simple rush program): The major disadvantage of a reverse decoy setup is on shorter tapes, where instead of some of a large number of decoys working, none do (assuming that the opponent skips them via the Rule of Nine), and the program is defeated trivially. Therefore, it is often a good idea to gain an idea of what the tape length is before embarking upon such a system. Unless the first few decoys are small and set up quickly, reverse decoy setups also often lose to tripwire avoidance, as the first decoys are skipped before the later decoys are set up. If it's suspected that the opponent is using a reverse decoy setup, it's also possible to skip cells until a zero cell is found, but doing so is incredibly risky as it will suicide against other sorts of programs.
 * Forward decoy setup:
 * Reverse decoy setup:

Interrupted decoy setup
Just as building the decoys furthest from your flag can give you more time to build more decoys behind it, one can also use the time afforded by the building of a decoy to do other things. This could be considered an "advanced" strategy. Check out the animation/trace of quintopia_space_elevator in the Major Programs section to see a decoy build interrupted to check a reverse tripwire. Also, see Deewiant_pendolino for an example of a decoy build interrupted to rush the opponent.

Breadcrumb decoys
By making a series of very small decoys as a trail, then reversing to the beginning of the tape and building them further, it is possible to build decoys precisely until the opponent is encountered. This is done by using the small decoys as reverse tripwires to see if the opponent has come this far already. The idea is to check a reverse tripwire immediately before each decoy is built and interrupt the build as soon as the opponent has been detected, changing strategies based on when and where the opponent was last seen. This is a special type of interrupted decoy build.

Here is a small example program that uses this strategy: >-(>[<<<(+)*10(<+[{}(+)*23>(--[(>)*7(>[(-)*15[+]])*21](-)*23>)*24])*24]+)*10 <<<(+)*10(<+[{}(+)*23>(--[(>)*8(>[(-)*15[+]])*21](-)*23>)*24])*24

Also, compare the early-game behavior (immediately after the deep poke) of Gregor_furry_furry_strapon_pegging_girls, the program that introduced it, with quintopia_a, a later champion that uses it, as they compete:

Trail
Any attacker which zeroes cells may also choose to change that value before moving on to the next cell. That changed value can effectively be a decoy. For instance, the below program combines a simple clear with a trail. (>+>-)*4>([-]+>[-]->)*11

Leaving a trail generally helps against slowly rushing enemies, as it effectively gives you a lot of free decoys. It hurts against fast rush programs, though, which will generally have overtaken you before the trail is even set, and in such cases a trail will just slow you down. (Not to mention, that offset clears can easily avoid a typical trail.) A typical defence program won't even notice whether you're trailing or not.

Lock
Upon somehow deducing the location of the enemy data pointer (e.g. via a tripwire), and its clear mechanism (e.g. guessing a two-cycle clear or two-cycle offset clear), it is possible to turn the cell in question in the other direction at much the same rate, thus forcing the opponent into an infinite loop. Locking can also be done less reliably by turning the cell in the same direction, such that the cell never becomes zero for two cycles; the risk in this is that the opponent can detect that it's happening and take countermeasures. If a defense program is lucky, the opponent will respond by falling off the tape, but rush programs rarely fall for such tactics nowadays. Here is possibly the simplest lock-based program: >+[]<(.)*90 ((+)*256 (>)*9 (+.)*119 (<)*9)*3 ((+)*256 (>)*10 (+.)*118 (<)*10)*3 ((+)*256 (>)*11 (+.)*117 (<)*11)*3 ((+)*256 (>)*12 (+.)*116 (<)*12)*3 ((+)*256 (>)*13 (+.)*115 (<)*13)*3 ((+)*256 (>)*14 (+.)*114 (<)*14)*3 ((+)*256 (>)*15 (+.)*113 (<)*15)*3 ((+)*256 (>)*16 (+.)*112 (<)*16)*3 ((+)*256 (>)*17 (+.)*111 (<)*17)*3 ((+)*256 (>)*18 (+.)*110 (<)*18)*3 ((+)*256 (>)*19 (+.)*109 (<)*19)*3 ((+)*256 (>)*20 (+.)*108 (<)*20)*3 ((+)*256 (>)*21 (+.)*107 (<)*21)*3 ((+)*256 (>)*22 (+.)*106 (<)*22)*3 ((+)*256 (>)*23 (+.)*105 (<)*23)*3 ((+)*256 (>)*24 (+.)*104 (<)*24)*3 ((+)*256 (>)*25 (+.)*103 (<)*25)*3 ((+)*256 (>)*26 (+.)*102 (<)*26)*3 ((+)*256 (>)*27 (+.)*101 (<)*27)*3 ((+)*256 (>)*28 (+.)*100 (<)*28)*3 ((+)*256 (>)*29 (+.)*99 (<)*29)*3

Locks can be broken by using a different clear algorithm from the one they expect; using a three-cycle clear is typically enough for this, and a five-cycle clear nearly always escapes locks perfectly. Varying clear algorithms from tape cell to tape cell makes it much harder for a lock to guess what you are doing. Locks are also incapable of trapping careless clears and full-tape clears on any particular cell, but often beat them anyway by incidentally tricking them off the end of the tape, so they cannot be seen as sensible countermeasures.

Another way to break free from a lock is to change strategy after a while, either based on time, or on observing a zero cell for one cycle but not two (a giveaway that you are either being locked, or facing a vibration or shudder program). The anti-shudder clear exploits this by dropping through into something crazy, like an eleven-cycle clear, that no program could be expected to have an appropriate lock algorithm for, or to figure out that that clear algorithm was being used even if it did.

Probabilistic lock
The invention of the timer clear meant that new defensive strategies were needed for defence programs to be able to maintain their locks. The probabilistic clear abandons any attempt to synchronize with an opponent; rather, it resigns itself to the fact that it will only get a lock some proportion of the time.

The basic idea is to pick a length of time that's a small power of 2 (such as 128 cycles), then throughout most of that time, increase then decrease the cell you're locking on. Here's an example from ais523_preparation (which increases and decreases the cell by 56.5 cycles, alternating the rounding between iterations):

(+)*56(-)*57(.)*15 (+)*57(-)*56(.)*15

Because the lock's repeat rate is a power of two and makes no net change to the lock cell, the sequence of values on the lock cell will have the same repeat rate as the opponent's clear loop would naturally; in particular, this means that the lock loop and the opposing clear loop will always be at the same point whenever the lock cell hits zero. Almost half the time, the lock is working with the opponent; almost half the time, it's working against. However, the value of the lock cell moves faster when the two loops are moving it in the same direction, than when they're moving it in opposite directions. Thus, the odds are in favour of both loops pushing in the same direction as the lock cell crosses zero, which is the most important condition for a lock to hold.

This sort of lock thus gives a chance to defeat timer clears. Because it does not care that much about the details of the enemy's clear loop, there is a good chance that, having changed clear loop upon the timer expiry, the new loop will immediately be locked again by the probabilistic lock.

The huge downside is that this sort of lock only leaves a very small window to do anything other than maintain the lock. The above example gives fifteen cycles of freedom each time round the lock; this is enough to (very slowly) check or adjust cells within seven cells of the lock, but does not allow any sort of full-tape clear. Typical uses of this time would be to set up very strong decoys, and/or to check tripwires to see if the lock had broken (which can happen by chance, due to the lack of any meaningful sort of synchronization).

Triplock
A triplock is a different sort of lock, which targets a different part of the opponent's program. While ordinary locks are almost exclusively aimed at the opponent's clear loop, a triplock instead tries to lock the opponent in its control flow; to be precise, it locks programs that contain three or more  commands in a row, which is very common in constructs such as , and can appear in programs for other reasons. (To be precise, it locks programs that contain three  commands with no   or   in between immediately after a clear (with the first   possibly part of the clear loop), and also certain other control flow patterns.)

The basic idea is to wait until a decoy (that cannot be the flag, unlike an ordinary lock) becomes zeroed, then immediately set it up to a large nonzero value again, using tripwire code such as. In order to actually win with the lock, it needs to be combined with a clear. A full-tape clear can be used, as typically is with locks; but a triplock (and no other known defensive construct, at the moment) also allows a much shorter "inline clear", allowing for a complete program that looks much like this: >(+)*128[](+)*128>(+)*15[-]<[](+)*128>>(+)*15[-]<<[[](+)*128>>[>]->([-{[<+]}])%10<[<]<] (warning: can be very slow to load on several browsers)

(The inline clear works by using the tape itself as working space to remember how far through its clear algorithm it is; but because it takes a variable rather than fixed amount of time like a full-tape clear does, it cannot be combined with an ordinary lock.) Other clears can also be used, such as 2-cycle clears or offset clears, but run quite a risk of the opponent breaking the lock if clearing one cell takes too long.

The simplest way to beat a triplock is merely to avoid the sort of control flow structures it is vulnerable to, but this can often cripple a program for other reasons (inability to use complex control flow is quite serious). Alternatively, certain types of clear algorithms can avoid triplocks by breaking the lock faster than it can complete its clear (reverse offset clears tend to do this, often on only one polarity depending on the details of the triplock). It may also be possible for a program to detect that it has been triplocked, although ordinary locks pushing in the wrong direction look much the same from the point of view of a program, so programs need to experiment further to determine exactly how the opponent is trying to lock them.

There is an interesting symmetry between triplock and shudder programs, incidentally; shudder programs tend to beat programs that check the value of the tape cell they are on once before moving on, whereas triplock programs tend to beat programs that check the value three or more times before moving on. Two would thus seem to be the optimal number, in a program that can afford the control flow restrictions that come with it.

Shudder
Shuddering is cycling the value of the flag through increments and decrements in order to cause attack loops to fail to put the flag at 0 for two cycles. The reversal of direction is key to this strategy. It usually results in a net increase/decrease over time, differentiating it from a "vibrator" below. Although moderately good as a defensive strategy, it tends to lose at random on certain tape lengths, and it renders its user entirely incapable of attacking as the shudder needs to be kept up constantly. Shudder programs thus can only win by tricking the opponent off the end of the tape. Canonical example: (++-)*100000

As shuddering can only win by tricking its opponent off the end of the tape, waiting for two zero cycles in a row before moving on guarantees at least a draw against it, and often a win. Clears designed to exploit this property are known as anti-shudder clears.

Vibrator
A vibrator vibrates its own flag between zero and nonzero constantly, thus making it very dangerous to assume a flag is cleared just because it hit zero. However, it is very easy to defeat by using a clear that is out of phase with its vibration. Canonical example: (-)*127(-+)*100000

If you fear falling off the tape due to missing a vibrator, you can check two cycles in a row before moving on (the difference between this and counter-shudder tactics is that it must be done at the start of the loop, rather than the end).

Reconnaissance
Although a program that entirely ignores it's opponents attacks (and in some cases, even the opponent's defences) can be effective, many programs will want to discover information about their opponent's strategy, the opponent's current actions (e.g. to synchronize a lock), the tape length, or some combination of these.

Tripwire
A tripwire is one of the simplest reconnaissance and synchronization devices, detecting an enemy clear loop.

An ordinary tripwire looks like this: +[] What it does is obvious, as it causes you to wait for your opponent to clear it. Using one is guaranteed to make you draw with any non-attacking program.

A variation is the two-cycle tripwire: + which waits for a cell to be zeroed for two cycles before you can continue. (The similarity of this to BF Joust's main victory condition means that all but careless clears are likely to do this, and probably just before moving to the next cell, rather than as part of an offset clear).

Sometimes tripwires will have a value larger than 1 or -1; this is typically for the purpose of causing the length of time it takes to zero them to provide more information about the opponent's strategy.

Tripwire avoidance allows obvious tripwires to be skipped altogether, giving an easy win, but is itself fooled by decoys. Careless clears will not trip an ordinary tripwire at one polarity, nor a two-cycle tripwire at either polarity, meaning that they can often beat tripwire-based programs too. (However, tripwire-based programs that change the value of their flag can exploit this effect in reverse, to beat careless clears by tricking them off the end of the tape!)

Timer tripwire
A normal tripwire represents a significant commitment; the program will not leave its tripwire until it is tripped, and if the tripwire is never tripped, the warrior cannot win. But a quality defense program is afraid of commitment, and wants to be able to change its mind if it thinks the tripwire will never get tripped. If it also cannot tolerate the synchronization inaccuracies associated with a reverse tripwire, it can use a timer tripwire, wherein it stops monitoring the tripwire after a preset number of cycles:

++++++([{give up and switch strategies}][handle careless or offset clear]synchronization achieved)%1000

Reverse tripwire
An alternative sort of tripwire, that allows other operations to be done at the same time. +go do some other processing-[tripped]not tripped This allows you to go do other things like building decoys or deciding whether you want to attack instead of wait any longer. As long as you check it often enough, you can get almost as close to being in sync with your opponent as you could be using a regular tripwire, and yet still be able to change your mind about defending, perhaps due to a belief that the opponent is also a defender.

The reverse tripwire has three major weaknesses: an opponent can set it back to its previous value (generally 1 or -1) upon clearing it; if the opponent encounters it while its value happens to be 0, they may miss its existence entirely and skip past it; and a cautious opponent can observe that its value becomes 0 every now and then (a giveaway for a reverse tripwire or a vibrated flag) and choose not to clear it. (Also, they aren't cycle-accurate with respect to timing like regular tripwires are; for some defensive programs, this matters, for most it won't.) Sometimes values greater than 1 or -1 are used for reverse tripwires in order to reduce the risks, but that also means they take longer to check. Tripwire avoidance also works almost as well against reverse tripwires as it does against regular tripwires. Using multiple reverse tripwires is common in programs that use any, as a method of increasing the chance that at least one spots the opponent, and helping to defeat countermeasures.

Decoy detection
Most programs will not change a tape element that's already set to zero, even if they encounter it during a clear loop; and even if they do (e.g. as part of a trail), if a program changes a tape element, that's a sure sign that it at least knows that the tape is long enough to contain that element. As such, decoys beyond that point are not helpful.

You can detect enemy decoys simply by doing a  on an element before setting a decoy there. If you discover it to be nonzero, there is no point in placing a decoy there or anywhere beyond that point. Lymia_nyuroki was the first program to exploit this in a reverse decoy setup, to detect clashing decoy patterns. An earlier and more common use involves placing decoys up to the first enemy decoy detected (e.g. via using a large trail); this is often wasteful against aggressive programs who set few decoys (as they will rule-of-nine past some), but helps against programs that set a lot of decoys (because the rule of nine is less useful for those programs), and against defensive programs (where the time wasted setting up unused decoys is not very costly, and thus setting up possibly-useful-possibly-useless decoys is worthwhile as the upside outweighs the downside).

Major Programs
This section details major programs, where "major" is, for the most part, defined as having been #1 on the #esoteric hill, and gives the source for the latest version.

2010
jix_wiggle3 and myndzi_slowrush fought for first during 2010, no other competitor ever achieved first place.