Skip to content

Instantly share code, notes, and snippets.

@elbosso
Created January 1, 2021 20:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elbosso/2b1e525d116ba0f78356d08608fa9eab to your computer and use it in GitHub Desktop.
Save elbosso/2b1e525d116ba0f78356d08608fa9eab to your computer and use it in GitHub Desktop.
Generator for tilings using the 13 example tiles from the wikipedia article https://de.wikipedia.org/wiki/Wang-Parkettierung
/*
* Copyright (c) 2021.
*
* Juergen Key. Alle Rechte vorbehalten.
*
* Weiterverbreitung und Verwendung in nichtkompilierter oder kompilierter Form,
* mit oder ohne Veraenderung, sind unter den folgenden Bedingungen zulaessig:
*
* 1. Weiterverbreitete nichtkompilierte Exemplare muessen das obige Copyright,
* die Liste der Bedingungen und den folgenden Haftungsausschluss im Quelltext
* enthalten.
* 2. Weiterverbreitete kompilierte Exemplare muessen das obige Copyright,
* die Liste der Bedingungen und den folgenden Haftungsausschluss in der
* Dokumentation und/oder anderen Materialien, die mit dem Exemplar verbreitet
* werden, enthalten.
* 3. Weder der Name des Autors noch die Namen der Beitragsleistenden
* duerfen zum Kennzeichnen oder Bewerben von Produkten, die von dieser Software
* abgeleitet wurden, ohne spezielle vorherige schriftliche Genehmigung verwendet
* werden.
*
* DIESE SOFTWARE WIRD VOM AUTOR UND DEN BEITRAGSLEISTENDEN OHNE
* JEGLICHE SPEZIELLE ODER IMPLIZIERTE GARANTIEN ZUR VERFUEGUNG GESTELLT, DIE
* UNTER ANDEREM EINSCHLIESSEN: DIE IMPLIZIERTE GARANTIE DER VERWENDBARKEIT DER
* SOFTWARE FUER EINEN BESTIMMTEN ZWECK. AUF KEINEN FALL IST DER AUTOR
* ODER DIE BEITRAGSLEISTENDEN FUER IRGENDWELCHE DIREKTEN, INDIREKTEN,
* ZUFAELLIGEN, SPEZIELLEN, BEISPIELHAFTEN ODER FOLGENDEN SCHAEDEN (UNTER ANDEREM
* VERSCHAFFEN VON ERSATZGUETERN ODER -DIENSTLEISTUNGEN; EINSCHRAENKUNG DER
* NUTZUNGSFAEHIGKEIT; VERLUST VON NUTZUNGSFAEHIGKEIT; DATEN; PROFIT ODER
* GESCHAEFTSUNTERBRECHUNG), WIE AUCH IMMER VERURSACHT UND UNTER WELCHER
* VERPFLICHTUNG AUCH IMMER, OB IN VERTRAG, STRIKTER VERPFLICHTUNG ODER
* UNERLAUBTE HANDLUNG (INKLUSIVE FAHRLAESSIGKEIT) VERANTWORTLICH, AUF WELCHEM
* WEG SIE AUCH IMMER DURCH DIE BENUTZUNG DIESER SOFTWARE ENTSTANDEN SIND, SOGAR,
* WENN SIE AUF DIE MOEGLICHKEIT EINES SOLCHEN SCHADENS HINGEWIESEN WORDEN SIND.
*
*/
//https://de.wikipedia.org/wiki/Wang-Parkettierung
//This is brute-forcing it - therefore sometimes the program does not find a solution and
//I added safety measures against endless loops because that would increase the universes
//entropy hastening its heat death and nobody would want that.
//Unchanged, it tries to create 10 tilings.
package de.elbosso.scratch.algorithms.graphics;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
public enum WangDomino
{
A(Color.GREEN, Color.GREEN, Color.BLUE, Color.RED),
B(java.awt.Color.GREEN,java.awt.Color.BLUE,java.awt.Color.GREEN,java.awt.Color.RED),
C(java.awt.Color.GREEN,java.awt.Color.BLUE,java.awt.Color.BLUE,java.awt.Color.GREEN),
D(java.awt.Color.RED,java.awt.Color.RED,java.awt.Color.GREEN,java.awt.Color.GREEN),
E(java.awt.Color.RED,java.awt.Color.RED,java.awt.Color.BLUE,java.awt.Color.BLUE),
F(java.awt.Color.RED,java.awt.Color.GREEN,java.awt.Color.GREEN,java.awt.Color.BLUE),
G(Color.YELLOW,java.awt.Color.YELLOW,java.awt.Color.RED,java.awt.Color.YELLOW),
H(java.awt.Color.BLUE,java.awt.Color.YELLOW,java.awt.Color.GREEN,java.awt.Color.YELLOW),
I(java.awt.Color.GREEN, Color.GRAY,java.awt.Color.RED,java.awt.Color.YELLOW),
J(java.awt.Color.GREEN,java.awt.Color.GRAY,java.awt.Color.YELLOW,java.awt.Color.YELLOW),
K(java.awt.Color.YELLOW,java.awt.Color.GRAY,java.awt.Color.RED,java.awt.Color.GRAY),
L(java.awt.Color.BLUE,java.awt.Color.GRAY,java.awt.Color.GREEN,java.awt.Color.GRAY),
M(java.awt.Color.GREEN,java.awt.Color.YELLOW,java.awt.Color.GREEN,java.awt.Color.GRAY);
private final static int NORTH=0;
private final static int EAST=1;
private final static int SOUTH=2;
private final static int WEST=3;
private final java.awt.Color[] colorsStartingAtNorthGoingClockwise=new java.awt.Color[4];
WangDomino(Color north, Color east, Color south, Color west)
{
colorsStartingAtNorthGoingClockwise[NORTH] = north;
colorsStartingAtNorthGoingClockwise[EAST] = east;
colorsStartingAtNorthGoingClockwise[SOUTH] = south;
colorsStartingAtNorthGoingClockwise[WEST] = west;
}
public java.awt.Color getNorth(int rotatedByQuadrants)
{
return(colorsStartingAtNorthGoingClockwise[(NORTH+rotatedByQuadrants)%colorsStartingAtNorthGoingClockwise.length]);
}
public java.awt.Color getEast(int rotatedByQuadrants)
{
return(colorsStartingAtNorthGoingClockwise[(EAST+rotatedByQuadrants)%colorsStartingAtNorthGoingClockwise.length]);
}
public java.awt.Color getSouth(int rotatedByQuadrants)
{
return(colorsStartingAtNorthGoingClockwise[(SOUTH+rotatedByQuadrants)%colorsStartingAtNorthGoingClockwise.length]);
}
public java.awt.Color getWest(int rotatedByQuadrants)
{
return(colorsStartingAtNorthGoingClockwise[(WEST+rotatedByQuadrants)%colorsStartingAtNorthGoingClockwise.length]);
}
public java.awt.image.BufferedImage getRotatedImageWithSize(int size,int rotatedByQuadrants,java.awt.Color line)
{
java.awt.image.BufferedImage bimg=new java.awt.image.BufferedImage(size,size, BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g2=bimg.createGraphics();
int middlex=size/2;
int middley=middlex;
g2.setTransform(AffineTransform.getQuadrantRotateInstance(-rotatedByQuadrants,middlex,middley));
g2.setPaint(colorsStartingAtNorthGoingClockwise[NORTH]);
int[] xPoints=new int[4];
int[] yPoints=new int[4];
xPoints[0]=middlex;
yPoints[0]=middley;
xPoints[1]=size;
yPoints[1]=0;
xPoints[2]=0;
yPoints[2]=0;
xPoints[3]=middlex;
yPoints[3]=middley;
g2.fillPolygon(xPoints,yPoints,xPoints.length);
if(line!=null)
{
g2.setPaint(line);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
}
g2.setPaint(colorsStartingAtNorthGoingClockwise[EAST]);
xPoints[2]=size;
yPoints[2]=size;
g2.fillPolygon(xPoints,yPoints,xPoints.length);
if(line!=null)
{
g2.setPaint(line);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
}
g2.setPaint(colorsStartingAtNorthGoingClockwise[SOUTH]);
xPoints[1]=0;
yPoints[1]=size;
g2.fillPolygon(xPoints,yPoints,xPoints.length);
if(line!=null)
{
g2.setPaint(line);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
}
g2.setPaint(colorsStartingAtNorthGoingClockwise[WEST]);
xPoints[2]=0;
yPoints[2]=0;
g2.fillPolygon(xPoints,yPoints,xPoints.length);
if(line!=null)
{
g2.setPaint(line);
g2.drawPolygon(xPoints, yPoints, xPoints.length);
}
g2.dispose();
return bimg;
}
public static WangDomino random(java.util.Random rand)
{
return values()[rand.nextInt(values().length)];
}
public static WangDomino[] randomizedValues()
{
java.util.List<WangDomino> shuffled=java.util.Arrays.asList(values());
java.util.Collections.shuffle(shuffled);
return shuffled.toArray(new WangDomino[0]);
}
static class Configuration
{
private final int rotatedByQuadrants;
private final WangDomino wangDomino;
public Configuration(WangDomino wangDomino,int rotatedByQuadrants)
{
this.rotatedByQuadrants = rotatedByQuadrants;
this.wangDomino = wangDomino;
}
public int getRotatedByQuadrants()
{
return rotatedByQuadrants;
}
public WangDomino getWangDomino()
{
return wangDomino;
}
public java.awt.Color getNorth()
{
return wangDomino.getNorth(rotatedByQuadrants);
}
public java.awt.Color getEast()
{
return wangDomino.getEast(rotatedByQuadrants);
}
public java.awt.Color getSouth()
{
return wangDomino.getSouth(rotatedByQuadrants);
}
public java.awt.Color getWest()
{
return wangDomino.getWest(rotatedByQuadrants);
}
public java.awt.image.BufferedImage getImageWithSize(int size,java.awt.Color line)
{
return wangDomino.getRotatedImageWithSize(size,rotatedByQuadrants,line);
}
}
static class Grid
{
private final java.awt.Dimension dim;
private final Configuration[] configurations;
public Grid(Dimension dim)
{
this.dim = dim;
configurations=new Configuration[dim.width*dim.height];
}
private int getTileIndex(int x, int y) {
int index=-1;
if (x >= dim.width || y >= dim.height || x < 0 || y < 0)
;//throw new IllegalArgumentException("Tile outside bounds: (" + x + ", " + y + ")");
else
index=dim.width * y + x;
return index;
}
public Configuration get(int x, int y)
{
Configuration rv=null;
int index=getTileIndex(x,y);
if(index>-1)
rv=configurations[index];
return rv;
}
public void set(int x, int y, Configuration configuration)
{
int index=getTileIndex(x,y);
if(index>-1)
configurations[index]=configuration;
}
int getWidth()
{
return dim.width;
}
int getHeight()
{
return dim.height;
}
}
public static void main(java.lang.String[] args) throws IOException
{
int tilesize=128;
java.util.Random rand=new java.util.Random(System.currentTimeMillis());
int loopCounter=0;
while(true)
{
try
{
Grid grid = new Grid(new java.awt.Dimension(35, 24));
int expcouter=0;
for (int y = 0; y < grid.getHeight(); ++y)
{
for (int x = 0; x < grid.getWidth(); ++x)
{
Configuration configuration = new Configuration(WangDomino.random(rand), rand.nextInt(4));
while(true)
{
try
{
grid.set(x, y, null);
if (x + 1 < grid.getWidth())
grid.set(x + 1, y, null);
configuration = findMatchingTileConfiguration(grid, x, y, rand);
grid.set(x, y, configuration);
break;
} catch (IndexOutOfBoundsException exp)
{
--y;
if(y<0)
{
++y;
x-=2;
if(x<-1)
break;
}
++expcouter;
if(expcouter>10)
throw exp;
}
}
}
}
java.awt.image.BufferedImage bimg = new java.awt.image.BufferedImage(grid.getWidth() * tilesize, grid.getHeight() * tilesize, BufferedImage.TYPE_INT_ARGB);
java.awt.Graphics2D g2 = bimg.createGraphics();
for (int y = 0; y < grid.getHeight(); ++y)
{
for (int x = 0; x < grid.getWidth(); ++x)
{
if(grid.get(x,y)!=null)
{
g2.drawImage(grid.get(x, y).getImageWithSize(tilesize, Color.BLACK), x * tilesize, y * tilesize, null);
}
}
}
g2.dispose();
java.io.File f = java.io.File.createTempFile("tiled_", ".png");
javax.imageio.ImageIO.write(bimg, "png", f);
++loopCounter;
if (loopCounter > 10)
break;
}
catch (IndexOutOfBoundsException exp)
{
System.err.println("tried and failed...");
}
}
}
private static Configuration findMatchingTileConfiguration(Grid grid, int x, int y,java.util.Random rand)
{
java.util.List<Configuration> candidates=new java.util.LinkedList();
WangDomino[] randomized=WangDomino.randomizedValues();
Configuration configuration=grid.get(x,y-1);
java.awt.Color north=configuration!=null?configuration.getSouth():null;
configuration=grid.get(x+1,y);
java.awt.Color east=configuration!=null?configuration.getWest():null;
configuration=grid.get(x,y+1);
java.awt.Color south=configuration!=null?configuration.getNorth():null;
configuration=grid.get(x-1,y);
java.awt.Color west=configuration!=null?configuration.getEast():null;
for(WangDomino wangDomino:randomized)
{
for(int i=0;i<4;++i)
{
Configuration sample=new Configuration(wangDomino,i);
if(((north==null)||(north.equals(sample.getNorth())))&&
((east==null)||(east.equals(sample.getEast())))&&
((south==null)||(south.equals(sample.getSouth())))&&
((west==null)||(west.equals(sample.getWest()))))
{
candidates.add(sample);
}
}
}
java.util.Collections.shuffle(candidates);
return candidates.get(0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment