{
  "id": "P078",
  "slug": "matrix-patterns",
  "key": "P078-matrix-patterns",
  "title": "Matrix patterns",
  "summary": "A grid filled in a clockwise spiral and read back along it, an odd magic square by the Siamese method with every row, column and diagonal sum checked, and the last grid turned and flipped - a magic square stays magic all eight ways.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P078 - Matrix patterns\n\nA grid filled in a clockwise spiral and read back along it. An odd magic\nsquare made by the Siamese method, with every row, column and diagonal\nadded up. And the last grid turned and flipped: a magic square stays\nmagic all eight ways. It starts with the 3 x 3 magic square.\n\n- `main.eml` - the menu, reading sizes, showing grids and sums\n- `grids.eml` - blank grids, the spiral both ways, the magic square, the\n  sums, the check, and the turns and flips\n\nHow each part works:\n\n- A grid is a list of rows, each made as its own list: repeating one row\n  list would make every row the same list, so writing one square would\n  change a whole column.\n- Spiral fill: 1, 2, 3, ... along the top edge, down the right, back\n  along the bottom, up the left, then the four edges move in by one.\n- Spiral reading, as in the corpus case `spiral-matrix-traversal`, walks\n  the same path. Two guards stop a last single row or column being read\n  twice, which the 1 x 7 and 5 x 1 grids test.\n- Magic square, Siamese method, for an odd n: 1 goes in the middle of the\n  top row. Each next number goes one up and one right, wrapping round the\n  edges; if that square is taken, it goes one down instead. Every row,\n  column and both diagonals then add up to n(n^2 + 1)/2: 15 for 3, 65 for\n  5, 175 for 7.\n- Turns and flips: a quarter turn clockwise, a mirror left to right, upside\n  down, and a transpose (rows become columns). They move whole rows,\n  columns and diagonals, so a magic square stays magic. Its 8 views are\n  all different.\n\nWhat is checked: menu choices 1 to 5; 1 to 12 rows and columns for a\nspiral; an odd size from 3 to 15 for a magic square; turn, mirror, upside\ndown or transpose. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the 3 x 3 magic square, then spirals of 4 x 5, 1 x 7 and 5 x 1, each\n  read back in order;\n- the 5 x 5 magic square with its sums, then turned, mirrored, transposed\n  (back to the start) and turned upside down, magic each time;\n- the 7 x 7 magic square: every sum 175.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- rows 0, 13 and abc, then a cancelled column count;\n- sizes 4, 17 and 1 for a magic square, then a cancel;\n- the move \"rotate\", then a cancel;\n- a 3 x 3 spiral turned: not a magic square.\n\nBuilt on the verified corpus case `spiral-matrix-traversal` (four\nshrinking edges, with the guards that stop a last row or column being\nread twice).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P078 matrix patterns: a grid filled in a spiral and read back, an odd\n# magic square by the Siamese method with all its sums, and the last grid\n# turned and flipped - a magic square stays magic all eight ways.\nimport grids\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 2:\n        return -1\n    0 => n\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef lpad(s, n):\n    while len(s) < n:\n        \" \" + s => s\n    return s\n\ndef ask(prompt, low, high):\n    while True:\n        trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \")) => a\n        if a == \"\":\n            return -1\n        number(a) => n\n        if n >= low and n <= high:\n            return n\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n\ndef show(g):\n    # Every column as wide as the widest number in the grid, plus a space.\n    1 => w\n    for row in g:\n        for x in row:\n            if len(str(x)) > w:\n                len(str(x)) => w\n    for row in g:\n        \"\" => line\n        for x in row:\n            line + lpad(str(x), w + 1) => line\n        line ^0\n\ndef joined(xs):\n    \"\" => s\n    for x in xs:\n        if s != \"\":\n            s + \" \" => s\n        s + str(x) => s\n    return s\n\ndef spiral(g):\n    ask(\"rows\", 1, 12) => r\n    if r == -1:\n        \"Cancelled.\" ^0\n        return g\n    ask(\"columns\", 1, 12) => c\n    if c == -1:\n        \"Cancelled.\" ^0\n        return g\n    grids.spiral_fill(r, c) => g\n    show(g)\n    grids.spiral_order(g) => path\n    True => inorder\n    for k in [0:len(path) - 1]:\n        if path[k] != k + 1:\n            False => inorder\n    if inorder:\n        (\"Read back along the same spiral: 1 to \" + str(r * c) + \" in order, each once.\") ^0\n    else:\n        (\"Read back along the same spiral: \" + joined(path)) ^0\n    return g\n\ndef square(g):\n    while True:\n        trim(input(\"odd size (3 to 15)> \")) => a\n        if a == \"\":\n            \"Cancelled.\" ^0\n            return g\n        number(a) => n\n        if n >= 3 and n <= 15 and n % 2 == 1:\n            grids.magic(n) => g\n            show(g)\n            report(g)\n            return g\n        \"Type an odd whole number from 3 to 15.\" ^0\n\ndef report(g):\n    len(g) => n\n    grids.sums(g) => s\n    (\"rows    \" + joined(s[0])) ^0\n    (\"columns \" + joined(s[1])) ^0\n    (\"diagonals \" + str(s[2]) + \" and \" + str(s[3])) ^0\n    int(n * (n * n + 1) / 2) => target\n    if grids.is_magic(g):\n        (\"Every row, column and both diagonals add up to \" + str(target) + \" = n(n^2 + 1)/2, with 1 to \" + str(n * n) + \" each once: magic.\") ^0\n    else:\n        (\"Not magic: n(n^2 + 1)/2 = \" + str(target) + \" is not every sum, or a number is missing.\") ^0\n\ndef move(g):\n    while True:\n        trim(input(\"turn, mirror, upside down or transpose> \")) => how\n        if how == \"\":\n            \"Cancelled.\" ^0\n            return g\n        if how == \"turn\" or how == \"mirror\" or how == \"upside down\" or how == \"transpose\":\n            if how == \"turn\":\n                grids.turned(g) => g\n            elif how == \"mirror\":\n                grids.mirrored(g) => g\n            elif how == \"upside down\":\n                grids.upside_down(g) => g\n            else:\n                grids.transposed(g) => g\n            show(g)\n            if len(g) == len(g[0]) and len(g) >= 3 and len(g) % 2 == 1:\n                if grids.is_magic(g):\n                    \"Still magic: turning and flipping move whole rows, columns and diagonals.\" ^0\n                else:\n                    \"Not a magic square - and turning or flipping cannot make one.\" ^0\n            return g\n        \"Type turn, mirror, upside down or transpose.\" ^0\n\ngrids.magic(3) => grid\n\n\"== Matrix patterns ==\" ^0\n\"Spirals, magic squares, and grids turned and flipped.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        spiral(grid) => grid\n    elif choice == \"2\":\n        square(grid) => grid\n    elif choice == \"3\":\n        move(grid) => grid\n    elif choice == \"4\":\n        show(grid)\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import grids\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 2:\n        return -1\n    n = 0\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef lpad(s, n):\n    while len(s) < n:\n        s = \" \" + s\n    return s\n\ndef ask(prompt, low, high):\n    while True:\n        a = trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \"))\n        if a == \"\":\n            return -1\n        n = number(a)\n        if n >= low and n <= high:\n            return n\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n\ndef show(g):\n    w = 1\n    for row in g:\n        for x in row:\n            if len(str(x)) > w:\n                w = len(str(x))\n    for row in g:\n        line = \"\"\n        for x in row:\n            line = line + lpad(str(x), w + 1)\n        print(line)\n\ndef joined(xs):\n    s = \"\"\n    for x in xs:\n        if s != \"\":\n            s = s + \" \"\n        s = s + str(x)\n    return s\n\ndef spiral(g):\n    r = ask(\"rows\", 1, 12)\n    if r == -1:\n        print(\"Cancelled.\")\n        return g\n    c = ask(\"columns\", 1, 12)\n    if c == -1:\n        print(\"Cancelled.\")\n        return g\n    g = grids.spiral_fill(r, c)\n    show(g)\n    path = grids.spiral_order(g)\n    inorder = True\n    for k in range(0, len(path)):\n        if path[k] != k + 1:\n            inorder = False\n    if inorder:\n        print(\"Read back along the same spiral: 1 to \" + str(r * c) + \" in order, each once.\")\n    else:\n        print(\"Read back along the same spiral: \" + joined(path))\n    return g\n\ndef square(g):\n    while True:\n        a = trim(input(\"odd size (3 to 15)> \"))\n        if a == \"\":\n            print(\"Cancelled.\")\n            return g\n        n = number(a)\n        if n >= 3 and n <= 15 and n % 2 == 1:\n            g = grids.magic(n)\n            show(g)\n            report(g)\n            return g\n        print(\"Type an odd whole number from 3 to 15.\")\n\ndef report(g):\n    n = len(g)\n    s = grids.sums(g)\n    print(\"rows    \" + joined(s[0]))\n    print(\"columns \" + joined(s[1]))\n    print(\"diagonals \" + str(s[2]) + \" and \" + str(s[3]))\n    target = int(n * (n * n + 1) / 2)\n    if grids.is_magic(g):\n        print(\"Every row, column and both diagonals add up to \" + str(target) + \" = n(n^2 + 1)/2, with 1 to \" + str(n * n) + \" each once: magic.\")\n    else:\n        print(\"Not magic: n(n^2 + 1)/2 = \" + str(target) + \" is not every sum, or a number is missing.\")\n\ndef move(g):\n    while True:\n        how = trim(input(\"turn, mirror, upside down or transpose> \"))\n        if how == \"\":\n            print(\"Cancelled.\")\n            return g\n        if how == \"turn\" or how == \"mirror\" or how == \"upside down\" or how == \"transpose\":\n            if how == \"turn\":\n                g = grids.turned(g)\n            elif how == \"mirror\":\n                g = grids.mirrored(g)\n            elif how == \"upside down\":\n                g = grids.upside_down(g)\n            else:\n                g = grids.transposed(g)\n            show(g)\n            if len(g) == len(g[0]) and len(g) >= 3 and len(g) % 2 == 1:\n                if grids.is_magic(g):\n                    print(\"Still magic: turning and flipping move whole rows, columns and diagonals.\")\n                else:\n                    print(\"Not a magic square - and turning or flipping cannot make one.\")\n            return g\n        print(\"Type turn, mirror, upside down or transpose.\")\n\ngrid = grids.magic(3)\nprint(\"== Matrix patterns ==\")\nprint(\"Spirals, magic squares, and grids turned and flipped.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        grid = spiral(grid)\n    elif choice == \"2\":\n        grid = square(grid)\n    elif choice == \"3\":\n        grid = move(grid)\n    elif choice == \"4\":\n        show(grid)\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "grids.eml",
      "eml": "# P078 matrix patterns - a grid filled in a spiral and read back the same\n# way, an odd magic square by the Siamese method with every sum checked,\n# and the grid turned and flipped. A grid is a list of rows.\n\ndef blank(rows, cols):\n    # A rows x cols grid of zeros, each row its own list ([[0] * cols] *\n    # rows would make every row the same list).\n    [0] * rows => g\n    for r in [0:rows - 1]:\n        [0] * cols => g[r]\n    return g\n\ndef spiral_fill(rows, cols):\n    # 1, 2, 3, ... clockwise from the top left, turning inwards: along the\n    # top edge, down the right, back along the bottom, up the left, and the\n    # four edges move in by one.\n    blank(rows, cols) => g\n    1 => n\n    0 => top\n    rows - 1 => bottom\n    0 => left\n    cols - 1 => right\n    while top <= bottom and left <= right:\n        for c in [left:right]:\n            n => g[top][c]\n            n + 1 => n\n        top + 1 => top\n        for r in [top:bottom]:\n            n => g[r][right]\n            n + 1 => n\n        right - 1 => right\n        if top <= bottom:\n            for k in [0:right - left]:\n                n => g[bottom][right - k]\n                n + 1 => n\n            bottom - 1 => bottom\n        if left <= right:\n            for k in [0:bottom - top]:\n                n => g[bottom - k][left]\n                n + 1 => n\n            left + 1 => left\n    return g\n\ndef spiral_order(g):\n    # The corpus case spiral-matrix-traversal: read the same path. The two\n    # guards stop a last single row or column being read twice.\n    len(g) => rows\n    len(g[0]) => cols\n    [] => out\n    0 => top\n    rows - 1 => bottom\n    0 => left\n    cols - 1 => right\n    while top <= bottom and left <= right:\n        for c in [left:right]:\n            out + [g[top][c]] => out\n        top + 1 => top\n        for r in [top:bottom]:\n            out + [g[r][right]] => out\n        right - 1 => right\n        if top <= bottom:\n            for k in [0:right - left]:\n                out + [g[bottom][right - k]] => out\n            bottom - 1 => bottom\n        if left <= right:\n            for k in [0:bottom - top]:\n                out + [g[bottom - k][left]] => out\n            left + 1 => left\n    return out\n\ndef magic(n):\n    # The Siamese method for an odd n: 1 in the middle of the top row, then\n    # each next number one up and one right, wrapping round the edges; if\n    # that square is taken, one down instead.\n    blank(n, n) => g\n    0 => r\n    int((n - 1) / 2) => c\n    for k in [1:n * n]:\n        k => g[r][c]\n        (r - 1 + n) % n => up\n        (c + 1) % n => over\n        if g[up][over] != 0:\n            (r + 1) % n => r\n        else:\n            up => r\n            over => c\n    return g\n\ndef sums(g):\n    # [row sums, column sums, main diagonal, other diagonal].\n    len(g) => n\n    [0] * n => rs\n    [0] * n => cs\n    0 => d1\n    0 => d2\n    for r in [0:n - 1]:\n        for c in [0:n - 1]:\n            rs[r] + g[r][c] => rs[r]\n            cs[c] + g[r][c] => cs[c]\n        d1 + g[r][r] => d1\n        d2 + g[r][n - 1 - r] => d2\n    return [rs, cs, d1, d2]\n\ndef is_magic(g):\n    # Every row, column and both diagonals add up to n(n^2 + 1)/2, and the\n    # numbers are 1 to n^2 once each.\n    len(g) => n\n    int(n * (n * n + 1) / 2) => target\n    sums(g) => s\n    for x in s[0] + s[1] + [s[2], s[3]]:\n        if x != target:\n            return False\n    [0] * (n * n + 1) => seen\n    for row in g:\n        for x in row:\n            if x < 1 or x > n * n or seen[x] == 1:\n                return False\n            1 => seen[x]\n    return True\n\ndef turned(g):\n    # A quarter turn clockwise: row r becomes column (rows - 1 - r).\n    len(g) => rows\n    len(g[0]) => cols\n    blank(cols, rows) => out\n    for r in [0:rows - 1]:\n        for c in [0:cols - 1]:\n            g[r][c] => out[c][rows - 1 - r]\n    return out\n\ndef mirrored(g):\n    # Flipped left to right.\n    len(g) => rows\n    len(g[0]) => cols\n    blank(rows, cols) => out\n    for r in [0:rows - 1]:\n        for c in [0:cols - 1]:\n            g[r][c] => out[r][cols - 1 - c]\n    return out\n\ndef upside_down(g):\n    # Flipped top to bottom.\n    len(g) => rows\n    blank(rows, len(g[0])) => out\n    for r in [0:rows - 1]:\n        for c in [0:len(g[0]) - 1]:\n            g[r][c] => out[rows - 1 - r][c]\n    return out\n\ndef transposed(g):\n    # Rows become columns: flipped over the main diagonal.\n    blank(len(g[0]), len(g)) => out\n    for r in [0:len(g) - 1]:\n        for c in [0:len(g[0]) - 1]:\n            g[r][c] => out[c][r]\n    return out\n",
      "python": "def blank(rows, cols):\n    g = [0] * rows\n    for r in range(0, rows):\n        g[r] = [0] * cols\n    return g\n\ndef spiral_fill(rows, cols):\n    g = blank(rows, cols)\n    n = 1\n    top = 0\n    bottom = rows - 1\n    left = 0\n    right = cols - 1\n    while top <= bottom and left <= right:\n        for c in range(left, right+1):\n            g[top][c] = n\n            n = n + 1\n        top = top + 1\n        for r in range(top, bottom+1):\n            g[r][right] = n\n            n = n + 1\n        right = right - 1\n        if top <= bottom:\n            for k in range(0, right - left+1):\n                g[bottom][right - k] = n\n                n = n + 1\n            bottom = bottom - 1\n        if left <= right:\n            for k in range(0, bottom - top+1):\n                g[bottom - k][left] = n\n                n = n + 1\n            left = left + 1\n    return g\n\ndef spiral_order(g):\n    rows = len(g)\n    cols = len(g[0])\n    out = []\n    top = 0\n    bottom = rows - 1\n    left = 0\n    right = cols - 1\n    while top <= bottom and left <= right:\n        for c in range(left, right+1):\n            out = out + [g[top][c]]\n        top = top + 1\n        for r in range(top, bottom+1):\n            out = out + [g[r][right]]\n        right = right - 1\n        if top <= bottom:\n            for k in range(0, right - left+1):\n                out = out + [g[bottom][right - k]]\n            bottom = bottom - 1\n        if left <= right:\n            for k in range(0, bottom - top+1):\n                out = out + [g[bottom - k][left]]\n            left = left + 1\n    return out\n\ndef magic(n):\n    g = blank(n, n)\n    r = 0\n    c = int((n - 1) / 2)\n    for k in range(1, n * n+1):\n        g[r][c] = k\n        up = (r - 1 + n) % n\n        over = (c + 1) % n\n        if g[up][over] != 0:\n            r = (r + 1) % n\n        else:\n            r = up\n            c = over\n    return g\n\ndef sums(g):\n    n = len(g)\n    rs = [0] * n\n    cs = [0] * n\n    d1 = 0\n    d2 = 0\n    for r in range(0, n):\n        for c in range(0, n):\n            rs[r] = rs[r] + g[r][c]\n            cs[c] = cs[c] + g[r][c]\n        d1 = d1 + g[r][r]\n        d2 = d2 + g[r][n - 1 - r]\n    return [rs, cs, d1, d2]\n\ndef is_magic(g):\n    n = len(g)\n    target = int(n * (n * n + 1) / 2)\n    s = sums(g)\n    for x in s[0] + s[1] + [s[2], s[3]]:\n        if x != target:\n            return False\n    seen = [0] * (n * n + 1)\n    for row in g:\n        for x in row:\n            if x < 1 or x > n * n or seen[x] == 1:\n                return False\n            seen[x] = 1\n    return True\n\ndef turned(g):\n    rows = len(g)\n    cols = len(g[0])\n    out = blank(cols, rows)\n    for r in range(0, rows):\n        for c in range(0, cols):\n            out[c][rows - 1 - r] = g[r][c]\n    return out\n\ndef mirrored(g):\n    rows = len(g)\n    cols = len(g[0])\n    out = blank(rows, cols)\n    for r in range(0, rows):\n        for c in range(0, cols):\n            out[r][cols - 1 - c] = g[r][c]\n    return out\n\ndef upside_down(g):\n    rows = len(g)\n    out = blank(rows, len(g[0]))\n    for r in range(0, rows):\n        for c in range(0, len(g[0])):\n            out[rows - 1 - r][c] = g[r][c]\n    return out\n\ndef transposed(g):\n    out = blank(len(g[0]), len(g))\n    for r in range(0, len(g)):\n        for c in range(0, len(g[0])):\n            out[c][r] = g[r][c]\n    return out\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n0\n13\nabc\n3\n\n2\n4\n17\n1\n\n3\nrotate\n\n1\n3\n3\n3\nturn\n5\n",
      "screen": "== Matrix patterns ==\nSpirals, magic squares, and grids turned and flipped.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 1\nrows (1 to 12)> 0\nType a whole number from 1 to 12.\nrows (1 to 12)> 13\nType a whole number from 1 to 12.\nrows (1 to 12)> abc\nType a whole number from 1 to 12.\nrows (1 to 12)> 3\ncolumns (1 to 12)> \nCancelled.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 2\nodd size (3 to 15)> 4\nType an odd whole number from 3 to 15.\nodd size (3 to 15)> 17\nType an odd whole number from 3 to 15.\nodd size (3 to 15)> 1\nType an odd whole number from 3 to 15.\nodd size (3 to 15)> \nCancelled.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> rotate\nType turn, mirror, upside down or transpose.\nturn, mirror, upside down or transpose> \nCancelled.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 1\nrows (1 to 12)> 3\ncolumns (1 to 12)> 3\n 1 2 3\n 8 9 4\n 7 6 5\nRead back along the same spiral: 1 to 9 in order, each once.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> turn\n 7 8 1\n 6 9 2\n 5 4 3\nNot a magic square - and turning or flipping cannot make one.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "4\n1\n4\n5\n1\n1\n7\n1\n5\n1\n2\n5\n3\nturn\n3\nmirror\n3\ntranspose\n3\nupside down\n2\n7\n5\n",
      "screen": "== Matrix patterns ==\nSpirals, magic squares, and grids turned and flipped.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 4\n 8 1 6\n 3 5 7\n 4 9 2\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 1\nrows (1 to 12)> 4\ncolumns (1 to 12)> 5\n  1  2  3  4  5\n 14 15 16 17  6\n 13 20 19 18  7\n 12 11 10  9  8\nRead back along the same spiral: 1 to 20 in order, each once.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 1\nrows (1 to 12)> 1\ncolumns (1 to 12)> 7\n 1 2 3 4 5 6 7\nRead back along the same spiral: 1 to 7 in order, each once.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 1\nrows (1 to 12)> 5\ncolumns (1 to 12)> 1\n 1\n 2\n 3\n 4\n 5\nRead back along the same spiral: 1 to 5 in order, each once.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 2\nodd size (3 to 15)> 5\n 17 24  1  8 15\n 23  5  7 14 16\n  4  6 13 20 22\n 10 12 19 21  3\n 11 18 25  2  9\nrows    65 65 65 65 65\ncolumns 65 65 65 65 65\ndiagonals 65 and 65\nEvery row, column and both diagonals add up to 65 = n(n^2 + 1)/2, with 1 to 25 each once: magic.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> turn\n 11 10  4 23 17\n 18 12  6  5 24\n 25 19 13  7  1\n  2 21 20 14  8\n  9  3 22 16 15\nStill magic: turning and flipping move whole rows, columns and diagonals.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> mirror\n 17 23  4 10 11\n 24  5  6 12 18\n  1  7 13 19 25\n  8 14 20 21  2\n 15 16 22  3  9\nStill magic: turning and flipping move whole rows, columns and diagonals.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> transpose\n 17 24  1  8 15\n 23  5  7 14 16\n  4  6 13 20 22\n 10 12 19 21  3\n 11 18 25  2  9\nStill magic: turning and flipping move whole rows, columns and diagonals.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 3\nturn, mirror, upside down or transpose> upside down\n 11 18 25  2  9\n 10 12 19 21  3\n  4  6 13 20 22\n 23  5  7 14 16\n 17 24  1  8 15\nStill magic: turning and flipping move whole rows, columns and diagonals.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 2\nodd size (3 to 15)> 7\n 30 39 48  1 10 19 28\n 38 47  7  9 18 27 29\n 46  6  8 17 26 35 37\n  5 14 16 25 34 36 45\n 13 15 24 33 42 44  4\n 21 23 32 41 43  3 12\n 22 31 40 49  2 11 20\nrows    175 175 175 175 175 175 175\ncolumns 175 175 175 175 175 175 175\ndiagonals 175 and 175\nEvery row, column and both diagonals add up to 175 = n(n^2 + 1)/2, with 1 to 49 each once: magic.\n\n1) spiral  2) magic square  3) turn or flip the last grid  4) show it  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "spiral-matrix-traversal",
      "caseId": "104-spiral-matrix-traversal",
      "title": "Spiral matrix traversal"
    }
  ],
  "updated": "2026-10-14"
}
