Good day, esteemed StackOverflow contributors.
I am currently building a Ludo game. It's a simple game and at the core of the game are tokens and dice. What I aim to achieve is generate all possible combination of moves. Usually, Ludo works with 2 dice. So a throw should give 2 dice values. There is an instance though, when a throw (double-six) makes provision for another throw by same player. But because the expected combination of moves is a huge result, I've decided to keep things simple by not repeating the dice Values, in other words, avoiding two 6's in the array, in order to aid clarity in the result's pattern.
Hence the arrays:
const diceValues = [1, 3, 4, 6];
const tokens = ['A', 'B', 'C', 'D'];
const moves = [];
My customary solution below was to make a note of the pattern of results I would expect, then tailor the code to match. With nested loops I made some progress but as expected, I reached that breaking point where I felt the need an elegant recursion (what else?) would avail.
Below is a list of the possible combinations I could muster on the loop of one token ("A"), in order to keep it not too long and as also provide an idea. Also, I write this list non-technically so it's easy on the eye:
e.g: A1 B346, A1 C346, A1 D346
but while coding, this is the format that gets pushed into the moves array:
e.g: [ {'A': [1], 'B': [3, 4, 6]}, {'A': [1], 'C': [3, 4, 6]}, {'A': [1], 'D': [3, 4, 6]} ]
Important things to note:
All dice values must be used up (i.e. associated with at least 1 token. Something like
A1 B3 C4is not a legal move because the remaining die value of 6 has not been used up).It is not mandatory all tokens are used up. (One token may associate with all 4 dice values e.g.
A1346).
Now we are clear, here's the list:
A1 B346, A1 C346, A1 D346
A3 B146, A3 C146, A3 D146
A4 B136, A4 B136, A4 D136
A6 B134, A6 C134, A6 D134
A13 B46, A13 C46, A13 D46
A13 B4 C6, A13 B4 D6
A13 C4 B6, A13 C4 D6
A13 D4 B6, A13 D4 C6
A14 B36, A14 C36, A14 D36
A14 B3 C6, A14 B3 D6
A14 C3 B6, A14 C3 D6
A14 D3 B6, A14 D3 C6
A16 B34, A16 C34, A16 D34
A16 B3 C4, A16 B3 D4
A16 C3 B4, A16 C3 D4
A16 D3 B4, A16 D3 C4
A34 B16, A34 C16, A34 D16
A34 B1 C6, A34 B1 D6
A34 C1 B6, A34 C1 D6
A34 D1 B6, A34 D1 D6
A34 B16, A34 C16, A34 D16
A34 B1 C6, A34 B1 D6
A34 C1 B6, A34 C1 D6
A34 D1 B6, A34 D1 C6
A36 B14, A34 C14, A36 D14
A36 B1 C4, A36 B1 D4
A36 C1 B4, A36 C1 D4
A36 D1 B4, A36 D1 C4
A46 B13, A46 C13, A46 D13
A46 B1 C3, A46 B1 D3
A46 C1 B3, A46 C1 D3
A46 D1 B3, A46 D1 C3
A134 B6, A134 C6, A134 D6
A136 B4, A136 C4, A136 D4
A146 B6, A146 C3, A146 D3
A346 B1, A346 C1, A346 D1
A1346
In total, this is 88 loops
Then the next token loop would be something like:
B1 A346, B1 C346, B1 D346 and on and on...
Thus at this point would be expecting 88 * 4 = 352 loops
Final segment which distributes all tokens to all dice values:
A1, B3, C4, D6
A1, B3, C6, D4
A1, B6, C3, D4
A6, B1, C3, D4
A1, B4, C3, D6
A1, B4, C6, D3
A1, B6, C4, D3
A6, B1, C4, D3
A4, B1, C3, D6
A4, B1, C6, D3
A4, B6, C1, D3
A6, B4, C1, D3
A3, B1, C4, D6
A3, B1, C6, D4
A3, B6, C1, D4
A6, B3, C1, D4
A3, B4, C1, D6
A3, B4, C6, D1
A3, B6, C4, D1
A6, B4, C3, D1
A6, B3, C4, D1
A4, B3, C1, D6
A4, B3, C6, D1
A4, B6, C3, D1
I estimate a total number of 448 cycles.
Both arrays were of 4 lengths, but I expect a solution that would work just fine on arrays with longer and varying lengths.
Thank you for your efforts. I would not mind a completely different approach. Something more terse too could be of help. Finally, but not necessary, would love to see a mathematical way to calculate the combination of possible moves so the accuracy of the results is somewhat guaranteed, at least by length.
My approach:
const tokens = ['A', 'B', 'C', 'D'];
const diceValues = [1, 3, 4, 6];
let moves = [];
const moveCollection = (tokens, diceValues) => {
diceValues.map((die, dieIndex) => { // [1, 3, 4, 6]
const leftDiceValues = diceValues.slice(0, dieIndex + 1); // [1], [1, 3], [1, 3, 4], [1, 3, 4, 6]
const rightDiceValues = diceValues.slice(dieIndex + 1, diceValues.length); // [3, 4, 6], [3, 4, 6], [4, 6], [6]
tokens.map((token, tokenIndex) => { // [A, B, C, D]
const remTokens = tokens.slice();
remTokens.splice(tokenIndex, 1); // [B, C, D], [A, C, D], [A, B, D], [A, B, C]
remTokens.map((remToken, remTokenIndex) => {
moves.push([
{ [token]: leftDiceValues }, // {A: [1]}
{ [remToken] : rightDiceValues } // {B: [3, 4, 6]}
]);
});
// It was at this point I felt the need for some sort of recursive solution
// This condition was necessary only to prove the 1st loop new format of
// [ {A: [3], B: [1, 4, 6]}, {A: [3], C: [1, 4, 6]}, {A: [3], D: [1, 4, 6]} ]
if (dieIndex === 0) {
rightDiceValues.map((rightDiceValue, rightDiceIndex) => { // [3, 4, 6]
const remDiceValues = diceValues.slice(); // [1, 3, 4, 6]
remDiceValues.splice(rightDiceIndex + 1, 1);
remTokens.map((remToken, remTokenIndex) => {
moves.push([
{ [token]: rightDiceValue }, // {A: [3]}
{ [remToken]: remDiceValues } // {B: [1, 4, 6]}
]);
});
});
}
});
});
}
moveCollection(tokens, diceValues);
So, do you need to get all the movements for these sticks for any dice value ?
well, to make such thing, we need to do something really important, list all the probabilities of a dice :
Now, let's start executing the solution, first we will make a positioning system, just make your own if you need, this is my own : the position starts from the first slot the stick gets from, and each color is different than the other, by using this :
Tell me anything you need, as my answer is not enough