Byte Heist Home Leaderboard
Join the Heist (with Github)
Solve View

Magic Squares

Print 100 magic squares.

A magic square is a square where each row, column, and diagonal must sum to the same value. In addition, each value in the matrix must be unique.

Additionally, each matrix must be distinct, and not be linear mapable to any other matrix. That means there must not exist a slope and offset so matrix1 * slope + offset == matrix2.

Seperate each matrix by a double line break, and the values in each matrix by a space or tab. Separate rows with a single line break.

Matrix sizes must be between 3x3 and 6x6. You can mix and match if you want.

Judge

(async function*(context: Context): Challenge {
	function printMatrix<T>(matrix: T[][]): string {
		return matrix.map(
			row => row.map(
				cell => ''+cell
			).join(" ")
		).join("\n")
	}

	function isMagicSquare(matrix: number[][]): undefined | TestCase {
		const length = matrix.length;
			let sum: number | undefined;
			for(const i of range(length)) {
				let newSum = range(length).map(
					j=>matrix[i][j]
				).reduce((a,b)=>a+b);

				if (sum === undefined) {
					sum = newSum;
				}

				if (!Number.isFinite(sum)) {
					return new TestCase(
						"Finite sum",
						"Fail",
						{
							"Text": `Sum must be finite:\n${printMatrix(matrix)}`
						}
					)
				}

				if (Number.isNaN(sum)) {
					return new TestCase(
						"NaN sum",
						"Fail",
						{
							"Text": `Sum must not be NaN:\n${printMatrix(matrix)}`
						}
					)
				}

				if (sum !== newSum) {
					return new TestCase(
						"Matrix Rows",
						"Fail",
						{
							"Text": `Matrix rows do not sum to the same value:\n`+
							`${printMatrix(matrix)}\n\n`+
							`Expected ${sum} Got ${newSum} `
						}
					)
				}
			}

			for(const i of range(length)) {
				let newSum = range(length).map(
					j=>matrix[j][i]
				).reduce((a,b)=>a+b);

				if (sum !== newSum) {
					return new TestCase(
						"Matrix Columns",
						"Fail",
						{
							"Text": `Matrix columns do not sum to the same value:\n`+
							`${printMatrix(matrix)}\n`+
							`Expected ${sum} Got ${newSum} `
						}
					)
				}
			}

			for (const i of [0,1]) {
				let newSum = range(length).map(
					j=>matrix[j][i ? j : length - 1 - j]
				).reduce((a,b)=>a+b);

				if (sum !== newSum) {
					return new TestCase(
						"Matrix Diagonal",
						"Fail",
						{
							"Text": `Matrix diagonals do not sum to the same value:\n`+
							`${printMatrix(matrix)}\n`+
							`Expected ${sum} Got ${newSum} `
						}
					)
				}
			}
	}
	
	// Single Test
	yield (await context.run(undefined)).assert(
		(e): TestCase => {
			const matrixes: string[][][] = e.trimEnd().split(/\n\n+/g).map(
				z => z.trimEnd().split("\n").map(
					e => e.split(/[ |\t]+/g)
				)
			);

			for (const matrix of matrixes) {
				if (matrix.length != matrix[0].length ||
				   !matrix.every(row=>row.every(cell=>!Number.isNaN(+cell))
				   )) {
					return new TestCase("Matrix Size", "Fail", {"Text": `Matrix is not square or does not contain numbers: \n${printMatrix(matrix)}\n\nLength=${matrix.length}\nRow=${matrix[0].length}`});
				}

				if (matrix.length < 3 || matrix.length > 6) {
					return new TestCase("Matrix Size", "Fail", {"Text": `Matrix size must be between 3 and 6, got ${matrix.length}: \n${printMatrix(matrix)}`});
				}
			}
			const mats: number[][][] = matrixes.map(
				i=>i.map(j=>j.map(k=>+k))
			);

			for (const matrix of mats) {
				let square;
				if (square = isMagicSquare(matrix)) {
					return square;
				}
			}

			for (const matrix of mats) {
				if (new Set(matrix.flatMap(i=>i)).size != matrix.length * matrix.length) {
					return new TestCase(
						"Matrix Unique Values", "Fail", 
						{
							"Text": `Matrix must have all unique numbers\n${printMatrix(matrix)}`
						}
					)
				}
			}

			for (const matrix1 of mats) {
				for (const matrix2 of mats) {
					if (matrix1 === matrix2) {
						continue;
					}
					if (matrix1.length != matrix2.length) {
						continue;
					}

					const slope = (matrix1[1][0] - matrix1[0][0]) /
						          (matrix2[1][0] - matrix2[0][0]);
					const offset = matrix1[0][0] - matrix2[0][0] * slope;
						
					const matrix2mapped = matrix2.map(
						row => row.map(
							value => value * slope + offset
						)
					);

					if (
						range(matrix1.length).every(
							i=>range(matrix1.length).every(
								j=>matrix1[i][j] == matrix2mapped[i][j]
							)
						)
					) {
						return new TestCase(
							"Matrix must not be a linear mapping",
							"Fail",
							{
								"Text": `Two matrixes are integer ratios of eachother.\n${printMatrix(matrix1)}\n\n${printMatrix(matrix2)}\n\nSlope: ${slope}\nOffset:${offset}`
							}
						)
					}
				}
			}

			if (matrixes.length != 100) {
				return new TestCase(
					"Matrix Count",
					"Fail",
					{
						"Text": `Expected 100 squares, got ${matrixes.length}`
					}
				)				
			}

			return new TestCase(
				"Ok",
				"Pass",
				{
					"Text": "All sqares are valid!"
				}
			)
			
			
		}
	)
	// Finally, the challenge is passed if no test cases failed
	return context.noFailures();
})

Example Code

console.log(`10 3 13 8
5 16 2 11
4 9 7 14
15 6 12 1

10 3 13 22
19 16 2 11
4 9 21 14
15 20 12 1

10 3 13 36
33 16 2 11
4 9 35 14
15 34 12 1

10 3 13 50
47 16 2 11
4 9 49 14
15 48 12 1

10 3 13 64
61 16 2 11
4 9 63 14
15 62 12 1

10 3 13 78
75 16 2 11
4 9 77 14
15 76 12 1

10 3 13 92
89 16 2 11
4 9 91 14
15 90 12 1

10 3 13 106
103 16 2 11
4 9 105 14
15 104 12 1

10 3 13 120
117 16 2 11
4 9 119 14
15 118 12 1

10 3 13 134
131 16 2 11
4 9 133 14
15 132 12 1

10 3 13 148
145 16 2 11
4 9 147 14
15 146 12 1

10 3 13 162
159 16 2 11
4 9 161 14
15 160 12 1

10 3 13 176
173 16 2 11
4 9 175 14
15 174 12 1

10 3 13 190
187 16 2 11
4 9 189 14
15 188 12 1

10 3 13 204
201 16 2 11
4 9 203 14
15 202 12 1

10 3 13 218
215 16 2 11
4 9 217 14
15 216 12 1

10 3 13 232
229 16 2 11
4 9 231 14
15 230 12 1

10 3 13 246
243 16 2 11
4 9 245 14
15 244 12 1

10 3 13 260
257 16 2 11
4 9 259 14
15 258 12 1

10 3 13 274
271 16 2 11
4 9 273 14
15 272 12 1

10 3 13 288
285 16 2 11
4 9 287 14
15 286 12 1

10 3 13 302
299 16 2 11
4 9 301 14
15 300 12 1

10 3 13 316
313 16 2 11
4 9 315 14
15 314 12 1

10 3 13 330
327 16 2 11
4 9 329 14
15 328 12 1

10 3 13 344
341 16 2 11
4 9 343 14
15 342 12 1

10 3 13 358
355 16 2 11
4 9 357 14
15 356 12 1

10 3 13 372
369 16 2 11
4 9 371 14
15 370 12 1

10 3 13 386
383 16 2 11
4 9 385 14
15 384 12 1

10 3 13 400
397 16 2 11
4 9 399 14
15 398 12 1

10 3 13 414
411 16 2 11
4 9 413 14
15 412 12 1

10 3 13 428
425 16 2 11
4 9 427 14
15 426 12 1

10 3 13 442
439 16 2 11
4 9 441 14
15 440 12 1

10 3 13 456
453 16 2 11
4 9 455 14
15 454 12 1

10 3 13 470
467 16 2 11
4 9 469 14
15 468 12 1

10 3 13 484
481 16 2 11
4 9 483 14
15 482 12 1

10 3 13 498
495 16 2 11
4 9 497 14
15 496 12 1

10 3 13 512
509 16 2 11
4 9 511 14
15 510 12 1

10 3 13 526
523 16 2 11
4 9 525 14
15 524 12 1

10 3 13 540
537 16 2 11
4 9 539 14
15 538 12 1

10 3 13 554
551 16 2 11
4 9 553 14
15 552 12 1

10 3 13 568
565 16 2 11
4 9 567 14
15 566 12 1

10 3 13 582
579 16 2 11
4 9 581 14
15 580 12 1

10 3 13 596
593 16 2 11
4 9 595 14
15 594 12 1

10 3 13 610
607 16 2 11
4 9 609 14
15 608 12 1

10 3 13 624
621 16 2 11
4 9 623 14
15 622 12 1

10 3 13 638
635 16 2 11
4 9 637 14
15 636 12 1

10 3 13 652
649 16 2 11
4 9 651 14
15 650 12 1

10 3 13 666
663 16 2 11
4 9 665 14
15 664 12 1

10 3 13 680
677 16 2 11
4 9 679 14
15 678 12 1

10 3 13 694
691 16 2 11
4 9 693 14
15 692 12 1

10 3 13 708
705 16 2 11
4 9 707 14
15 706 12 1

10 3 13 722
719 16 2 11
4 9 721 14
15 720 12 1

10 3 13 736
733 16 2 11
4 9 735 14
15 734 12 1

10 3 13 750
747 16 2 11
4 9 749 14
15 748 12 1

10 3 13 764
761 16 2 11
4 9 763 14
15 762 12 1

10 3 13 778
775 16 2 11
4 9 777 14
15 776 12 1

10 3 13 792
789 16 2 11
4 9 791 14
15 790 12 1

10 3 13 806
803 16 2 11
4 9 805 14
15 804 12 1

10 3 13 820
817 16 2 11
4 9 819 14
15 818 12 1

10 3 13 834
831 16 2 11
4 9 833 14
15 832 12 1

10 3 13 848
845 16 2 11
4 9 847 14
15 846 12 1

10 3 13 862
859 16 2 11
4 9 861 14
15 860 12 1

10 3 13 876
873 16 2 11
4 9 875 14
15 874 12 1

10 3 13 890
887 16 2 11
4 9 889 14
15 888 12 1

10 3 13 904
901 16 2 11
4 9 903 14
15 902 12 1

10 3 13 918
915 16 2 11
4 9 917 14
15 916 12 1

10 3 13 932
929 16 2 11
4 9 931 14
15 930 12 1

10 3 13 946
943 16 2 11
4 9 945 14
15 944 12 1

10 3 13 960
957 16 2 11
4 9 959 14
15 958 12 1

10 3 13 974
971 16 2 11
4 9 973 14
15 972 12 1

10 3 13 988
985 16 2 11
4 9 987 14
15 986 12 1

10 3 13 1002
999 16 2 11
4 9 1001 14
15 1000 12 1

10 3 13 1016
1013 16 2 11
4 9 1015 14
15 1014 12 1

10 3 13 1030
1027 16 2 11
4 9 1029 14
15 1028 12 1

10 3 13 1044
1041 16 2 11
4 9 1043 14
15 1042 12 1

10 3 13 1058
1055 16 2 11
4 9 1057 14
15 1056 12 1

10 3 13 1072
1069 16 2 11
4 9 1071 14
15 1070 12 1

10 3 13 1086
1083 16 2 11
4 9 1085 14
15 1084 12 1

10 3 13 1100
1097 16 2 11
4 9 1099 14
15 1098 12 1

10 3 13 1114
1111 16 2 11
4 9 1113 14
15 1112 12 1

10 3 13 1128
1125 16 2 11
4 9 1127 14
15 1126 12 1

10 3 13 1142
1139 16 2 11
4 9 1141 14
15 1140 12 1

10 3 13 1156
1153 16 2 11
4 9 1155 14
15 1154 12 1

10 3 13 1170
1167 16 2 11
4 9 1169 14
15 1168 12 1

10 3 13 1184
1181 16 2 11
4 9 1183 14
15 1182 12 1

10 3 13 1198
1195 16 2 11
4 9 1197 14
15 1196 12 1

10 3 13 1212
1209 16 2 11
4 9 1211 14
15 1210 12 1

10 3 13 1226
1223 16 2 11
4 9 1225 14
15 1224 12 1

10 3 13 1240
1237 16 2 11
4 9 1239 14
15 1238 12 1

10 3 13 1254
1251 16 2 11
4 9 1253 14
15 1252 12 1

10 3 13 1268
1265 16 2 11
4 9 1267 14
15 1266 12 1

10 3 13 1282
1279 16 2 11
4 9 1281 14
15 1280 12 1

10 3 13 1296
1293 16 2 11
4 9 1295 14
15 1294 12 1

10 3 13 1310
1307 16 2 11
4 9 1309 14
15 1308 12 1

10 3 13 1324
1321 16 2 11
4 9 1323 14
15 1322 12 1

10 3 13 1338
1335 16 2 11
4 9 1337 14
15 1336 12 1

10 3 13 1352
1349 16 2 11
4 9 1351 14
15 1350 12 1

10 3 13 1366
1363 16 2 11
4 9 1365 14
15 1364 12 1

10 3 13 1380
1377 16 2 11
4 9 1379 14
15 1378 12 1

4 3 8
9 5 1
2 7 6`)