Algorithmically generate a Zebra/Einstein puzzle - algorithm

Firstly I'm not necessarily looking for a complete algorithm I can just copy and paste, then call it a day. Any "general approach" solutions would be fine for me!
This entire post was spurred by a slow day at work, and stumbling upon this site and not being able to figure out how they implemented their generator.
The Problem
For those of you who don't know, the "Zebra Puzzle" or "Einstein's Puzzle" is a famous logic puzzle that you've probably ran into before.
The full wiki article is here, but I'll post the relevent bits.
There are five houses.
The Englishman lives in the red house.
The Spaniard owns the dog.
Coffee is drunk in the green house.
The Ukrainian drinks tea.
The green house is immediately to the right of the ivory house.
The Old Gold smoker owns snails.
Kools are smoked in the yellow house.
Milk is drunk in the middle house.
The Norwegian lives in the first house.
The man who smokes Chesterfields lives in the house next to the man with the fox.
Kools are smoked in the house next to the house where the horse is kept.
The Lucky Strike smoker drinks orange juice.
The Japanese smokes Parliaments.
The Norwegian lives next to the blue house.
Now, who drinks water? Who owns the zebra? In the interest of clarity, it must be
added that each of the five houses is painted a different color, and their inhabitants
are of different national extractions, own different pets, drink different beverages
and smoke different brands of American cigarets [sic]. One other thing: in statement
6, right means your right.
This is all well and good. I've found several concise and neat ways online to solve this problem, especially using constraint programming. However, what interests me is making more of these types of puzzles.
Making More
Obviously, a matrix representation is a logical way to think about this. With each column containing a person, house, what they drink, what type of car they drive, etc.
My initial thought was to start with a randomly generated grid that is complete (ie, solved) then (somehow) create hints from the solved version that uniquely identify it. Every time something can be determined, it's removed from the grid.
Ripping off the site I listed at the beginning, the following "hints" that can be used to solve the grid can be of the following type:
The person/animal/plant lives/grows in a given house.
The person/animal/plant does not live/grow in a given house.
The person/animal/plant lives in the same house as the other
person/animal/plant.
The person/animal/plant is a direct neighbor of the other
person/animal/plant.
The person/animal/plant is the left or right neighbor of other
person/animal/plant.
There is one house between the person/animal/plant and the other
person/animal/plant.
There is one house between the person/animal/plan and the other
person/animal/plant on the left or right.
There are two houses between the person/animal/plant and the other
person/animal/plant.
There are two houses between the person/animal/plan and the other
person/animal/plant on the left or right.
The person/animal/plant lives left or right from the other
person/animal/plant.
You can see how these could be generalized, extended, etc;
The difficulty is, using my approach (starting with a complete grid and generating these hints), I'm not sure how to make sure the set of hints I create would absolutely result in the target grid.
For example, if you say "The Englishman does not own a pine tree" you cannot decisively pair two things together at any given time in the puzzle. Yet if there were only two trees remaining to solve for, this could in fact be a decisive piece of evidence.
Am I thinking about this the entirely wrong way?
Would a better approach be to create a grid with some randomized, pre-defined known elements (ie, the red house is in the middle) and then build up the grid using these hints as rules for building?
Any advice, articles to read, programming techniques to learn about, etc. would be greatly appreciated!

Here's a simple algorithm making use of your solver:
Generate a random puzzle instance.
Build a set C of all possible clues that pertain to this puzzle instance. (There are a finite and in fact quite small number of possible clues: for example if there are 5 houses, there are 5 possible clues of the form "Person A lives in house B", 8 possible clues of the form "Person A lives next to house B", and so on.)
Pick a random permutation c1, c2, ..., cn of the clues in C.
Set i = 1.
If i > n, we are done. The set C of clues is minimal.
Let D = C − { ci }. Run your solver on the set D of clues and count the number of possible solutions.
If there is exactly one solution, set C = D.
Set i = i + 1 and go back to step 5.
(You can speed this up by removing clues in batches rather than one at a time, but it makes the algorithm more complicated to describe.)

I'm not entirely confident in this solution but this is how I would approach it:
Starting from a random solution (i.e. green house holds polish that smokes LM, red house holds irish that smokes cloves etc). you may look at that solution as a graph of relations between statements. where every element (polish, red house etc) is connected to all other elements either by a "yes" edge or a "no edge" (in our case the polish is connected to the green house with a "yes" edge and to the cloves with a "no edge" (amongst many other edges, this initial graph is a full, directional graph)).
Now, if you randomly take away edges, until you are left with a minimal connected graph, you should have a graph representing a solvable puzzle. translate every yes edge to "the foo is/does bar" and every no edge to "the foo isn't/doesn't bar".
intuitively this sounds about right to me. but again, this is in no way a formal or recognized way to do this, and may be entirely wrong.

You can also do it the other way around (which will get you a solver as well):
Generate S, a list of all possible solutions (i.e. tables).
Generate a random fact f (for example: the Norwegian has a cat).
Set T = S
Filter out from T all solutions that violate f.
If |T| = 0 then goto 2 (the fact contradicts a previous one)
If |T| < |S| then set S = T and F.append(f) (the fact is not already embodied by previous facts)
IF |S| > 1 then goto 2
Once done - F will be the list of facts that lead to the only table left in S.
Admittedly, this is very much brute force, and will probably not work well with a table that is 5X5 or more.

Interestingly, the "Einstein" Puzzle
(quotes intended, everything "clever" tends to be assigned to Einstein maybe to have more glamour),
is related to Sudoku Generation Algorithm (by a proper translation of terms) and also to Rubik Cube (3x3x3) Solving Algorithm
In the case of Sudoku, the clues correspond to already assigned numbers on the grid and the missing information to empty slots
In the case of Rubik Cube (which i find more interesting), the clues correspond to symmetries of the cube (eg green color is next to red color, sth like that) And the missing data are found by re-aligning (solving) the cube
This is a rough outline, thanks

Here's another thought - once you've generated your complete grid take a photo of it on your phone (Or duplicate the design) before removing items as you provide clues. You might get half way through the task and forget what the original/ final layout is meat to look like and can avoid misinforming your subjects/ test takers.
Thinking of doing one for Easter, similar pattern,
5 people, 5 chocolate types, 5 ages, 5 different Easter hats, 5 different favourite drinks, ice creams etc.

Related

Alternate plain English algorithm for Towers of Hanoi

There are four plain English algorithms for the Towers of Hanoi Puzzle available on Wikipedia, but when I was first solving the puzzle, I came up with an algorithm that is different from any of the solutions I have seen.
Wikipedia algorithms:
Iterative solution
Simpler statement of iterative solution
Equivalent iterative solution
Recursive solution
Of course the results of the algorithms are the same, and they are really just different ways of thinking about the same thing, but I am talking about plain English ways of describing the process.
My process goes like this:
Never move same tile twice in a row(obviously)
Prioritize moving right
When moving right, move to the closest pole that can be legally moved to.
When moving left, move to the farthest pole that can be legally moved to.
..
These rules differ from other descriptions of the algorithm in that:
The initial stack can be placed on any of the 3 pillars and still work without any adjustment to the rules needed.(Unlike solutions 2 and 3 and 4)
You don't have to number the disks(Unlike solutions 1 and 3 and 4)
I have tested this programmatically, and it always solved the puzzle in (2^n)-1 moves where n is the number of rings.
It seems to me that my description really is different from the other plain English descriptions I have found. Has any one seen this description before? If so, please show reference.
I think your description is pretty much the same as Iterative Solution. Just imagine the posts arranged around a circle or a triangle, mod 3 style. Your instructions and Wikipedia's instructions translate to the same thing in that way of viewing things.
This solution is a unidirectional version of the first iterative solution.
The difference between the unidirectional solution and the mono-directional version is the unidirectional solution doesn't specify an end position.
A simple solution for the toy puzzle: Alternate moves between the
smallest piece and a non-smallest piece. When moving the smallest
piece, always move it to the next position in the same direction (to
the right if the starting number of pieces is even, to the left if the
starting number of pieces is odd). If there is no tower position in
the chosen direction, move the piece to the opposite end, but then
continue to move in the correct direction. For example, if you started
with three pieces, you would move the smallest piece to the opposite
end, then continue in the left direction after that. When the turn is
to move the non-smallest piece, there is only one legal move. Doing
this will complete the puzzle in the fewest number of moves.
This description of the mono-directional version can be changed to be unidirectional if direction choices of direction are replaced with the rules from the unidirectional solution revolving around prioritizing moving right.

Fixing floor algorithm

Okay, I have this task: John's bathroom floor was broken. We have a map of this floor, where '.' is good plate, and '+' is bad plate, for example:
.+++
.+.+
Here we have 5 broken plates, and 3 good ones. There are two kinds of plates, which are sold in shop: 1x1 plates and 2x1 plates. 1x1 plate costs A, and 2x1 plate costs B. Task is: given map of floor, count minimum price of floor fixing.
Looking at example on top: we can place 2 2x1 plates and 1 1x1 plate. So price will be A+2*B.
I have an idea: for every broken plate count maximum length of connected broken plates. Then price is length/2*B + length%2*A.
Problem is, that I really don't know how to do it. I have an idea of some recursive algorithm, but there are so many problems like circles:
+++
+.+
+++
So I have two questions:
Am i going in the right direction?
Can you give me any hints on implementing this algorithm?
Thank you!
EDIT
There is trivial case when 2*A < B, but let's talk about non-trivial=)
/EDIT
Classic tiling problem. It's a weighted exact cover, in the non-trivial case (when using two 1x1 tiles costs more than using one 1x2 tile) I'd use ZDDs to solve it. Look in The Art of Computer Programming V4 1B for an example (dominos on a chessboard).
There are libraries available (for example CUDD) so you don't have to implement ZDDs from scratch, though that isn't too hard either.
As a bonus, you can get also get other information that's usually not supplied by other algorithms, such as the number of valid tilings without enumerating them all. It also easily generalizes to other sizes/shapes of tile (3x1, 2x2, L-piece, etc).
If 2*A<=B then this is trivial, just cover everything with 1x1s.
In the opposite case you have to maximize the number of 2x1s. The fact that the tiles are exactly 2x1 makes it easier than the general tiling problem. In particular this is equivalent to finding a maximum cardinality matching in a bipartite graph, see my answer here.
Once you find the maximum configuration of 2x1, you just have to cover the rest of the tiles with 1x1s.

Algorithm for solving Flow Free Game

I recently started playing Flow Free Game.
Connect matching colors with pipe to create a flow. Pair all colors, and cover the entire board to solve each puzzle in Flow Free. But watch out, pipes will break if they cross or overlap!
I realized it is just path finding game between given pair of points with conditions that no two paths overlap. I was interested in writing a solution for the game but don't know where to start. I thought of using backtracking but for very large board sizes it will have high time complexity.
Is there any suitable algorithm to solve the game efficiently. Can using heuristics to solve the problem help? Just give me a hint on where to start, I will take it from there.
I observed in most of the boards that usually
For furthest points, you need to follow path along edge.
For point nearest to each other, follow direct path if there is one.
Is this correct observation and can it be used to solve it efficiently?
Reduction to SAT
Basic idea
Reduce the problem to SAT
Use a modern SAT solver to solve the problem
Profit
Complexity
The problem is obviously in NP: If you guess a board constellation, it is easy (poly-time) to check whether it solves the problem.
Whether it is NP-hard (meaning as hard as every other problem in NP, e.g. SAT), is not clear. Surely modern SAT solvers will not care and solve large instances in a breeze anyway (I guess up to 100x100).
Literature on Number Link
Here I just copy Nuclearman's comment to the OP:
Searching for "SAT formulation of numberlink" and "NP-completeness of numberlink" leads to a couple references. Unsurprisingly, the two most interesting ones are in Japanese. The first is the actual paper proof of NP-completeness. The second describes how to solve NumberLink using the SAT solver, Sugar. –
Hint for reduction to SAT
There are several possibilities to encode the problem. I'll give one that I could make up quickly.
Remark
j_random_hacker noted that free-standing cycles are not allowed. The following encoding does allow them. This problem makes the SAT encoding a bit less attractive. The simplest method I could think of to forbid free-standing loops would introduce O(n^2) new variables, where n is the number of tiles on the board (count distance from next sink for each tile) unless one uses log encoding for this, which would bring it down to O(n*log n), possible making the problem harder for the solver.
Variables
One variable per tile, piece type and color. Example if some variable X-Y-T-C is true it encodes that the tile at position X/Y is of type T and has color C. You don't need the empty tile type since this cannot happen in a solution.
Set initial variables
Set the variables for the sink/sources and say no other tile can be sink/source.
Constraints
For every position, exactly one color/piece combination is true (cardinality constraint).
For every variable (position, type, color), the four adjacent tiles have to be compatible (if the color matches).
I might have missed something. But it should be easily fixed.
I suspect that no polynomial-time algorithm is guaranteed to solve every instance of this problem. But since one of the requirements is that every square must be covered by pipe, a similar approach to what both people and computers use for solving Sudoku should work well here:
For every empty square, form a set of possible colours for that square, and then repeatedly perform logical deductions at each square to shrink the allowed set of colours for that square.
Whenever a square's set of possible colours shrinks to size 1, the colour for that square is determined.
If we reach a state where no more logical deductions can be performed and the puzzle is not completely solved yet (i.e. there is at least one square with more than one possible colour), pick one of these undecided squares and recurse on it, trying each of the possible colours in turn. Each try will either lead to a solution, or a contradiction; the latter eliminates that colour as a possibility for that square.
When picking a square to branch on, it's generally a good idea to pick a square with as few allowed colours as possible.
[EDIT: It's important to avoid the possibility of forming invalid "loops" of pipe. One way to do this is by maintaining, for each allowed colour i of each square x, 2 bits of information: whether the square x is connected by a path of definite i-coloured tiles to the first i-coloured endpoint, and the same thing for the second i-coloured endpoint. Then when recursing, don't ever pick a square that has two neighbours with the same bit set (or with neither bit set) for any allowed colour.]
You actually don't need to use any logical deductions at all, but the more and better deductions you use, the faster the program will run as they will (possibly dramatically) reduce the amount of recursion. Some useful deductions include:
If a square is the only possible way to extend the path for some particular colour, then it must be assigned that colour.
If a square has colour i in its set of allowed colours, but it does not have at least 2 neighbouring squares that also have colour i in their sets of allowed colours, then it can't be "reached" by any path of colour i, and colour i can be eliminated as a possibility.
More advanced deductions based on path connectivity might help further -- e.g. if you can determine that every path connecting some pair of connectors must pass through a particular square, you can immediately assign that colour to the square.
This simple approach infers a complete solution without any recursion in your 5x5 example: the squares at (5, 2), (5, 3), (4, 3) and (4, 4) are forced to be orange; (4, 5) is forced to be green; (5, 5) is also forced to be green by virtue of the fact that no other colour could get to this square and then back again; now the orange path ending at (4, 4) has nowhere to go except to complete the orange path at (3, 4). Also (3, 1) is forced to be red; (3, 2) is forced to be yellow, which in turn forces (2, 1) and then (2, 2) to be red, which finally forces the yellow path to finish at (3, 3). The red pipe at (2, 2) forces (1, 2) to be blue, and the red and blue paths wind up being completely determined, "forcing each other" as they go.
I found a blog post on Needlessly Complex that completely explains how to use SAT to solve this problem.
The code is open-source as well, so you can look at it (and understand it) in action.
I'll provide a quote from it here that describes the rules you need to implement in SAT:
Every cell is assigned a single color.
The color of every endpoint cell is known and specified.
Every endpoint cell has exactly one neighbor which matches its color.
The flow through every non-endpoint cell matches exactly one of the six direction types.
The neighbors of a cell specified by its direction type must match its color.
The neighbors of a cell not specified by its direction type must not match its color.
Thank you #Matt Zucker for creating this!
I like solutions that are similar to human thinking. You can (pretty easily) get the answer of a Sudoku by brute force, but it's more useful to get a path you could have followed to solve the puzzle.
I observed in most of the boards that usually
1.For furthest points, you need to follow path along edge.
2.For point nearest to each other, follow direct path if there is one.
Is this correct observation and can it be used to solve it efficiently?
These are true "most of the times", but not always.
I would replace your first rule by this one : if both sinks are along edge, you need to follow path along edge. (You could build a counter-example, but it's true most of the times). After you make a path along the edge, the blocks along the edge should be considered part of the edge, so your algorithm will try to follow the new edge made by the previous pipe. I hope this sentence makes sense...
Of course, before using those "most of the times" rules, you need to follow absolutes rules (see the two deductions from j_random_hacker's post).
Another thing is to try to eliminate boards that can't lead to a solution. Let's call an unfinished pipe (one that starts from a sink but does not yet reach the other sink) a snake, and the last square of the unfinished pipe will be called the snake's head. If you can't find a path of blank squares between the two heads of the same color, it means your board can't lead to a solution and should be discarded (or you need to backtrack, depending of your implementation).
The free flow game (and other similar games) accept as a valid solution a board where there are two lines of the same color side-by-side, but I believe that there always exists a solution without side-by-side lines. That would mean that any square that is not a sink would have exactly two neighbors of the same color, and sinks would have exactly one. If the rule happens to be always true (I believe it is, but can't prove it), that would be an additional constraint to decrease your number of possibilities. I solved some of Free Flow's puzzles using side-by-side lines, but most of the times I found another solution without them. I haven't seen side-by-side lines on Free Flow's solutions web site.
A few rules that lead to a sort of algorithm to solve levels in flow, based on the IOS vertions by Big Duck Games, this company seems to produce the canonical versions. The rest of this answer assumes no walls, bridges or warps.
Even if your uncannily good, the huge 15x18 square boards are a good example of how just going at it in ways that seem likely get you stuck just before the end over and over again and practically having to start again from scratch. This is probably to do with the already mentioned exponential time complexity in the general case. But this doesn’t mean that a simple stratergy isn’t overwhelmingly effective for most boards.
Blocks are never left empty, therefore orphaned blocks mean you’ve done something wrong.
Cardinally neighbouring cells of the same colour must be connected. This rules out 2x2 blocks of the same colour and on the hexagonal grid triangles of 3 neighbouring cells.
You can often make perminent progress by establishing that a color goes or is excluded from a certain square.
Due to points 1 and 2, on the hexagonal grid on boards that are hexagonal in shape a pipe going along an edge is usually stuck going along it all the way round to the exit, effectively moving the outer edge in and making the board smaller so the process can be repeated. It is predictable what sorts of neighbouring conditions guarantee and what sorts can break this cycle for both sorts of grid.
Most if not all 3rd party variants I’ve found lack 1 to 4, but given these restraints generating valid boards may be a difficult task.
Answer:
Point 3 suggests a value be stored for each cell that is able to be either a colour, or a set of false/indeterminate values there being one for each colour.
A solver could repeatedly use points 1 and 2 along with the data stored for point 3 on small neighbourhoods of paths around the ends of pipes to increasingly set colours or set the indeterminate values to false.
A few of us have spent quite a bit of time thinking about this. I summarised our work in a Medium article here: https://towardsdatascience.com/deep-learning-vs-puzzle-games-e996feb76162
Spoiler: so far, good old SAT seems to beat fancy AI algorithms!

Level solving and pathfinding

I have played a little flash game recently called Just A Trim Please and really liked the whole concept.
The basic objective of the game is to mow the whole lawn by going over each square once. Your lawn mower starts on a tile and from there you can move in all directions (except where there are walls blocking you). If you run on the grass tiles more than once it will deteriorate and you will lose the level. You can only move left, right, up, or down.
However, as you finish the game, more tiles get added:
A tile you can only mow once (grass).
A tile you can run over twice before deteriorating it (taller grass).
A tiie you can go over as much as you want (concrete).
A tile you can't go over (a wall).
If you don't get what I mean, go play the game and you'll understand.
I managed to code a brute-force algorithm that can solve puzzles with only the first kind of tile (which is basically a variation of the Knight's Tour problem). However, this is not optimal and only works for puzzles with tiles that can only be ran on once. I'm completely lost as to how I'd deal with the extra tiles.
Given a starting point and a tile map, is there a way or an algorithm to find the path that will solve the level (if it can be solved)? I don't care about efficiency, this is just a question I had in mind. I'm curious as to how you'd have to go to solve it.
I'm not looking for code, just guidelines or if possible a plain text explanation of the procedure. If you do have pseudocode however, then please do share! :)
(Also, I'm not entirely sure if this has to do with path-finding, but that's my best guess at it.)
The problem is finite so, sure, there is an algorithm.
A non-deterministic algorithm can solve the problem easily by guessing the correct moves and then verifying that they work. This algorithm can be determinized by using for example backtracking search.
If you want to reduce the extra tiles (taller grass and concrete) to standard grass, you can go about it like this:
Every continuous block of concrete is first reduced into a single graph vertex, and then the vertex is removed, because the concrete block areas are actually just a way to move around to reach other tiles.
Every taller grass tile is replaced with two vertices that are connected to the original neighbors, but not to each other.
Example: G = grass, T = tall grass, C = concrete
G G T
G C T
C C G
Consider it as a graph:
Now transform the concrete blocks away. First shrink them to one (as they're all connected):
Then remove the vertex, connecting "through" it:
Then expand the tall grass tiles, connecting the copies to the same nodes as the originals.
Then replace T, T' with G. You have now a graph that is no longer rectangular grid but it only contains grass nodes.
The transformed problem can be solved if and only if the original one can be solved, and you can transform a solution of the transformed problem into a solution of the original one.
There is a DP approach for the travelling salesman.
Perhaps you could modify it (recalculating as more pieces are added).
For a long piece of grass, you could perhaps split it into two nodes since you must visit it twice. Then reconnect the two nodes to the nodes around it.

Rubber band and pegs game

I have to make a flash game like this:
There is a board with holes in it (more than 1000). Initially, there are 3 pegs placed on the board and a rubber band around them.
We have 3 possible operations:
1. Add peg - adds a peg on the board
2. Remove peg - removes a peg ( if there are more than 3 pegs) - the rubber band must take the shape of the remaining pegs.
3. Move peg - rubber band must be updated with current positions of the pegs.
How would you solve the problem of finding the rubber band's shape optimally?
I have 2 ideeas, but I have to work on them a little bit. The main ideea is that we have to change the rubber band's shape only at "Move" operation, and we use the same number of pegs, only one is changing position:
A derivation from convex hull algorithm. We have to know wich pegs are inside the rubber band and wich are outside. It might get a little complicated.
We work with only 3 pegs: 2 anchors and 1 middle. The 2 anchors form a boundary line for the interaction of the 1 middle peg. On the active side of the line the rubber band functions as 2 segments between the 2 anchor pegs and the middle peg. On the inactive side the 1 middle peg is free to move while the rubber band functions as a straight line between the 2 anchor pegs. The caveat to the above is that there are cases in which movement of the 1 middle peg in the active side of the boundary line can can cause one of the 2 segments to contact a 4th peg. The program must detect this occurrence and update the new anchor pegs accordingly. These are just suggestions from some limited experience with this concept. The developer should determine the best approach based on his experience and judgement.
Do you have any other ideeas, or suggestions?
"The developer should determine the best approach based on his experience and judgement." — did you copy and paste this from the spec you were given? :)
You ask for an "optimal" solution but if I were you I'd aim for a "correct, and fast enough" solution. You've got a contract to fulfil, you can leave the asymptotics to the academics.
Anyway, your plan to update the band only when the player moves a peg looks like a good one. We are going to need to remember all the pegs that are touching the rubber band, and for each peg we have to remember which side of the rubber band it's on (in order to draw the band correctly).
Now, suppose the player moves peg A from a to a'.
As a general principle, it's worth bearing in mind that even if your time segments are short, and the distance from a to a' is small, nonetheless there might be multiple things that happen. So you're going to have to consider all the events that might happen in that time segment, pick the earliest such event, update your data structures accordingly, and then continue with the remainder of the time segment.
So what kind of events are there?
Peg A "picks up" the band. (It does so if peg A was not already on the band, and the line a–a' crosses one of the lines between pegs on the band.)
Peg A "puts down" the band. (It does so if peg A was on the band, with neighbours B and C, and the line a–a' crosses the line B–C.)
Peg A gains a neighbour on the band. (This happens when peg A is on the band, B is a neighbour of A and the triangle a–a'–B contains another peg C.)
Peg A loses a neighbour on the band. (This happens when peg A is on the band, the neighbouring pegs on the band go A–B–C, and peg B is in the triangle a–a'–C.)
So you should determine all such events; work out the time that each event would happen; sort the events into order by time; handle the earliest event (only); repeat for the remainder of the time segment.
(What to do if two events happen simultaneously? I'll leave that up to your experience and judgement.)
Edited to add: a complication is that a peg may appear on more than one segment of the rubber band (for example, the band may go A-B–A-C-A). But I think the above sketch of an algorithm still works.
A further wrinkle is that even with a small number of pegs, you can make arbitrarily twisty configurations of the band. For example, suppose the band is stretched between pegs B and C. Now take peg A and move it in a figure-of-8 around pegs B and C (clockwise around B, anti-clockwise around C, let's say). Each time round the loop, peg A picks up another couple of pieces of the band. You can't afford to let the complexity of the configuration grow without bound, so need some way of stopping things getting out of hand. I suggest imposing a maximum limit on the length of the band, so that any attempt to stretch it too far causes it to snap (of course you'd have warning signs before this happens, e.g. band getting thinner, changing colour, ominous creaky sounds).

Resources