Task: Free polyominoes enumeration





Java: import java.awt.Point;
import java.util.*;
import static java.util.Arrays.asList;
import java.util.function.Function;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;

public class FreePolyominoesEnum {
    static final List<Function<Point, Point>> transforms = new ArrayList<>();

    static {
        transforms.add(p -> new Point(p.y, -p.x));
        transforms.add(p -> new Point(-p.x, -p.y));
        transforms.add(p -> new Point(-p.y, p.x));
        transforms.add(p -> new Point(-p.x, p.y));
        transforms.add(p -> new Point(-p.y, -p.x));
        transforms.add(p -> new Point(p.x, -p.y));
        transforms.add(p -> new Point(p.y, p.x));
    }

    static Point findMinima(List<Point> poly) {
        return new Point(
                poly.stream().mapToInt(a -> a.x).min().getAsInt(),
                poly.stream().mapToInt(a -> a.y).min().getAsInt());
    }

    static List<Point> translateToOrigin(List<Point> poly) {
        final Point min = findMinima(poly);
        poly.replaceAll(p -> new Point(p.x - min.x, p.y - min.y));
        return poly;
    }

    static List<List<Point>> rotationsAndReflections(List<Point> poly) {
        List<List<Point>> lst = new ArrayList<>();
        lst.add(poly);
        for (Function<Point, Point> t : transforms)
            lst.add(poly.stream().map(t).collect(toList()));
        return lst;
    }

    static Comparator<Point> byCoords = Comparator.<Point>comparingInt(p -> p.x)
            .thenComparingInt(p -> p.y);

    static List<Point> normalize(List<Point> poly) {
        return rotationsAndReflections(poly).stream()
                .map(lst -> translateToOrigin(lst))
                .map(lst -> lst.stream().sorted(byCoords).collect(toList()))
                .min(comparing(Object::toString)) 
                .get();
    }

    static List<Point> neighborhoods(Point p) {
        return asList(new Point(p.x - 1, p.y), new Point(p.x + 1, p.y),
                new Point(p.x, p.y - 1), new Point(p.x, p.y + 1));
    }

    static List<Point> concat(List<Point> lst, Point pt) {
        List<Point> r = new ArrayList<>();
        r.addAll(lst);
        r.add(pt);
        return r;
    }

    static List<Point> newPoints(List<Point> poly) {
        return poly.stream()
                .flatMap(p -> neighborhoods(p).stream())
                .filter(p -> !poly.contains(p))
                .distinct()
                .collect(toList());
    }

    static List<List<Point>> constructNextRank(List<Point> poly) {
        return newPoints(poly).stream()
                .map(p -> normalize(concat(poly, p)))
                .distinct()
                .collect(toList());
    }

    static List<List<Point>> rank(int n) {
        if (n < 0)
            throw new IllegalArgumentException("n cannot be negative");

        if (n < 2) {
            List<List<Point>> r = new ArrayList<>();
            if (n == 1)
                r.add(asList(new Point(0, 0)));
            return r;
        }

        return rank(n - 1).stream()
                .parallel()
                .flatMap(lst -> constructNextRank(lst).stream())
                .distinct()
                .collect(toList());
    }

    public static void main(String[] args) {
        for (List<Point> poly : rank(5)) {
            for (Point p : poly)
                System.out.printf("(%d,%d) ", p.x, p.y);
            System.out.println();
        }
    }
}



Python: from itertools import imap, imap, groupby, chain, imap
from operator import itemgetter
from sys import argv
from array import array

def concat_map(func, it):
    return list(chain.from_iterable(imap(func, it)))

def minima(poly):
    
    return (min(pt[0] for pt in poly), min(pt[1] for pt in poly))

def translate_to_origin(poly):
    (minx, miny) = minima(poly)
    return [(x - minx, y - miny) for (x, y) in poly]

rotate90   = lambda (x, y): ( y, -x)
rotate180  = lambda (x, y): (-x, -y)
rotate270  = lambda (x, y): (-y,  x)
reflect    = lambda (x, y): (-x,  y)

def rotations_and_reflections(poly):
    
    return (poly,
            map(rotate90, poly),
            map(rotate180, poly),
            map(rotate270, poly),
            map(reflect, poly),
            [reflect(rotate90(pt)) for pt in poly],
            [reflect(rotate180(pt)) for pt in poly],
            [reflect(rotate270(pt)) for pt in poly])

def canonical(poly):
    return min(sorted(translate_to_origin(pl)) for pl in rotations_and_reflections(poly))

def unique(lst):
    lst.sort()
    return map(next, imap(itemgetter(1), groupby(lst)))


contiguous = lambda (x, y): [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]

def new_points(poly):
    
    return unique([pt for pt in concat_map(contiguous, poly) if pt not in poly])

def new_polys(poly):
    return unique([canonical(poly + [pt]) for pt in new_points(poly)])

monomino = [(0, 0)]
monominoes = [monomino]

def rank(n):
    
    assert n >= 0
    if n == 0: return []
    if n == 1: return monominoes
    return unique(concat_map(new_polys, rank(n - 1)))

def text_representation(poly):
    
    min_pt = minima(poly)
    max_pt = (max(p[0] for p in poly), max(p[1] for p in poly))
    table = [array('c', ' ') * (max_pt[1] - min_pt[1] + 1)
             for _ in xrange(max_pt[0] - min_pt[0] + 1)]
    for pt in poly:
        table[pt[0] - min_pt[0]][pt[1] - min_pt[1]] = '
    return "\n".join(row.tostring() for row in table)

def main():
    print [len(rank(n)) for n in xrange(1, 11)]

    n = int(argv[1]) if (len(argv) == 2) else 5
    print "\nAll free polyominoes of rank %d:" % n

    for poly in rank(n):
        print text_representation(poly), "\n"

main()