https://github.com/asarg/AutoTile
Raw File
Tip revision: c6f3135642358c0cd08fde7d45f34b6b842e6a4e authored by Michael on 29 March 2022, 16:27:43 UTC
Merge pull request #6 from asarg/Text-fixes
Tip revision: c6f3135
assemblyEngine.py
import random
import UniversalClasses
import copy
from PyQt5.QtWidgets import QMessageBox

# Debugging Functions
def printMove(move):
    if move["type"] == "a":
        print("Attach: ", move["state1"].get_label(),
              " at ", move["x"], ", ", move["y"])
    if move["type"] == "t":
        print("Transition ", move["state1"].get_label(), ", ", move["state2"].get_label(
        ), " to ", move["state1Final"].get_label(), ", ", move["state2Final"].get_label())
        print(" at ", move["x"], ", ", move["y"])


class Engine:
    def __init__(self, currentSystem):
        self.reset_engine(currentSystem)

    def reset_engine(self, currentSystem):
        self.system = currentSystem
        self.moveList = []
        self.TimeTaken = []
        self.currentIndex = 0
        self.lastIndex = 0
        self.errorStates = "" 

        # Get seed
        print(self.system.returnSeedStates())

        seedState = random.choice(self.system.returnSeedStates())
        seed = UniversalClasses.Tile(seedState, 0, 0)
        self.seedAssembly = UniversalClasses.Assembly()
        self.seedAssembly.set_tiles([seed])
        # Changed from adding to list to setting it as the current assembly
        # self.assemblyList.append(seedAssembly)
        # self.currentAssembly = self.seedAssembly
        self.currentAssembly = copy.deepcopy(self.seedAssembly)

        self.validMoves = self.currentAssembly.getMoves(self.system)

    def step(self, nextMove=None):

        # we are in our history, but they did NOT give us a new move
        if nextMove == None and self.currentIndex < self.lastIndex:
            move = self.moveList[self.currentIndex]
            res = self.build(move)
            if res == -1:
                return res
            else:
                # added a duplicate inside of build, remove it
                self.moveList.pop()
                self.currentIndex += 1
                return 0

        # we are in our history, and they gave us a new move to use instead
        if nextMove != None and self.currentIndex < self.lastIndex:
            # remove move list entries that are ahead of us
            while self.currentIndex != self.lastIndex:
                self.moveList.pop()
                self.lastIndex -= 1

            # now we can act like this is just a normal step
            # fall out the if statement into the normal step case

        # No history to worry about, just call build
        res = self.build(nextMove)
        if res == -1:
            return res
        else:
            self.lastIndex += 1
            self.currentIndex += 1
            return 0

    def back(self):
        if(self.currentIndex > 0):
            self.currentIndex = self.currentIndex - 1
            move = self.moveList[self.currentIndex]
            self.build(move, False)

    def first(self):
        self.currentIndex = 0
        self.currentAssembly = copy.deepcopy(self.seedAssembly)
        self.validMoves = self.currentAssembly.getMoves(self.system)

    def last(self):
        while self.currentIndex < self.lastIndex:
            # Will update current assembly, break if terminal
            if self.step() == -1:
                break

    def getCurrentAssembly(self):
        return self.currentAssembly

    def getCurrentIndex(self):
        return self.currentIndex

    def getCurrentMove(self):
        if len(self.moveList) != 0:
            return self.moveList[self.getCurrentIndex() - 1]

    def getLastMove(self):
        if len(self.moveList) != 0:
            return self.moveList[self.getCurrentIndex()]

    def getCurrentBorders(self):
        return self.currentAssembly.get_borders()

    def build(self, nextMove=None, forwards=True):

        # Check if assembly is terminal
        if(len(self.validMoves) == 0 and forwards):
            print("Terminal")
            self.currentAssembly.print_size()
            return -1

        # Add or Update only if we going forwards
        if forwards:
            if len(self.TimeTaken) <= self.currentIndex:
                self.TimeTaken.append(len(self.validMoves))
            else:
                self.TimeTaken[self.currentIndex] = len(self.validMoves)

        # Get next assembly and add to list
        # If given a move, choose that one, otherwise do random move
        move = None
        if nextMove == None:
            move = random.choice(self.validMoves)
        else:
            move = nextMove

        moveX = move["x"]
        moveY = move["y"]

        # get all moves that need to be removed

        # If Attachment
        if move["type"] == "a":
            # Remove other moves for self
            attOldMoves = self.currentAssembly.getAttat(
                self.system, moveX, moveY)
            self.removeMoves(attOldMoves)

            # remove Attachments for neighbors
            nAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY + 1)
            self.removeMoves(nAtts)

            sAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY - 1)
            self.removeMoves(sAtts)

            wAtts = self.currentAssembly.getAttat(
                self.system, moveX - 1, moveY)
            self.removeMoves(wAtts)

            eAtts = self.currentAssembly.getAttat(
                self.system, moveX + 1, moveY)
            self.removeMoves(eAtts)

            # remove transitions when going backwards
            if not forwards:
                # remove other move for self
                trOldMoves = self.currentAssembly.getTRat(
                    self.system, moveX, moveY)
                self.removeMoves(trOldMoves)

                # remove "v" TR moves from N neighbor
                vOldMoves = self.currentAssembly.getTRat(
                    self.system, moveX, moveY + 1, "v")
                self.removeMoves(vOldMoves)

                # remove "h" TR moves from W Neighbor
                hOldMoves = self.currentAssembly.getTRat(
                    self.system, moveX - 1, moveY, "h")
                self.removeMoves(hOldMoves)

        elif move["type"] == "t":
            #quick check to see if states exist
            errorState = ""
            if move["state1Final"] == None:
                errorState += self.findProblemTile(move["state1"], move["state2"], move["dir"], 1)

            if move["state2Final"] == None:
                errorState += self.findProblemTile(move["state1"], move["state2"], move["dir"], 2)

            if errorState != "":
               #self.validMoves = 0 
                msgBox = QMessageBox()
                msgBox.setIcon(QMessageBox.Information)
                msgBox.setText("The following states dont exist: \n" + errorState)
                msgBox.setWindowTitle("Missing states")
                msgBox.setStandardButtons(QMessageBox.Ok)

                returnValue = msgBox.exec()

                return -1
            

            # Removing Moves
            # remove other move for self
            trOldMoves = self.currentAssembly.getTRat(
                self.system, moveX, moveY)
            self.removeMoves(trOldMoves)

            # remove "v" TR moves from N neighbor
            vOldMoves = self.currentAssembly.getTRat(
                self.system, moveX, moveY + 1, "v")
            self.removeMoves(vOldMoves)

            # remove "h" TR moves from W Neighbor
            hOldMoves = self.currentAssembly.getTRat(
                self.system, moveX - 1, moveY, "h")
            self.removeMoves(hOldMoves)

            # remove attachment rules from neighbors
            nAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY + 1)
            self.removeMoves(nAtts)

            sAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY - 1)
            self.removeMoves(sAtts)

            wAtts = self.currentAssembly.getAttat(
                self.system, moveX - 1, moveY)
            self.removeMoves(wAtts)

            eAtts = self.currentAssembly.getAttat(
                self.system, moveX + 1, moveY)
            self.removeMoves(eAtts)

            # Update tile 2
            # If V rule
            if move["dir"] == "v":
                # Removing moves
                # remove TR from self
                vOldMoves = self.currentAssembly.getTRat(
                    self.system, moveX, moveY - 1)
                self.removeMoves(vOldMoves)

                # Remove "h" rules for SW
                swMoves = self.currentAssembly.getTRat(
                    self.system, moveX - 1, moveY - 1, "h")
                self.removeMoves(swMoves)

                # Remove attachments from neighbors
                s2Atts = self.currentAssembly.getAttat(
                    self.system, moveX, moveY - 2)
                self.removeMoves(s2Atts)

                swAtts = self.currentAssembly.getAttat(
                    self.system, moveX - 1, moveY - 1)
                self.removeMoves(swAtts)

                seAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY - 1)
                self.removeMoves(seAtts)

            # If H rule
            if move["dir"] == "h":
                # remove TR from self
                vOldMoves = self.currentAssembly.getTRat(
                    self.system, moveX + 1, moveY)
                self.removeMoves(vOldMoves)

                # Remove "v" rules for NE
                neMoves = self.currentAssembly.getTRat(
                    self.system, moveX + 1, moveY + 1, "v")
                self.removeMoves(neMoves)

                # remove attachments from neighbors
                e2Atts = self.currentAssembly.getAttat(
                    self.system, moveX + 2, moveY)
                self.removeMoves(e2Atts)

                neAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY + 1)
                self.removeMoves(neAtts)

                seAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY - 1)
                self.removeMoves(seAtts)

        # perform move
        if forwards:
            self.currentAssembly.performMove(move)
            self.moveList.append(move)
        else:
            self.currentAssembly = self.currentAssembly.undoMove(move)

        # add all moves that need to be added

        # If Attachment
        if move["type"] == "a":

            # Add Attachments for neighbors
            nAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY + 1)
            self.addMoves(nAtts)

            sAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY - 1)
            self.addMoves(sAtts)

            wAtts = self.currentAssembly.getAttat(
                self.system, moveX - 1, moveY)
            self.addMoves(wAtts)

            eAtts = self.currentAssembly.getAttat(
                self.system, moveX + 1, moveY)
            self.addMoves(eAtts)

            # Add attachments for the current x,y when going backwards
            if not forwards:
                backwardMoves = self.currentAssembly.getAttat(
                    self.system, moveX, moveY)
                self.addMoves(backwardMoves)

            # Add transitions for self
            newTR = self.currentAssembly.getTRat(self.system, moveX, moveY)
            self.addMoves(newTR)

            # Add "v" transitions for North Neighbor (New assembly)
            vTR = self.currentAssembly.getTRat(
                self.system, moveX, moveY + 1, "v")
            self.addMoves(vTR)

            # Add "h" transitions for W Neighbor (New Assembly)
            hTR = self.currentAssembly.getTRat(
                self.system, moveX - 1, moveY, "h")
            self.addMoves(hTR)

        elif move["type"] == "t":

            # Adding Moves
            # add new transitions rules for self
            newTR = self.currentAssembly.getTRat(self.system, moveX, moveY)
            self.addMoves(newTR)

            # Add "v" transitions for North Neighbor (New assembly)
            vTR = self.currentAssembly.getTRat(
                self.system, moveX, moveY + 1, "v")
            self.addMoves(vTR)

            # Add "h" transitions for W Neighbor (New Assembly)
            hTR = self.currentAssembly.getTRat(
                self.system, moveX - 1, moveY, "h")
            self.addMoves(hTR)

            # remove attachment rules from neighbors
            nAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY + 1)
            self.addMoves(nAtts)

            sAtts = self.currentAssembly.getAttat(
                self.system, moveX, moveY - 1)
            self.addMoves(sAtts)

            wAtts = self.currentAssembly.getAttat(
                self.system, moveX - 1, moveY)
            self.addMoves(wAtts)

            eAtts = self.currentAssembly.getAttat(
                self.system, moveX + 1, moveY)
            self.addMoves(eAtts)

            # Update tile 2
            # If V rule
            if move["dir"] == "v":

                # Adding Moves
                # Add TR to self
                vNewMoves = self.currentAssembly.getTRat(
                    self.system, moveX, moveY - 1)
                self.addMoves(vNewMoves)

                # Add "h" rules for SW
                swNewMoves = self.currentAssembly.getTRat(
                    self.system, moveX - 1, moveY - 1, "h")
                self.addMoves(swNewMoves)

                # Add attachments from neighbors
                s2Atts = self.currentAssembly.getAttat(
                    self.system, moveX, moveY - 2)
                self.addMoves(s2Atts)

                swAtts = self.currentAssembly.getAttat(
                    self.system, moveX - 1, moveY - 1)
                self.addMoves(swAtts)

                seAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY - 1)
                self.addMoves(seAtts)

            # If H rule
            if move["dir"] == "h":

                # add TR from self
                vNewMoves = self.currentAssembly.getTRat(
                    self.system, moveX + 1, moveY)
                self.addMoves(vNewMoves)

                # add "v" rules for ne
                neMoves = self.currentAssembly.getTRat(
                    self.system, moveX + 1, moveY + 1, "v")
                self.addMoves(neMoves)

                # add attachments from neighbors
                e2Atts = self.currentAssembly.getAttat(
                    self.system, moveX + 2, moveY)
                self.addMoves(e2Atts)

                neAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY + 1)
                self.addMoves(neAtts)

                seAtts = self.currentAssembly.getAttat(
                    self.system, moveX + 1, moveY - 1)
                self.addMoves(seAtts)

        return 0

    def removeMoves(self, oldMoves):
        if oldMoves == None:
            return

        if not isinstance(oldMoves, list):
            oldMoves = [oldMoves]

        for oMove in oldMoves:
            self.validMoves.remove(oMove)

    def addMoves(self, newMoves):
        if newMoves == None:
            return

        if not isinstance(newMoves, list):
            newMoves = [newMoves]

        for nMove in newMoves:
            self.validMoves.append(nMove)

    def timeTaken(self):
        if len(self.TimeTaken) > 0:
            return 1 / self.TimeTaken[self.currentIndex - 1]
        else:
            return 0

    def findProblemTile(self, tile1, tile2, dir, problem):
        errorTile = ""
        sys_h_tr = self.system.returnHorizontalTransitionDict()
        sys_v_tr = self.system.returnVerticalTransitionDict()

        if dir == "v":
            rules = sys_v_tr.get((tile1.get_label(), tile2.get_label()))
                  
        if dir == "h":
            rules = sys_h_tr.get((tile1.get_label(), tile2.get_label()))
            
        if rules != None:
            for i in range(0, len(rules), 2):
                if problem == 1:
                    if self.system.get_state(rules[i]) == None:
                        errorTile += rules[i]
                        errorTile += " "
                elif problem == 2:
                    if self.system.get_state(rules[i+1]) == None:
                        errorTile += rules[i + 1]
                        errorTile += " "

        return errorTile

back to top