diff --git a/dancing_links/python/KelKum/algorithm_x.py b/dancing_links/python/KelKum/algorithm_x.py new file mode 100644 index 0000000..f5b16ee --- /dev/null +++ b/dancing_links/python/KelKum/algorithm_x.py @@ -0,0 +1,97 @@ +import incidence_matrix +import pentominos +import examples +import copy + +class Algorithm_X(object): + def __init__(self, matrix): + self.matrix = matrix + #print(self.matrix.columnObjectOfName.keys()) + #self.o = [] + self.search(0, []) + #print(self.matrix.representation()) + + def search(self, k, o): + #print("search k = " + str(k) + " R[h] = " + str(self.matrix.h.right.name)) + if self.matrix.h.right == self.matrix.h: + print("All pentominos placed") + print_current_solution(o) + return + c = self.chooseColumnObj() + self.matrix.coverColumn(c) + #print("column c covered") + #print(str(c.representation())) + r = c.down + while r is not c: + #print(str(r.name) + " " + str(c.down.name)) + #o.append(r) + j = r.right + while j is not r: + #print(str(j.name) + " " + str(j.listHeader.name)) + self.matrix.coverColumn(j.listHeader) + j = j.right + self.search(k + 1, o + [r]) + j = r.left + while j is not r: + self.matrix.uncoverColumn(j.listHeader) + j = j.left + r = r.down + self.matrix.uncoverColumn(c) + + def print_current_solution(self, o): + for obj in o: + s = str(obj.listHeader.name) + r = obj.right + while r is not obj: + s += " " + str(r.listHeader.name) + r = r.right + print(s + "\n") + print("\n") + + def chooseColumnObj(self): + #print("choose column object") + c = self.matrix.h + s = 1e100000 + j = self.matrix.h.right + while j is not self.matrix.h and not incidence_matrix.is_number(j.name): + if j.size < s: + c = j + s = j.size + j = j.right + #print(str(c.representation())) + return c + +def append_all_possible_placements(matrix): + names = [] + tiles = [] + currentColumnObject = matrix.h.right + while currentColumnObject.name is not "root": + if incidence_matrix.is_number(currentColumnObject.name): + tiles.append(currentColumnObject.name) + else: + names.append(currentColumnObject.name) + currentColumnObject = currentColumnObject.right + pset = pentominos.fixed_pentominos_of_name_list(names) + #print(str(len(names)) + " " + str(pset.size())) + max0 = max([int(s[0]) for s in tiles]) + max1 = max([int(s[1]) for s in tiles]) + #print(str(max0) + " " + str(max1)) + for p in pset.set: + for i in range(0, max0): + for j in range(0, max1): + q = copy.deepcopy(p) + q.translate_by([i, j]) + if matrix.is_valid_placement(q.coos): + matrix.appendRow(q.name, [str(c[0]) + str(c[1]) for c in q.coos]) + +def run_scott_example(): + matrix = examples.scott_example() + #print(str(matrix.representation())) + append_all_possible_placements(matrix) + #print(str(matrix.representation())) + Algorithm_X(matrix) + +if __name__ == '__main__': + run_scott_example() + + \ No newline at end of file diff --git a/dancing_links/python/KelKum/examples.py b/dancing_links/python/KelKum/examples.py new file mode 100644 index 0000000..823c068 --- /dev/null +++ b/dancing_links/python/KelKum/examples.py @@ -0,0 +1,26 @@ +import incidence_matrix + +def running_example(): + I = incidence_matrix.IncidenceMatrix(["A", "B", "C", "D", "E", "F", "G"]) + I.appendRow("C", ["E", "F"]) + I.appendRow("A", ["D", "G"]) + I.appendRow("B", ["C", "F"]) + I.appendRow("A", ["D"]) + I.appendRow("B", ["G"]) + I.appendRow("D", ["E", "G"]) + return I + +def AllowOutsideHole(c): + if c not in [[3,3],[3,4],[4,3],[4,4]]: + return True + else: + return False + +def scott_example(): + names = ["F", "I", "L", "P", "N", "T", "U", "V", "W", "X", "Y", "Z"] + for i in range(8): + for j in range(8): + if AllowOutsideHole([i,j]): + names.append(str(i)+str(j)) + return incidence_matrix.IncidenceMatrix(names) + diff --git a/dancing_links/python/KelKum/incidence_matrix.py b/dancing_links/python/KelKum/incidence_matrix.py new file mode 100644 index 0000000..e63f616 --- /dev/null +++ b/dancing_links/python/KelKum/incidence_matrix.py @@ -0,0 +1,156 @@ +def is_number(string): + try: + float(string) + return True + except ValueError: + pass + return False + + +class IncidenceCell(object): + def __init__(self, left, right, up, down, listHeader, name): + self.left = left + self.right = right + self.up = up + self.down = down + self.listHeader = listHeader + self.name = name + + def representation(self): + rep = ["c", self.name] + for c in [self.left, self.right, self.up, self.down]: + rep.append(c.name) + return rep + + +class ColumnObject(IncidenceCell): + def __init__(self, left, right, up, down, name): + IncidenceCell.__init__(self, left, right, up, down, self, name) + self.size = 0 + + def representation(self): + hrep = ["h(" + str(self.size) + ")", self.name] + for c in [self.left, self.right, self.up, self.down]: + hrep.append(c.name) + rep = [[hrep]] + + currentCell = self.down + while currentCell is not self: + rep[0].append(currentCell.representation()) + currentCell = currentCell.down + + return rep + + +class IncidenceMatrix(object): + def __init__(self, names): + self.h = ColumnObject(None, None, None, None, "root") + self.h.left = self.h.right = self.h.up = self.h.down = self.h + + currentColumnObject = self.h + self.columnObjectOfName = dict() + self.columnObjectOfName["root"] = self.h + + self.indexOfPiecePlacement = dict() + for n in names: + self.indexOfPiecePlacement[n] = 0 + self.insertColumnObject(currentColumnObject, self.h, n) + currentColumnObject = currentColumnObject.right + self.columnObjectOfName[n] = currentColumnObject + + self.rows = 0 + + def representation(self): + currentColumnObject = self.h + + rep = currentColumnObject.representation() + currentColumnObject = currentColumnObject.right + + while currentColumnObject.name is not "root": + rep += currentColumnObject.representation() + currentColumnObject = currentColumnObject.right + + return rep + + def rowRepresentation(self): + rowRep = [] + currentColumnObject = self.h.right + while currentColumnObject.name is not "root" and not is_number(currentColumnObject.name): + head_elt = currentColumnObject.down + while head_elt is not currentColumnObject: + row = [ head_elt.name ] + current_elt = head_elt.right + while current_elt is not head_elt: + row.append(current_elt.listHeader.name) + current_elt = current_elt.right + rowRep.append(row) + head_elt = head_elt.down + currentColumnObject = currentColumnObject.right + return rowRep + + def insertColumnObject(self, left, right, name): + colobj = ColumnObject(left, right, None, None, name) + colobj.up = colobj.down = colobj + left.right = colobj + right.left = colobj + + def appendRow(self, tileName, placement): + """ a placement is a list of coordinates that indicates which squares the piece named tileName covers""" + tile = self.columnObjectOfName[tileName] + cell = IncidenceCell(None, None, tile.up, tile, tile, str(tile.name) + "[" + str(self.indexOfPiecePlacement[tileName]) + "]") + self.indexOfPiecePlacement[tileName] += 1 + tile.up.down = tile.up = cell + tile.size += 1 + for place in placement: + pcol = self.columnObjectOfName[place] + pcell = IncidenceCell(None, None, pcol.up, pcol, pcol, str(tile.name) + str(pcol.name)) + pcol.up.down = pcell + pcol.up = pcell + pcell.left = cell + cell.right = pcell + cell = pcell + pcol.size += 1 + cell.right = tile.up + tile.up.left = cell + + def coverColumn(self, c): + #print(str(c.name)) + c.right.left = c.left + c.left.right = c.right + currow = c.down + while currow is not c: + curcell = currow.right + while curcell is not currow: + curcell.down.up = curcell.up + curcell.up.down = curcell.down + curcell.listHeader.size -= 1 + curcell = curcell.right + #print(str(currow.name) + " " + str(curcell.name)) + currow = currow.down + + def uncoverColumn(self, c): + currow = c.up + while currow is not c: + curcell = currow.left + while curcell is not currow: + curcell.down.up = curcell + curcell.up.down = curcell + curcell.listHeader.size += 1 + curcell = curcell.left + currow = currow.up + c.right.left = c + c.left.right = c + + def is_valid_placement(self, coos): + tiles = [] + j = self.h.right + while j is not self.h: + if is_number(j.name): + tiles.append(j.name) + j = j.right + for c in coos: + if str(c[0]) + str(c[1]) not in tiles: + #print(str(coos) + " is not valid") + return False + return True + \ No newline at end of file diff --git a/dancing_links/python/pentominos.py b/dancing_links/python/KelKum/pentominos.py similarity index 52% rename from dancing_links/python/pentominos.py rename to dancing_links/python/KelKum/pentominos.py index 99c91bf..e2628eb 100644 --- a/dancing_links/python/pentominos.py +++ b/dancing_links/python/KelKum/pentominos.py @@ -7,34 +7,59 @@ def __init__(self, name, coos): self.dim = len(coos[0]) def normalize_coo(self, coo): - pass + m = min([c[coo] for c in self.coos]) + return self.translate_coo(coo, -m) def normalize(self): - pass + self = self.normalize_coo(0) + self = self.normalize_coo(1) + self.coos.sort() + return self def flip(self, coo): - pass + for c in self.coos: + c[coo] = -c[coo] + return self.normalize_coo(coo) def translate_one(self, coo): - pass - + return self.translate_coo(coo, 1) + def translate_coo(self, coo, amount): - pass + x, y = 0, 0 + if coo == 0: + x = amount + elif coo == 1: + y = amount + for c in self.coos: + c[0] += x + c[1] += y + return self def translate_by(self, by_vector): - pass + #print("Translate " + str(self.coos) + " by " + str(by_vector)) + self = self.translate_coo(0, by_vector[0]) + self = self.translate_coo(1, by_vector[1]) + return self def turn90(self): - pass + self.coos = [[-c[1], c[0]] for c in self.coos] + return self.normalize() def max(self): - pass + return max(self.coos) def __hash__(self): - pass + self.coos.sort() + h = str() + for c in self.coos: + h += str(c[0] + 1) + str(c[1] + 1) + return int(h) def __eq__(self, other): - pass + for c in self.coos: + if c not in other.coos: + return False + return True def representation(self): return "[" + self.name + ":" + str(self.coos) + "]" @@ -91,22 +116,66 @@ def __init__(self): def all_pentominos(): return [F(), I(), L(), P(), N(), T(), U(), V(), W(), X(), Y(), Z()] - + +def fixed_pentominos_of(p): + t = TileSet() + t.add(p) + t.add(p.flip(0)) + t.add(p.flip(1)) + t.add(p.flip(0)) + t.add(p.flip(1).turn90()) + t.add(p.flip(0)) + t.add(p.flip(1)) + t.add(p.flip(0)) + return t + +def fixed_pentominos_of_name_list(list): + is_list_of_pentominos(list) + t = TileSet() + for name in list: + p = pentomino_by_name(name) + t.addlist(fixed_pentominos_of(p)) + return t + +def is_list_of_pentominos(list): + all = ["F", "I", "L", "P", "N", "T", "U", "V", "W", "X", "Y", "Z"] + for p in list: + if p not in all: + raise ValueError("Error in pentomino list: " + str(p) + " is not a pentomino") + +def pentomino_by_name(name): + pentominos = all_pentominos() + for p in pentominos: + if p.name == name: + return p + +def all_fixed_pentominos(): + plist = all_pentominos() + t = TileSet() + for p in plist: + t.addlist(fixed_pentominos_of(p)) + return t class TileSet(object): def __init__(self, plist=[]): self.set = set() - for p in plist: - self.add(p) + self.addlist(plist) def __iter__(self): return iter(self.set) def add(self, p): - pass + q = copy.deepcopy(p) + #print(p.representation() + ' ' + str(p.__hash__())) + #print(q.representation() + ' ' + str(q.__hash__())) + self.set.add(q) + + def addlist(self, plist): + for p in plist: + self.add(p) def size(self): - pass + return len(self.set) def representation(self): rep = "[" diff --git a/dancing_links/python/KelKum/test_incidence_matrix.py b/dancing_links/python/KelKum/test_incidence_matrix.py new file mode 100644 index 0000000..673bcc0 --- /dev/null +++ b/dancing_links/python/KelKum/test_incidence_matrix.py @@ -0,0 +1,57 @@ +import unittest +import incidence_matrix +import pentominos +import examples +import copy + +class TestIncidenceMatrixMethods(unittest.TestCase): + + def setUp(self): + pass + + def test_init(self): + I = incidence_matrix.IncidenceMatrix(["0", "1", "2"]) + self.assertEqual([[['h(0)', 'root', '2', '0', 'root', 'root']], [['h(0)', '0', 'root', '1', '0', '0']], [['h(0)', '1', '0', '2', '1', '1']], [['h(0)', '2', '1', 'root', '2', '2']]], I.representation()) + + def test_append_one_row(self): + I = examples.scott_example() + I.appendRow("I", ["00", "01", "02", "03", "04"]) + rep = [I.columnObjectOfName[i].representation() for i in ["I", "00", "01", "02", "03", "04"]] + self.assertEqual([[[['h(1)', 'I', 'F', 'L', 'I[0]', 'I[0]'], ['c', 'I[0]', 'I04', 'I00', 'I', 'I']]], [[['h(1)', '00', 'Z', '01', 'I00', 'I00'], ['c', 'I00', 'I[0]', 'I01', '00', '00']]], [[['h(1)', '01', '00', '02', 'I01', 'I01'], ['c', 'I01', 'I00', 'I02', '01', '01']]], [[['h(1)', '02', '01', '03', 'I02', 'I02'], ['c', 'I02', 'I01', 'I03', '02', '02']]], [[['h(1)', '03', '02', '04', 'I03', 'I03'], ['c', 'I03', 'I02', 'I04', '03', '03']]], [[['h(1)', '04', '03', '05', 'I04', 'I04'], ['c', 'I04', 'I03', 'I[0]', '04', '04']]]], rep) + + + def test_append_two_rows(self): + I = examples.scott_example() + I.appendRow("I", ["00", "01", "02", "03", "04"]) + I.appendRow("I", ["01", "02", "03", "04", "05"]) + rep = [I.columnObjectOfName[i].representation() for i in ["I", "00", "01", "02", "03", "04"]] + self.assertEqual([[[['h(2)', 'I', 'F', 'L', 'I[1]', 'I[0]'], ['c', 'I[0]', 'I04', 'I00', 'I', 'I[1]'], ['c', 'I[1]', 'I05', 'I01', 'I[0]', 'I']]], [[['h(1)', '00', 'Z', '01', 'I00', 'I00'], ['c', 'I00', 'I[0]', 'I01', '00', '00']]], [[['h(2)', '01', '00', '02', 'I01', 'I01'], ['c', 'I01', 'I00', 'I02', '01', 'I01'], ['c', 'I01', 'I[1]', 'I02', 'I01', '01']]], [[['h(2)', '02', '01', '03', 'I02', 'I02'], ['c', 'I02', 'I01', 'I03', '02', 'I02'], ['c', 'I02', 'I01', 'I03', 'I02', '02']]], [[['h(2)', '03', '02', '04', 'I03', 'I03'], ['c', 'I03', 'I02', 'I04', '03', 'I03'], ['c', 'I03', 'I02', 'I04', 'I03', '03']]], [[['h(2)', '04', '03', '05', 'I04', 'I04'], ['c', 'I04', 'I03', 'I[0]', '04', 'I04'], ['c', 'I04', 'I03', 'I05', 'I04', '04']]]], rep) + + def test_construct_running_example(self): + I = examples.running_example() + origRep = [[['h(0)', 'root', 'G', 'A', 'root', 'root']], [['h(2)', 'A', 'root', 'B', 'A[1]', 'A[0]'], ['c', 'A[0]', 'AG', 'AD', 'A', 'A[1]'], ['c', 'A[1]', 'AD', 'AD', 'A[0]', 'A']], [['h(2)', 'B', 'A', 'C', 'B[1]', 'B[0]'], ['c', 'B[0]', 'BF', 'BC', 'B', 'B[1]'], ['c', 'B[1]', 'BG', 'BG', 'B[0]', 'B']], [['h(2)', 'C', 'B', 'D', 'BC', 'C[0]'], ['c', 'C[0]', 'CF', 'CE', 'C', 'BC'], ['c', 'BC', 'B[0]', 'BF', 'C[0]', 'C']], [['h(3)', 'D', 'C', 'E', 'D[0]', 'AD'], ['c', 'AD', 'A[0]', 'AG', 'D', 'AD'], ['c', 'AD', 'A[1]', 'A[1]', 'AD', 'D[0]'], ['c', 'D[0]', 'DG', 'DE', 'AD', 'D']], [['h(2)', 'E', 'D', 'F', 'DE', 'CE'], ['c', 'CE', 'C[0]', 'CF', 'E', 'DE'], ['c', 'DE', 'D[0]', 'DG', 'CE', 'E']], [['h(2)', 'F', 'E', 'G', 'BF', 'CF'], ['c', 'CF', 'CE', 'C[0]', 'F', 'BF'], ['c', 'BF', 'BC', 'B[0]', 'CF', 'F']], [['h(3)', 'G', 'F', 'root', 'DG', 'AG'], ['c', 'AG', 'AD', 'A[0]', 'G', 'BG'], ['c', 'BG', 'B[1]', 'B[1]', 'AG', 'DG'], ['c', 'DG', 'DE', 'D[0]', 'BG', 'G']]] + self.assertEqual(origRep, I.representation()) + + def test_cover_running_example(self): + I = examples.running_example() + origRep = I.representation() + I.coverColumn(I.columnObjectOfName["A"]) + self.assertEqual([[['h(0)', 'root', 'G', 'B', 'root', 'root']], [['h(2)', 'B', 'root', 'C', 'B[1]', 'B[0]'], ['c', 'B[0]', 'BF', 'BC', 'B', 'B[1]'], ['c', 'B[1]', 'BG', 'BG', 'B[0]', 'B']], [['h(2)', 'C', 'B', 'D', 'BC', 'C[0]'], ['c', 'C[0]', 'CF', 'CE', 'C', 'BC'], ['c', 'BC', 'B[0]', 'BF', 'C[0]', 'C']], [['h(1)', 'D', 'C', 'E', 'D[0]', 'D[0]'], ['c', 'D[0]', 'DG', 'DE', 'D', 'D']], [['h(2)', 'E', 'D', 'F', 'DE', 'CE'], ['c', 'CE', 'C[0]', 'CF', 'E', 'DE'], ['c', 'DE', 'D[0]', 'DG', 'CE', 'E']], [['h(2)', 'F', 'E', 'G', 'BF', 'CF'], ['c', 'CF', 'CE', 'C[0]', 'F', 'BF'], ['c', 'BF', 'BC', 'B[0]', 'CF', 'F']], [['h(2)', 'G', 'F', 'root', 'DG', 'BG'], ['c', 'BG', 'B[1]', 'B[1]', 'G', 'DG'], ['c', 'DG', 'DE', 'D[0]', 'BG', 'G']]], I.representation()) + + I.uncoverColumn(I.columnObjectOfName["A"]) + self.assertEqual(origRep, I.representation()) + + def test_cover_3_running_example(self): + I = examples.running_example() + for n in ["A", "D", "G"]: + I.coverColumn(I.columnObjectOfName[n]) + + self.assertEqual([[['h(0)', 'root', 'F', 'B', 'root', 'root']], [['h(1)', 'B', 'root', 'C', 'B[0]', 'B[0]'], ['c', 'B[0]', 'BF', 'BC', 'B', 'B']], [['h(2)', 'C', 'B', 'E', 'BC', 'C[0]'], ['c', 'C[0]', 'CF', 'CE', 'C', 'BC'], ['c', 'BC', 'B[0]', 'BF', 'C[0]', 'C']], [['h(1)', 'E', 'C', 'F', 'CE', 'CE'], ['c', 'CE', 'C[0]', 'CF', 'E', 'E']], [['h(2)', 'F', 'E', 'root', 'BF', 'CF'], ['c', 'CF', 'CE', 'C[0]', 'F', 'BF'], ['c', 'BF', 'BC', 'B[0]', 'CF', 'F']]], I.representation()) + + + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestIncidenceMatrixMethods) + +if __name__ == '__main__': + unittest.main() diff --git a/dancing_links/python/test_pentominos.py b/dancing_links/python/KelKum/test_pentominos.py similarity index 100% rename from dancing_links/python/test_pentominos.py rename to dancing_links/python/KelKum/test_pentominos.py