Square Tile Pattern Counter (Eternity II Puzzle)

char* title = "Pieces";

char* help[] = { "Program to determine how many unique square tiles can be ",

"generated from a given number of possible edge patterns." };

#include <assert.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define debugprint(p) printf(#p ": %d" EOL, p);

#define dimensionof(p) (sizeof(p)/sizeof(p[0]))

#define EOL "\r\n"

#define putEOL() printf(EOL);

#define writeln(p) printf("%s" EOL, p)

int ipower10(int p)

{

int result = 1;

while(p--)

result *= 10;

return result;

}

int printnc(char c, int n)

{

int result = 0;

while (n--)

result +=putchar(c);

return result;

}

int encommulate(int n, int fieldwidth) // Print a number with comma separaters

{

int m;

int tenx;

int digits;

char dummy = ' ';

char spacer = ' ';

digits = fieldwidth - (fieldwidth / 4);

digits = max(1, digits);

tenx = ipower10(digits);

// check for overflow

if (n / tenx) return printnc('*', fieldwidth);

if (fieldwidth > 13) printnc(' ', fieldwidth - 13);

while(digits--)

{

tenx /= 10;

m = n / tenx;

n -= m * tenx;

if (m || (tenx==1))

{

putchar('0' + m);

dummy = '0';

spacer = ',';

}

else

putchar(dummy);

if (digits && ((digits % 3)==0))

putchar(spacer);

}

return fieldwidth;

}

void getenter(void)

{

printf("Press ENTER to continue/exit.");

while (getchar()!=0xa)

continue;

}

void showhelp(void)

{

int index;

for (index=0; index<dimensionof(help); index++)

writeln(help[index]);

}

int icalculate(int N)

{

// Original formula

return N // All four edges are the same

+ (N * (N-1)) // One edge is different

+ (N * (N-1) / 2)

+ (N * (N-1) / 2)

+ (N * (N-1) * (N-2)) // Two edges are different, two same edges are adjacent

+ (N * (N-1) * (N-2) / 2)

+ (N * (N-1) * (N-2) * (N-3) / 4); // All four edges are different

}

int fcalculate(int n)

{

/* Combining like terms

return ( N )

+ (2 * N * (N-1) )

+ (3 * N * (N-1) * (N-2) / 2 )

+ ( N * (N-1) * (N-2) * (N-3) / 4 );

*/

// Factoring works, but only if N is made a floating point number.

float N;

N = (float)n;

return (int)(N * (1 + ((N-1) * (2 + ((N-2) * (1.5 + ((N-3)/4)))))));

}

int compare_integers(const void* p1, const void* p2)

{

#ifndef invertsort

#define invertsort 0

#endif

#define n1 *(int*)p1

#define n2 *(int*)p2

if (n1==n2) return 0;

if (invertsort)

{

if (n1< n2) return 1;

else return -1;

}

else

{

if (n1< n2) return -1;

else return 1;

}

}

int main(int argc, char** argv)

{

#define FIELDWIDTH 9

#define MAXCOLORS 24

int number;

int duplicates;

int ix;

int jx;

int kx;

int mx;

int index;

int limit;

int n0; // original number

int n4; // n0 to the 4th power

int n5; // number under test

int* array; // where we store all possible tile arrangements

int* doubles;

int* result;

char* indent = " | ";

char* column_labels[] =

{

"Number of edge patterns/colors set to",

"Number of possible combinations",

"Number of duplicates",

"Number of unique pieces",

"Calculated Number (integer)",

"Calculated Number (float)"

};

writeln(title);

showhelp();

// Display column labels

for (ix=0; ix<dimensionof(column_labels); ix++)

{

for (jx=0; jx<ix; jx++)

printf(indent);

printf(" %s" EOL, column_labels[ix]);

}

for (ix=0; ix<dimensionof(column_labels); ix++)

{

printnc(' ', 1);

printnc('-', FIELDWIDTH-1);

}

putEOL();

limit = MAXCOLORS;

limit *= MAXCOLORS;

limit *= MAXCOLORS;

limit *= MAXCOLORS;

limit *= sizeof(*array);

array = (int*)malloc(limit);

doubles = (int*)malloc(limit);

for (number=1; number<=MAXCOLORS; number++)

{

n0 = number;

n4 = number;

n4 *= n4;

n4 *= n4;

encommulate(number, FIELDWIDTH);

encommulate(n4, FIELDWIDTH);

memset(array, 0, limit);

memset(doubles, 0, limit);

index = 0;

for (ix=0; ix<n0; ix++)

for (jx=0; jx<n0; jx++)

for (kx=0; kx<n0; kx++)

for (mx=0; mx<n0; mx++)

array[index++] = (ix << 24) + (jx << 16) + (kx << 8) + mx;

assert((int)index==n4);

duplicates = 0;

for (ix=0; ix<n4; ix++)

{

if (doubles[ix]) // Verify that we haven't already eliminated this one.

continue;

n5 = array[ix];

for (jx = 0; jx<3; jx++) // rotate each piece to the other three positions

{

kx = (n5 & 0xff) << 24;

n5 = kx | (n5 >> 8);

result = bsearch((void*)&n5, (void*)array, (size_t)n4, sizeof(int), compare_integers);

// result==array[ix] does not happen often, but it does happen occasionally

// It happens when we only have one color, then all four sides match, no

// matter which way you rotate it, or when both sets of opposite sides match,

// which only happens when you are only using two of the available colors, and

// you have rotated two positions.

if (result && (result!=&array[ix]))

{

kx = result - array; // Subtract two pointers and the

// difference is index units, not bytes.

if (doubles[kx]==0) // Verify that we haven't already counted this one.

{

doubles[kx] = 1;

duplicates++;

}

}

}

}

encommulate( duplicates, FIELDWIDTH);

encommulate(n4 - duplicates, FIELDWIDTH);

encommulate(icalculate(number), FIELDWIDTH);

encommulate(fcalculate(number), FIELDWIDTH);

if ( ((n4 - duplicates) != icalculate(number))

|| ((n4 - duplicates) != fcalculate(number))

)

printf(" ERROR!");

putEOL();

}

putEOL();

getenter();

return 0;

}