Free polyominoes enumeration: Difference between revisions

Content added Content deleted
m (→‎{{header|Go}}: Minor simplifications. Added comment about use of intermediate set .)
(Added c# version)
Line 24: Line 24:
# # # #
# # # #
#</pre>
#</pre>
Or perhaps with corner characters (rank 5):
<pre> ┌───┐ ┌─────┐ ┌─┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌─┐ ┌─────┐ ┌─┐ ┌─┐
│ │ │ ┌───┘ ┌─┘ │ │ ┌─┘ │ ┌─┘ ┌─┘ ┌─┘ │ ┌─┘ ┌─┘ ┌─┘ │ └─┐ └─┐ ┌─┘ │ │ ┌─┘ └─┐
│ ┌─┘ │ │ │ ┌─┘ │ │ │ └─┐ └─┐ │ ┌─┘ │ │ ┌─┘ │ ┌─┘ │ │ │ │ └─┐ ┌─┘
└─┘ └─┘ │ │ │ │ └───┘ └─┘ └───┘ └─┘ │ │ └─┘ │ │ └─┘
└─┘ └─┘ └─┘ │ │
└─┘</pre>


For a slow but clear solution see this Haskell Wiki page:
For a slow but clear solution see this Haskell Wiki page:
Line 39: Line 46:
[[Pentomino_tiling|Pentomino tiling]]
[[Pentomino_tiling|Pentomino tiling]]


=={{header|C sharp|C#}}==
{{trans|D}}
Turns out the source for the counting only version of the D code example could be tweaked to show solutions as well. The max rank can be changed by supplying a command line parameter. The free polyominos of any rank can be displayed by changing the variable named '''target''' to a reasonable number. This program will also indicate the estimated times for larger ranks.
<lang csharp>using System;
using System.Collections.Generic;
using System.Linq;

namespace cppfpe
{
class Program
{
static int n, ns; // rank, rank squared
static long[] AnyR; // Any Rotation count
static long[] nFlip; // Non-Flipped count
static long[] Frees; // Free Polyominoes count
static int[] fChk, fCkR; // field checks
static int fSiz, fWid; // field size, width
static int[] dirs; // directions
static int[] rotO, rotX, rotY; // rotations
static List<string> polys; // results
static int target; // rank to display
static int clipAt; // max columns for display

static int Main(string[] args)
{
polys = new List<string>();
n = 11; if (!(args.Length == 0)) int.TryParse(args[0], out n);
if (n < 1 || n > 24) return 1;
target = 5;
Console.WriteLine("Counting polyominoes to rank {0}...", n);
clipAt = 120;
DateTime start = DateTime.Now;
CountEm();
TimeSpan ti = DateTime.Now - start;
if (polys.Count > 0)
{
Console.WriteLine("Displaying rank {0}:", target);
Console.WriteLine(Assemble(polys));
}
Console.WriteLine("Displaying results:");
Console.WriteLine(" n All Rotations Non-Flipped Free Polys");
for (int i = 1; i <= n; i++)
Console.WriteLine("{0,2} :{1,17}{2,16}{3,16}", i, AnyR[i], nFlip[i], Frees[i]);
Console.WriteLine(string.Format("Elasped: {0,2}d {1,2}h {2,2}m {3:00}s {4:000}ms",
ti.Days, ti.Hours, ti.Minutes, ti.Seconds, ti.Milliseconds).Replace(" 0d ", "")
.Replace(" 0h", "").Replace(" 0m", "").Replace(" 00s", ""));
long ms = (long)ti.TotalMilliseconds, lim = int.MaxValue >> 2;
if (ms > 250)
{
Console.WriteLine("Estimated completion times:");
for (int i = n + 1; i <= n + 10; i++)
{
if (ms >= lim) break; ms += 44; ms <<= 2; ti = TimeSpan.FromMilliseconds(ms);
Console.WriteLine("{0,2} : {1,2}d {2,2}h {3,2}m {4:00}.{5:000}s", i,
ti.Days, ti.Hours, ti.Minutes, ti.Seconds, ti.Milliseconds);
}
}
if (System.Diagnostics.Debugger.IsAttached) Console.ReadKey();
return 0;
}

static void CountEm()
{
ns = n * n;
AnyR = new long[n + 1];
nFlip = new long[n + 1];
Frees = new long[n + 1];
fWid = n * 2 - 2;
fSiz = (n - 1) * (n - 1) * 2 + 1;
int[] pnField = new int[fSiz];
int[] pnPutList = new int[fSiz];
fChk = new int[ns];
fCkR = new int[ns];
dirs = new int[] { 1, fWid, -1, -fWid };
rotO = new int[] { 0, n - 1, ns - 1, ns - n, n - 1, 0, ns - n, ns - 1 };
rotX = new int[] { 1, n, -1, -n, -1, n, 1, -n };
rotY = new int[] { n, -1, -n, 1, n, 1, -n, -1 };
Recurse(0, pnField, pnPutList, 0, 1);
}

static void Recurse(int lv, int[] field, int[] putlist, int putno, int putlast)
{
CheckIt(field, lv);
if (n == lv) return;
int pos;
for (int i = putno; i < putlast; i++)
{
field[pos = putlist[i]] |= 1;
int k = 0;
foreach (int dir in dirs)
{
int pos2 = pos + dir;
if (0 <= pos2 && pos2 < fSiz && (field[pos2] == 0))
{
field[pos2] = 2;
putlist[putlast + k++] = pos2;
}
}
Recurse(lv + 1, field, putlist, i + 1, putlast + k);
for (int j = 0; j < k; j++) field[putlist[putlast + j]] = 0;
field[pos] = 2;
}
for (int i = putno; i < putlast; i++) field[putlist[i]] &= -2;
}

static void CheckIt(int[] field, int lv)
{
AnyR[lv]++;
for (int i = 0; i < ns; i++) fChk[i] = 0;
int x, y;
for (x = n; x < fWid; x++)
for (y = 0; y + x < fSiz; y += fWid)
if ((field[x + y] & 1) == 1) goto bail;
bail:
int x2 = n - x, t;
for (int i = 0; i < fSiz; i++)
if ((field[i] & 1) == 1) fChk[((t = (i + n - 2)) % fWid) + x2 + (t / fWid * n)] = 1;
int of1; for (of1 = 0; of1 < fChk.Length && (fChk[of1] == 0); of1++) ;
bool c = true; int r;
for (r = 1; r < 8 && c; r++)
{
for (x = 0; x < n; x++) for (y = 0; y < n; y++)
fCkR[rotO[r] + rotX[r] * x + rotY[r] * y] = fChk[x + y * n];
int of2; for (of2 = 0; of2 < fCkR.Length && (fCkR[of2] == 0); of2++) ;
of2 -= of1;
for (int i = of1; i < ns - ((of2 > 0) ? of2 : 0); i++)
{
if (fChk[i] > fCkR[i + of2]) break;
if (fChk[i] < fCkR[i + of2]) { c = false; break; }
}
}
if (r > 4) nFlip[lv]++;
if (c)
{
if (lv == target) polys.Add(toStr(field.ToArray()));
Frees[lv]++;
}
}

static string toStr(int[] field) // converts field into a minimal string
{
char [] res = new string(' ', n * (fWid + 1) - 1).ToCharArray();
for (int i = fWid; i < res.Length; i += fWid+1) res[i] = '\n';
for (int i = 0, j = n - 2; i < field.Length; i++, j++)
{
if ((field[i] & 1) == 1) res[j] = '#';
if (j % (fWid + 1) == fWid) i--;
}
List<string> t = new string(res).Split('\n').ToList();
int nn = 100, m = 0, v, k = 0; // trim down
foreach (string s in t)
{
if ((v = s.IndexOf('#')) < nn) if (v >= 0) nn = v;
if ((v = s.LastIndexOf('#')) > m) if (v < fWid +1) m = v;
if (v < 0) break; k++;
}
m = m - nn + 1; // convert difference to length
for (int i = t.Count - 1; i >= 0; i--)
{
if (i >= k) t.RemoveAt(i);
else t[i] = t[i].Substring(nn, m);
}
return String.Join("\n", t.ToArray());
}

// assembles string representation of polyominoes into larger horizontal band
static string Assemble(List<string> p)
{
List<string> lines = new List<string>();
for (int i = 0; i < target; i++) lines.Add(string.Empty);
foreach (string poly in p)
{
List<string> t = poly.Split('\n').ToList();
if (t.Count < t[0].Length) t = flipXY(t);
for (int i = 0; i < lines.Count; i++)
lines[i] += (i < t.Count) ? ' ' + t[i] + ' ': new string(' ', t[0].Length + 2);
}
for (int i = lines.Count - 1; i > 0; i--)
if (lines[i].IndexOf('#') < 0) lines.RemoveAt(i);
if (lines[0].Length >= clipAt / 2-2) Wrap(lines, clipAt / 2-2);
lines = Cornered(string.Join("\n", lines.ToArray())).Split('\n').ToList();
return String.Join("\n", lines.ToArray());
}

static List<string> flipXY(List<string> p) // flips a small string
{
List<string> res = new List<string>();
for (int i = 0; i < p[0].Length; i++) res.Add(string.Empty);
for (int i = 0; i < res.Count; i++)
for(int j = 0; j < p.Count; j++) res[i] += p[j][i];
return res;
}

static string DW(string s) // double widths a string
{
string t = string.Empty;
foreach (char c in s) t += string.Format("{0}{0}",c);
return t;
}

static void Wrap(List<string> s, int w) // wraps a wide List<string>
{
int last = 0;
while (s.Last().Length >= w)
{
int x = w, lim = s.Count; bool ok;
do
{
ok = true;
for (int i = last; i < lim; i++)
if (s[i][x] != ' ')
{ ok = false; x--; break; }
} while (!ok);
for (int i = last; i < lim; i++)
if (s[i].Length > x) { s.Add(s[i].Substring(x)); s[i] = s[i].Substring(0, x + 1); }
last = lim;
}
last = 0;
for (int i = s.Count - 1; i > 0; i--)
if ((last = (s[i].IndexOf('#') < 0) ? last + 1 : 0) > 1) s.RemoveAt(i + 1);
}

static string Cornered(string s) // converts plain ascii art into cornered version
{
string[] lines = s.Split('\n');
string res = string.Empty;
string line = DW(new string(' ', lines[0].Length)), last;
for (int i = 0; i < lines.Length; i++)
{
last = line; line = DW(lines[i]);
res += Puzzle(last, line) + '\n';
}
res += Puzzle(line, DW(new string(' ', lines.Last().Length))) + '\n';
return res;
}

static string Puzzle(string a, string b) // tests each intersection to determine correct corner symbol
{
string res = string.Empty;
if (a.Length > b.Length) b += new string(' ', a.Length - b.Length);
if (a.Length < b.Length) a += new string(' ', b.Length - a.Length);
for (int i = 0; i < a.Length - 1; i++)
res += " 12└4┘─┴8│┌├┐┤┬┼"[(a[i] == a[i + 1] ? 0 : 1) +
(b[i + 1] == a[i + 1] ? 0 : 2) +
(a[i] == b[i] ? 0 : 4) +
(b[i] == b[i + 1] ? 0 : 8)];
return res;
}
}
}
</lang>
{{out}}
<pre>Counting polyominoes to rank 11...
Displaying rank 5:
┌───┐ ┌─────┐ ┌─┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌─┐ ┌─────┐ ┌─┐ ┌─┐
│ │ │ ┌───┘ ┌─┘ │ │ ┌─┘ │ ┌─┘ ┌─┘ ┌─┘ │ ┌─┘ ┌─┘ ┌─┘ │ └─┐ └─┐ ┌─┘ │ │ ┌─┘ └─┐
│ ┌─┘ │ │ │ ┌─┘ │ │ │ └─┐ └─┐ │ ┌─┘ │ │ ┌─┘ │ ┌─┘ │ │ │ │ └─┐ ┌─┘
└─┘ └─┘ │ │ │ │ └───┘ └─┘ └───┘ └─┘ │ │ └─┘ │ │ └─┘
└─┘ └─┘ └─┘ │ │
└─┘

Displaying results:
n All Rotations Non-Flipped Free Polys
1 : 1 1 1
2 : 2 1 1
3 : 6 2 2
4 : 19 7 5
5 : 63 18 12
6 : 216 60 35
7 : 760 196 108
8 : 2725 704 369
9 : 9910 2500 1285
10 : 36446 9189 4655
11 : 135268 33896 17073
Elasped: 562ms
Estimated completion times:
12 : 0d 0h 0m 02.424s
13 : 0d 0h 0m 09.872s
14 : 0d 0h 0m 39.664s
15 : 0d 0h 2m 38.832s
16 : 0d 0h 10m 35.504s
17 : 0d 0h 42m 22.192s
18 : 0d 2h 49m 28.944s
19 : 0d 11h 17m 55.952s
20 : 1d 21h 11m 43.984s
21 : 7d 12h 46m 56.112s</pre>


=={{header|D}}==
=={{header|D}}==