package org.artisanlogiciel.games.maze;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;

/**
 * Model of labyrinth storing only paths not walls. wall are regenerated later
 * on based on adjacent paths. each position (x,y) stores what move can be done
 * from here to go to deeper path. a node is tagged OPEN and contained in
 * openList if all moves from its position have not been resolved a node is
 * tagged CLOSED when fully processed
 **/
public class LabyModel
        implements WallsProvider, MovesProvider {

    /**
     * WARNING don't change those values, they are used as it is for
     * optimisation
     */
    private final static short FLAGLENGTH = 7;
    private final static short CLEAR = 0; // mandatory 0 since array creation is
    // initialized with 0.
    public final static short HORIZONTAL = 1;
    public final static short VERTICAL = 2;
    public final static short DIRECTION = 4; // could we get rid of that to
    // free one bit for other purpose
    // ?
    // can be both POSITIVE and NEGATIVE, it means that you can move in positive direction and in negative direction.
    public final static short POSITIVE = 8;
    public final static short NEGATIVE = 16;

    // completed
    public final static short LEFT = Brick.LEFT << FLAGLENGTH | DIRECTION | HORIZONTAL | NEGATIVE;
    public final static short DOWN = Brick.DOWN << FLAGLENGTH | DIRECTION | VERTICAL | POSITIVE;
    public final static short RIGHT = Brick.RIGHT << FLAGLENGTH | DIRECTION | HORIZONTAL | POSITIVE;
    public final static short UP = Brick.UP << FLAGLENGTH | DIRECTION | VERTICAL | NEGATIVE;

    public final static short EMPTY = LEFT | DOWN | RIGHT | UP ;

    // flag when a wall should be open to access this for entry
    public final static short ENTRY = Brick.ENTRY << FLAGLENGTH;
    // flag when a wall should be open to access this for exit
    private final static short GOAL = Brick.GOAL << FLAGLENGTH;

    // can it be both OPEN and CLOSED ?
    private final static short OPEN = 32; // can be reused once generation is
    // completed
    private final static short CLOSED = 64; // can be reused once generation is
    // flag when solution is on this path.
    public final static short SOLVED = 64 << FLAGLENGTH;
    // free flag
    private final static short FREE = 128 << FLAGLENGTH;
    // remains 2 free bits ( keep one out for sign )

    // orders matters see getReverseDirection
    public static final short[] AllDirections = {LEFT, DOWN, RIGHT, UP};

    private int width;
    private int height;
    // wall flags Brick.(LEFT,DOWN,RIGHT,UP,ENTRY,GOAL) + status flags for each position (x,y)
    private short[][] t;
    private int depth = 0;
    /**
     * will enforce maxdepth as maximum length of path in depth first. It means
     * that after every maxdepth moves there should be crossing paths. I think
     * that to shorter maxdepth is the harder the labyrinth is ...
     **/
    private int maxdepth = 0;
    // longest path found
    private int deepest = 0;
    // each move is a linearwork step
    private int linearwork = 0;

    private Position deepestEnd = null;
    boolean maxreached = false;
    java.util.Random random;
    // list of positions not fully walked
    private final LinkedList<Position> openList = new LinkedList<Position>();
    // list of entries and exits.
    private final LinkedList<Position> entryExits = new LinkedList<Position>();
    private final Object coherentLock = new Object(); // before getting the lock
    // and after lock release
    // all is coherent ( ie
    // check() is ok ),

    MazeCreationListener listener = null;

    // do we create wall when only one way is possible ?
    private boolean onewaywall = false;

    private LabyModel(int width, int height, int maxdepth, Random random) {
        this.width = width;
        this.height = height;
        this.maxdepth = maxdepth;
        this.random = random;
        // CLEAR == 0 and array is initialized with 0s
        t = new short[width][height];
    }

    public LabyModel(MazeParams params) {
        this(params.getWidth(), params.getHeight(), params.getMaxDepth(), new java.util.Random(params.getSeed()));
    }

    /**
     * construct LabyModel from an InputStream, yet only "raw" is supported
     **/
    public LabyModel(int width, int height, short [][] t) {
        random = null;
        this.width = width;
        this.height = height;
        this.t = t;
    }

    public LabyModel(LabyModel other ) {
        random = null;
        this.width = other.width;
        this.height = other.height;
        this.t = other.t;
    }

    public void setMazeListener(MazeCreationListener listener) {
        this.listener = listener;
    }

    // FIXME FULLY BREAK RESOLVING...
    public void noWalls(int x, int y) {
        if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
            // t[x][y] |= DIRECTION | VERTICAL | POSITIVE | HORIZONTAL | NEGATIVE | LEFT | RIGHT | UP | DOWN;
            t[x][y] |= LEFT | RIGHT | UP | DOWN;
        }
    }

    /* set direction existing onee are lost */
    // FIXME FULLY BREAK RESOLVING...
    public void setDirection(int x, int y, short path) {
        if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
            t[x][y] = (short) (path | OPEN);
        }
    }

    /**
     * add a new direction, exiting ones are kept
     */
    // FIXME FULLY BREAK RESOLVING...
    public void addDirection(int x, int y, short path) {
        if ((x >= 0) && (x < width) && (y >= 0) && (y < height)) {
            t[x][y] |= (short) (path | OPEN);
        }
    }

    // entry and exit can be outside the model boundaries a one x or one y out.
    public boolean addEntryOrExit(int x, int y) {
        entryExits.add(new Position(x, y));
        if ((x > 0) && (x < width) && (y > 0) && (y < height)) {
            t[x][y] |= ENTRY;
        }
        return true;
    }

    public void debugOut() {
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                out4XY(x, y);
            }
            System.out.print('\n');
        }
    }

    public void out4XY(int x, int y) {
        int direction = t[x][y];
        if ((direction & OPEN) == OPEN) {
            System.out.print("?");
        } else if ((direction & CLOSED) == CLOSED) {
            System.out.print(".");
        } else {
            System.out.print(" ");
        }
        // don't display information about short.
        direction &= ~OPEN;
        direction &= ~GOAL;
        direction &= ~CLOSED;
        switch (direction) {
            case LEFT: // left
                System.out.print("<-");
                break;
            case DOWN: // down
                System.out.print("vv");
                break;
            case RIGHT: // right
                System.out.print("->");
                break;
            case UP: // up
                System.out.print("^^");
                break;
            case HORIZONTAL: // -
                System.out.print("--");
                break;
            case VERTICAL: // |
                System.out.print("||");
                break;
            case CLEAR:
                System.out.print("  ");
                break;
            case CLOSED:
                System.out.print("00");
                break;
            case RIGHT | LEFT:
                System.out.print("--");
                break;
            case UP | DOWN:
                System.out.print("||");
                break;
            case RIGHT | UP:
                System.out.print("^>");
                break;
            case RIGHT | DOWN:
                System.out.print("v>");
                break;
            case LEFT | DOWN:
                System.out.print("<v");
                break;
            case LEFT | UP:
                System.out.print("<^");
                break;
            case LEFT | RIGHT | UP:
                System.out.print("LL");
                break;
            case LEFT | RIGHT | DOWN:
                System.out.print("TT");
                break;
            case LEFT | DOWN | UP:
                System.out.print("<|");
                break;
            case RIGHT | DOWN | UP:
                System.out.print("|>");
                break;
            case LEFT | RIGHT | UP | DOWN:
                System.out.print("++");
                break;
            case OPEN:
                System.out.print("??");
                break;
            case GOAL:
                System.out.print("**");
                break;
            default:
                System.out.print("..");
        }

        if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) {
            System.out.print("-");
        } else {
            System.out.print("H");
        }

    }

    public String outHorizWall2XY(int x, int y) {
        String freeway = " ";
        if ((t[x][y] & SOLVED) == SOLVED) {
            freeway = "*";
        }
        if ((x < 0) || (x > width)) {
            return " H";
        }
        if ((y < 0) || (y >= height)) {
            return "HH";
        }

        if (((t[x][y] & DOWN) == DOWN) || ((y + 1 < height) && ((t[x][y + 1] & UP) == UP))) {
            return freeway + "H";
        } else {
            return "HH";
        }
    }

    public String out2XY(int x, int y) {
        // can check for entry exits.

        if ((y < 0) || (y >= height) || (x < 0) || (x > width)) {
            return " H";
        }
        String low = "";
        int direction = t[x][y];
        String freeway = " ";
        if ((t[x][y] & SOLVED) == SOLVED) {
            freeway = "*";
        }
        // don't display information about short.
        direction &= ~OPEN;
        direction &= ~GOAL;
        direction &= ~CLOSED;
        direction &= ~SOLVED;
        switch (direction) {
            case LEFT:
            case DOWN:
            case RIGHT:
            case UP:
                low = freeway;
                break;
            case HORIZONTAL: // -
                low = " ";
                break;
            case VERTICAL: // |
                low = " ";
                break;
            case CLEAR:
                low = " ";
                break;
            case CLOSED:
                low = "0";
                break;
            case RIGHT | LEFT:
            case UP | DOWN:
            case RIGHT | UP:
            case RIGHT | DOWN:
            case LEFT | DOWN:
            case LEFT | UP:
            case LEFT | RIGHT | UP:
            case LEFT | RIGHT | DOWN:
            case LEFT | DOWN | UP:
            case RIGHT | DOWN | UP:
            case LEFT | RIGHT | UP | DOWN:
                low = ".";
                break;
            case OPEN:
                low = "?";
                break;
            case GOAL:
                low = "*";
                break;
            default:
                low = ".";
        }

        for (Position exit : entryExits) {
            if ((exit.getX() == x + 1) && (exit.getY() == y)) {
                low = low + ">";
                return low;
            }
        }

        if (((t[x][y] & RIGHT) == RIGHT) || ((x + 1 < width) && ((t[x + 1][y] & LEFT) == LEFT))) {
            low = low + freeway;
        } else {
            low = low + "H";
        }

        return low;
    }

    public LabyMap toLabyMap() {
        synchronized (coherentLock) {
            Brick[][] brickMap = new Brick[width][height];
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    brickMap[x][y] = new Brick(out2XY(x, y), outHorizWall2XY(x, y), getWalls(x, y));
                }
            }
            return new LabyMap(brickMap, new LinkedList<Position>(entryExits));
        }
    }

    public int getWidth() {
        return this.width;
    }

    public int getHeight() {
        return this.height;
    }

    public LinkedList<Position> getOpenList() {
        return openList;
    }

    public void reset() {
        depth = 0;
        computeOpenList();
        resetResolving();
    }

    public void resetResolving() {
        // don't keep solved path
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                t[x][y] &= ~(SOLVED);
            }
        }

    }

    public void fullReset() {
        depth = 0;
        openList.clear();
        maxreached = false;
        deepest = 0;
        deepestEnd = null;
        // clear open
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // resetCell(x,y);
                t[x][y] &= ~(OPEN | SOLVED);
            }

        }
    }

    /**
     * check model coherency and fix if model needed a fix it means that
     * algorithm has a problem
     *
     * @return true if model is ok and no fix was applied.
     **/
    public boolean check() {
        boolean check = true;
        synchronized (coherentLock) {
            // node in OPEN should be tagged OPEN and not CLOSED.
            for (Position p : openList) {
                int x = p.getX();
                int y = p.getY();
                if (isFlagSet(t[x][y], OPEN)) {
                    check = false;
                    t[x][y] |= OPEN;
                }
                if (isFlagSet(t[x][y], CLOSED)) {
                    check = false;
                    t[x][y] &= ~CLOSED;
                }
            }
            // should do reverse : every node tagged OPEN should be in open
            // list.
            // a little more cpu consuming...
        }
        return check;
    }

    // coherency : if CLOSED then not OPEN
    public void computeOpenList() {
        openList.clear();
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                if (isFlagSet(t[x][y], CLOSED)) {
                    t[x][y] &= ~OPEN;
                }
                if (isFlagSet(t[x][y], OPEN) || (t[x][y] == CLEAR)) {
                    openList.add(new Position(x, y));
                }
            }
        }
    }

    /**
     * This is core of generator.
     * <p>
     * generate a maze with an entry set at (x,y) with a maxdepth
     **/
    public void generateWithEntry(int x, int y) {

        openList.add(new Position(x, y));
        while (!openList.isEmpty()) {
            // this is where magic happens
            step();

            synchronized (coherentLock) {
                linearwork++;

                if (linearwork % maxdepth == 0) {
                    coherentLock.notifyAll();
                    if (listener != null) {
                        listener.changed(null, null, this);
                    }
                    // should not continue in depth first...
                    if (!openList.isEmpty()) {
                        // insert head as next position to pick up.
                        Position current = openList.removeFirst();
                        openList.add(current);
                    }
                }
            }

        }

        if (deepestEnd != null) {
            t[deepestEnd.getX()][deepestEnd.getY()] |= GOAL;
        }
    }

    public int getDepth() {
        return depth;
    }

    public Position getDeepestEnd() {
        return deepestEnd;
    }

    public int getDeepestPath() {
        return deepest;
    }

    public boolean maxReached() {
        return maxreached;
    }

    /**
     * @param p Position
     * @return true if newly added , false if already open.
     **/
    private boolean open(boolean first, Position p) {
        int x = p.getX();
        int y = p.getY();
        if ((t[x][y] & OPEN) != OPEN) {
            t[x][y] |= OPEN;
            openList.addLast(p);
            return true;
        }
        return false;
    }

    public final static boolean isFlagSet(short check, short flag) {
        return ((check & flag) == flag);
    }

    public final short getCell(Position p) {
        return t[p.getX()][p.getY()];
    }

    public final void updateCell(Position p, short flags) {
        t[p.getX()][p.getY()] |= flags;
    }

    public final static short getReverseDirection(int index) {
        return AllDirections[(index + 2) % 4];
    }

    /**
     * return direction to use to be closer to 'to' from 'from'
     */
    public final static short getDirection(Position from, Position to) {
        short pointingdirection = DIRECTION;
        if (from.equals(to)) {
            return pointingdirection;
        }
        if (from.getX() < to.getX()) {
            pointingdirection |= RIGHT;
        } else if (from.getX() > to.getX()) {
            pointingdirection |= LEFT;
        }
        if (from.getY() < to.getY()) {
            pointingdirection |= DOWN;
        } else if (from.getY() > to.getY()) {
            pointingdirection |= UP;
        }
        return pointingdirection;
    }


    private final void closePosition(int x, int y) {
        t[x][y] &= ~OPEN;
        t[x][y] |= CLOSED;
    }

    /**
     * One step in Maze generation process
     * get last element in open list and explore one random free direction with it.
     *
     * @returns whether process closed current node.
     **/
    public boolean step() {
        boolean complete = false;
        Position current = null;

        synchronized (coherentLock) {

            if (!openList.isEmpty()) {
                // last : in depth before.
                current = openList.getLast();
            } else {
                return true;
            }

            if (current != null) {
                int x = current.getX();
                int y = current.getY();

                // should find all free positions...
                ArrayList<Short> freeDirection = new ArrayList<Short>();

                for (short direction : AllDirections) {
                    int delta = 0;
                    int newx = -1;
                    int newy = -1;

                    if ((direction & POSITIVE) == POSITIVE) {
                        delta = 1;
                    } else {
                        delta = -1;
                    }

                    if ((direction & HORIZONTAL) == HORIZONTAL) {
                        newx = x + delta;
                        newy = y;
                    } else {
                        newy = y + delta;
                        newx = x;
                    }

                    if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) {
                        // CLEAR is 0 : no tag at all.
                        if ((t[newx][newy]) == CLEAR) {
                            freeDirection.add(Short.valueOf(direction));
                        }
                    }
                }

                if (!freeDirection.isEmpty()) {
                    // control random using our own pseudorandom
                    short direction = 0;
                    // System.out.println("" + freeDirection.size() + "\n" );
                    if (freeDirection.size() > 1) {
                        direction = freeDirection.get(random.nextInt(freeDirection.size()));
                    } else {
                        direction = freeDirection.get(0);
                        Position last = openList.removeLast();
                        if (last != current) {
                            // GUARD, should not happen ( or multi-thread access
                            // to model error )
                            System.err.println("INTERNAL ERROR 3");
                            return false;
                        }
                    }

                    int delta = 0;
                    int newx = -1;
                    int newy = -1;

                    if ((direction & POSITIVE) == POSITIVE) {
                        delta = 1;
                    } else {
                        delta = -1;
                    }

                    if ((direction & HORIZONTAL) == HORIZONTAL) {
                        newx = x + delta;
                        newy = y;
                    } else {
                        newy = y + delta;
                        newx = x;
                    }

                    Position target = new Position(newx, newy, current.getDepth() + 1);
                    open(false, target);
                    if ((t[x][y] & DIRECTION) == DIRECTION) {
                        t[x][y] |= direction;
                    } else {
                        t[x][y] = direction;
                    } // not a 'direction' ... is it necessary to check ?
                    if (freeDirection.size() > 1) {
                        // keep it open at the very same place, previous open
                        // did add another node to inspect.
                        return false;
                    }
                    // else this was the unique direction , object already
                    // removed ( since if we try to remove it now it is not the
                    // last )

                    // can proceed to close
                } else {
                    // no free direction remaining => closing
                    Position last = openList.removeLast();
                    if (last != current) {
                        // GUARD, should not happen.
                        System.err.println("INTERNAL ERROR 3");
                        return false;
                    }
                }
                complete = true;
                closePosition(x, y);

                final int currentdepth = current.getDepth();
                if (currentdepth > deepest) {
                    deepest = currentdepth;
                    deepestEnd = current;
                }
            } // current != null;
        } // synchronized
        return complete;
    }

    public short getWalls(int x, int y) {
        short walls = 0;
        for (short direction : AllDirections) {
            if (hasWallInDirection(x, y, direction)) {
                walls |= (direction >> FLAGLENGTH);
            }
        }
        return walls;
    }

    public short getPath(int x, int y) {
        short path = 0;
        if ((x < width) && (y < height)) {
            path = t[x][y];
        }
        return path;
    }

    public short getMoves(int x, int y)
    {
        return getPath(x,y);
    }
    /**
     * is there a wall in that direction ?
     **/
    public boolean hasWallInDirection(int x, int y, short direction) {
        int newx = 0;
        int newy = 0;
        int delta = 0;
        // best way to find reversedirection ?
        int reversedirection = 0;

        // is this direction on the path ? yes => no wall
        if ( isFlagSet(t[x][y], direction)) {
            if (!onewaywall) {
                return false;
            }
            // onewaywall : should check reverse direction is ok too.
        }
        else
        {
            if (onewaywall)
            {
                return true;
            }
        }

        // is adjacent tile in direction pointing in reverse direction ? yes =>
        // no wall
        delta = ((direction & POSITIVE) == POSITIVE) ? 1 : -1;

        if ((direction & HORIZONTAL) == HORIZONTAL) {
            newx = x + delta;
            newy = y;
            reversedirection = (direction == RIGHT) ? LEFT : RIGHT;
        } else {
            newy = y + delta;
            newx = x;
            reversedirection = (direction == UP) ? DOWN : UP;
        }

        if ((newx >= 0) && (newy >= 0) && (newx < width) && (newy < height)) {
            return ! isFlagSet(t[newx][newy], (short) reversedirection);
        } else {
            // outside boundaries.
            // TODO CHECK exits.
            return true;
        }
    }

    public void setOnewaywall(boolean onewaywall) {
        this.onewaywall = onewaywall;
    }

    /**
     * is there no wall in that direction ?
     **/
    public boolean canMoveInDirection(int x, int y, short direction) {
        // one way wall will create walll if it not possible to step back to current position from next position
        return onewaywall ? ( ! hasWallInDirection(x,y, (short) (direction << FLAGLENGTH)) ) : ((getWalls(x, y) & direction ) == 0);
    }

    public boolean reverse(boolean grow)
    {
        return reversefast(grow);
    }

    public boolean reversefast(boolean grow)
    {
        // use current moves as walls ... and rebuild new direction from it.

        int deltasize = grow ? 1 : -1;
        int newwidth = getWidth() + deltasize;
        int newheight = getHeight() + deltasize;
        int delta = grow ? 1 : 0;

        if ( ( newwidth < 1) || (newheight < 1 ) )
        {
            System.err.println("Can't reverse, too small");
            return false;
        }
        short newt[][] = new short[newwidth][newheight];
        short[] rdmw = { Brick.RIGHT, Brick.DOWN };
        short[] lumw = { Brick.UP, Brick.LEFT };
        short[] ldmw = { Brick.LEFT, Brick.DOWN };
        short[] urmw = { Brick.UP, Brick.RIGHT };

        short[] allmoves = { LEFT,UP,RIGHT, DOWN };
        for (int x = 0;  x < getWidth() -1 ; x ++)
        {
            for (int y = 0;  y < getHeight() -1 ; y ++)
            {
                short walls = 0;
                short ulwalls = (short) (t[x][y] >> FLAGLENGTH);
                short rdwalls = (short) (t[x+1][y+1] >> FLAGLENGTH);
                short ldwalls = (short) (t[x][y+1] >> FLAGLENGTH);
                short ruwalls = (short) (t[x+1][y] >> FLAGLENGTH);

                // invert moves to walls
                for (int i = 0 ; i < 2; i ++)
                {
                    if ((ulwalls & rdmw[i] ) != 0) {
                        walls |= lumw[i];
                    }
                    if ((rdwalls & lumw[i] ) != 0 ) {
                        walls |= rdmw[i];
                    }
                    if ((ldwalls & urmw[i] ) != 0) {
                        walls |= ldmw[i];
                    }
                    if ((ruwalls & ldmw[i] ) != 0 ) {
                        walls |= urmw[i];
                    }
                }
                // convert walls to moves, walls are actualy where there is no path : XOR to invert.
                short moves = (short) ((walls ^ ( Brick.RIGHT | Brick.LEFT | Brick.UP | Brick.DOWN )) << FLAGLENGTH);
                short finalmoves = 0;
                if ( moves == 0 )
                {
                    finalmoves = 0;
                }
                else {
                    for (short m : allmoves) {
                        if ((moves & m) != 0) {
                            finalmoves |= m;
                        }
                    }
                }
                newt[x+delta][y+delta] = finalmoves;
            }
        }
        width = newwidth;
        height = newheight;
        t = newt;

        return true;
    }

    public boolean obfuscated_reverse(boolean grow)
    {
        // use current moves as walls ... and rebuild new direction from it.

        int deltasize = grow ? 1 : -1;
        int newwidth = getWidth() + deltasize;
        int newheight = getHeight() + deltasize;
        int delta = grow ? 1 : 0;

        if ( ( newwidth < 1) || (newheight < 1 ) )
        {
            System.err.println("Can't reverse, too small");
            return false;
        }
        short newt[][] = new short[newwidth][newheight];

        short[][] rdul = {
                { Brick.RIGHT, Brick.DOWN },
                { Brick.UP, Brick.LEFT }};
        short[] allmoves = { LEFT,UP,RIGHT, DOWN };

        for (int x = 0;  x < getWidth() -1 ; x ++)
        {
            for (int y = 0;  y < getHeight() -1 ; y ++)
            {
                short walls = 0;
                // invert moves to walls

                // OBFUSCATED UNDEBUGGABLE VERSION
                for (int i = 0 ; i < 2; i ++)
                {
                    for( int a=0 ; a < 2; a ++) {
                        for (int c = 0; c < 2; c ++) {
                            int d = a + c;
                            int f = a * c;
                            int b = d - 2 * f;
                            int g = c * i;
                            int e = c * b - g;
                            if (((short) (t[x + a][y + b] >> FLAGLENGTH) & rdul[d - f - g ][i + f - g]) != 0) {
                                walls |= rdul[1 - b +  e][i + e];
                            }
                        }
                    }
                }
                // convert walls to moves, walls are actualy where there is no path : XOR to invert.
                short moves = (short) ((walls ^ ( Brick.RIGHT | Brick.LEFT | Brick.UP | Brick.DOWN )) << FLAGLENGTH);
                short finalmoves = 0;
                if ( moves == 0 )
                {
                    finalmoves = 0;
                }
                else {
                    for (short m : allmoves) {
                        if ((moves & m) != 0) {
                            finalmoves |= m;
                        }
                    }
                }
                newt[x+delta][y+delta] = finalmoves;
            }
        }
        width = newwidth;
        height = newheight;
        t = newt;

        return true;
    }


}
