{"id":1558,"date":"2023-09-20T15:30:47","date_gmt":"2023-09-20T15:30:47","guid":{"rendered":"https:\/\/www.antpace.com\/blog\/?p=1558"},"modified":"2025-08-25T18:06:51","modified_gmt":"2025-08-25T18:06:51","slug":"magic-squares-in-javascript","status":"publish","type":"post","link":"https:\/\/www.antpace.com\/blog\/magic-squares-in-javascript\/","title":{"rendered":"Magic Squares in JavaScript"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1580\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2022\/06\/magic-square-antpace.png\" alt=\"magic square\" width=\"500\" height=\"500\" \/><\/p>\n<p>In ma<span style=\"font-size: 1rem;\">thematics, a &#8220;<\/span><a style=\"font-size: 1rem;\" href=\"https:\/\/en.wikipedia.org\/wiki\/Magic_square\" target=\"_blank\" rel=\"noopener\">magic square<\/a><span style=\"font-size: 1rem;\">&#8221; is a matrix of numbers where each row, column, and diagonal add-up to the same number. That number is called the &#8220;<\/span><a style=\"font-size: 1rem;\" href=\"https:\/\/en.wikipedia.org\/wiki\/Magic_constant\" target=\"_blank\" rel=\"noopener\">magic constant<\/a><span style=\"font-size: 1rem;\">&#8220;. The integers used are only positive, and do not repeat<\/span><span style=\"font-size: 1rem;\">. The constant sum is determined by the size of the square and is described by a formula:<\/span><\/p>\n<pre>M = n * ((n^2 + 1) \/ 2)\n<\/pre>\n<p>Facts about the properties and classifications of these numeric formations have been discussed by scholars for millennia. Knowledge of this topic goes back thousands of years, and can be found referenced throughout the world.<\/p>\n<h2>History &amp; Culture<\/h2>\n<p>Magic squares have a fascinating historical and cultural significance, often with mystical undertones. They are mentioned in the <a href=\"https:\/\/en.wikipedia.org\/wiki\/I_Ching\" target=\"_blank\" rel=\"noopener\"><em>I Ching<\/em><\/a>, <em>Brhat Samhita<\/em>, and other works concerned with the transcendental and occult. They can be seen used in art, divination, perfumery, recreational gaming, computer science, and more.<\/p>\n<figure id=\"attachment_1790\" aria-describedby=\"caption-attachment-1790\" style=\"width: 600px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1790 size-full\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2022\/10\/Brhat-Samhita.png\" alt=\"a computer programmer using the Brhat Samhita to generate a talismans\" width=\"600\" height=\"600\" \/><figcaption id=\"caption-attachment-1790\" class=\"wp-caption-text\">AI images generated using Midjourney<\/figcaption><\/figure>\n<p>In the <em>Brhat Samhita<\/em>, the magic square is used as a symbolic representation of the planets. It makes use of magic squares in the creation of talismans for astrological purposes.<\/p>\n<p>For the purposes of this blog post, we&#8217;ll view them through the lens of software engineering.<\/p>\n<h2>3&#215;3 Magic Squares<\/h2>\n<p>There&#8217;s so much to cover on this topic. I&#8217;ll narrow it down to 3&#215;3 lattices (magic squares can actually be any size), specifically in the context of the JavaScript programming language. A quad of numbers, like the one pictured above, can be described as a two-dimensional array:<\/p>\n<pre>const magicSquare = [[4, 9, 2], [3, 5, 7], [8, 1, 6]];\n<\/pre>\n<p><strong>Squares of this size always have a magic constant of 15<\/strong>. And, the number 5 will be in the middle. There&#8217;s additional logic that explains which numerals can appear where and why. Those ideas are explored in the comments section of a <a href=\"https:\/\/www.hackerrank.com\/challenges\/magic-square-forming\/forum\" target=\"_blank\" rel=\"noopener\">HackerRank coding challenge<\/a> titled &#8220;Forming a Magic Square&#8221;.<\/p>\n<h2>HackerRank Coding Challenge<\/h2>\n<p><a href=\"https:\/\/www.hackerrank.com\/challenges\/magic-square-forming\/problem\" target=\"_blank\" rel=\"noopener\">This coding problem found on HackerRank<\/a> asks programmers to figure out what it would take to convert a 3&#215;3 matrix of integers into a magic square. The input array is almost valid, but requires a few replacements. For each change, we must track the difference between the numbers and return the total variance &#8211; referred to as the &#8220;cost&#8221;. The correct answer will be the lowest <strong>cost<\/strong> required to convert the input data into a magic square.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1575\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2022\/06\/hackerrank.png\" alt=\"hackerrank coding challenge\" width=\"916\" height=\"813\" \/><\/p>\n<p>The difficulty of this assignment is considered &#8220;medium&#8221;. The first step is realizing that there are a finite number of valid magic square configurations. As it turns out, there are exactly eight 3&#215;3 permutations:<\/p>\n<pre>const magicSquares = [[[4, 9, 2], [3, 5, 7], [8, 1, 6]],\n                [[6, 1, 8], [7, 5, 3], [2, 9, 4]],\n                [[8, 1, 6], [3, 5, 7], [4, 9, 2]],\n                [[2, 9, 4], [7, 5, 3], [6, 1, 8]],\n                [[8, 3, 4], [1, 5, 9], [6, 7, 2]],\n                [[4, 3, 8], [9, 5, 1], [2, 7, 6]],\n                [[6, 7, 2], [1, 5, 9], [8, 3, 4]],\n                [[2, 7, 6], [9, 5, 1], [4, 3, 8]]];\n<\/pre>\n<p>Starting with any one of these, we can generate the other seven programmatically. The subsequent arrangements can be derived through rotation and reflection. Using JavaScript, I rotate an initial seed square three times to have the first four series. Then, I flip each one of those to get the final records.<\/p>\n<pre>function generateMagicSquares(magicSquare1){\n\tconst magicSquares = [];\n\tmagicSquares.push(magicSquare1);\n\n\t\/\/ we need to rotate it 3 times to get all rotations\n\tfor(let i = 0; i &lt; 3; i++){\n\t\tvar rotation = magicSquares[i].map((val, index) =&gt; magicSquares[i].map(row =&gt; row[index]).reverse());\n\t\t\/\/ console.log(rotation)\n\t\tmagicSquares.push(rotation);\n\t}\n\n\t\/\/ and then flip each one\n\tfor(let i = 0; i &lt; 4; i++){\n\t\tvar flipped = magicSquares[i].map((_, colIndex) =&gt; magicSquares[i].map(row =&gt; row[colIndex]));\n\t\tmagicSquares.push(flipped);\n\t}\n\n\treturn magicSquares;\n}\n\nconst magicSquare1 = [[4, 9, 2], [3, 5, 7], [8, 1, 6]];\nconst magicSquares = generateMagicSquares(magicSquare1);\nconsole.log(magicSquares);<\/pre>\n<p>To solve this exercise, we&#8217;ll take the input array and compare it to each of the valid magic squares. We keep track of the cost on each iteration, and finally return the minimum.<\/p>\n<pre>function formingMagicSquare(s){\n\tconst magicSquares = [[[4, 9, 2], [3, 5, 7], [8, 1, 6]], [[6, 1, 8], [7, 5, 3], [2, 9, 4]], [[8, 1, 6], [3, 5, 7], [4, 9, 2]], [[2, 9, 4], [7, 5, 3], [6, 1, 8]], [[8, 3, 4], [1, 5, 9], [6, 7, 2]], [[4, 3, 8], [9, 5, 1], [2, 7, 6]], [[6, 7, 2], [1, 5, 9], [8, 3, 4]], [[2, 7, 6], [9, 5, 1], [4, 3, 8]]];\n\n\t\/\/ let minCost = 100000;\n\tlet minCost = Number.MAX_SAFE_INTEGER;\n\tlet cost = 0;\n\tfor(let i = 0; i &lt; magicSquares.length; i++){\n\t\tcost = determineCost(s, magicSquares[i]);\n\t\tif(cost &lt; minCost){\n\t\t\tminCost = cost;\n\t\t}\n\t}\n\treturn minCost;\n}\n<\/pre>\n<p>You&#8217;ll notice that the initial minimum cost is set to <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Number\/MAX_SAFE_INTEGER\" target=\"_blank\" rel=\"noopener\">a very high number<\/a>. As I loop through each of the valid magic squares, I check if the cost is lower than the current minimum and then replace the value.<\/p>\n<p>With each comparison, subtraction is used to determine the cost to complete the transformation. That code loops through each digit of each row on both 2D arrays. The absolute value of the difference between each coordinate is tallied and returned.<\/p>\n<pre>function determineCost(inputArray, validMagicSquare){\n\tlet cost = 0;\n\tfor(let i = 0; i &lt; 3; i++){ \/\/ each row\n\n\t\tfor(let j = 0; j &lt; 3; j++){ \/\/ each digit\n\n\t\t\tcost += Math.abs(inputArray[i][j] - validMagicSquare[i][j]);\n\t\t}\n\t}\n\treturn cost;\n}\n<\/pre>\n<p>The working code all stitched together looks like this:<\/p>\n<pre>&lt;script&gt;\n\nfunction generateMagicSquares(magicSquare1){\n\tconst magicSquares = [];\n\tmagicSquares.push(magicSquare1);\n\n\t\/\/ we need to rotate it 3 times to get all rotations\n\tfor(let i = 0; i &lt; 3; i++){\n\t\tvar rotation = magicSquares[i].map((val, index) =&gt; magicSquares[i].map(row =&gt; row[index]).reverse());\n\t\t\/\/ console.log(rotation)\n\t\tmagicSquares.push(rotation);\n\t}\n\n\t\/\/ and then flip each one\n\tfor(let i = 0; i &lt; 4; i++){\n\t\tvar flipped = magicSquares[i].map((_, colIndex) =&gt; magicSquares[i].map(row =&gt; row[colIndex]));\n\t\tmagicSquares.push(flipped);\n\t}\n\n\treturn magicSquares;\n}\n\nfunction determineCost(inputArray, validMagicSquare){\n\tlet cost = 0;\n\n\tfor(let i = 0; i &lt; 3; i++){ \/\/ each row\n\n\t\tfor(let j = 0; j &lt; 3; j++){ \/\/ each digit\n\n\t\t\tcost += Math.abs(inputArray[i][j] - validMagicSquare[i][j]);\n\t\t}\n\t}\n\n\treturn cost;\n\n}\n\nfunction formingMagicSquare(s){\n\t\/\/ const magicSquares = [[[4, 9, 2], [3, 5, 7], [8, 1, 6]], [[6, 1, 8], [7, 5, 3], [2, 9, 4]], [[8, 1, 6], [3, 5, 7], [4, 9, 2]], [[2, 9, 4], [7, 5, 3], [6, 1, 8]], [[8, 3, 4], [1, 5, 9], [6, 7, 2]], [[4, 3, 8], [9, 5, 1], [2, 7, 6]], [[6, 7, 2], [1, 5, 9], [8, 3, 4]], [[2, 7, 6], [9, 5, 1], [4, 3, 8]]];\n\tconst magicSquare1 = [[4, 9, 2], [3, 5, 7], [8, 1, 6]];\n\tconst magicSquares = generateMagicSquares(magicSquare1);\n\n\t\/\/ let minCost = 100000;\n\tlet minCost = Number.MAX_SAFE_INTEGER;\n\tlet cost = 0;\n\tfor(let i = 0; i &lt; magicSquares.length; i++){\n\t\tcost = determineCost(s, magicSquares[i]);\n\t\tif(cost &lt; minCost){\n\t\t\tminCost = cost;\n\t\t}\n\t}\n\treturn minCost;\n}\n\nconst finalCost = formingMagicSquare([[4, 9, 2], [3, 5, 7], [8, 1, 5]]);\nconsole.log(finalCost);\n&lt;\/script&gt;\n<\/pre>\n<p>This solution was not intuitive to me and took some research and experimentation. It was interesting to learn about the concept of magic squares (and other shapes) along the way.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1589\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2022\/06\/hackerrank-success.png\" alt=\"HackerRank challenge completed\" width=\"949\" height=\"768\" \/><\/p>\n<h4>Additional References<\/h4>\n<p><iframe loading=\"lazy\" title=\"Forming a Magic Square HackerRank Challenge Solution in Python\" width=\"525\" height=\"295\" src=\"https:\/\/www.youtube.com\/embed\/Jn9BtGA1EQk?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In mathematics, a &#8220;magic square&#8221; is a matrix of numbers where each row, column, and diagonal add-up to the same number. That number is called the &#8220;magic constant&#8220;. The integers used are only positive, and do not repeat. The constant sum is determined by the size of the square and is described by a formula: &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.antpace.com\/blog\/magic-squares-in-javascript\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Magic Squares in JavaScript&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":3275,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[71,81,82,99],"class_list":["post-1558","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development","tag-javascript","tag-math","tag-mathematics","tag-programming"],"_links":{"self":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1558","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/comments?post=1558"}],"version-history":[{"count":1,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1558\/revisions"}],"predecessor-version":[{"id":3276,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1558\/revisions\/3276"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/media\/3275"}],"wp:attachment":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/media?parent=1558"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/categories?post=1558"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/tags?post=1558"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}