9 x 9 Magic Square

 

(Top 9 submitted solutions Zimmermann contest)

 

 

                                1408 units retained

 

 

 

 

                                    1401 units 2nd best pattern

 

 

                                                        1400 units

 

 

                                                                      1400 

 

 

                                                                     1398

 

                                                                   1397

 

                                                                      1394

 

 

                                                                        1389

 

 

 

 

 

 

Question #1.  How good are we at predicting the correct pattern for maximum water retention for the various orders of magic squares.

Answer – not very good.  The top contestants did not find the best pattern for many orders

 

Question #2.  If one has the correct pattern in hand … how good is the machinery in finding the best solution for that pattern?

Answer –   computer assisted human ingenuity is probably better at this than #1 above.

 

Question #3.   What volume of water can be exchanged between the lakes and the ponds?

Answer –         Order 7       12 units

                          Order 8     25 units (? greater)

Lake capacities:   284 – 295      7x7 MS   418 units

 

Lake Base:   284 – 308       8x8 MS   797 units retained

 

 

 

 

For most orders the interior of the lake seems boring and amorphous.  The enumeration of solutions for the order 7 magic square shows that the lake can have a base sum of 161 to 172.  The spillway value of course is a constant.  The lakes and ponds can exchange a fair amount of water.  The Order 9 MS may require a more precise placement of numbers in the lake interior.  Its solution was not turned in until the last day of the contest. 

 

I imagine that a careful study was made of the 1407 solution. Improvements in number positioning were then made to achieve the 1408 retention.  The dissection below may help to expose the thought process.  The yellow cell with red font shows the auto sum of the 9x9 square to the left.  Conditional formatting from excel spreadsheet shows duplicate numbers in both solutions in red for the outer border.  When the sum of the 4 corner cells decreases by 1 the sum of the outer border increases by one.

                                                                                                                                                              

 

1408

1407

1

37

57

47

66

50

53

55

3

1

40

57

43

66

51

55

53

3

43

59

11

77

25

75

10

15

54

38

59

11

76

30

77

10

14

54

58

7

78

21

34

22

81

12

56

58

4

79

18

35

22

81

16

56

45

73

14

23

32

30

27

74

51

50

72

23

12

41

29

20

74

48

69

17

39

28

52

36

35

26

67

3321

69

17

33

45

52

28

31

26

68

3321

44

72

18

38

42

13

20

76

46

47

73

13

39

27

24

25

75

46

64

6

80

16

31

24

79

8

61

63

5

80

21

36

19

78

7

60

40

65

9

70

19

71

4

62

29

37

65

9

71

15

70

8

62

32

5

33

63

49

68

48

60

41

2

6

34

64

44

67

49

61

42

2

1

37

57

47

66

50

53

55

3

1

40

57

43

66

51

55

53

3

43

 

 

 

 

 

 

 

54

38

 

 

 

 

 

 

 

54

58

 

 

 

 

 

 

 

56

58

 

 

 

 

 

 

 

56

45

 

 

 

 

 

 

 

51

50

 

 

 

 

 

 

 

48

69

 

 

 

 

 

 

 

67

1465

69

 

 

 

 

 

 

 

68

1464

44

 

 

 

 

 

 

 

46

47

 

 

 

 

 

 

 

46

64

 

 

 

 

 

 

 

61

63

 

 

 

 

 

 

 

60

40

 

 

 

 

 

 

 

29

37

 

 

 

 

 

 

 

32

5

33

63

49

68

48

60

41

2

6

34

64

44

67

49

61

42

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

25

 

 

 

 

 

 

 

 

30

 

 

 

 

 

 

 

21

 

22

 

 

 

 

 

 

18

 

22

 

 

 

 

 

14

 

 

 

27

 

 

 

 

23

 

 

 

20

 

 

 

17

 

 

 

 

 

26

 

249

 

17

 

 

 

 

 

26

 

249

 

 

18

 

 

 

20

 

 

 

 

13

 

 

 

25

 

 

 

 

 

16

 

24

 

 

 

 

 

 

21

 

19

 

 

 

 

 

 

 

19

 

 

 

 

 

 

 

 

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

25

 

 

 

 

 

 

 

 

30

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

17

 

 

 

 

 

26

 

87

 

17

 

 

 

 

 

26

 

88

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

19

 

 

 

 

 

 

 

 

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

23

 

30

 

 

 

 

 

 

12

 

29

 

 

 

 

 

 

 

52

 

 

 

 

156

 

 

 

 

52

 

 

 

 

156

 

 

 

38

 

13

 

 

 

 

 

 

39

 

24

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

17

 

 

 

 

 

 

 

70

 

17

 

 

 

 

 

 

 

66

 

 

18

 

 

 

 

 

 

 

 

13

 

 

 

 

 

 

 

 

 

16

 

 

 

 

 

 

 

 

21

 

 

 

 

 

 

 

 

 

19

 

 

 

 

 

 

 

 

15

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

25

 

 

 

 

 

 

 

 

30

 

 

 

 

 

 

 

 

 

22

 

 

 

 

 

 

 

 

22

 

 

 

 

 

 

 

 

 

27

 

 

 

 

 

 

 

 

20

 

 

 

 

 

 

 

 

 

26

 

100

 

 

 

 

 

 

 

26

 

98

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

34

 

 

 

 

 

 

 

 

35

 

 

 

 

 

 

 

 

 

30

 

 

 

 

 

 

 

 

29

 

 

 

 

 

 

 

 

 

35

 

 

99

 

 

 

 

 

 

31

 

 

95

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

66

50

53

55

3

 

 

 

 

66

51

55

53

3

 

 

 

 

25

75

10

15

54

 

 

 

 

30

77

10

14

54

 

 

 

 

34

22

81

12

56

 

 

 

 

35

22

81

16

56

 

 

 

 

32

30

27

74

51

 

 

 

 

41

29

20

74

48

 

 

 

 

52

36

35

26

67

1041

 

 

 

 

52

28

31

26

68

1040

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

50

53

55

3

 

 

 

 

 

51

55

53

3

 

 

 

 

 

75

10

15

54

 

 

 

 

 

77

10

14

54

 

 

 

 

 

22

81

12

56

 

 

 

 

 

22

81

16

56

 

 

 

 

 

30

27

74

51

 

 

 

 

 

29

20

74

48

 

 

 

 

 

 

 

 

 

668

 

 

 

 

 

 

 

 

 

663

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

53

55

3

 

 

 

 

 

 

55

53

3

 

 

 

 

 

 

10

15

54

 

 

 

 

 

 

10

14

54

 

 

 

 

 

 

81

12

56

 

 

 

 

 

 

81

16

56

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

339

 

 

 

 

 

 

 

 

 

342

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

55

3

 

 

 

 

 

 

 

53

3

 

 

 

 

 

 

 

15

54

 

 

 

 

 

 

 

14

54

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

127

 

 

 

 

 

 

 

 

 

124

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

For most orders the interior of the lake seems boring and amorphous.  The enumeration of solutions for the order 7 magic square shows that the lake can have a base sum of 161 to 172.  The spillway value of course is a constant.  The lakes and ponds can exchange a fair amount of water.  The Order 9 MS may require a more precise placement of numbers in the lake interior.  Its solution was not turned in until the last day of the contest. 

 

I find myself increasingly going to Harry White’s website to utilize the magic square tools he provides.  The program below produces a 3d image of the square.  The code allows easy modification of the RGB color scheme.

 

 

/*

 *  File:    WaterRetention3D.cpp

 *  Author:  S Harry White

 *  Created: 2011-03-01

 *  Updated: 2011-04-12

 *  visual studio 2008 compatability 2011-5-11 Andrew Knecht

 *  color change 2011-5-18 Craig Knecht

 */

 

#include "stdafx.h"

#include <assert.h>

#include <direct.h>

#include <errno.h>

#include <io.h>

#include <math.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#using <mscorlib.dll>

//------------------------------------------------------------------------------

 

#define max(A, B) ((A) > (B) ? (A) : (B))

#define Uint unsigned long int

 

int N;                 // The order of the square.

int N_1;               // N - 1

int NN;                // N * N

int NN_1;              // NN - 1

Uint MagicConstant;    // N * (NN + 1) / 2 for 1 base

const int maxN = 200;  // browsers can't handle big squares

                       // and units retained may overflow anyway

int inputSquareNumber, smallestNumber, biggestNumber;

 

int **xSquare = NULL, **bSquare = NULL;

bool **isSpillWay = NULL;

bool *numberUsed = NULL; // Check that numbers 1..NN are in a magic square.

const int startSquaresSize = 28;

int allocatedSquaresSize = 0;

 

struct t_Cell {

  int priority;  // Smaller value has higher priority.

  int row;

  int col;

};

 

int allocatedQueueSize = 0;

t_Cell *Queue = NULL;

int qIndex = 0;

//---------------------------------------------------------------------------

 

char *storeAllocFail = "Storage allocation failed";

void reportError(char *msg)

//   -----------

{

  printf("\a\nError: %s.\n", msg);

} // reportError

//---------------------------------------------------------------------------

 

void freeSquare(int*** square, int size)

//   ----------

{

  if (*square != NULL) {

    for(int i = 0; i < size; i++) {

      free((*square)[i]);

    }

    free(*square); *square = NULL;

  }

} // freeSquare

 

void freeIsSpillWay(int size)

//   --------------

{

  if (isSpillWay != NULL) {

    for(int i = 0; i < size; i++) {

      free(isSpillWay[i]);

    }

    free(isSpillWay); isSpillWay = NULL;

  }

} // freeIsSpillWay

 

void freeUsed()

//   --------

{

  if (numberUsed != NULL) {

      free(numberUsed); numberUsed = NULL;

  }

} // freeUsed

 

void freeQueue()

//   ---------

{

  if (Queue != NULL) { free(Queue); Queue = NULL; }

}

 

void freeStore()

//   ---------

{

  freeSquare(&bSquare, allocatedSquaresSize);

  freeSquare(&xSquare, allocatedSquaresSize);

  freeIsSpillWay(allocatedSquaresSize);

 

  allocatedSquaresSize = 0;

  freeUsed(); freeQueue();

}

 

bool allocateSquare(int*** square, int size)

//   --------------

{

  bool ok;

 

  *square = (int**) malloc(size * sizeof(int*));

  ok = (*square != NULL);

  if (ok) {

    int numAllocated = 0;

    for(int i = 0; i < size; i++) {

      int* p = (int*) malloc(size * sizeof(int)); (*square)[i] = p;

      if (p == NULL) { numAllocated = i; ok = false; break; }

    }

    if (!ok) freeSquare(square, numAllocated);

  }

  return ok;

} // allocateSquare

 

bool allocateIsSpillWay(int size)

//   ------------------

{

  bool ok;

 

  isSpillWay = (bool**) malloc(size * sizeof(bool*));

  ok = (isSpillWay != NULL);

  if (ok) {

    int numAllocated = 0;

    for(int i = 0; i < size; i++) {

      bool *p = (bool*) malloc(size * sizeof(bool)); isSpillWay[i] = p;

      if (p == NULL) { numAllocated = i; ok = false; break; }

    }

    if (!ok) freeIsSpillWay(numAllocated);

  }

  return ok;

} // allocateIsSpillWay

 

bool allocateUsed(int size)

//   ------------

{

  return ( (numberUsed = (bool*) malloc(size * sizeof(bool))) != NULL);

} // allocateUsed

//---------------------------------------------------------------------------

 

bool allocateQueue(int size)

//   -------------

{

  bool ok = true;

  if (allocatedQueueSize < size) {

      freeQueue();

    Queue = (t_Cell *) malloc(size * sizeof(t_Cell));

      if (ok = (Queue != NULL)) allocatedQueueSize = size;

  }

  return ok;

} // allocateQueue

 

bool increaseQueue()

//   -------------

{

  int size = allocatedQueueSize + allocatedQueueSize;

 

  t_Cell *tmp = (t_Cell *) malloc(size * sizeof(t_Cell));

  if (tmp == NULL) { reportError(storeAllocFail); return false; }

  for (int i = 0; i < qIndex; i++) tmp[i] = Queue[i];

  freeQueue(); Queue = tmp; allocatedQueueSize = size;

  return true;

} // increaseQueue;

 

bool allocateStore(int size)

//   -------------

{

  bool ok = true;

  if (size < startSquaresSize) size = startSquaresSize;

  if (size > allocatedSquaresSize) {

    freeStore();

    if (ok = allocateSquare(&xSquare, size))

        if (ok = allocateSquare(&bSquare, size))

        if (ok = allocateIsSpillWay(size))

          if (ok = allocateQueue(size*size))

                if (ok = allocateUsed(size*size+1))

              allocatedSquaresSize = size;

    if (!ok) freeStore();

  }

  return ok;

} // allocateStore

//---------------------------------------------------------------------------

 

//FILE *Qfp = NULL;

//void printQueue()

////   ----------

//{

//

//  int i = 0;

//  while (i < qIndex) {

//    t_Cell *q = &Queue[i];

//    fprintf(Qfp,"priority %d row %d col %d\n",

//          q->priority, q->row, q->col);

//    i++;

//  }

//  fprintf(Qfp,"\n");

//} // printQueue

//---------------------------------------------------------------------------

 

enum magicType {notMagic, notNormal, Normal};

magicType isMagic(bool checkZeroBased)

//        -------

{

  //if (N > maxNMagicConstant) return Normal; // Overflow, can't check.

  Uint chkSum = 0;

  Uint magicSum = checkZeroBased ? MagicConstant - N : MagicConstant;

  int min = checkZeroBased ? 0 : 1, max = NN + min - 1;

  Uint sumX, sumY, sumXY = 0, sumYX = 0;

 

  for (int i = min; i <= max; i++) numberUsed[i] = false;

  for (int i = 0; i < N; i++) {

    sumX = 0; sumY = 0;

    for (int j = 0; j < N; j++) {

        int tmp = xSquare[i][j];

 

        if ((min <= tmp) && (tmp <= max)) numberUsed[tmp] = true;

      sumX += tmp;

      sumY += xSquare[j][i];

    }

      if (i == 0) chkSum = sumX;

    if ((sumX != chkSum) || (sumY != chkSum)) return notMagic;

    sumXY += xSquare[i][N_1-i];

    sumYX += xSquare[i][i];

  }

  if ((sumXY != chkSum) || (sumYX != chkSum)) return notMagic;

  if (chkSum != magicSum) return notNormal;

  for (int i = min; i <= max; i++) if (!numberUsed[i]) return notNormal;

  return Normal;

} // isMagic

//--------------------------------------------------------------------------

 

void outputLocalTime()

//   --------------

{

  time_t startTime = time(NULL);

  tm local;  localtime_s(&local, &startTime);  char dateTime[100];

  size_t slen = strftime(dateTime, 100, "%A %Y-%m-%d %X %Z\n\n\0", &local);

 

  printf(dateTime);

} // outputLocalTime

//--------------------------------------------------------------------------

 

void initBounds(int value)

//   ----------

{

  for (int r = 0; r < N; r++) {

      bSquare[r][0] = xSquare[r][0];

      bSquare[r][N_1] = xSquare[r][N_1];

  }

  for (int c = 1; c < N_1; c++) {

      bSquare[0][c] = xSquare[0][c];

      bSquare[N_1][c] = xSquare[N_1][c];

  }

 

  for (int r = 1; r < N_1; r++)

      for (int c = 1; c < N_1; c++)

      bSquare[r][c] = value;

  qIndex = 0;

} // initBounds

//---------------------------------------------------------------------------

 

void pushQueue(int priority, int row, int col)

//   ---------

{

  assert (qIndex < allocatedQueueSize);

  t_Cell cell;

  cell.priority = priority; cell.row = row; cell.col = col;

  Queue[qIndex++] = cell;

} //  pushQueue

 

bool insertQueue(int priority, int row, int col)

//   -----------

{

  if ((qIndex >= allocatedQueueSize) && !increaseQueue()) return false;

  t_Cell cell;

  cell.priority = priority; cell.row = row; cell.col = col;

 

  int i = qIndex-1;

  while (Queue[i].priority < priority) {

    Queue[i+1] = Queue[i]; if (--i < 0) break;

  }

  Queue[++i] = cell; ++qIndex;

  //printQueue();

  return true;

} // insertQueue

 

void popQueue(t_Cell *cell)

//   --------

{

  assert(qIndex > 0); *cell = Queue[--qIndex];

}

//---------------------------------------------------------------------------

 

bool queueBorder()

//   -----------

{

  pushQueue(bSquare[1][0], 1, 0);

  if (!insertQueue(bSquare[1][N_1], 1, N_1)) return false;

 

  for (int r = 2; r < N_1; r++) {

      if (!insertQueue(bSquare[r][0], r, 0)) return false;

      if (!insertQueue(bSquare[r][N_1], r, N_1)) return false;

  }

 

  for (int c = 1; c < N_1; c++) {

      if (!insertQueue(bSquare[0][c], 0, c)) return false;

      if (!insertQueue(bSquare[N_1][c], N_1, c)) return false;

  }

  return true;

} // queueBorder

//---------------------------------------------------------------------------

 

bool computeBounds(int p, int r, int c)

//   -------------

{

  if ( ( (0 < r) && (r < (N_1)) ) &&

         ( (0 < c) && (c < (N_1)) ) ) {

    int *bp = &bSquare[r][c];

    int tmp = max(xSquare[r][c],p);

    if (tmp < *bp) {

      *bp = tmp;

      if (!insertQueue(tmp, r, c)) return false;

    }

  }

  return true;

} // computeBounds

//---------------------------------------------------------------------------

 

void computeCapacity()

//   ---------------

{

  Uint count = 0, units = 0;

  for (int r = 0; r < N; r++)

      for (int c = 0; c < N; c++) {

        int delta = bSquare[r][c] - xSquare[r][c];

      if (delta != 0) { ++count; units += delta; }

      }

  if (units == 0)

    printf("Square number %d: units retained 0\n\n", inputSquareNumber);

  else

    printf("Square number %d: units retained %u, cells retaining %u\n\n",

           inputSquareNumber, units, count);

} // computeCapacity

//---------------------------------------------------------------------------

 

const int di[] = {1, 0, -1, 0}, dj[] = {0, 1, 0, -1};

void getSpillWays()

//   ------------

{

  for (int j = 0; j < N; ++j) for (int i = 0; i < N; ++i)

    isSpillWay[j][i] = false;

  int **x = xSquare, **b = bSquare;

  for (int j = 1; j < N_1; ++j) for (int i = 1; i < N_1; ++i) {

    if (b[j][i] > x[j][i]) {

      for (int d = 0; d < 4; ++d) {

        int li = di[d] + i, lj = dj[d] + j;

        if (x[lj][li] == b[j][i]) isSpillWay[lj][li] = true;

      }

    }

  }

} // getSpillWays

//---------------------------------------------------------------------------

 

/*

 *  Based on an algorithm of Gareth McCaughan supplied by Craig Knecht.

 */

bool getWater(int base1factor)

//   --------

{

//fopen_s(&Qfp, "C:\\QueueTrace.txt", "w");

  for (int j = 0; j < N; ++j) for (int i = 0; i < N; ++i)

    xSquare[j][i] -= base1factor;

 

  initBounds(biggestNumber-base1factor);

  if (!queueBorder()) return false;

  while (qIndex != 0) {

//printQueue();

      t_Cell cell; popQueue(&cell);

      int p = cell.priority, r = cell.row, c = cell.col;

      if (!computeBounds(p, r-1, c)) return false;

    if (!computeBounds(p, r+1, c)) return false;

      if (!computeBounds(p, r, c-1)) return false;

    if (!computeBounds(p, r, c+1)) return false;

  }

  computeCapacity();

  getSpillWays();

//fclose(Qfp);

  return true;

} // getWater

//==========================================================================

 

bool inputSquare(FILE *rfp)

//   -----------

{

  bool checkZeroBased = false;

  smallestNumber = LONG_MAX; biggestNumber = LONG_MIN;

 

  for (int r = 0; r < N; r++) {

    for (int c = 0; c < N; c++) {

        int rv, tmp;

 

        if ( (rv = fscanf_s(rfp, "%d", &tmp)) != 1) {

        if ( (rv != EOF) || (r != 0) || (c != 0) )

          reportError("Reading square from file");

        return false;

      } else {

        if (tmp == 0) checkZeroBased = true;

        if (tmp > biggestNumber)  biggestNumber  = tmp;

        if (tmp < smallestNumber) smallestNumber = tmp;

        xSquare[r][c] = tmp;

      }

    }

  }

  ++inputSquareNumber;

 

  magicType t = isMagic(checkZeroBased);

  if (t == notMagic) {

    printf("Square number %d is not a magic square.\n",

           inputSquareNumber);

  } else if (t == notNormal) {

    printf("Square number %d is not a normal magic square.\n",

           inputSquareNumber);

  }

  return true;

} // inputSquare

//--------------------------------------------------------------------------

 

void initGlobals()

//   -----------

{

  N_1 = N - 1;

  NN  = N * N;

  MagicConstant = N * (NN + 1) / 2;

} // initGlobals

//--------------------------------------------------------------------------

 

void get_rest_of_line(int c)

//   ----------------

{

  if (c != '\n') do { c = getchar(); } while (c != '\n');

}

 

bool getY()

//   ----

{

  int c;  do { c = getchar(); }

          while ((c == ' ') || (c == '\t') || (c == '\n'));

  get_rest_of_line(c);  return (c == 'Y') || (c == 'y');

}

 

// If user inputs size instead of 'y' or 'n', use it.

bool getYorOrder(int *n)

//   -----------

{

  bool result = false; int c; *n = 0;

 

  do { c = getchar(); } while ((c == ' ') || (c == '\t') || (c == '\n') );

  if ( (c == 'Y') || (c == 'y') )

    result = true;

  else if ( (c != 'N') && (c != 'n') )

    if ( ('1' <= c) && (c <= '9') ) {

      int i = c - '0';

      while ( ('0' <= (c = getchar())) && (c <= '9') )

        i = i*10 + c - '0';

      *n = i; result = true;

    }  

  get_rest_of_line(c);

  return result;

} // getYorOrder

 

int getSize()

//  -------

{

  int n = 0;  int unused = scanf_s("%d", &n);

  int c = getchar(); get_rest_of_line(c);

  return n;

}

 

bool getFileName(char *buf, int size)

//   -----------

{

  int c, i = 0; char *s = buf;

 

  do { c = getchar(); } while ((c == ' ') || (c == '\t') || (c == '\n') );

  *s = c;

  while (i++ < size) if ( (*++s = getchar()) == '\n') break;

  if (*s != '\n') {

    printf("\nFile name too long.\n");

    get_rest_of_line(*s);

    return false;

  }

  while ( (*--s == ' ') || (*s == '\t') ); // strip any trailing whitespace

  *++s = '\0';

 

  return true;

} // getFileName

//--------------------------------------------------------------------------

 

const int bufSize = 1024;

FILE *openInput(char *buf, int size) {

//    ---------

  char *rFname = NULL;

 

  do {

    printf("\nEnter the name of the squares file: ");

    if (getFileName(buf, size - 4)) {  // reserve 4 to add .txt if needed

      rFname = buf; break;

    } else {

       printf("\a\nCan't read the file name. Try again? y (yes) or n (no) ");

         if (!getY()) break;

    }

  } while (true);

 

  FILE *rfp = NULL;

  if (rFname != NULL) {

    char *s = buf; bool txt = false; while (*s++ != '\0');

    while (--s != buf)

      if (*s == '.') {

        txt = (*++s == 't') && (*++s == 'x') && (*++s == 't') && (*++s == '\0');

        break;

      }

    if (!txt) strcat_s(buf, size, ".txt"); // no .txt, add it    

    if (fopen_s(&rfp, rFname, "r") != 0) {

      const int msgSize = bufSize + 50; char msg[msgSize];

      sprintf_s(msg, msgSize, "\a\nCan't open for read %s", buf);

      perror(msg);

    }

  }

  return rfp;

} // openInput

//--------------------------------------------------------------------------

 

const int outSize = bufSize + 50;

void stripName(char *inFname, char *obuf)

//   ---------

{

  char *s = inFname;

  while (*s++ != '\0');

  while (--s != inFname)

    if (*s == '.') *s = '\0';            // Remove .txt

    else if (*s == '\\') { ++s; break; } // Remove any directory path

  strcpy_s(obuf, outSize, s);

} // stripName

//--------------------------------------------------------------------------

 

bool openDir(char *dir) {

//   -------

  char baseName[bufSize];

  sprintf_s(baseName, bufSize, "Water%d_3D", N);

  strcpy_s(dir, bufSize, baseName);

  int fd = -1, sub = 0;

 

  do {

    if ( (fd = _mkdir(dir)) != -1) break;

    if (errno != EEXIST) {

      sprintf_s(baseName, bufSize, "\a\nCan't make folder %s", dir);

      perror(baseName); return false;

    }

    sprintf_s(dir, bufSize, "%s_%d", baseName, ++sub);

  } while (true);

  printf("Output file(s) are in folder %s\n", dir);

  return true;

} // openDir

//--------------------------------------------------------------------------

 

FILE *openOutput(char *dir, char* inFname)

//    ----------

{

  const int baseSize = bufSize + 25;

  char buf[outSize], baseName[baseSize];

  sprintf_s(baseName, baseSize, "%s\\%sSquare%d",

            dir, inFname, inputSquareNumber);

  sprintf_s(buf, outSize, "%s.svg", baseName);

  FILE *wfp = NULL; int sub = 0;

 

  do {

    if (_access_s(buf, 00) == ENOENT) {

      break;

    } else {

      sprintf_s(buf, outSize, "%s%d.svg", baseName, ++sub);

    }

  } while (true);

 

  if (fopen_s(&wfp, buf, "w") != 0) {

    sprintf_s(baseName, outSize, "\a\nCan't open for write %s", buf);

    perror(baseName);

  }

  return wfp;

} // openOutput

//--------------------------------------------------------------------------

 

bool checkSize()

//   ---------

{

  if (N <= 0) {

    reportError("Order must be a positive integer"); return false;

  } else if (N > maxN) {

    char msg[100];

    sprintf_s(msg, 100, "Order limit is set at %d", maxN);

    reportError(msg); return false;

  }

  return true;

} // checkSize

//--------------------------------------------------------------------------

 

void reportElapsedTime(time_t startTime)

//   -----------------

{

  int et = (int)(time(NULL) - startTime);

  if (et > 0)

    printf("\nelapsed time %d:%02d:%02d\n", et/3600, et%3600/60, et%60);

} // reportElapsedTime

//--------------------------------------------------------------------------

 

bool doAnother(bool *inputSize, bool writeError)

//   ---------

{

  if (writeError) return false;

  printf

    ("\nContinue? input y (yes) or n (no) or the order of the squares: ");

  if (getYorOrder(&N)) {

    *inputSize = (N == 0); return true;

  } else {

    freeStore(); return false;

  }

} // doAnother

//--------------------------------------------------------------------------

 

double dim3D()

//     -----

{

  return N <  20 ? 300.0 * log((double)N)

       : N <  32 ? 42.0*N : N < 100 ? 45.0*N : 50.0*N;

} // dim3D

//-------------------------------------------------------------------------

 

int getMinMax(int *minDepth, int *maxDepth)

//  ---------

{

  int **x = xSquare, **b = bSquare;;

  int min = LONG_MAX, max = 0, maxStep = 0;

  for (int j = 0; j < N; ++j) for (int i = 0; i < N; ++i) {

    int d = b[j][i] - x[j][i];

    if (min > d) min = d; if (max < d) max = d;

  }

  *minDepth = min; *maxDepth = max;

 

  for (int j = 0; j < N; ++j) for (int i = 0; i < N_1; ++i) {

    int d = x[j][i+1] - x[j][i];

    if (maxStep < d) maxStep = d;

    d = x[i+1][j] - x[j][i];

    if (maxStep < d) maxStep = d;

  }

  return maxStep;

} // getMinMax

//-------------------------------------------------------------------------

 

bool writeHead(FILE *sfp, double dim)

//   ------------

{

  return

    fprintf(sfp,

      "<?xml version=\"1.0\" encoding=\"UTF-8\" "

      "standalone=\"no\"?>\n<svg\n"

      "xmlns:svg=\"http://www.w3.org/2000/svg\"\n"

      "xmlns=\"http://www.w3.org/2000/svg\"\n"

      "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"

      "version=\"1.0\"\nwidth=\"%g\"\nheight=\"%g\"\n"

      "id=\"magic_s\"\n>\n", dim, dim) > 0;

} // writeHead

//-------------------------------------------------------------------------

 

const char black[] = "000000", white[] = "ffffff", blue[] = "000fff";

void getColors

//   ---------

       (int j, int i, int minDepth, int maxDepth,

       int *r, int *g, int *b, const char **textColor)

{

  if (bSquare[j][i] == xSquare[j][i]) {

    *textColor = black;

    if (isSpillWay[j][i]) {  // yellow

      *r = 224; *g = 224; *b = 50;

    } else { // green

      int xji = xSquare[j][i]; if (xji > NN) xji = NN;

      *r = 222; *g = 128 + xji*127/NN; *b = 0;

    }

  } else { // blue

    int d = bSquare[j][i] - xSquare[j][i];

    *r = 0; *g = 0;

    *b = 200 - (d - minDepth)*128/(maxDepth - minDepth);

    *textColor = black;

  }

} // getColors

//-------------------------------------------------------------------------

 

/*

 *  Adapted from code written in 2010 by Claudio Rocchini supplied

 *  by Craig Knecht.

 *

 *  Changes:

 *    . code the water retention calculations using an algorithm

 *      of Gareth McCaughan, (handles repeated square numbers)

 *    . normalize the cell numbers wrt the smallest number

 *    . adjust the green color calculation for large relative numbers

 */

bool drawRetained(FILE *sfp, bool *writeError)

//   ------------

{

  const bool view3D = true;

  int **x = xSquare;

  int base1Factor = smallestNumber - 1;

 

  if (!getWater(base1Factor)) { *writeError = false; return false; }

  int minDepth = NN, maxDepth = 0,

    maxStep = getMinMax(&minDepth, &maxDepth);

 

  double dim = dim3D();                         // drawing size

  if (!writeHead(sfp, dim)) return false;

 

  const double B = 64;                                // white border

  const double S = (dim - B*2)/N;               // tile size

  const double fontSize = S/3;

  const double DZ = view3D ? (S/3)/maxStep : 0; // deltaz

 

  *writeError = true;

  for (int j = 0; j < N; ++j) for (int i = 0; i < N; ++i) {  

      int red, green, blue; const char *textColor;

    getColors(j, i, minDepth, maxDepth, &red, &green, &blue, &textColor);

 

    double z = DZ * (x[j][i] - 1);

      if (fprintf(sfp,

              "<path d=\"M %5.1lf,%5.1lf L %5.1lf,%5.1lf "

          "L %5.1lf,%5.1lf L %5.1lf,%5.1lf Z\" "

              "style=\"fill:#%02X%02X%02X;stroke:#000000;"

          "stroke-width:1;stroke-opacity:1\" />\n",

              B+(i+0)*S-z, B+(j+0)*S-z,

              B+(i+1)*S-z, B+(j+0)*S-z,

              B+(i+1)*S-z, B+(j+1)*S-z,

              B+(i+0)*S-z, B+(j+1)*S-z,

              red, green, blue) < 0) return false;

 

    int xji =  x[j][i] + base1Factor;

      if (fprintf(sfp,

              "<text x=\"%5.1f\" y=\"%5.1f\" font-size=\"%gpx\" "

              "text-anchor=\"middle\" fill=\"#%s\">%d</text>\n",

            B+(i+0.5)*S-z, B+(j+0.5)*S-z+fontSize/2,

          fontSize, textColor, xji) < 0) return false;

 

    double zr = DZ * (i == N_1 ? 0 : x[j][i+1] - 1);

      if (view3D && zr < z)

      if (fprintf(sfp,

              "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f "

            "L %5.1f,%5.1f L %5.1f,%5.1f Z\" "

              "style=\"fill:#808080;stroke:#000000;"

            "stroke-width:1;stroke-opacity:1\" />\n",

              B+(i+1)*S-z,  B+(j+0)*S-z,

              B+(i+1)*S-zr, B+(j+0)*S-zr,

              B+(i+1)*S-zr, B+(j+1)*S-zr,

              B+(i+1)*S-z,  B+(j+1)*S-z) < 0) return false;

 

    double zb = DZ * (j == N_1 ? 0 : x[j+1][i]-1);

      if (view3D && zb < z)

        if (fprintf(sfp,

                  "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f "

            "L %5.1f,%5.1f L %5.1f,%5.1f Z\" "

                  "style=\"fill:#404040;stroke:#000000;"

            "stroke-width:1;stroke-opacity:1\" />\n",

                  B+(i+1)*S-z,  B+(j+1)*S-z,

                  B+(i+1)*S-zb, B+(j+1)*S-zb,

                  B+(i+0)*S-zb, B+(j+1)*S-zb,

                  B+(i+0)*S-z,  B+(j+1)*S-z) < 0) return false;

 

    if (view3D && (x[j][i] < bSquare[j][i])) {

      double z = DZ * (bSquare[j][i] - 1);

        if (fprintf(sfp,

                  "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f "

            "L %5.1f,%5.1f L %5.1f,%5.1f Z\" "

                  "style=\"fill:#0080FF;stroke:none;fill-opacity:0.4\" />\n",

                  B+(i+0)*S-z, B+(j+0)*S-z,

                  B+(i+1)*S-z, B+(j+0)*S-z,

                  B+(i+1)*S-z, B+(j+1)*S-z,

                  B+(i+0)*S-z, B+(j+1)*S-z) < 0) return false;

      }

  }

  *writeError = false;

  return fprintf(sfp, "</svg>\n") > 0;

} // drawRetained

//------------------------------------------------------------------------------

 

bool make3D(FILE *sfp, bool *writeError)

//   ------

{

  if (drawRetained(sfp, writeError)) {

    return true;

  } else {

    if (*writeError) reportError("problem writing svg file");

    return false;

  }

} // make3D

//------------------------------------------------------------------------------

 

bool make3Dsquare(char *dir, char *inFname, bool *writeError)

//   ------------

{

  FILE *wfp = openOutput(dir, inFname);

  if (wfp == NULL) return false;

 

  bool ok = make3D(wfp, writeError); fclose(wfp);

  return ok;

} // make3Dsquare

//--------------------------------------------------------------------------

 

using namespace System;

using namespace System::Threading;

int main() {

//  ----

  outputLocalTime();

  bool another = true, inputSize = true;

  do { // for each input file

    bool writeError = false;

    if (inputSize)

      { printf("\nInput the order of the squares: "); N = getSize(); }

    if (checkSize()) {

      initGlobals();

      if (allocateStore(N)) {

        char dir[bufSize];

        if (openDir(dir)) {

          const int inSize = bufSize + 4; // +4 to add .txt if needed

          char ibuf[inSize], obuf[outSize];

          FILE *rfp = openInput(ibuf, inSize);

          stripName(ibuf, obuf);

          if (rfp != NULL) {

            time_t startTime = time(NULL);

            inputSquareNumber = 0;

            while (inputSquare(rfp)) { // for each square in file

              if (!make3Dsquare(dir, obuf, &writeError)) break;

            }

            fclose(rfp);

            reportElapsedTime(startTime);

          } // (rfp != NULL

        } // if (openDir

      } // (allocateStore

    } // (checkSize

    another = doAnother(&inputSize, writeError);

  } while (another);

 

  printf("\n\nPress a key to close the console.");

  while (!Console::KeyAvailable) Thread::Sleep(250);

  return 0;

} // main