# Designing algorithm to solve Ball Sort Puzzle

In Ball Sort Puzzle game, we have p balls of each colour and n different colours, for a total of pÃ—n balls, arranged in n stacks. In addition, we have 2 empty stacks. A maximum of p balls can be in any stack at a given time. The goal of the game is to sort the balls by colour in each of the n stacks.

Rules:

• Only the top ball of each stack can be moved.
• A ball can be moved on top of another ball of the same colour
• A ball can be moved in an empty stack.

Refer to the following GIF for an example game play (Level-7):

Level 7 Gameplay

Approach I [Recursion and BackTrack]:

• From the given rules, a simple recursive algorithm could be generated as below:
• Start with the given initial position of all the balls
• Create an initial empty Queue.
• loop:
• If the current position is sorted:
• return
• else
• Enqueue all possible moves in a Queue.
• Dequeue the next move from the Queue.
• Go to loop.

However, the approach looks simple and correct, it has few caveats:

• Incorrect:
• We might end up in an infinite loop if there are >1 moves in the Queue which lead to the same position of balls.
• Inefficient:
• We might end up visiting the same position multiple times.

Thus, eliminating the above-mentioned bottlenecks would solve the issue.

Approach II [Memoization using HashMap]:

• Assumptions:
• We’ll represent ball positions as a vector of strings: {“gbbb”, “ybry”, “yggy”, “rrrg”}
• Create a set called Visited of <String> which will contain the visited positions as one long string.
• Create an empty vector for Answer which will store positions<a, b> of the tubes to move the top ball from tube a to and put it in tube b.
• Initialise grid with the initial settings of the balls.
• func solver(grid):
• add grid to Visited
• loop over all the stacks (i):
• loop over all the stacks (j):
• If move i->j is valid, create newGrid with that move.
• if the balls are sorted in newGrid,
• update Answer;
• return;
• if newGrid is NOT in Visited
• solver(newGrid)
• if solved:
• update Answer

Sample Game Input I:

Level 3

Sample Input I:

```5
ybrb
byrr
rbyy```

Sample Output I:

```Move 1 to 4 1 times
Move 1 to 5 1 times
Move 1 to 4 1 times
Move 2 to 5 2 times
Move 1 to 2 1 times
Move 3 to 1 1 times
Move 1 to 2 1 times
Move 3 to 1 1 times
Move 2 to 1 3 times
Move 2 to 3 1 times
Move 3 to 4 1 times
Move 3 to 2 1 times
Move 2 to 4 1 times
Move 3 to 5 1 times```

Sample Game Input II:

Level 5

Sample Input II:

```6
gbbb
ybry
yggy
rrrg```

Sample Output II:

```Move 1 to 5 3 times
Move 2 to 6 1 times
Move 3 to 6 1 times
Move 1 to 3 1 times
Move 2 to 1 1 times
Move 2 to 5 1 times
Move 2 to 6 1 times
Move 3 to 2 3 times
Move 3 to 6 1 times
Move 4 to 2 1 times
Move 1 to 4 1 times```

Refer to the below C++ implementation with the comments for the reference:

## C++

 `// C++ program for the above approach` `#include ` `using` `namespace` `std;` `using` `Grid = vector;`   `Grid configureGrid(string stacks[], ``int` `numberOfStacks)` `{`   `    ``Grid grid;` `    ``for` `(``int` `i = 0; i < numberOfStacks; i++)` `        ``grid.push_back(stacks[i]);`   `    ``return` `grid;` `}`   `// Function to find the max` `int` `getStackHeight(Grid grid)` `{` `    ``int` `max = 0;` `    ``for` `(``auto` `stack : grid)` `        ``if` `(max < stack.size())` `            ``max = stack.size();` `    ``return` `max;` `}`   `// Convert vector of strings to` `// canonicalRepresentation of strings` `string canonicalStringConversion(Grid grid)` `{` `    ``string finalString;` `    ``sort(grid.begin(), grid.end());` `    ``for` `(``auto` `stack : grid) {` `        ``finalString += (stack + ``";"``);` `    ``}` `    ``return` `finalString;` `}`   `// Function to check if it is solved` `// or not` `bool` `isSolved(Grid grid, ``int` `stackHeight)` `{`   `    ``for` `(``auto` `stack : grid) {` `        ``if` `(!stack.size())` `            ``continue``;` `        ``else` `if` `(stack.size() < stackHeight)` `            ``return` `false``;` `        ``else` `if` `(std::count(stack.begin(),` `                            ``stack.end(),` `                            ``stack[0])` `                 ``!= stackHeight)` `            ``return` `false``;` `    ``}` `    ``return` `true``;` `}`   `// Check if the move is valid` `bool` `isValidMove(string sourceStack,` `                 ``string destinationStack,` `                 ``int` `height)` `{`   `    ``// Can't move from an empty stack` `    ``// or to a FULL STACK` `    ``if` `(sourceStack.size() == 0` `        ``|| destinationStack.size() == height)` `        ``return` `false``;`   `    ``int` `colorFreqs` `        ``= std::count(sourceStack.begin(),` `                     ``sourceStack.end(),` `                     ``sourceStack[0]);`   `    ``// If the source stack is same colored,` `    ``// don't touch it` `    ``if` `(colorFreqs == height)` `        ``return` `false``;`   `    ``if` `(destinationStack.size() == 0) {`   `        ``// If source stack has only` `        ``// same colored balls,` `        ``// don't touch it` `        ``if` `(colorFreqs == sourceStack.size())` `            ``return` `false``;` `        ``return` `true``;` `    ``}` `    ``return` `(` `        ``sourceStack[sourceStack.size() - 1]` `        ``== destinationStack[destinationStack.size() - 1]);` `}`   `// Function to solve the puzzle` `bool` `solvePuzzle(Grid grid, ``int` `stackHeight,` `                 ``unordered_set& visited,` `                 ``vector >& answerMod)` `{` `    ``if` `(stackHeight == -1) {` `        ``stackHeight = getStackHeight(grid);` `    ``}` `    ``visited.insert(` `        ``canonicalStringConversion(grid));`   `    ``for` `(``int` `i = 0; i < grid.size(); i++) {`   `        ``// Iterate over all the stacks` `        ``string sourceStack = grid[i];` `        ``for` `(``int` `j = 0; j < grid.size(); j++) {` `            ``if` `(i == j)` `                ``continue``;` `            ``string destinationStack = grid[j];` `            ``if` `(isValidMove(sourceStack,` `                            ``destinationStack,` `                            ``stackHeight)) {`   `                ``// Creating a new Grid` `                ``// with the valid move` `                ``Grid newGrid(grid);`   `                ``// Adding the ball` `                ``newGrid[j].push_back(newGrid[i].back());`   `                ``// Adding the ball` `                ``newGrid[i].pop_back();` `                ``if` `(isSolved(newGrid, stackHeight)) {` `                    ``answerMod.push_back(` `                        ``vector<``int``>{ i, j, 1 });` `                    ``return` `true``;` `                ``}` `                ``if` `(visited.find(` `                        ``canonicalStringConversion(newGrid))` `                    ``== visited.end()) {` `                    ``bool` `solveForTheRest` `                        ``= solvePuzzle(newGrid, stackHeight,` `                                      ``visited, answerMod);` `                    ``if` `(solveForTheRest) {` `                        ``vector<``int``> lastMove` `                            ``= answerMod[answerMod.size()` `                                        ``- 1];`   `                        ``// Optimisation - Concatenating` `                        ``// consecutive moves of the same` `                        ``// ball` `                        ``if` `(lastMove[0] == i` `                            ``&& lastMove[1] == j)` `                            ``answerMod[answerMod.size() - 1]` `                                     ``[2]++;` `                        ``else` `                            ``answerMod.push_back(` `                                ``vector<``int``>{ i, j, 1 });` `                        ``return` `true``;` `                    ``}` `                ``}` `            ``}` `        ``}` `    ``}` `    ``return` `false``;` `}`   `// Checks whether the grid is valid or not` `bool` `checkGrid(Grid grid)` `{`   `    ``int` `numberOfStacks = grid.size();` `    ``int` `stackHeight = getStackHeight(grid);` `    ``int` `numBallsExpected` `        ``= ((numberOfStacks - 2) * stackHeight);` `    ``// Cause 2 empty stacks` `    ``int` `numBalls = 0;`   `    ``for` `(``auto` `i : grid)` `        ``numBalls += i.size();` `    ``if` `(numBalls != numBallsExpected) {` `        ``cout << ``"Grid has incorrect # of balls"` `             ``<< endl;` `        ``return` `false``;` `    ``}` `    ``map<``char``, ``int``> ballColorFrequency;` `    ``for` `(``auto` `stack : grid)` `        ``for` `(``auto` `ball : stack)` `            ``if` `(ballColorFrequency.find(ball)` `                ``!= ballColorFrequency.end())` `                ``ballColorFrequency[ball] += 1;` `            ``else` `                ``ballColorFrequency[ball] = 1;` `    ``for` `(``auto` `ballColor : ballColorFrequency) {` `        ``if` `(ballColor.second != getStackHeight(grid)) {` `            ``cout << ``"Color "` `<< ballColor.first` `                 ``<< ``" is not "` `<< getStackHeight(grid)` `                 ``<< endl;` `            ``return` `false``;` `        ``}` `    ``}` `    ``return` `true``;` `}`   `// Driver Code` `int` `main(``void``)` `{`   `    ``// Including 2 empty stacks` `    ``int` `numberOfStacks = 6;` `    ``std::string stacks[]` `        ``= { ``"gbbb"``, ``"ybry"``, ``"yggy"``, ``"rrrg"``, ``""``, ``""` `};`   `    ``Grid grid = configureGrid(` `        ``stacks, numberOfStacks);` `    ``if` `(!checkGrid(grid)) {` `        ``cout << ``"Invalid Grid"` `<< endl;` `        ``return` `1;` `    ``}` `    ``if` `(isSolved(grid, getStackHeight(grid))) {` `        ``cout << ``"Problem is already solved"` `             ``<< endl;` `        ``return` `0;` `    ``}` `    ``unordered_set visited;` `    ``vector > answerMod;`   `    ``// Solve the puzzle instance` `    ``solvePuzzle(grid, getStackHeight(grid),` `                ``visited,` `                ``answerMod);`   `    ``// Since the values of Answers are appended` `    ``// When the problem was completely` `    ``// solved and backwards from there` `    ``reverse(answerMod.begin(), answerMod.end());`   `    ``for` `(``auto` `v : answerMod) {` `        ``cout << ``"Move "` `<< v[0] + 1` `             ``<< ``" to "` `<< v[1] + 1` `             ``<< ``" "` `<< v[2] << ``" times"` `             ``<< endl;` `    ``}` `    ``return` `0;` `}`

## Python3

 `def` `configureGrid(stacks, numberOfStacks):` `    `  `    ``grid ``=` `[]` `    ``for` `i ``in` `range``(numberOfStacks):` `        ``grid.append(stacks[i])` `    ``return` `grid`   `# Function to find the max` `def` `getStackHeight(grid):` `    ``max` `=` `0` `    ``for` `stack ``in` `grid:` `        ``if` `max` `< ``len``(stack):` `            ``max` `=` `len``(stack)` `    ``return` `max`   `# Convert vector of strings to` `# canonicalRepresentation of strings` `def` `canonicalStringConversion(grid):` `    ``finalString ``=` `""` `    ``grid.sort()` `    ``for` `stack ``in` `grid:` `        ``finalString ``+``=` `(stack ``+` `";"``)` `    ``return` `finalString`   `# Function to check if it is solved` `# or not` `def` `isSolved(grid, stackHeight):` `    ``for` `stack ``in` `grid:` `        ``if` `len``(stack) ``=``=` `0``:` `            ``continue` `        ``elif` `len``(stack) < stackHeight:` `            ``return` `False` `        ``elif` `stack.count(stack[``0``]) !``=` `stackHeight:` `            ``return` `False` `    ``return` `True`   `# Check if the move is valid` `def` `isValidMove(sourceStack, destinationStack, height):` `  `  `    ``# Can't move from an empty stack` `    ``# or to a FULL STACK` `    ``if` `len``(sourceStack) ``=``=` `0` `or` `len``(destinationStack) ``=``=` `height:` `        ``return` `False` `    `  `    ``colorFreqs ``=` `sourceStack.count(sourceStack[``0``])` `    `  `    ``# If the source stack is same colored,` `    ``# don't touch it` `    ``if` `colorFreqs ``=``=` `height:` `        ``return` `False` `    ``if` `len``(destinationStack) ``=``=` `0``:` `      `  `        ``# If source stack has only` `        ``# same colored balls,` `        ``# don't touch it` `        ``if` `colorFreqs ``=``=` `len``(sourceStack):` `            ``return` `False` `        ``return` `True` `    ``return` `sourceStack[``len``(sourceStack) ``-` `1``] ``=``=` `destinationStack[``len``(destinationStack) ``-` `1``]`   `# Function to solve the puzzle` `def` `solvePuzzle(grid, stackHeight, visited, answerMod):` `    ``if` `stackHeight ``=``=` `-``1``:` `        ``stackHeight ``=` `getStackHeight(grid)` `    ``visited.add(canonicalStringConversion(grid))` `    ``for` `i ``in` `range``(``len``(grid)):` `        ``# Iterate over all the stacks` `        ``sourceStack ``=` `grid[i]` `        ``for` `j ``in` `range``(``len``(grid)):` `            ``if` `i ``=``=` `j:` `                ``continue` `            ``destinationStack ``=` `grid[j]` `            ``if` `isValidMove(sourceStack, destinationStack, stackHeight):` `              `  `                ``# Creating a new Grid` `                ``# with the valid move` `                ``newGrid ``=` `list``(grid)` `                `  `                ``# Adding the ball` `                ``newGrid[j] ``+``=` `newGrid[i][``len``(newGrid[i]) ``-` `1``]` `                `  `                ``# Removing the ball` `                ``newGrid[i] ``=` `newGrid[i][:``-``1``]` `                ``if` `isSolved(newGrid, stackHeight):` `                    ``answerMod.append([i, j, ``1``])` `                    ``return` `True` `                ``if` `canonicalStringConversion(newGrid) ``not` `in` `visited:` `                    ``if` `solvePuzzle(newGrid, stackHeight, visited, answerMod):` `                        ``lastMove ``=` `answerMod[``len``(answerMod) ``-` `1``]` `                        `  `                        ``# Optimisation - Concatenating` `                        ``# consecutive moves of the same` `                        ``# ball` `                        ``if` `lastMove[``0``] ``=``=` `i ``and` `lastMove[``1``] ``=``=` `j:` `                            ``answerMod[``len``(answerMod) ``-` `1``][``2``] ``+``=` `1` `                        ``else``:` `                            ``answerMod.append([i, j, ``1``])` `                        ``return` `True` `    ``return` `False`   `# Checks whether the grid is valid or not` `def` `checkGrid(grid):` `    ``numberOfStacks ``=` `len``(grid)` `    ``stackHeight ``=` `getStackHeight(grid)` `    ``numBallsExpected ``=` `((numberOfStacks ``-` `2``) ``*` `stackHeight)` `    ``# Cause 2 empty stacks` `    ``numBalls ``=` `0` `    ``for` `i ``in` `grid:` `        ``numBalls ``+``=` `len``(i)` `    ``if` `numBalls !``=` `numBallsExpected:` `        ``print``(``"Grid has incorrect # of balls"``)` `        ``return` `False` `    ``ballColorFrequency ``=` `{}` `    ``for` `stack ``in` `grid:` `        ``for` `ball ``in` `stack:` `            ``if` `ball ``in` `ballColorFrequency:` `                ``ballColorFrequency[ball] ``+``=` `1` `            ``else``:` `                ``ballColorFrequency[ball] ``=` `1` `    ``for` `ballColor ``in` `ballColorFrequency:` `        ``if` `ballColorFrequency[ballColor] !``=` `getStackHeight(grid):` `            ``print``(``"Color"``, ballColor, ``"is not"``, getStackHeight(grid))` `            ``return` `False` `    ``return` `True`   `# Driver Code` `if` `__name__ ``=``=` `"__main__"``:` `    ``# Including 2 empty stacks` `    ``numberOfStacks ``=` `6` `    ``stacks ``=` `[``"gbbb"``, ``"ybry"``, ``"yggy"``, ``"rrrg"``, "``", "``"]` `    ``grid ``=` `configureGrid(stacks, numberOfStacks)` `    ``if` `not` `checkGrid(grid):` `        ``print``(``"Invalid Grid"``)` `        ``exit()` `    ``if` `isSolved(grid, getStackHeight(grid)):` `        ``print``(``"Problem is already solved"``)` `        ``exit()` `    ``visited ``=` `set``()` `    ``answerMod ``=` `[]` `    ``# Solve the puzzle instance` `    ``solvePuzzle(grid, getStackHeight(grid), visited, answerMod)` `    ``# Since the values of Answers are appended` `    ``# When the problem was completely` `    ``# solved and backwards from there` `    ``answerMod.reverse()` `    ``for` `v ``in` `answerMod:` `        ``print``(``"Move"``, v[``0``] ``+` `1``, ``"to"``, v[``1``] ``+` `1``, v[``2``], ``"times"``)`

Output

```Move 1 to 5 3 times
Move 2 to 6 1 times
Move 3 to 6 1 times
Move 1 to 3 1 times
Move 2 to 1 1 times
Move 2 to 5 1 times
Move 2 to 6 1 times
Move 3 to 2 3 times
Move 3 to 6 1 times
Move 4 to 2 1 times
Move 1 to 4 1 times```

Time Complexity: O(n!) where n is the number of stacks.

Auxiliary Space: O(n^2)

Feeling lost in the world of random DSA topics, wasting time without progress? It's time for a change! Join our DSA course, where we'll guide you on an exciting journey to master DSA efficiently and on schedule.
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 geeks!

Previous
Next
Similar Reads
Complete Tutorials