123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- #!/usr/bin/env python3
- from __future__ import print_function
- import numpy as np
- import itertools
- import sys
- class Stick(object):
- XPAR = 0
- YPAR = 1
- ZPAR = 2
- def __init__(self, name, array):
- self.name = name
- self.array = np.asarray(array)
- @property
- def dir(self):
- if len(self.array) > 2:
- return self.ZPAR
- elif len(self.array[0]) > 2:
- return self.YPAR
- return self.XPAR
- def rot(self, axis, n=1):
- if n == 0:
- return self
- if axis == 0: # X
- newArray = np.rot90(self.array, n, (0, 1))
- elif axis == 1: # Y
- newArray = np.rot90(self.array, n, (0, 2))
- elif axis == 2: # Z
- newArray = np.rot90(self.array, n, (1, 2))
- return self.__class__(self.name, newArray)
- def __genXZRotations(self):
- self.rotX = self.rot(0)
- self.rotZ = self.rot(2)
- def __genBasicRotations(self):
- self.basicRotations = []
- for rotY in (0, 1, 2, 3):
- for rotX in (0, 2):
- r = self.rot(0, rotX).rot(1, rotY)
- for other in self.basicRotations:
- if np.array_equal(other.array, r.array):
- break
- else:
- r.__genXZRotations()
- self.basicRotations.append(r)
- def genRotations(self):
- self.__genXZRotations()
- self.rotX.__genBasicRotations()
- self.rotZ.__genBasicRotations()
- self.__genBasicRotations()
- def __str__(self):
- if self.dir == self.XPAR:
- ret = [ self.name + " " ]
- for y in range(2):
- if y > 0:
- ret.append(" " * (len(self.name) + 1))
- for x in range(16):
- if x <= 5 or x >= 10:
- ret.append("=")
- else:
- xx = x - 6
- if self.array[0][y][xx] and\
- self.array[1][y][xx]:
- ret.append("#")
- elif self.array[0][y][xx]:
- ret.append("@")
- elif self.array[1][y][xx]:
- ret.append("*")
- else:
- ret.append(" ")
- ret.append("\n")
- elif self.dir == self.YPAR:
- ret = [ self.name + "\n" ]
- for y in range(16):
- for x in range(2):
- if y <= 5 or y >= 10:
- ret.append("=")
- else:
- yy = y - 6
- if self.array[0][yy][x] and\
- self.array[1][yy][x]:
- ret.append("#")
- elif self.array[0][yy][x]:
- ret.append("@")
- elif self.array[1][yy][x]:
- ret.append("*")
- else:
- ret.append(" ")
- ret.append("\n")
- else:
- ret = [ self.name + " " ]
- for y in range(2):
- if y > 0:
- ret.append(" " * (len(self.name) + 1))
- ret.append("|")
- for x in range(2):
- s = np.sum(self.array, 0)
- ret.append(str(s[y][x]))
- ret.append("|\n")
- return "".join(ret)
- def __repr__(self):
- return repr(self.array).replace("array", "Stick")
- def __hash__(self):
- return hash(self.name)
- def __eq__(self, other):
- return self.name == other.name
- def __ne__(self, other):
- return self.name != other.name
- class Solution(object):
- def __init__(self, a, b, c, d, e, f):
- self.a = a
- self.b = b
- self.c = c
- self.d = d
- self.e = e
- self.f = f
- def rot(self, axis):
- new = self.__class__(self.a, self.b, self.c, self.d, self.e, self.f)
- new.a = new.a.rot(axis, 2)
- new.b = new.b.rot(axis, 2)
- new.c = new.c.rot(axis, 2)
- new.d = new.d.rot(axis, 2)
- new.e = new.e.rot(axis, 2)
- new.f = new.f.rot(axis, 2)
- if axis == 0:
- new.c, new.d = new.d, new.c
- new.e, new.f = new.f, new.e
- elif axis == 1:
- new.a, new.b = new.b, new.a
- new.c, new.d = new.d, new.c
- elif axis == 2:
- new.a, new.b = new.b, new.a
- new.e, new.f = new.f, new.e
- return new
- def __eq_exact(self, other):
- return np.array_equal(self.a.array, other.a.array) and\
- np.array_equal(self.b.array, other.b.array) and\
- np.array_equal(self.c.array, other.c.array) and\
- np.array_equal(self.d.array, other.d.array) and\
- np.array_equal(self.e.array, other.e.array) and\
- np.array_equal(self.f.array, other.f.array)
- def __eq__(self, other):
- sx = self
- #FIXME this is wrong
- for x in range(2):
- if x > 0:
- sx = sx.rot(0)
- sy = sx
- for y in range(2):
- if y > 0:
- sy = sy.rot(1)
- sz = sy
- for z in range(2):
- if z > 0:
- sz = sz.rot(2)
- if sz.__eq_exact(other):
- return True
- return False
- def __ne__(self, other):
- return not self.__eq__(other)
- def __str__(self):
- a, b, c, d, e, f = self.a, self.b, self.c, self.d, self.e, self.f
- ret = "a=%s, b=%s, c=%s, d=%s e=%s f=%s\n" % (
- a.name, b.name, c.name, d.name, e.name, f.name)
- lines = []
- for lineA, lineB in zip(str(a).splitlines(), str(b).splitlines()):
- lines.append(lineA + " " + lineB)
- ret += "\n".join(lines) + "\n\n"
- ret += "\n".join((str(c), str(d), str(e), str(f)))
- return ret
- s1 = Stick("s1", (
- ( (1, 1),
- (1, 1),
- (1, 1),
- (1, 1), ),
- ( (1, 1),
- (1, 1),
- (1, 1),
- (1, 1), ), ))
- s2 = Stick("s2", (
- ( (0, 0),
- (0, 0),
- (0, 0),
- (0, 0), ),
- ( (1, 1),
- (1, 1),
- (1, 1),
- (1, 1), ), ))
- s3 = Stick("s3", (
- ( (1, 0),
- (0, 0),
- (0, 0),
- (1, 1), ),
- ( (1, 0),
- (1, 0),
- (1, 1),
- (1, 1), ), ))
- s4 = Stick("s4", (
- ( (0, 1),
- (0, 0),
- (0, 0),
- (1, 1), ),
- ( (0, 1),
- (0, 1),
- (1, 1),
- (1, 1), ), ))
- s5 = Stick("s5", (
- ( (0, 0),
- (0, 0),
- (0, 0),
- (0, 0), ),
- ( (1, 1),
- (0, 1),
- (0, 1),
- (1, 1), ), ))
- s6 = Stick("s6", (
- ( (0, 1),
- (0, 0),
- (0, 0),
- (0, 1), ),
- ( (0, 1),
- (1, 1),
- (1, 1),
- (0, 1), ), ))
- sticks = {s1, s2, s3, s4, s5, s6}
- for s in sticks:
- s.genRotations()
- emptyPlaneXY = np.asarray(
- (
- ( (0, 0, 0, 0),
- (0, 0, 0, 0),
- (0, 0, 0, 0),
- (0, 0, 0, 0), ),
- )
- )
- emptyPlaneYZ = np.rot90(emptyPlaneXY, 1, (0, 2))
- emptyPlaneXZ = np.rot90(emptyPlaneXY, 1, (0, 1))
- cornersXY = np.asarray(
- (
- ( (1, 0, 0, 1),
- (0, 0, 0, 0),
- (0, 0, 0, 0),
- (1, 0, 0, 1), ),
- ( (1, 0, 0, 1),
- (0, 0, 0, 0),
- (0, 0, 0, 0),
- (1, 0, 0, 1), ),
- )
- )
- cornersYZ = np.rot90(cornersXY, 1, (0, 2))
- cornersXZ = np.rot90(cornersXY, 1, (0, 1))
- def processZPlane(a, b, c, d, cRot, dRot, abcd):
- remainingSticks = sticks - {a, b, c, d}
- for _e, _f in itertools.permutations(remainingSticks, 2):
- for e in _e.basicRotations:
- eRot = e.rotX
- for f in _f.basicRotations:
- fRot = f.rotX
- ef = np.hstack((eRot.array, fRot.array))
- if np.any(ef - cornersYZ < 0):
- continue # At least one corner is not filled.
- efFilled = np.dstack((emptyPlaneYZ, ef, emptyPlaneYZ))
- abcdef = abcd + efFilled
- if not np.any(abcdef > 1):
- yield Solution(a, b, cRot, dRot, eRot, fRot)
- def processXYPlane():
- for _a, _b, _c, _d in itertools.permutations(sticks, 4):
- for a in _a.basicRotations:
- for b in _b.basicRotations:
- ab = np.dstack((a.array, b.array))
- if np.any(ab - cornersXY < 0):
- continue # At least one corner is not filled.
- for c in _c.basicRotations:
- cRot = c.rotZ
- for d in _d.basicRotations:
- dRot = d.rotZ
- cd = np.vstack((cRot.array, dRot.array))
- if np.any(cd - cornersXZ < 0):
- continue # At least one corner is not filled.
- abFilled = np.vstack((emptyPlaneXY, ab, emptyPlaneXY))
- cdFilled = np.hstack((emptyPlaneXZ, cd, emptyPlaneXZ))
- abcd = abFilled + cdFilled
- if np.any(abcd > 1):
- continue # This one collides in the XY plane already.
- yield from processZPlane(a, b, c, d, cRot, dRot, abcd)
- solutions = []
- for solution in processXYPlane():
- for otherSolution in solutions:
- if solution == otherSolution:
- break # We have that one already
- else:
- solutions.append(solution)
- print("Found %s solution(s).\n" % len(solutions))
- for i, solution in enumerate(solutions):
- print("Solution #%d: %s" % (i + 1, solution))
|