TEST(HeuristicTest, PrioritizeWinningSooner)
{
	auto gb = GameBoard{"   /OX /OX "};
	// {0,0} prevents a loss that would happen in 1 move
	// {1,0} immediately wins
	EXPECT_GT(gb.getHeuristic(Move{1,0}), gb.getHeuristic(Move{0,0}));
}
TEST(HeuristicTest, BetterMinimizingMovesHaveSmallerValues)
{
	auto gb = GameBoard{"   / X /   "};
	// Because this is the second move, it's OPlayer's turn, and
	// X is the maximizing player. Hence the better move has the
	// smaller value.
	EXPECT_LT(gb.getHeuristic(Move{0,0}), gb.getHeuristic(Move{1,0}));
}
//see https://en.wikipedia.org/wiki/A*_search_algorithm
std::vector<sf::Vector2f> Pathfinder::findPath(const sf::Vector2i& startPos, const sf::Vector2i& goalPos){
	if (startPos.x >= 0 && startPos.y >= 0 && goalPos.x >= 0 && goalPos.y >= 0 &&
		startPos.x < m_mapWidth && startPos.y < m_mapHeight && goalPos.x < m_mapWidth && goalPos.y < m_mapHeight) {
		int size = m_mapWidth * m_mapHeight;

		int startIndex = startPos.x + (startPos.y * m_mapWidth);
		int goalIndex = goalPos.x + (goalPos.y * m_mapWidth);
		Node* start = m_nodes[startIndex];
		Node* goal = m_nodes[goalIndex];
		if (start != 0 && goal != 0){
			//reset nodes
			for (int i = 0; i < size; i++){
				if (m_nodes[i] != 0)
					m_nodes[i]->reset();
			}
			//pair is fcost,index. storing node pointers causes invalid heap
			priority_queue<std::pair<int, int>, vector<std::pair<int, int>>, NodeSearchCostComparer> openset;
			start->setGCost(0);
			start->setFCost(getHeuristic(start, goal));
			start->setOpen(true);
			openset.push(std::pair<int,int>(start->fCost(), startIndex));


			while (openset.size() != 0) {
				Node* current = m_nodes[openset.top().second];
				openset.pop();
				if (current == goal)
					return createPath(startIndex, goalIndex);
				current->setOpen(false); //remove from open
				current->setClose(true); //set as closed
				//	if (neighbour != current->getPrevious()){
				for (int i = 0; i < 8; i++) { //loop through neighbours
					int neighbourIndex = getNeighbourIndex(current, i);
					Node* neighbour = (neighbourIndex == -1) ? 0 : m_nodes[neighbourIndex];
					if (neighbour == 0 || neighbour->close() ||
						neighbour == current->getPrevious() ||
						neighbour->walkable() == false)
						continue;
					int tenativeGCost = current->gCost() + getCost(m_neighbourOffsets[i]);
					if (tenativeGCost <= neighbour->gCost()){
						neighbour->setPrevious(current);
						neighbour->setGCost(tenativeGCost);
						neighbour->setFCost(neighbour->gCost() + getHeuristic(neighbour, goal));
					}

					if (neighbour->open() == false){ //not explored
						neighbour->setOpen(true);
						neighbour->setColour(sf::Color(0, 128, 128, 255));
						openset.push(std::pair<int,int>(neighbour->fCost(), neighbourIndex));
					}
				}
			}
			if (openset.size() == 0)
				cout << "Couldn't find path." << endl;
		}
	}
	return std::vector<sf::Vector2f>();
}
TEST(HeuristicTest, BestFirstMoveXInCenter)
{
	auto gb = GameBoard{"   /   /   "};
	EXPECT_LT(gb.getHeuristic(Move{0,0}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{1,0}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{2,0}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{0,1}), gb.getHeuristic(Move{1,1}));
	EXPECT_EQ(gb.getHeuristic(Move{1,1}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{2,1}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{0,2}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{1,2}), gb.getHeuristic(Move{1,1}));
	EXPECT_LT(gb.getHeuristic(Move{2,2}), gb.getHeuristic(Move{1,1}));
}
int main(int argc, char **argv){
	start = (State *) malloc(sizeof(State));
	start->from = -1;
	goal = (State *) malloc(sizeof(State));
	char defaultMaze[] = "maze1.txt";
	char * mazeFilename = read_string(argc, argv, "-m", defaultMaze);
	loadMaze(mazeFilename);
	
	AS_Node * startNode = newASNode(getHeuristic(start), 0);
	startNode->state = start;
	AS_Config config;
	AS_initConfig(&config);
	config.areSameStates = &areSameState;
	config.isGoalState = &isGoalState;
	config.expandNode = &expandNode;
	config.queueInitialCapacity = 20000;
	config.closedSetChunkSize = 20000;
	config.startNode = startNode;
	
	AS_NodePointer * path = AS_search(&config);
	
	printMaze();
	
	if(path){
		printf("Solution found.\n");
		assignPathToMaze(path);
		printMaze();
		AS_freePath(path);
	}else{
		printf("Solution not found\n.");
	}
	return 0;
}
void createNode(AS_Node * node, State * oldState, int swapIndexA, int swapIndexB){
	State * state = (State *) malloc(sizeof(int)*dimension*dimension);
	memcpy(state, oldState, sizeof(int)*dimension*dimension);
	state[swapIndexA] = oldState[swapIndexB];
	state[swapIndexB] = oldState[swapIndexA];
	ASNode_init(node, getHeuristic(state));
	node->state = state;
}
TEST(HeuristicTest, SymmetricBoardStatesAreEquivalent)
{
	auto gb = GameBoard{"   / X /   "};
	// Corners
	EXPECT_EQ(gb.getHeuristic(Move{0,0}), gb.getHeuristic(Move{2,2}));
	EXPECT_EQ(gb.getHeuristic(Move{2,0}), gb.getHeuristic(Move{2,2}));
	EXPECT_EQ(gb.getHeuristic(Move{0,2}), gb.getHeuristic(Move{2,2}));
	// Sides
	EXPECT_EQ(gb.getHeuristic(Move{1,2}), gb.getHeuristic(Move{1,0}));
	EXPECT_EQ(gb.getHeuristic(Move{2,1}), gb.getHeuristic(Move{1,0}));
	EXPECT_EQ(gb.getHeuristic(Move{0,1}), gb.getHeuristic(Move{1,0}));
}
TEST(HeuristicTest, MoveRankingTransitivity)
{
	auto gb = GameBoard{"   /OX /   "};
	// Best moves are {0,0}, {2,0}, {1,0}, and {2,1} in that order
	EXPECT_LT(gb.getHeuristic(Move{2,0}), gb.getHeuristic(Move{0,0}));
	EXPECT_LT(gb.getHeuristic(Move{1,0}), gb.getHeuristic(Move{2,0}));
	EXPECT_LT(gb.getHeuristic(Move{2,1}), gb.getHeuristic(Move{1,0}));
	// Check transitivity
	EXPECT_LT(gb.getHeuristic(Move{1,0}), gb.getHeuristic(Move{0,0}));
	EXPECT_LT(gb.getHeuristic(Move{2,1}), gb.getHeuristic(Move{0,0}));
	EXPECT_LT(gb.getHeuristic(Move{2,1}), gb.getHeuristic(Move{2,0}));
}
Exemple #9
0
int heuristicForState(GameState* gs, int player, int other) {
	if (isDraw(gs))
		return 0;

	int term_stat = getWinner(gs);
	if (term_stat == player)
		return 1000;

	if (term_stat)
		return -1000;

	
	return getHeuristic(gs, player, other);
		     
}
int main(int argc, char **argv){
	dimension = read_int(argc, argv, "-d", 3);
	unsigned int seed = read_int(argc, argv, "-r", time(NULL) );
	srand (seed);
	start = (State *) malloc(sizeof(State) * dimension * dimension);
	
	do{
		int l = dimension * dimension;
		for(int i = 0; i<l; i++){
			start[i] = 0;
		}
		
		//Create a random state
		for(int i = 1; i<l; i++){
			int index = rand() % l;
			while(start[index] != 0) index = rand() % l;
			start[index] = i;
		}
	}while(!stateHasSolution(start));
	
	
	AS_Node * startNode = newASNode(getHeuristic(start));
	startNode->state = start;
	
	AS_Config config;
	AS_initConfig(&config);
	config.areSameStates = &areSameState;
	config.isGoalState = &isGoalState;
	config.expandNode = &expandNode;
	config.queueInitialCapacity = 20000;
	config.closedSetChunkSize = 20000;
	config.startNode = startNode;
	
	printf("Initial state:\n");
	printState(start);
	
	AS_NodePointer * path = AS_search(&config);
	
	if(path){
		printf("Solution found.\n");
		//printPath(path);
		AS_freePath(path);
	}else{
		printf("Solution not found\n.");
	}
	return 0;
	
}
void createNode(AS_Node * node, AS_Node * oldNode, int x, int y){
	State * state = (State *) malloc(sizeof(State));
	State * oldState = (State *) oldNode->state;
	state->x = x;
	state->y = y;
	state->from = oldState->y*maze.sizeX + oldState->x;
	state->points = (bool *) malloc(sizeof(bool) * maze.pointsLength);
	memcpy(state->points, oldState->points, sizeof(bool) * maze.pointsLength);
	if(MAZE_AT(x,y) == M_POINT){
		int pointIndex = y*maze.sizeX + x;
		for(int i = 0; i<maze.pointsLength; i++){
			if(maze.points[i] == pointIndex){
				state->points[i] = true;
				break;
			}
		}
	}
	ASNode_init(node, getHeuristic(state), oldNode->cost + 1);
	node->state = state;
}
Exemple #12
0
minmax_t Minimax::minimax(Node * node, unsigned int depth, bool isMax) {
    if (depth == 0 || node->isLeaf()) {
        node->setHeuristic(getHeuristic(node->getMove()));

        minmax_t ret;
        ret.node = node;
        ret.heuristc = node->getHeuristic();

        return ret;
    }

    if (isMax) {
        uint8_t best = INT8_MAX;

        for (int i = 0; i < node->getLength(); ++i) {
            minmax_t val = minimax(node->getNodeAt(i), depth - 1, true);
            best = std::min(best, val.heuristc);
        }

        minmax_t ret;
        ret.node = node;
        ret.heuristc = best;

        return ret;
    } else {
        uint8_t best = INT8_MIN;

        for (int i = 0; i < node->getLength(); ++i) {
            minmax_t val = minimax(node->getNodeAt(i), depth - 1, false);
            best = std::max(best, val.heuristc);
        }

        minmax_t ret;
        ret.node = node;
        ret.heuristc = best;

        return ret;
    }
}
Exemple #13
0
void Player::calculateStep(const Position& target)
{
	// Abbruchbedienung (Spieler-Position erreicht)
	if (current != 0 && current->position == position)
		return;

	// Nachbarn des aktuellen Knoten beziehen
	std::list<Node> neighbors = map.getNeighbors(current->position);

	for (std::list<Node>::iterator it = neighbors.begin(); it != neighbors.end(); ++it)
	{
		Node& neighbor = *it;

		// wenn Kosten größer 0 und noch nicht finalized
		if (neighbor.costs > 0 && finalized.find(neighbor.position) == finalized.end())
		{
			// in todo Liste nach Nachbarn suchen
			nodeMap::iterator todoIt = todo.find(neighbor.position);

			// wenn noch nicht in todo Liste oder aktuelle Kosten kleiner als Kosten in todo Liste
			if (todoIt == todo.end() || todoIt->second.costs > neighbor.costs + current->costs)
			{
				// wenn in todo Liste, Nachbarn aus todo Liste löschen
				if (todoIt != todo.end())
					todo.erase(todoIt);

				// Nachfolger auf aktuellen Knoten setzen
				neighbor.next = current;

				// Kosten um bisherige Kosten erhöhen
				neighbor.costs += current->costs;

				// in todo Liste einfügen
				todo.insert(mapElement(neighbor.position, neighbor));
			}
		}
	}

	current = &(todo.begin()->second);
	int currentHeuristic = getHeuristic(current->position, position);

	// in todo Liste nach billigstem Knoten suchen
	for (nodeMap::iterator it = todo.begin(); it != todo.end(); ++it)
	{
		Node& node = it->second;

		// Heuristik berechnen
		int heuristic = getHeuristic(node.position, position);

		// Gesamtkosten vergleichen
		if (node.costs + heuristic < current->costs + currentHeuristic)
		{
			currentHeuristic = heuristic;
			current = &node;
		}
	}

	// Knoten in die finalized List einfügen
	finalized.insert(mapElement(current->position, *current));
	current = &finalized.at(current->position);

	// Knoten aus der todo Liste löschen
	todo.erase(current->position);

	calculateStep(target);
}
Exemple #14
0
	int GridMap::getHeuristic(int startNodeID, int targetNodeID) const
	{
		return getHeuristic(toNodeCoord(startNodeID), toNodeCoord(targetNodeID));
	}
Exemple #15
0
int HeuristicLimitedSearchMin(SearchBoard *board,Player player,int HMaxZero,const int Level, int Alpha, int Beta, int n, clock_t AcumulatedTime)
{
    bool FoundOnHash;
    int HashLevel;
    int HashHeuristic;
    clock_t Defasagem = AcumulatedTime - clock();
            //printf("\nfuncionou ate aqui min\n");
            if(n>nivelmax) nivelmax = n;
    int HMaxLimit = HMaxZero - Level;
    int HMinLimit = -HMaxZero - Level;
    numchamadas++;
    SearchList Greedy;
    PossibleMovesListMin(board, player,&Greedy);
    SearchList NextLevel = NewEmptyListMin();
    SearchMove M;// = DequeueList(&Greedy);
    SearchBoard NewBoard;
    enum piece backup;
    int LastHeuristic = getHeuristic(board,player);
    //if(IsEmpty(&Greedy))
    //{printf("\n<<<<<<<<<<<<<<<<<<<<<<BUGOU MUITO BIZARRO>>>>>>>>>>>>>>>>>>>>>\n");printBoard(board->board);}
    while((!IsEmpty(&Greedy))/*&&(-M.hmax-n>HMinLimit)*/)
    {
            if(clock()-Tzero>TimeMax)
            {
                printf("\nacabou o tempo: %d\n",clock()-Tzero);
                TimeInterruptionLastSearch = true;
                return -1000;
            }
        numjogadas++;
        M = DequeueList(&Greedy);
        NewBoard = *board;
        if(mkCmpMove(&NewBoard,player,M.Move)!=invalid)
        {
            FoundOnHash = false;
            /*if(getHashValue(&NewBoard, (Player)ChangePlayer(player), &HashHeuristic, &HashLevel))
            {
                if(HashLevel>1+Level-n)
                {
                    M.h_max = HashHeuristic;
                    FoundOnHash = true;
                    printf("\n%d", HashLevel);
                }
            }*/

            //if((n<=NIVEL_MAX)&&(-M.h_max-n>HMaxLimit||-M.h_max!=LastHeuristic))
            if(!FoundOnHash&&/*n<=NIVEL_MAX&&*/(n<Level||(M.h_max!=LastHeuristic)&&(n<Level+2)))
            {
                M.h_max = HeuristicLimitedSearchMax(&NewBoard,
                                                    (Player)ChangePlayer(player),
                                                    HMaxZero,
                                                    Level,
                                                    Alpha,
                                                    Beta,
                                                    n+1,
                                                    Defasagem+clock());
                if(TimeInterruptionLastSearch)
                    return -1000;
                //addToHash(&NewBoard, (Player)ChangePlayer(player), M.h_max, 1+Level-n);
                //if(n==Ntest) printf("\nmin%d:%d",n,M.h_max);
            }
            //else printf("\n%d\n",n);
            //undoMov(board->board,M.Move,backup);
            EnqueueList(&NextLevel,M);

            // AlphaBeta condition
            // Alpha is the value of the best alternative for MAX along the path to board
            // Beta is the value of the best alternative for MIN along the path to board
            if(M.h_max<Alpha)
            {
                EmptyList(&Greedy);
                EmptyList(&NextLevel);
                return M.h_max;
            }
            if(M.h_max<Beta)
                Beta = M.h_max;
        }

        //M = DequeueList(&Greedy);
    }
    move Mov1,Mov2;
    int resp1 = REAL_MAX, resp2 = REAL_MAX;
    OrdenateList(&Greedy);
    OrdenateList(&NextLevel);
    if(IsEmpty(&Greedy   )&&IsEmpty(&NextLevel))
    {
        //printf("BIZU!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",n);
        return REAL_MAX-n;
    }
    if(!IsEmpty(&Greedy   )) resp1 = DequeueList(&Greedy).h_max;
    if(!IsEmpty(&NextLevel)) resp2 = DequeueList(&NextLevel).h_max;
    //while(!IsEmpty(&Greedy)) printf("\n%d:%d",n,DequeueList(&Greedy).h_max);
    //while(!IsEmpty(&NextLevel)) printf("\n%d:%d",n,DequeueList(&NextLevel).h_max);
    //printf("\n%d:final: %d",n,(resp1<resp2?resp1:resp2));
    //if(resp1==resp2&&resp1==REAL_MAX&&IsEmpty(&Greedy   )&&IsEmpty(&NextLevel)) return REAL_MAX-10;//printf("MIN%dBUGOU BIZARRO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",n);
    EmptyList(&Greedy);
    EmptyList(&NextLevel);
    //if(n==Ntest) printf("\nMIN%d:%d",n,(resp1<resp2?resp1:resp2));
    return (resp1<resp2?resp1:resp2);
}
TEST(HeuristicTest, GreaterThan)
{
	auto gb = GameBoard{"   /OX /   "};
	// Best moves are {0,0}, {2,0}, {1,0}, and {2,1} in that order
	EXPECT_GT(gb.getHeuristic(Move{0,0}), gb.getHeuristic(Move{2,0}));
}
Exemple #17
0
int Player1::alphaBeta (int depth, int alpha, int beta, bool MAXplayer)
{
	int heuristic = 0;	bool go = false;
	int temp;			char winner = 'N'; // 'N' for NULL
	
	go = isSolved(winner); // get heur info 

    if (depth == 3 || go ) // or node is a terminal node
	{
		heuristic = getHeuristic(depth, winner, !(MAXplayer));
        return heuristic;// value of the terminal game tree node
	}
	
    if (MAXplayer)	//for each child of node
	{ 
		for (int p = 0; p < 19; p++){
			for (int q = 0; q < 19; q++)
			{
				if (pOneBoard[p][q] == 'E' && playerDetected(p,q))
				{
					pOneBoard[p][q] = 'P';	// make move on board
					temp = alphaBeta(depth+1, alpha, beta, !(MAXplayer)); 

					// total recursive calls
					boardsExamined++; 

// -- maxmax start-
					//if (temp > alpha) alpha = temp; //a = max()
					//pOneBoard[p][q] = 'E'; //unmake move on board
// -- maxmax end---

// -- ab code start-
					if (temp < alpha) alpha = temp; //alpha = max()
					if (beta <= alpha) { pOneBoard[p][q] = 'E';break; } 
					else  pOneBoard[p][q] = 'E';	
// -- ab cod end ---	
				
				}}}

		return alpha; // α
	}
    else	//MINplayer	//for each child of node
	{    
		for (int p = 0; p < 19; p++){
			for (int q = 0; q < 19; q++)
			{
				if (pOneBoard[p][q] == 'E' && playerDetected(p,q))
				{
					pOneBoard[p][q] = 'T'; // make move on board
					temp = alphaBeta(depth+1, alpha, beta, !(MAXplayer)); 

					// total recursive calls
					boardsExamined++;

// -- maxmax start-
					//if (temp > beta) beta = temp; // β := max()
					//pOneBoard[p][q] = 'E'; //unmake move on board
// -- maxmax end---

// -- ab code start- 
					if (temp > beta) beta = temp; // β := min()
					if (beta <= alpha) { pOneBoard[p][q] = 'E'; break; }
					else pOneBoard[p][q] = 'E';		
// -- ab cod end --- 	

				}}}

		return beta;	// β 
	}
}
Exemple #18
0
move IterativeMinMax(SearchBoard *board, Player player, int time_max)
{
    char name[6];
    int TIME_INCREASE = 1;

    SearchList Greedy = NewEmptyListMax();
    SearchList NextLevel;

    if(player==whites)
        PossibleMovesListMax(board, player, &NextLevel);
    else
        PossibleMovesListMin(board, player, &NextLevel);
    SearchMove M;
    SearchBoard NewBoard;
    enum piece backup;

    int HMaxLimit = getHeuristic(board, player);
    int Alpha = REAL_MIN;
    int Beta = REAL_MAX;
    int ContadorDoNumerodeJogadasPossiveis=0;

    Tzero = clock();
    clock_t SpentTime = 1;// para nao ter 0 no denominador
    TimeMax = time_max*CLOCKS_PER_SEC;
    TimeInterruptionLastSearch = false;
    clock_t AcumulatedTime;

    int auxiliar;
    for(int i=2; clock()-Tzero<time_max && !TimeInterruptionLastSearch /*&& i<=NIVEL_MIN*/; i++)
    {
        //NIVEL_MAX = i;
        Alpha = REAL_MIN;
        Beta = REAL_MAX;
        OrdenateList(&NextLevel);
        AtributeList(&Greedy,&NextLevel);
        EmptyList(&NextLevel);
        //M = DequeueList(&Greedy);
        ContadorDoNumerodeJogadasPossiveis = 0;
        while(!TimeInterruptionLastSearch&&clock()-Tzero<time_max&&!IsEmpty(&Greedy))
        {
            M = DequeueList(&Greedy);
            movToStr(M.Move,name);
            //printf("\n%s:",name);
            NewBoard = *board;
            if(mkCmpMove(&NewBoard,player,M.Move)!=invalid)
            {
                ContadorDoNumerodeJogadasPossiveis++;
                movToStr(M.Move,name);
                printf("\n%d : ",i);
                printf("%s: %d",name,M.h_max);
                AcumulatedTime = clock();
                if(player==whites)
                    auxiliar = HeuristicLimitedSearchMin(&NewBoard,(Player)ChangePlayer(player),HMaxLimit,i,Alpha,Beta,1,AcumulatedTime);
                else
                    auxiliar = HeuristicLimitedSearchMax(&NewBoard,(Player)ChangePlayer(player),HMaxLimit,i,Alpha,Beta,1,AcumulatedTime);
                if(!TimeInterruptionLastSearch)
                {
                    M.h_max = auxiliar;
                }
                printf(" : %d\n",M.h_max);
                //printf("%d",M.h_max);
                EnqueueList(&NextLevel,M);
            }
            else
            {
                //printf("\nfail");
            }
                //M = DequeueList(&Greedy);
        }
        if(ContadorDoNumerodeJogadasPossiveis==1)
            return DequeueList(&NextLevel).Move;
        //TIME_INCREASE = (time(NULL)-Tzero)/SpentTime;
        //SpentTime = time(NULL)-Tzero;
    }
    ConcatenateList(&Greedy,&NextLevel);
    OrdenateList(&Greedy);
    SearchMove resp, aux;
    resp = DequeueList(&Greedy);
    NewBoard = *board;
    mkCmpMove(&NewBoard,player,aux.Move);
    int contador;
    srand(time(NULL));
    SearchBoard AnotherNewBoard;
    while(!IsEmpty(&Greedy))
    {
        aux = DequeueList(&Greedy);
        AnotherNewBoard = *board;
        mkCmpMove(&AnotherNewBoard,player,aux.Move);
        if((player==whites&&aux.h_max>resp.h_max)||(player==blacks&&aux.h_max<resp.h_max))
        {
            resp = aux;
            NewBoard = *board;
            mkCmpMove(&NewBoard,player,aux.Move);
        }
        if(aux.h_max==resp.h_max)
        {
            AnotherNewBoard = *board;
            mkCmpMove(&AnotherNewBoard,player,aux.Move);
            if((player==whites&&get_heuristics(&AnotherNewBoard,player)>get_heuristics(&NewBoard,player))
               ||(player==blacks&&get_heuristics(&AnotherNewBoard,player)<get_heuristics(&NewBoard,player))
               ||rand()%50==0)
            {
                resp = aux;
                NewBoard = AnotherNewBoard;
            }
        }
    }
    return resp.Move;
}