Wooden Box

Task Description Link to heading

Essentially, I was presented with a wooden box containing 14 smaller wooden boxes of varying sizes and shapes - some of them were cubes, while others were not. My objective was to fit all the smaller wooden boxes neatly inside a larger wooden box, ensuring that there was no wasted space.

While one could certainly attempt to solve this problem manually by trying out different combinations, I opted to develop a program to tackle it instead. By doing so, I was able to simplify the process and more efficiently arrive it a viable solution.

A special thanks goes to NoRelect who gave me this task and helped me to achieve my first result. Also thank you to J.L who even got a faster working solution in Python and removed all the Object instantiations.

Code Link to heading

import java.util.HashSet;
import java.util.Set;

public class Solver {
    private final boolean[][][] boxContainer;
    private final int[][] boxes;
    private final boolean[] used;

    public Solver(int[][] boxes, boolean[][][] boxContainer, boolean[] used) {
        this.boxContainer = boxContainer;
        this.boxes = boxes;
        this.used = used;
    }

    //check if the box is placeable --> is it inside the box? does any other box collide with the current one?
    private boolean placeable(int x, int y, int z, int[] box) {
        for (int i = x; i < x + box[0]; i++) {
            for (int j = y; j < y + box[1]; j++) {
                for (int k = z; k < z + box[2]; k++) {
                    if (i >= boxContainer.length || j >= boxContainer[0].length ||
                            k >= boxContainer[0][0].length || boxContainer[i][j][k]) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    private void removeBox(int x, int y, int z, int[] box) {
        for (int i = x; i < x + box[0]; i++) {
            for (int j = y; j < y + box[1]; j++) {
                for (int k = z; k < z + box[2]; k++) {
                    boxContainer[i][j][k] = false;
                }
            }
        }
    }

    private void placeBox(int x, int y, int z, int[] box) {
        for (int i = x; i < x + box[0]; i++) {
            for (int j = y; j < y + box[1]; j++) {
                for (int k = z; k < z + box[2]; k++) {
                    boxContainer[i][j][k] = true;
                }
            }
        }
    }

    //first occurrence of a false
    private int[] findPos() {
        for (int i = 0; i < boxContainer.length; i++) {
            for (int j = 0; j < boxContainer[0].length; j++) {
                for (int k = 0; k < boxContainer[0][0].length; k++) {
                    if (!boxContainer[i][j][k]) {
                        return new int[] {i, j, k};
                    }
                }
            }
        }
        return null;
        //no position found
    }

    //put all rotation in a hashset TODO: check if I need an override equal method for the hashset --> array as an object
    private Set<int[]> getAllRotations(int box) {
        Set<int[]> hashSet = new HashSet<>();
        hashSet.add(new int[]{boxes[box][0], boxes[box][1], boxes[box][2]});
        hashSet.add(new int[]{boxes[box][0], boxes[box][2], boxes[box][1]});
        hashSet.add(new int[]{boxes[box][1], boxes[box][2], boxes[box][0]});
        hashSet.add(new int[]{boxes[box][1], boxes[box][0], boxes[box][2]});
        hashSet.add(new int[]{boxes[box][2], boxes[box][1], boxes[box][0]});
        hashSet.add(new int[]{boxes[box][2], boxes[box][0], boxes[box][1]});
        return hashSet;
    }

    public void solve() {
        solve(0);
    }

    private boolean solve(int numBox) {
        //numBox = number of boxes --> if it is full, recursion stops
        if (numBox == boxes.length) {
            System.out.println("There is a solution");
            return true;
        }

        int[] pos = findPos();
        for (int i = 0; i < boxes.length; i++) {
            //the box is already used or there does no position exist
            if (used[i] || pos == null) {
                continue;
            }
            Set<int[]> comb = getAllRotations(i);
            //TODO: check if it is better to use x, y, z or if I should stick with pos[0]...
            int x = pos[0];
            int y = pos[1];
            int z = pos[2];
            for (int[] box : comb) {
                if (placeable(x, y, z, box)) {
                    used[i] = true;
                    placeBox(x, y, z, box);
                    if (solve(numBox + 1)) {
                        System.out.println("Box{" + "length=" + box[0] + ", width=" + box[1] + ", height=" + box[2] + "}"
                                + " Coordinates: x: " + x + " y: " + y + " z: " + z);
                        return true;
                    }
                    //if recursion doesn't work with this order, remove the box again
                    removeBox(x, y, z, box);
                    used[i] = false;
                }
            }
        }
        return false;
    }
}