#include "a_star.h" #include "expansion_groups.h" #include #include #include #define SQRT2 1.414213562373095f #define LINES 321 #define COLS 221 #define MAX_RADIUS 5 // obstacle max radius in meters #define IN_GOAL_LINE 312 // target line when go_to_goal is 'true' (312 -> 15.2m) using std::chrono::high_resolution_clock; using std::chrono::duration_cast; using std::chrono::microseconds; /* Map dimensions: 32m*22m col 0 ... col 220 line 0 --- our goal --- line 1 | | line 2 | | line 3 |--------------| ... | | line 317 | | line 318 -- their goal -- line 319 line 320 [(H)ard wall: -3, (S)oft wall: -2, (E)mpty: 0, 0 < Cost < inf] */ // build board cost statically #define H27 -3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3,-3 #define S11 -2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 #define S19 S11,-2,-2,-2,-2,-2,-2,-2,-2 #define S97 S11,S11,S11,S11,S11,S11,S11,S11,-2,-2,-2,-2,-2,-2,-2,-2,-2 #define S98 S11,S11,S11,S11,S11,S11,S11,S11,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2 #define S221 S98,S98,S11,S11,-2,-2,-2 #define E19 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 #define E197 E19,E19,E19,E19,E19,E19,E19,E19,E19,E19,0,0,0,0,0,0,0 #define L0 S221 // Line 0: soft W #define L0_1 L0,L0 #define L2 S97,H27,S97 // Line 2: soft W, (goal post, back net, goal post), soft W #define L2_5 L2,L2,L2,L2 #define L6 S98,-3,-3,-3,S19,-3,-3,-3,S98 // Line 6: soft W, goal post, soft W, goal post, soft W #define L6_10 L6,L6,L6,L6,L6 #define L11 S98,-2,-3,-3,S19,-3,-3,-2,S98 // Line 11:soft W, empty field, goal post, soft W, goal post,empty field, soft W #define L12 S11,-2,E197,-2,S11 // Line 12:soft W, empty field, soft W #define L12x33 L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12,L12 #define LIN12_308 L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33,L12x33 #define L309 S98,-2,-3,-3,E19,-3,-3,-2,S98 // Line 309: soft W, empty field, goal post, empty field, goal post,empty field, soft W #define L310 S98,-3,-3,-3,E19,-3,-3,-3,S98 // Line 310: soft W, goal post, inside goal, goal post, soft W #define L310_314 L310,L310,L310,L310,L310 using std::min; using std::max; #define MIN min_node Node* min_node; // non-expanded node with lowest predicted total cost (f) namespace open{ Node* insert(Node* new_node, Node* root) { new_node->left = nullptr; new_node->right = nullptr; // Empty BST, return without saving min_node if(root == nullptr){ new_node->up = nullptr; MIN = new_node; // save min node for fast access return new_node; } // If new_node is the new min node if(new_node->f < MIN->f){ MIN->left = new_node; new_node->up = MIN; MIN = new_node; return root; } Node* node = root; float key = new_node->f; while(true){ if (key < node->f) if(node->left == nullptr){ node->left = new_node; break; }else{ node = node->left; } else{ if(node->right == nullptr){ node->right = new_node; break; }else{ node = node->right; } } } new_node->up = node; return root; } // Remove min node Node* pop(Node* root) { // Minimum node can have right child, but not left child if (MIN->right == nullptr){ if(MIN == root){ //------(A)------ min node is root and has no children return nullptr; // BST is empty } MIN->up->left = nullptr; //------(B)------ min node has no children but has parent MIN = MIN->up; }else{ if(MIN == root){ //------(C)------ min node is root and has right child MIN = MIN->right; root = MIN; root->up = nullptr; while(MIN->left != nullptr){ // update new min node MIN = MIN->left; } return root; // right child is now root } MIN->right->up = MIN->up; //------(D)------ min node has right child and parent MIN->up->left = MIN->right; MIN = MIN->right; while(MIN->left != nullptr){ // update new min node MIN = MIN->left; } } return root; } // Remove specific node Node* delete_node(Node* node, Node* root) { if(node == MIN){ // remove min node return pop(root); } if(node->left==nullptr and node->right==nullptr){ //------(A)------ node has no children (it can't be root, otherwise it would be min node) // Redirect incoming connection if(node->up->left == node){ node->up->left = nullptr; } else{ node->up->right = nullptr; } }else if(node->left==nullptr){ //------(B)------ node has right child (it can't be root, otherwise it would be min node) // Redirect incoming connections node->right->up = node->up; if(node->up->left == node){ node->up->left = node->right; } else{ node->up->right = node->right; } }else if(node->right==nullptr){ //------(C)------ node has left child if(node == root){ node->left->up = nullptr; return node->left; // left child becomes root } // Redirect incoming connections (if not root) node->left->up = node->up; if(node->up->left == node){ node->up->left = node->left; } else{ node->up->right = node->left; } }else{ //------(D)------ node has 2 children Node *successor = node->right; if(successor->left == nullptr){ //----- if successor is the node's right child (successor has no left child) //-------------- successor replaces node // Outgoing connections (successor's right child is not changed) successor->left = node->left; successor->up = node->up; // if node is root this is also ok // Incoming connections node->left->up = successor; if(node == root){ return successor; } // successor becomes root // Incoming connections (if not root) if(node->up->left == node){ node->up->left = successor; } else{ node->up->right = successor; } }else{ //------ if successor is deeper (successor has no left child, and is itself a left child) do{ successor = successor->left; }while(successor->left != nullptr); //-------------- Remove successor by redirecting its incoming connections if(successor->right==nullptr){ // no children successor->up->left = nullptr; }else{ successor->up->left = successor->right; successor->right->up = successor->up; } //-------------- successor replaces node // Outgoing connections successor->left = node->left; successor->right = node->right; successor->up = node->up; // if node is root this is also ok // Incoming connections node->left->up = successor; node->right->up = successor; if(node == root){ return successor; } // successor becomes root // Incoming connections (if not root) if(node->up->left == node){ node->up->left = successor; } else{ node->up->right = successor; } } } return root; } // Inorder Traversal // void inorder(Node* root, Node* board) { // if (root != nullptr) { // // Traverse left // inorder(root->left, board); // // Traverse root // std::cout << (root-board)/COLS << " " << (root-board)%COLS << " -> "; // // Traverse right // inorder(root->right, board); // } // return; // } } inline int x_to_line(float x){ return int(fmaxf(0.f, fminf(10*x+160, 320.f)) + 0.5f); } inline int y_to_col(float y){ return int(fmaxf(0.f, fminf(10*y+110, 220.f)) + 0.5f); } inline float diagonal_distance(bool go_to_goal, int line, int col, int end_l, int end_c){ // diagonal distance - adapted from http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html int dl, dc; if( go_to_goal ){ dl = abs(IN_GOAL_LINE - line); if (col>119) { dc = col-119; } else if (col<101) { dc = 101-col; } else { dc = 0; } }else{ dl = abs(line - end_l); dc = abs(col - end_c); } return (dl + dc) - 0.585786437626905f * min(dl,dc); } inline Node* expand_child(Node* open_root, float cost, float wall_index, Node* curr_node, Node* board, int pos, int state, bool go_to_goal, int line, int col, int end_l, int end_c, unsigned int* node_state, float extra ){ // child can be as inaccessible as current pos (but there is a cost penalty to avoid inaccessible paths) if(cost <= wall_index){ cost = 100.f; } // g (min cost from start to n) float g = curr_node->g + extra + std::fmaxf(0.f,cost); // current cost + child distance Node* child = &board[pos]; // if child is already in the open set if (state){ if (g >= child->g){ return open_root; // if not an improvement, we discard the new child }else{ open_root = open::delete_node(child, open_root); // if it is an improvement: remove reference, update it, add it again in correct order } }else{ node_state[pos] = 1; } // f (prediction of min total cost passing through n) float f = g + diagonal_distance(go_to_goal,line,col,end_l,end_c); child->g = g; child->f = f; child->parent = curr_node; return open::insert(child, open_root); } float final_path[2050]; int final_path_size; inline void build_final_path(Node* const best_node, const Node* board, float status, const bool override_end=false, const float end_x=0, const float end_y=0){ // Node* pt = best_node; // while( pt != nullptr ){ // int pos = pt - board; // board_cost[pos] = -4; // pt = pt->parent; // } // std::cout << "\n"; // for(int l=l_min; l<=l_max; l++){ // for(int c=c_min; c<=c_max; c++){ // //[(H)ard wall: -3, (S)oft wall: -2, (E)mpty: 0, 0 < Cost < inf] // //if (board[l][c].closed) std::cout << "o"; // if (board_cost[l*COLS+c] == -3) std::cout << "h"; // else if (board_cost[l*COLS+c] == -2) std::cout << "s"; // else if (board_cost[l*COLS+c] == -4) std::cout << "."; // else if (board_cost[l*COLS+c] == -1) std::cout << "g"; // else if (board_cost[l*COLS+c] == 0 and node_state[l*COLS+c]==2) std::cout << "o"; // else if (board_cost[l*COLS+c] == 0) std::cout << " "; // //ele ainda nao sabe ler hard walls // else std::cout << int(board_cost[l*COLS+c]+0.5f); // } // std::cout << "\n"; // } // Using 'current_node' would suffice if A* reaches the objective (but 'best_node' works with impossible paths or timeout) Node* ptr = best_node; int counter=0; do{ ptr = ptr->parent; counter++; }while( ptr != nullptr ); final_path_size = min(counter*2,2048); ptr = best_node; int i = final_path_size-1; // if enabled, replace end point with correct coordinates instead of discrete version if(override_end){ final_path[i--] = end_y; final_path[i--] = end_x; ptr = ptr->parent; } for(; i>0;){ final_path[i--] = ((ptr-board) % COLS)/10.f-11.f; // y final_path[i--] = ((ptr-board) / COLS)/10.f-16.f; // x ptr = ptr->parent; } // add status (& increment path size) final_path[final_path_size++] = status; // 0-success, 1-timeout, 2-impossible, 3-no obstacles(this one is not done in this function) // add cost (& increment path size) final_path[final_path_size++] = best_node->g / 10.f; // min. A* cost from start to best_node } /** * @brief Returns true if line segment 'ab' intersects either goal (considering the unreachable area) * - This function assumes that 'a' and 'b' are two points outside the unreachable goal area * - Therefore, 'ab' must enter and exit the goal unreachable area * - To detect this, we consider only the intersection of 'ab' and the goal outer borders (back+sides) * - The front should already be covered by independent goal posts checks */ inline bool does_intersect_any_goal(float a_x, float a_y, float b_x, float b_y){ float ab_x = b_x - a_x; float ab_y = b_y - a_y; float k; if(ab_x != 0){ // Check if 'ab' and goal back is noncollinear (collinear intersections are ignored) k = (15.75-a_x) / ab_x; // a_x + ab_x*k = 15.75 if (k >= 0 and k <= 1 and fabsf(a_y + ab_y * k) <= 1.25){ // collision (intersection_y = a_y + ab_y*k) return true; } k = (-15.75-a_x) / ab_x; // a_x + ab_x*k = -15.75 if (k >= 0 and k <= 1 and fabsf(a_y + ab_y * k) <= 1.25){ // collision (intersection_y = a_y + ab_y*k) return true; } } if(ab_y != 0){ // Check if 'ab' and goal sides are noncollinear (collinear intersections are ignored) k = (1.25-a_y) / ab_y; // a_y + ab_y*k = 1.25 if( k >= 0 and k <= 1){ float intersection_x_abs = fabsf(a_x + ab_x * k); if( intersection_x_abs >= 15 and intersection_x_abs <= 15.75 ){ // check one side for both goals at same time return true; } } k = (-1.25-a_y) / ab_y; // a_y + ab_y*k = -1.25 if( k >= 0 and k <= 1){ float intersection_x_abs = fabsf(a_x + ab_x * k); if( intersection_x_abs >= 15 and intersection_x_abs <= 15.75 ){ // check one side for both goals at same time return true; } } } return false; } /** * @brief Add space cushion near the midlines and endlines */ inline void add_space_cushion(float board_cost[]){ #define CUSHION_WIDTH 6 // opponent goal line for(int i=0; i 0)){ return true; } if (go_to_goal){ // This is a safe target. If it generates a collision with any goal post, we use A* instead. end_x = 15.2; end_y = max(-0.8f, min(start_y, 0.8f)); }else{ // Restrict end coordinates to map end_x = max(-16.f, min(end_x, 16.f)); end_y = max(-11.f, min(end_y, 11.f)); // Let A* handle it if the end position is unreachable or (is nearly out of bounds && out of bounds is not allowed && not near end) if(e_cost <= wall_index or (!is_near and e_cost > 0)){ return true; } } /** * Check if path intersects either goal (considering the unreachable area) * - at this point we know that 'start' and 'end' are reachable * - Therefore, the path must enter and exit the goal unreachable area for an intersection to exist * - To detect this, we consider only the intersection of the path and the goal outer borders (back+sides) * - The front is covered next by goal posts checks */ if (does_intersect_any_goal(start_x, start_y, end_x, end_y)) { return true; } /** * ----------------------- List all obstacles: given obstacles + goal posts * note that including the goal posts in the given obstacles is not a bad idea since the default * goal posts we add here provide no space cushion (which may be needed if the robot is not precise) * values explanation: * - goal post location (tested in simulator, collision with robot, robot is sideways to the goal post and arms are close to body) * - hard radius (tested in the same way, robot collides when closer than 0.15m from goal post border) * post radius 0.02 + agent radius 0.15 = 0.17 * - largest radius (equivalent to hard radius, since there is no soft radius) */ float obst[given_obst_size*4/5+16] = { 15.02, 1.07, 0.17, 0.17, 15.02, -1.07, 0.17, 0.17, -15.02, 1.07, 0.17, 0.17, -15.02, -1.07, 0.17, 0.17 }; // x, y, hard radius, largest radius int obst_size = 16; for(int i=0; istart center<->end return true; } } }else{ //-------------------- Normal case (start !~= end): the path is obstructed if it intersects any hard or soft circumference // for each obstacle: check if circle intersects line segment (start-end) for(int ob=0; obcenter onto start->end float sc_proj_y = se_y * sc_proj_scale; // projection of start->center onto start->end // check if projection falls on top of trajectory (start->projection = k * start->end) float k = abs(se_x)>abs(se_y) ? sc_proj_x/se_x : sc_proj_y/se_y; // we use the largest dimension of start->end to avoid division by 0 if(k <= 0){ if(sc_x*sc_x + sc_y*sc_y <= r_sq){ // check distance: center<->start return true; } }else if(k >= 1){ float ec_x = c_x - end_x; float ec_y = c_y - end_y; if(ec_x*ec_x + ec_y*ec_y <= r_sq){ // check distance: center<->end return true; } }else{ float proj_c_x = c_x - (sc_proj_x + start_x); float proj_c_y = c_y - (sc_proj_y + start_y); if(proj_c_x*proj_c_x + proj_c_y*proj_c_y <= r_sq){ // check distance: center<->projection return true; } } } } float path_x = end_x - start_x; float path_y = end_y - start_y; final_path_size = 6; final_path[0] = start_x; final_path[1] = start_y; final_path[2] = end_x; final_path[3] = end_y; final_path[4] = 3; // status: 3-no obstacles final_path[5] = sqrtf(path_x*path_x+path_y*path_y) + max(0.f, e_cost/10.f); // min. A* cost from start to end (e_cost is added even if start==end to help debug cell costs) return false; // no obstruction was found } // opponent players + active player + restricted areas (from referee) // data: // [start x][start y] // [allow out of bounds?][go to goal?] // [optional target x][optional target y] // [timeout] // [x][y][hard radius][soft radius][force] void astar(float params[], int params_size){ auto t1 = high_resolution_clock::now(); const float s_x = params[0]; // start x const float s_y = params[1]; // start y const bool allow_out_of_bounds = params[2]; const int wall_index = allow_out_of_bounds ? -3 : -2; // (cost <= wall_index) means 'unreachable' const bool go_to_goal = params[3]; const float opt_t_x = params[4]; // optional target x const float opt_t_y = params[5]; // optional target y const int timeout_us = params[6]; float* obstacles = ¶ms[7]; int obst_size = params_size-7; // size of obstacles array //======================================================== Populate board 0: add field layout float board_cost[LINES*COLS] = {L0_1,L2_5,L6_10,L11,LIN12_308,L309,L310_314,L2_5,L0_1}; if (!allow_out_of_bounds){ // add cost to getting near sideline or endline (except near goal) add_space_cushion(board_cost); } //======================================================== Check if path is obstructed if (!is_path_obstructed(s_x, s_y, opt_t_x, opt_t_y, obstacles, obst_size, go_to_goal, wall_index, board_cost)){ return; // return if path is not obstructed } //======================================================== Define board basics (start, end, limits) // if the start point is out of field, it is brought in const int start_l = x_to_line(s_x); const int start_c = y_to_col(s_y); const int start_pos = start_l * COLS + start_c; // define objective (go to goal or a specific point) int end_l, end_c; if(!go_to_goal){ end_l = x_to_line(opt_t_x); end_c = y_to_col(opt_t_y); }else{ end_l = IN_GOAL_LINE; } // define board limits considering the initial and final positions (and obstacles in the next section, and goals after that) int l_min = min(start_l, end_l); int l_max = max(start_l, end_l); int c_min, c_max; if(go_to_goal){ c_min = min(start_c,119); c_max = max(start_c,101); }else{ c_min = min(start_c, end_c); c_max = max(start_c, end_c); } if (!allow_out_of_bounds){ // workspace must contain a bit of empty field if out of bounds is not allowed l_min = min(l_min, 306); l_max = max(14, l_max); c_min = min(c_min, 206); c_max = max(14, c_max); } //======================================================== Initialize A* Node* open_root = nullptr; Node board[LINES*COLS]; unsigned int node_state[LINES*COLS] = {0}; //0-unknown, 1-open, 2-closed //======================================================== Populate board 1: convert obstacles to cost for(int ob=0; ob=0 and c>=0 and l=0 and c>=0 and l wall_index and cost < fr){ board_cost[p] = fr; } } } // adjust board limits if working area overlaps goal area (which includes walking margin) if (c_max > 96 and c_min < 124){ // Otherwise it does not overlap any goal if (l_max > 1 and l_min < 12 ){ // Overlaps our goal l_max = max(12,l_max); // Extend working area to include our goal l_min = min(l_min,1); c_max = max(124,c_max); c_min = min(c_min,96); } if (l_max > 308 and l_min < 319 ){ // Overlaps their goal l_max = max(319,l_max); // Extend working area to include their goal l_min = min(l_min,308); c_max = max(124,c_max); c_min = min(c_min,96); } } //======================================================== Populate board 2: add objective // Explanation: if objective is not accessible we do not add it to the map, although its reference still exists. // Therefore, we know how far we are from that reference, but it cannot be reached. // Even if we are on top of the objective, the idea is to get away, to the nearest valid position. if(!go_to_goal){ float *end_cost = &board_cost[end_l*COLS+end_c]; if(*end_cost > wall_index){ *end_cost = -1; } }else{ for(int i=IN_GOAL_LINE*COLS+101; i<=IN_GOAL_LINE*COLS+119; i++){ if(board_cost[i] > wall_index){ board_cost[i] = -1; } } } // add board limits as an additional restriction to workspace l_min = max(0, l_min); l_max = min(l_max, 320); c_min = max(0, c_min); c_max = min(c_max, 220); // add start node to open list (it will be closed right away, so there is not need to set it as open) board[start_pos].g = 0; // This is needed to compute the cost of child nodes, but f is not needed because there are no comparisons with other nodes in the open BST board[start_pos].parent = nullptr; //This is where the path ends open_root = open::insert(&board[start_pos], open_root); int measure_timeout=0; Node* best_node = &board[start_pos]; // save best node based on distance to goal (useful if impossible/timeout to get best path) float best_node_dist = std::numeric_limits::max(); // infinite distance if start is itself unreachable if(board_cost[start_pos] > wall_index){ best_node_dist = diagonal_distance(go_to_goal,start_l,start_c,end_l,end_c); } //======================================================== A* algorithm while (open_root != nullptr){ // Get next best node (lowest predicted total cost (f)) Node* curr_node = min_node; const int curr_pos = curr_node - board; const int curr_line = curr_pos / COLS; const int curr_col = curr_pos % COLS; const float curr_cost = board_cost[curr_pos]; measure_timeout = (measure_timeout+1) & 31; // check timeout at every 32 iterations // save best node based on distance to goal (useful if impossible/timeout) if(curr_cost > wall_index){ float dd = diagonal_distance(go_to_goal,curr_line,curr_col,end_l,end_c); if(best_node_dist > dd){ best_node = curr_node; best_node_dist = dd; } } open_root = open::pop(open_root); node_state[curr_pos] = 2; // Check if we reached objective if( curr_cost == -1 ){ // replace end point with correct coordinates instead of discrete version if the optional target was defined (not going to goal) build_final_path(best_node, board, 0, !go_to_goal, opt_t_x, opt_t_y); return; } if( measure_timeout==0 and duration_cast(high_resolution_clock::now() - t1).count() > timeout_us ){ build_final_path(best_node, board, 1); return; } // Expand child nodes bool rcol_ok = curr_col < c_max; bool lcol_ok = curr_col > c_min; if(curr_line > l_min){ int line = curr_line - 1; int col = curr_col - 1; int pos = curr_pos - COLS - 1; float cost = board_cost[pos]; int state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2); } col++; pos++; cost = board_cost[pos]; state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost)){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,1); } col++; pos++; cost = board_cost[pos]; state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2); } } if(curr_line < l_max){ int line = curr_line + 1; int col = curr_col - 1; int pos = curr_pos + COLS - 1; float cost = board_cost[pos]; int state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2); } col++; pos++; cost = board_cost[pos]; state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost)){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,1); } col++; pos++; cost = board_cost[pos]; state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,line,col,end_l,end_c,node_state,SQRT2); } } { int col = curr_col - 1; int pos = curr_pos - 1; float cost = board_cost[pos]; int state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and lcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,curr_line,col,end_l,end_c,node_state,1); } col+=2; pos+=2; cost = board_cost[pos]; state = node_state[pos]; // check if not an obstacle and if node is not closed (child can be as inaccessible as current pos) if (state!=2 and !(cost <= wall_index and cost < curr_cost) and rcol_ok){ open_root = expand_child(open_root,cost,wall_index,curr_node,board,pos,state,go_to_goal,curr_line,col,end_l,end_c,node_state,1); } } } build_final_path(best_node, board, 2); return; }