/*
File: M55.cpp
Program for walking through all valid "X Patterns" and checking each
one for a 5x5 Magic Square solution.
Scott Bowden
August 13, 2020
*/#include <stdio.h>
#include <memory.h>
/* X Pattern count, solvable, unsolvable */
int nRLine, nRSLine, nRNLine;
/* The 5x5 work area */
int M5[25];
/* Bit mask for the numbers 1 to 25 (to check for duplicate numbers */
int B25[26] = { 0,
0x01000000, 0x00800000, 0x00400000, 0x00200000, 0x00100000,
0x00080000, 0x00040000, 0x00020000, 0x00010000, 0x00008000,
0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400,
0x00000200, 0x00000100, 0x00000080, 0x00000040, 0x00000020,
0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001,
};
/*
Codes for the 5 horizontal sums, 5 vertical sums, and 2 diagonal sums.
Each must sum to 65.
*/
#define H1 0
#define H2 1
#define H3 2
#define H4 3
#define H5 4
#define V1 5
#define V2 6
#define V3 7
#define V4 8
#define V5 9
#define D1 10
#define D2 11
/*
There are 512 parity combinations - but only 128 occur in valid X Patterns.
Here are the tally arrays for the total, solvable, and unsolvable counts.
*/
int naParity[512], naParityS[512], naParityN[512];
/*
Pointers for easy access into the Parity Tally arrays.
*/
const int *pT0 = naParity+0;
const int *pT1 = naParity+64;
const int *pT2 = naParity+128;
const int *pT3 = naParity+192;
const int *pT4 = naParity+256;
const int *pT5 = naParity+320;
const int *pT6 = naParity+384;
const int *pT7 = naParity+448;
const int *pS0 = naParityS+0;
const int *pS1 = naParityS+64;
const int *pS2 = naParityS+128;
const int *pS3 = naParityS+192;
const int *pS4 = naParityS+256;
const int *pS5 = naParityS+320;
const int *pS6 = naParityS+384;
const int *pS7 = naParityS+448;
const int *pN0 = naParityN+0;
const int *pN1 = naParityN+64;
const int *pN2 = naParityN+128;
const int *pN3 = naParityN+192;
const int *pN4 = naParityN+256;
const int *pN5 = naParityN+320;
const int *pN6 = naParityN+384;
const int *pN7 = naParityN+448;
/* Indices of the horizontal, vertical, and diagonal lines */
int T5[12][5] = {
{ 0, 1, 2, 3, 4 },
{ 5, 6, 7, 8, 9 },
{ 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19 },
{ 20, 21, 22, 23, 24 },
{ 0, 5, 10, 15, 20 },
{ 1, 6, 11, 16, 21 },
{ 2, 7, 12, 17, 22 },
{ 3, 8, 13, 18, 23 },
{ 4, 9, 14, 19, 24 },
{ 0, 6, 12, 18, 24 },
{ 4, 8, 12, 16, 20 }
};
/*
Instructions for building a Magic 5x5 square.
LS,LO = C, -1: Try all remaining value for cell #C
LS,LO = C, L: Compute the value of cell #cell by completing line #L
*/
int LS25[25] = {12, 0, 6, 18, 24, 4, 8, 16, 20, 1, 11, 21, 2, 3, 13, 23, 22, 10, 14, 5, 15, 7, 9, 17, 19};
int LO25[25] = {-1, -1, -1, -1, D1, -1, -1, -1, D2, -1, -1, V2, -1, H1, -1, V4, H5, -1, H3, -1, V1, -1, H2, V3, H4};
bool Solve25(int nLevel, int nB25L);
void Tally(bool bSolved);
void Report(bool bSolved);
void Report512(void);int main(int argv, void*argc[])
{
nRLine = 0;
nRSLine = 0;
nRNLine = 0;
memset(naParity,0,sizeof(naParity));
memset(naParityS,0,sizeof(naParity));
memset(naParityN,0,sizeof(naParity));
Solve25(0, 0x01FFFFFF);
printf("Final Report:\n");
printf(
"Final Report: %6.1d = %6.1d(solved) + %6.1d(unsolved)\n",
nRLine, nRSLine, nRNLine
);
Report512();
printf("Done\n");
getchar();
}
/*
Solve25 (recursive):
nLevel: Which level of recursion (and cell count) we are working on.
nB25L: Mask indicating which numbers have not been used so far.
return: Whether a solution was found.
Up to level 8, we are completing the X Pattern.
At level 8, we tally the results and periodically generate a report.
Also, at level 8, we always return false to force a search through all
X Patterns.
*/
bool Solve25(int nLevel, int nB25L)
{
bool bReport, bResult;
int n, nLS, nLO, nNext, nPick, nMask;
if(nLevel>=25) return true;
bReport = nLevel == 8;
nLS = LS25[nLevel];
nLO = LO25[nLevel];
nNext = nLevel+1;
if(nLO>=0) {
nPick = 0;
for(n=0;n<5;n++) nPick += M5[T5[nLO][n]];
nPick = 65 - nPick;
if((nPick<=0)||(nPick>25)) return false;
nMask = B25[nPick];
if((nMask & nB25L)==0) return false;
M5[nLS] = nPick;
bResult = Solve25(nNext,nB25L-nMask);
if(bReport) {
Report(bResult);
bResult = false;
}
} else {
nMask = 0x01000000;
for(nPick=1;nPick<=25;nPick++,nMask = nMask>>1) {
if((nB25L & B25[nPick])==0) continue;
M5[nLS] = nPick;
bResult = Solve25(nNext, nB25L-nMask);
if(bReport) {
Report(bResult);
bResult = false;
}
if(bResult) break;
}
}
M5[nLS] = 0;
if(nNext>=25) bResult = true;
return bResult;
}
void Report(bool bSolved)
{
Tally(bSolved);
if(nRLine % 10000) return;
printf(
"%6.1d=%6.1d+%6.1d: %2d xxx %2d; x %2d x %2d x; xx %2d xx; x %2d x %2d x; %2d xxx %2d, %s\n",
nRLine, nRSLine, nRNLine,
M5[0], M5[4], M5[6], M5[8], M5[12], M5[16], M5[18], M5[20], M5[24],
bSolved ? "Solved" : "None"
);
Report512();
}
void Report512(void)
{
int i;
for(i=0;i<64;i++) {
printf(
"%2d: %1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d |%1s%7d%7d%7d\n",
i,
((pT0[i]>16)&&((!pS0[i])!=(!pN0[i])))?"*":" ", pT0[i], pS0[i], pN0[i],
((pT1[i]>16)&&((!pS1[i])!=(!pN1[i])))?"*":" ", pT1[i], pS1[i], pN1[i],
((pT2[i]>16)&&((!pS2[i])!=(!pN2[i])))?"*":" ", pT2[i], pS2[i], pN2[i],
((pT3[i]>16)&&((!pS3[i])!=(!pN3[i])))?"*":" ", pT3[i], pS3[i], pN3[i],
((pT4[i]>16)&&((!pS4[i])!=(!pN4[i])))?"*":" ", pT4[i], pS4[i], pN4[i],
((pT5[i]>16)&&((!pS5[i])!=(!pN5[i])))?"*":" ", pT5[i], pS5[i], pN5[i],
((pT6[i]>16)&&((!pS6[i])!=(!pN6[i])))?"*":" ", pT6[i], pS6[i], pN6[i],
((pT7[i]>16)&&((!pS7[i])!=(!pN7[i])))?"*":" ", pT7[i], pS7[i], pN7[i]
);
}
}
#define X0 (*(M5+12))
#define X1 (*(M5+ 6))
#define X2 (*(M5+ 8))
#define X3 (*(M5+18))
#define X4 (*(M5+16))
#define X5 (*(M5+ 0))
#define X6 (*(M5+ 4))
#define X7 (*(M5+24))
#define X8 (*(M5+20))
void Tally(bool bSolved)
{
int nParity, nParityA, nParityB;
nParityA = (X1&1) + ((X2&1)<<1) + ((X3&1)<<2) + ((X4&1)<<3);
nParityB = (X5&1) + ((X6&1)<<1) + ((X7&1)<<2) + ((X8&1)<<3);
nParity = (X0&1) + (nParityA<<1) + (nParityB<<5);
nRLine ++;
naParity[nParity]++;
if(bSolved) {
nRSLine++;
naParityS[nParity]++;
} else {
nRNLine++;
naParityN[nParity]++;
}
}