Example #1
0
    def initialize_for_fourier(self, grid):
        # NOTE: This code is only temporary until we get the
        #       more versatile initial value specifications.

        # TODO: Make a specification of the IV setup in configurations
        Psi = []

        for packet_descr in self._parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)

            # Evaluate the
            X = grid.get_nodes(flat=True)
            values = packet.evaluate_at(X, prefactor=True)

            # Reshape values into hypercubic shape
            values = [ val.reshape(grid.get_number_nodes()) for val in values ]
            Psi.append( values )

        # TODO: Maybe sum up immediately instead of at the end to reduce memory usage
        Psi = reduce(lambda x,y: x+y, Psi)

        # Pack the values in a WaveFunction instance
        WF = WaveFunction(self._parameters)
        WF.set_grid(grid)
        WF.set_values(Psi)

        return WF
def load_wavepacket(self, timestep, blockid=0, key=("q","p","Q","P","S")):
    r"""Load a wavepacket at a given timestep and return a fully configured instance.
    This method just calls some other :py:class:`IOManager` methods in the correct
    order. It is included only for convenience and is not particularly efficient.

    :param timestep: The timestep :math:`n` at which we load the wavepacket.
    :param key: Specify which parameters to save. All are independent.
    :type key: Tuple of valid identifier strings that are ``q``, ``p``, ``Q``, ``P``, ``S`` and ``adQ``.
               Default is ``("q", "p", "Q", "P", "S")``.
    :param blockid: The ID of the data block to operate on.
    :return: A :py:class:`HagedornWavepacket` instance.
    """
    from BlockFactory import BlockFactory
    BF = BlockFactory()

    descr = self.load_wavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(descr)

    # Parameters and coefficients
    Pi = self.load_wavepacket_parameters(timestep=timestep, blockid=blockid, key=key)
    hashes, coeffs = self.load_wavepacket_coefficients(timestep=timestep, get_hashes=True, blockid=blockid)

    # Basis shapes
    Ks = []
    for ahash in hashes:
        K_descr = self.load_wavepacket_basisshapes(the_hash=ahash, blockid=blockid)
        Ks.append(BF.create_basis_shape(K_descr))

    # Configure the wavepacket
    HAWP.set_parameters(Pi, key=key)
    HAWP.set_basis_shapes(Ks)
    HAWP.set_coefficients(coeffs)

    return HAWP
Example #3
0
    def initialize_for_fourier(self, grid):
        # NOTE: This code is only temporary until we get the
        #       more versatile initial value specifications.

        # TODO: Make a specification of the IV setup in configurations
        Psi = []

        for packet_descr in self._parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)

            # Evaluate the
            X = grid.get_nodes(flat=True)
            values = packet.evaluate_at(X, prefactor=True)

            # Reshape values into hypercubic shape
            values = [val.reshape(grid.get_number_nodes()) for val in values]
            Psi.append(values)

        # TODO: Maybe sum up immediately instead of at the end to reduce memory usage
        Psi = reduce(lambda x, y: x + y, Psi)

        # Pack the values in a WaveFunction instance
        WF = WaveFunction(self._parameters)
        WF.set_grid(grid)
        WF.set_values(Psi)

        return WF
    def prepare_simulation(self):
        r"""Set up a Fourier propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Compute the position space grid points
        grid = BlockFactory().create_grid(self.parameters)

        # Construct initial values
        I = Initializer(self.parameters)
        initialvalues = I.initialize_for_fourier(grid)

        # Transform the initial values to the canonical basis
        BT = BasisTransformationWF(potential)
        BT.set_grid(grid)
        BT.transform_to_canonical(initialvalues)

        # Finally create and initialize the propagator instance
        self.propagator = FourierPropagator(potential, initialvalues, self.parameters)

        # Write some initial values to disk
        slots = self._tm.compute_number_saves()

        self.IOManager.add_grid(self.parameters, blockid="global")
        self.IOManager.add_fourieroperators(self.parameters)
        self.IOManager.add_wavefunction(self.parameters, timeslots=slots)

        self.IOManager.save_grid(grid.get_nodes(flat=True), blockid="global")
        self.IOManager.save_fourieroperators(self.propagator.get_operators())
        self.IOManager.save_wavefunction(initialvalues.get_values(), timestep=0)
Example #5
0
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise ValueError: For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instace
        # TODO: Attach the "leading_component to the hawp as codata
        self.propagator = HagedornPropagatorInhomogeneous(
            self.parameters, potential)

        # Create suitable wavepackets
        for packet_descr in self.parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_quadrature())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, ))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_saves()

        for i in xrange(npackets):
            bid = self.IOManager.create_block()
            self.IOManager.add_inhomogwavepacket(self.parameters,
                                                 timeslots=slots,
                                                 blockid=bid)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_inhomogwavepacket_description(
                packet.get_description())
            # Pi
            self.IOManager.save_inhomogwavepacket_parameters(
                packet.get_parameters(), timestep=0)
            # Basis shapes
            for shape in packet.get_basis_shape():
                self.IOManager.save_inhomogwavepacket_basisshapes(shape)
            # Coefficients
            self.IOManager.save_inhomogwavepacket_coefficients(
                packet.get_coefficients(),
                packet.get_basis_shape(),
                timestep=0)
Example #6
0
    def compute_parameters(self):
        """Compute some further parameters from the given ones.
        """
        # Perform the computation only if the basic values are available.
        # This is necessary to add flexibility and essentially read in *any*
        # parameter file with heavily incomplete value sets. (F.e. spawn configs)
        try:
            # The number of time steps we will perform.
            tm = TimeManager(self)
            self._params["nsteps"] = tm.compute_number_timesteps()
        except:
            pass

        if self._params.has_key("potential"):
            # Ugly hack. Should improve handling of potential libraries
            Potential = BlockFactory().create_potential(self)
            # Number of components of $\Psi$
            self._params["ncomponents"] = Potential.get_number_components()
    def compute_parameters(self):
        """Compute some further parameters from the given ones.
        """
        # Perform the computation only if the basic values are available.
        # This is necessary to add flexibility and essentially read in *any*
        # parameter file with heavily incomplete value sets. (F.e. spawn configs)
        try:
            # The number of time steps we will perform.
            tm = TimeManager(self)
            self._params["nsteps"] = tm.compute_number_timesteps()
        except:
            pass

        if self._params.has_key("potential"):
            # Ugly hack. Should improve handling of potential libraries
            Potential = BlockFactory().create_potential(self)
            # Number of components of $\Psi$
            self._params["ncomponents"] = Potential.get_number_components()
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise: :py:class:`ValueError` For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instance
        # TODO: Attach the "leading_component to the hawp as codata
        # TODO: Clean up this ugly if tree
        if self.parameters["propagator"] == "magnus_split":
            from MagnusPropagator import MagnusPropagator
            self.propagator = MagnusPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "semiclassical":
            from SemiclassicalPropagator import SemiclassicalPropagator
            self.propagator = SemiclassicalPropagator(self.parameters, potential)
        elif self.parameters["propagator"] == "hagedorn":
            from HagedornPropagator import HagedornPropagator
            self.propagator = HagedornPropagator(self.parameters, potential)
        else:
            raise NotImplementedError("Unknown propagator type: " + self.parameters["propagator"])

        # Create suitable wavepackets
        chi = self.parameters["leading_component"]

        for packet_descr in self.parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_innerproduct())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, chi))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_saves()
        key = ("q","p","Q","P","S","adQ")

        for i in xrange(npackets):
            bid = self.IOManager.create_block()
            self.IOManager.add_wavepacket(self.parameters, timeslots=slots, blockid=bid, key=key)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_wavepacket_description(packet.get_description())
            # Pi
            self.IOManager.save_wavepacket_parameters(packet.get_parameters(key=key), timestep=0, key=key)
            # Basis shapes
            for shape in packet.get_basis_shapes():
                self.IOManager.save_wavepacket_basisshapes(shape)
            # Coefficients
            self.IOManager.save_wavepacket_coefficients(packet.get_coefficients(), packet.get_basis_shapes(), timestep=0)
Example #9
0
    def __init__(self, n):
        self.blockFactory = BlockFactory()
        self.grid = []
        #add second, empty grid
        for i in range(n):
            self.grid.append([])
            for j in range(n):
                self.grid[i].append(None)
        #create grid filled with blocks
        for i in range(n):
            self.grid.append([])
            for j in range(n):
                cellPos = (j * (cellWidth + padding),
                           (n + i) * (cellHeight + padding))
                self.grid[n + i].append(self.blockFactory.build(cellPos))
        self.n = n
        self.center = (n * (cellWidth + padding) / 2, n *
                       (cellHeight + padding) + n * (cellHeight + padding) / 2)

        #cursor set to (0,0) on the grid
        self.cursor = Cursor(n, n, [n - 1, 0])
Example #10
0
def load_wavepacket_inhomogeneous(iom, timestep, blockid=0):
    r"""Utility function to load an inhomogeneous
    wavepacket from an :py:class:`IOManager` instance.

    :param iom: The :py:class:`IOManager` instance from which to load data.
    :param timestep: Load the data corresponding to the given `timestep`.
    :param blockid: The `datablock` from which to read the data.
                    Default is the block with `blockid=0`.

    Note: This function is a pure utility function and is not efficient. It is
          built for interactive use only and should not be use in scripts.
    """
    if not iom.has_inhomogwavepacket(blockid=blockid):
        print("There is no (inhomogeneous) wavepacket to load in block "+str(blockid))
        return

    BF = BlockFactory()

    wpd = iom.load_inhomogwavepacket_description(blockid=blockid)
    HAWP = BF.create_wavepacket(wpd)

    # Basis shapes
    BS_descr = iom.load_inhomogwavepacket_basisshapes(blockid=blockid)
    BS = {}
    for ahash, descr in BS_descr.iteritems():
        BS[ahash] = BF.create_basis_shape(descr)

    KEY = ("q","p","Q","P","S","adQ")
    # Retrieve simulation data
    params = iom.load_inhomogwavepacket_parameters(timestep=timestep, blockid=blockid, key=KEY)
    hashes, coeffs = iom.load_inhomogwavepacket_coefficients(timestep=timestep, get_hashes=True, blockid=blockid)
    # Configure the wavepacket
    HAWP.set_parameters(params, key=KEY)
    HAWP.set_basis_shapes([ BS[int(ha)] for ha in hashes ])
    HAWP.set_coefficients(coeffs)

    return HAWP
Example #11
0
    def prepare_simulation(self):
        r"""Set up a Fourier propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise ValueError: For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Compute the position space grid points
        grid = BlockFactory().create_grid(self.parameters)

        # Construct initial values
        I = Initializer(self.parameters)
        initialvalues = I.initialize_for_fourier(grid)

        # Transform the initial values to the canonical basis
        BT = BasisTransformationWF(potential)
        BT.set_grid(grid)
        BT.transform_to_canonical(initialvalues)

        # Finally create and initialize the propagator instace
        self.propagator = FourierPropagator(potential, initialvalues,
                                            self.parameters)

        # Write some initial values to disk
        slots = self._tm.compute_number_saves()

        self.IOManager.add_grid(self.parameters, blockid="global")
        self.IOManager.add_fourieroperators(self.parameters)
        self.IOManager.add_wavefunction(self.parameters, timeslots=slots)

        self.IOManager.save_grid(grid.get_nodes(flat=False), blockid="global")
        self.IOManager.save_fourieroperators(self.propagator.get_operators())
        self.IOManager.save_wavefunction(initialvalues.get_values(),
                                         timestep=0)
Example #12
0
    def __init__(self, parameters, potential, packets=[]):
        r"""Initialize a new :py:class:`HagedornPropagator` instance.

        :param parameters: A :py:class:`ParameterProvider` instance containing at least
                           the key ``dt`` for providing the timestep :math:`\tau`.
        :type parameters: A :py:class:`ParameterProvider` instance

        :param potential: The potential :math:`V(x)` the wavepacket :math:`\Psi` feels during the time propagation.
        :param packet: The initial homogeneous Hagedorn wavepacket :math:`\Psi` we propagate in time.
        :raises ValueError: If the number of components of :math:`\Psi` does not match
                            the number of energy levels :math:`\lambda_i` of the potential.
        """
        # The potential :math:`V(x)` the packet(s) feel.
        self._potential = potential

        # Number :math:`N` of components the wavepacket :math:`\Psi` has got.
        self._number_components = self._potential.get_number_components()
        self._dimension = self._potential.get_dimension()

        # A list of Hagedorn wavepackets :math:`\Psi` together with some codata
        # like the leading component :math:`\chi` which is the index of the eigenvalue
        # :math:`\lambda_\chi` of the potential :math:`V` that is responsible for
        # propagating the Hagedorn parameters.
        # TODO: We assume a list of (packet, leading_component) tuples here. Generalize tuples to dicts!
        # TODO: Do not use a list but better use a hashtable by packet IDs?
        self._packets = packets[:]

        # Keep a reference to the parameter provider instance
        self._parameters = parameters

        self._dt = self._parameters["dt"]

        # The relative mass scaling matrix M
        if self._parameters.has_key("mass_scaling"):
            self._M = atleast_2d(self._parameters["mass_scaling"])
            assert self._M.shape == (self._dimension, self._dimension)
            self._Minv = inv(self._M)
        else:
            # No mass matrix given. Scale all masses equally
            self._M = eye(self._dimension)
            self._Minv = self._M

        # Decide about the matrix exponential algorithm to use
        self.__dict__["_matrix_exponential"] = BlockFactory().create_matrixexponential(parameters)

        # Precalculate the potential splittings needed
        self._prepare_potential()
Example #13
0
	def addBlock(self):
		## create block
		bf = BlockFactory(self.height, self.width, self.wall, self.ground)
		bf.createBlocK()

		## connect blocks
		lastBlockIndex = len(self.row) - 1
		endingYPoint = self.row[lastBlockIndex].getEndingYPoint()
		if endingYPoint != bf.getStartingYPoint():
			bf.connectBlockHorizontal(endingYPoint)

		## add block to row
		self.row.append(bf)
    def prepare_simulation(self):
        r"""Set up a Hagedorn propagator for the simulation loop. Set the
        potential and initial values according to the configuration.

        :raise ValueError: For invalid or missing input data.
        """
        # The potential instance
        potential = BlockFactory().create_potential(self.parameters)

        # Project the initial values to the canonical basis
        BT = BasisTransformationHAWP(potential)

        # Finally create and initialize the propagator instace
        # TODO: Attach the "leading_component to the hawp as codata
        self.propagator = HagedornPropagator(self.parameters, potential)

        # Create suitable wavepackets
        chi = self.parameters["leading_component"]

        for packet_descr in self.parameters["initvals"]:
            packet = BlockFactory().create_wavepacket(packet_descr)
            # Transform to canonical basis
            BT.set_matrix_builder(packet.get_quadrature())
            BT.transform_to_canonical(packet)
            # And hand over
            self.propagator.add_wavepacket((packet, chi))

        # Add storage for each packet
        npackets = len(self.parameters["initvals"])
        slots = self._tm.compute_number_saves()

        for i in xrange(npackets):
            bid = self.IOManager.create_block()
            self.IOManager.add_wavepacket(self.parameters, timeslots=slots, blockid=bid)

        # Write some initial values to disk
        for packet in self.propagator.get_wavepackets():
            self.IOManager.save_wavepacket_description(packet.get_description())
            # Pi
            self.IOManager.save_wavepacket_parameters(packet.get_parameters(), timestep=0)
            # Basis shapes
            for shape in packet.get_basis_shape():
                self.IOManager.save_wavepacket_basisshapes(shape)
            # Coefficients
            self.IOManager.save_wavepacket_coefficients(packet.get_coefficients(), packet.get_basis_shape(), timestep=0)
Example #15
0
moveLeft = [-blockSize,0]

# The boardColor will be a solid black (for now)
boardColor = (0,0,0,255)

# Score is set to 0 at the beginning of the game
score = 0

# Set the size of the display to the size we defined above, and store it as screen
screen = pygame.display.set_mode(size)
# The board will be the entire screen size (so no hold piece or scoreboard, for now)
board = pygame.Surface(screen.get_size(),pygame.SRCALPHA)
board.fill(boardColor)

# Initialize a blockfactory (a custom class in the BlockFactory.py file)
bFactory = BlockFactory(board, blockSize, boardColor)
# Generate a mino (a custom class in the BlockFactory.py file)
block, gameOver = bFactory.generateMino()

# We are ready to start the game!
# Here we grab the current time so that we can measure game time relative to it
# This returns time in milliseconds
dropTime = pygame.time.get_ticks()

# Cannonical game loop, run forever until game over
while 1:

    # Get current time
    currTime = pygame.time.get_ticks()

    # More than the drop speed time has passed since the last drop
Example #16
0
from BaseBlock import BaseBlock
from BlockArmor import BlockArmor

from GridCell import GridCellContainer

from BlockFactory import BlockFactory

import pygame

pygame.init()
screen = pygame.display.set_mode((1280, 720))
center = (1280 / 2 - 25, 720 / 2 - 25)
done = False
clock = pygame.time.Clock()

factory = BlockFactory()

cell = factory.build(center)
offset = (20, 20)

scale = 1
while not done:
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.QUIT:
            done = True
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_DELETE:
                cell = factory.build(center)
    scale -= .025
    if scale <= 0:
Example #17
0
	def appendBlock(self):
		bf = BlockFactory(self.height, self.width, self.wall, self.ground)
		bf.createBlocK()
		self.row.append(bf)
Example #18
0
class ColorGrid:

    # None = empty cell
    # 1 - 4 = color cell

    # (n) by (n) grid
    def __init__(self, n):
        self.blockFactory = BlockFactory()
        self.grid = []
        #add second, empty grid
        for i in range(n):
            self.grid.append([])
            for j in range(n):
                self.grid[i].append(None)
        #create grid filled with blocks
        for i in range(n):
            self.grid.append([])
            for j in range(n):
                cellPos = (j * (cellWidth + padding),
                           (n + i) * (cellHeight + padding))
                self.grid[n + i].append(self.blockFactory.build(cellPos))
        self.n = n
        self.center = (n * (cellWidth + padding) / 2, n *
                       (cellHeight + padding) + n * (cellHeight + padding) / 2)

        #cursor set to (0,0) on the grid
        self.cursor = Cursor(n, n, [n - 1, 0])

    def rotateClock(self):
        for i in range(3):
            self.rotateCounter()

    def rotateCounter(self):
        N = self.n
        for x in range(0, int(N / 2)):
            for y in range(x, N - x - 1):
                top = ((x + N), y)
                right = ((y + N), (N - 1 - x))
                bottom = ((N - 1 - x + N), (N - 1 - y))
                left = ((N - 1 - y + N), (x))
                self.grid[top[0]][top[1]].rotate()
                self.grid[right[0]][right[1]].rotate()
                self.grid[bottom[0]][bottom[1]].rotate()
                self.grid[left[0]][left[1]].rotate()
                self.swapCells(top, left)
                self.swapCells(top, bottom)
                self.swapCells(top, right)
        self.rotateCursor(True)

    #coord - (row,col)
    def getCell(self, coord):
        return self.grid[coord[0]][coord[1]]

    #A / B = (row,col) of desired cells
    def swapCells(self, A, B):
        temp = self.grid[A[0]][A[1]]
        self.grid[A[0]][A[1]] = self.grid[B[0]][B[1]]
        self.grid[B[0]][B[1]] = temp
        cellA = self.grid[A[0]][A[1]]
        cellB = self.grid[B[0]][B[1]]
        if cellA != None:
            cellA.updatePosition(
                (A[1] * (cellWidth + padding), A[0] * (cellHeight + padding)))
        if cellB != None:
            cellB.updatePosition(
                (B[1] * (cellWidth + padding), B[0] * (cellHeight + padding)))

    def moveCursor(self, key):
        self.cursor.move(key)

    def rotateCursor(self, counter=True):
        currPos = self.cursor.getPosition()
        n = self.n
        xMin = min(currPos[1], n - currPos[1] - 1)
        yMin = min(currPos[0], n - currPos[0] - 1)
        layer = min(xMin, yMin)
        #top
        if currPos[0] - layer == 0:
            if counter == False:
                #top - > right
                self.cursor.setPosition((currPos[1], self.n - 1 - layer))
            else:
                #top -> left
                self.cursor.setPosition((self.n - currPos[1] - 1, layer))
        #bottom
        elif currPos[0] + layer == self.n - 1:
            if counter == False:
                #bottom -> left
                self.cursor.setPosition((currPos[1], layer))
            else:
                #bottom -> right
                self.cursor.setPosition(
                    (self.n - currPos[1] - 1, self.n - 1 - layer))
        else:
            #left
            if currPos[1] - layer == 0:
                if counter == False:
                    #left -> top
                    self.cursor.setPosition((layer, self.n - currPos[0] - 1))
                else:
                    #left -> bottom
                    self.cursor.setPosition((self.n - 1 - layer, currPos[0]))
            #right
            else:
                if counter == False:
                    #right -> bottom
                    self.cursor.setPosition(
                        (self.n - 1 - layer, self.n - currPos[0] - 1))
                else:
                    #right -> top
                    self.cursor.setPosition((layer, currPos[0]))

    def deleteBlock(self):
        self.grid[self.cursor.position[0] +
                  self.n][self.cursor.position[1]] = None

    def getVerticalMatches(self, minMatchLength):
        matchSet = set([])
        #check bottom -> top
        for col in range(self.n):
            matchStack = []
            #only check the bottom grid (n/2)
            for row in range(self.n - 1, self.n * 2, 1):
                cell = self.grid[row][col]
                if cell != None:
                    if len(matchStack) == 0 or cell.getColor() == self.grid[
                            matchStack[0][0]][matchStack[0][1]].getColor():
                        matchStack.append((row, col))
                    else:
                        if len(matchStack) >= minMatchLength:
                            for cell in matchStack:
                                matchSet.add(cell)
                        matchStack = [(row, col)]
                else:
                    if len(matchStack) >= minMatchLength:
                        for cell in matchStack:
                            matchSet.add(cell)
                    matchStack = []
            if len(matchStack) >= minMatchLength:
                for cell in matchStack:
                    matchSet.add(cell)
        return matchSet

    def getHorizontalMatches(self, minMatchLength):
        matchSet = set([])
        #check L -> R
        #only check the bottom grid (n/2)
        for rowIndex in range(self.n - 1, self.n * 2, 1):
            rowList = self.grid[rowIndex]
            matchStack = []
            for cellIndex, cell in enumerate(rowList):
                if cell != None:
                    if len(matchStack) == 0 or cell.getColor() == self.grid[
                            matchStack[0][0]][matchStack[0][1]].getColor():
                        matchStack.append((rowIndex, cellIndex))
                    else:
                        if len(matchStack) >= minMatchLength:
                            for cell in matchStack:
                                matchSet.add(cell)
                        matchStack = [(rowIndex, cellIndex)]
                else:
                    if len(matchStack) >= minMatchLength:
                        for cell in matchStack:
                            matchSet.add(cell)
                    matchStack = []
            if len(matchStack) >= minMatchLength:
                for cell in matchStack:
                    matchSet.add(cell)
        return matchSet

    def getMatches(self, minMatchLength):
        return self.getHorizontalMatches(minMatchLength).union(
            self.getVerticalMatches(minMatchLength))

    def removeMatches(self, matchList):
        for matchCell in matchList:
            self.grid[matchCell[0]][matchCell[1]] = None

    def moveCellsDown(self):
        colList = []
        for col in range(self.n):
            emptyStack = []
            toMove = []
            for row in range(self.n * 2 - 1, -1, -1):
                if self.grid[row][col] == None:
                    emptyStack.append((row, col))
                else:
                    if self.grid[row][col].getIsMoveable(
                    ) and len(emptyStack) > 0:
                        toMove.append(((row, col), emptyStack.pop(0)))
                        emptyStack.append((row, col))
                    if self.grid[row][col].getIsMoveable() == False:
                        emptyStack = []
            if len(toMove) > 0:
                colList.append(toMove)
        if len(colList) > 0:
            return colList
        return None

    def getEmptyCells(self):
        emptyCellList = []
        for col in range(self.n):
            for row in range(self.n, self.n * 2, 1):
                if self.grid[row][col] == None:
                    emptyCellList.append((row, col))
                else:
                    break
        return emptyCellList

    def spawnNewCells(self, emptyCells):
        for cell in emptyCells:
            self.grid[cell[0] - self.n][cell[1]] = self.blockFactory.build(
                (cell[1] * (cellWidth + padding),
                 (cell[0] - self.n) * (cellHeight + padding)))

    def scaleCells(self, cellList, factor):
        for index in cellList:
            self.grid[index[0]][index[1]].scale(factor)

    def rotateCells(self, cellList, factor):
        for index in cellList:
            self.grid[index[0]][index[1]].rotateInPlace(factor)

    def rotateGrid(self, factor):
        #t = (self.center[0] - 30, self.n*60 + int(self.n/2)*60)
        t = (self.center[0] - 5, self.center[1] - 5)
        for row in self.grid:
            for cell in row:
                if cell != None:
                    #cell.rotateInPlace(-factor)
                    cell.rotateAroundPoint(t, factor)
        self.cursor.poly.rotateAroundPoint(t, factor)

    def draw(self, screen, position, drawCursor=True):
        for row in range(self.n * 2):
            for col in range(self.n):
                if self.grid[row][col] != None:
                    self.grid[row][col].draw(screen, position)
        cursorX = self.cursor.position[1] * (cellWidth + padding) - (
            abs(cellWidth - self.cursor.width)) / 2
        cursorY = (self.cursor.position[0] + self.n) * (
            cellHeight + padding) - (abs(cellHeight - self.cursor.height)) / 2
        #print(position)
        if drawCursor:
            #self.cursor.draw(screen, (position[0] + cursorX, position[1] + cursorY))
            self.cursor.draw(screen, position)

    def __str__(self):
        outStr = ""
        for i in range(len(self.grid)):
            for cell in self.grid[i]:
                cellStr = str(cell)
                if cell == None:
                    cellStr = "0"
                for j in range(len(cellStr), 3):
                    cellStr += " "
                outStr += cellStr + " "
            outStr += "\n"
        return outStr