Exercising Coding Skills Through Practice

In my current role, I am not writing as much code as I used to as I am being given opportunities to lead other ways. For the most part I am okay with that, but I do enjoy busting out a quick win here and there to help out when I can. That said, being a developer is definitly a skill I’d like to maintain and not lose to much strength with.

Ironically over the holiday my son received a pack of brain teasers, one of which was called the Magic Square. Given a 3x3 grid and the numbers 1 - 9, you were to arrange the numbers in the grid so that all rows, columns, and diagonals equal 15. After aa few tries, my son was manually able to find the solution on paper. I offered that I was thinking about how to do it with code and he seemed interested, which isn’t always the case. Next thing you know, we were looking at VSCode and seeing what we could do.

I first attacked the problem with a very direct approach to the 3 x 3 square. This took 20 minutes of so to get a working solution:

var array = [1,2,3,4,5,6,7,8,9];
var validSolutions = 0;
var solutions = []
var startTime = new Date();

function solution(arr, rows, columns) {
    var row1 = arr[0] + arr[1] + arr[2] === 15;
    var row2 = arr[3] + arr[4] + arr[5] === 15;
    var row3 = arr[6] + arr[7] + arr[8] === 15;

    var col1 = arr[0] + arr[3] + arr[6] === 15;
    var col2 = arr[1] + arr[4] + arr[7] === 15;
    var col3 = arr[2] + arr[5] + arr[8] === 15;

    var dia1 = arr[0] + arr[4] + arr[8] === 15;
    var dia2 = arr[2] + arr[4] + arr[6] === 15;

    if (row1 && row2 && row3 && col1 && col2 && col3 && dia1 && dia2) {
        validSolutions++;
        solutions.push(arr.join(','));
        return true;
    }

    

    return false;
}

const shuffleArray = array => {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      const temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  }

var tries = 1;

function game() {
    while (!solution(array, r, c)) {
        //shuffle
        shuffleArray(array);
        // console.log(array);
        tries++;
    }

    var endTime = new Date();
    var seconds = Math.abs(startTime - endTime) / 1000;
    //// print solutions
    for (i = 0; i < solutions.length; i++) {
        var arr = solutions[i].split(',');
        console.log("--------- Solution: # " + (i+1).toString() + " ------------")
        console.log(arr[0] + ", " + arr[1] + ", " + arr[2] )
        console.log(arr[3] + ", " + arr[4] + ", " + arr[5] )
        console.log(arr[6] + ", " + arr[7] + ", " + arr[8] )
    }

    console.log("--------------------------------")
    console.log("For a " + r.toString() + " x " + c.toString() + " grid.  The Magic # is " + magicNumber);
    console.log("Valid Solutions: " + validSolutions.toString());
    console.log("Attempts: " + tries.toString());
    console.log("Seconds Ran: " + seconds.toString());

}
game();

I added some logging to show him a little bit more of what was happening when the code executed. He was blown away that it was trying 50,000+ iterations at times to find an answer.

As with any problem however, sometimes they are hard to put down. Doing some research showed that there were 8 valid solutions to a 3 X 3 magic square. I was able to eventually find them all, and then go one step forward and abstract it to allow for any size square, such as a 4 X 4 square with the magic number of 34. It took my system 10 minutes to find a single solution after just shy of 600 million iterations.

var magicNumber = 0;

var array = [];
var validSolutions = 0;
var solutions = []
var startTime = new Date();

const reducer = (previousValue, currentValue) => previousValue + currentValue;

function solution(arr, number) {
    
    var valid = [];
    for (i=0; i<number; i++) {
        var row = [];
        for (r = 0; r < number; r++) {
            row.push(arr[i*number+r])
        }
        var sum = row.reduce(reducer);
        valid.push(sum === magicNumber)
    }

    for (i=0; i<number; i++) {
        var col = [];
        for (c = 0; c < number; c++) {
            col.push(arr[i+c*number]);
        }
        var sum = col.reduce(reducer);
        // arr[i] + arr[i+number] + array[i+number*2];
        valid.push(sum === magicNumber)
    }

    var dia1 = [];
    for (i=0; i < number; i++) {
        dia1.push(arr[i*number+i]);
    }
    valid.push(dia1.reduce(reducer) === magicNumber);

    var dia2= [];
    for (i=number; i > 0; i--) {
        dia2.push(arr[i*number-i]);
    }
    valid.push(dia2.reduce(reducer) === magicNumber);

    var dupSolution = solutions.length > 0 && solutions.find(x => x === arr.join(',')) !== undefined;
    
    if (valid.every((x) => x === true) && !dupSolution) {
        validSolutions++;
        solutions.push(arr.join(','));

        if (validSolutions === solutions.length) {
            return true;
        }
    }

    

    return false;
}

const shuffleArray = array => {
    for (let i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      const temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
  }

var tries = 1;

function game(number, matches) {
    var lastNumber = number*number;
    validSolutions = matches;
    var sum = 0;
    for (i=1; i <= lastNumber; i++) {
        array.push(i);
        sum += i;
    }

    magicNumber = sum / number;

    while (!solution(array, number)) {
        //shuffle
        shuffleArray(array);
        // console.log(array);
        tries++;
    }

    var endTime = new Date();
    var seconds = Math.abs(startTime - endTime) / 1000;
    //// print solutions
    for (i = 0; i < solutions.length; i++) {
        var arr = solutions[i].split(',');
        console.log("--------- Solution: # " + (i+1).toString() + " ------------")
        if (number === 3) {
            console.log(arr[0] + ", " + arr[1] + ", " + arr[2] )
            console.log(arr[3] + ", " + arr[4] + ", " + arr[5] )
            console.log(arr[6] + ", " + arr[7] + ", " + arr[8] )
        } else {
            console.log(arr[0] + ", " + arr[1] + ", " + arr[2] + ", " + arr[3] )
            console.log(arr[4] + ", " + arr[5] + ", " + arr[6] + ", " + arr[7] )
            console.log(arr[8] + ", " + arr[9] + ", " + arr[10] + ", " + arr[11] )
            console.log(arr[12] + ", " + arr[13] + ", " + arr[14] + ", " + arr[15] )
        }
    }

    console.log("--------------------------------")
    console.log("For a " + number.toString() + " x " + number.toString() + " grid.  The Magic # is " + magicNumber);
    console.log("Valid Solutions: " + validSolutions.toString());
    console.log("Attempts: " + tries.toString());
    console.log("Seconds Ran: " + seconds.toString());

}
game(4,1);

It was really fun not just to practice, but also show my son some of skills too!

Categories: