# Designing algorithm to solve Ball Sort Puzzle

• Difficulty Level : Expert
• Last Updated : 22 Nov, 2021

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:

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.

• 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:
• 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):
• 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,
• return;
• if newGrid is NOT in Visited
• solver(newGrid)
• if solved:

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)` `                 ``!= 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);`   `    ``// 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 == i` `                            ``&& lastMove == j)` `                            ``answerMod[answerMod.size() - 1]` `                                     ``++;` `                        ``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 + 1` `             ``<< ``" to "` `<< v + 1` `             ``<< ``" "` `<< v << ``" times"` `             ``<< endl;` `    ``}` `    ``return` `0;` `}`

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```

My Personal Notes arrow_drop_up
Recommended Articles
Page :