void calc_next_move(const struct plane *p, const int srow, const int scol, int *alt, const struct xyz target, int *bearing, const bool cleared_exit, struct frame *frame) { // Avoid obstacles. Obstacles are: The boundary except for the // target exit at alt==9, adjacency with another plane (props have // to check this at t+1 and t+2), within 2 of an exit at alt 6-8 if // it's cleared the exit, the exclusion area of an airport at alt <= 2. // Incur a penalty for matching the bearing/altitude of a // blocking airplane (because it'll just continue to block). struct blp blocking_planes[BLP_MAX]; int n_blp = 0; int nalt; const bool trace = (p->id == 'j' && srow == 1 && scol == 9 && *alt == 9 && bearings[*bearing].aircode == '<' && target.row == 0 && target.col == 29 && target.alt == 9); // If the plane's at the airport, it can only hold or take off. if (*alt == 0) { struct xy rc = apply(srow, scol, *bearing); frame->cand[0].bearing = frame->cand[1].bearing = *bearing; frame->cand[0].alt = 0; frame->cand[1].alt = 1; if (adjacent_another_plane(rc, 1, p->isjet, frame->opc_start).alt > 0) { // Can't take off, can only hold. frame->n_cand = 1; } else { *alt = 1; frame->n_cand = 2; } return; } frame->n_cand = 0; for (int turn = -2; turn <= 2; turn++) { int nb = (*bearing + turn) & 7; struct xy rc = apply(srow, scol, nb); if (rc.row < 0 || rc.col < 0 || rc.row >= board_height || rc.col >= board_width) continue; bool on_boundary = (rc.row == 0 || rc.row == board_height-1 || rc.col == 0 || rc.col == board_width-1); for (nalt = *alt-1; nalt <= *alt+1; nalt++) { if (nalt == 0 || nalt == 10) continue; if (target.alt == 9 && nalt == 9 && rc.row == target.row && rc.col == target.col) { // Reached the proper exit gate. Can't collide here, // planes just immediately disappear. new_cand(frame, nb, nalt, -10*MATCHCOURSE_PENALTY); break; } if (on_boundary) // ... and not at the target exit continue; if (cleared_exit && p->target_airport && in_airport_excl(rc, nalt, p->target_num)) continue; if (nalt == 1 && p->target_airport && rc.row == target.row && rc.col == target.col && in_airport_excl(apply(srow, scol, -1), *alt, p->target_num)) continue; struct blp adjacent_plane = adjacent_another_plane(rc, nalt, p->isjet, frame->opc_start); if (adjacent_plane.alt > 0) { add_blocking_plane(blocking_planes, &n_blp, adjacent_plane); tracelog(trace, "Candidate move to (%d, %d, %d) bearing %s " "blocked by %s plane at altitude %d " "bearing %s\n", rc.row, rc.col, nalt, bearings[nb].shortname, adjacent_plane.isjet ? "jet" : "prop", adjacent_plane.alt, bearings[adjacent_plane.bearing].shortname); continue; } if (cleared_exit && (rc.row <= 2 || rc.row >= board_height - 3 || rc.col <= 2 || rc.col >= board_width - 3) && ((p->target_airport && nalt >= 6) || (!p->target_airport && nalt != 9))) continue; bool aligned = planes_aligned(rc, nalt, p->isjet, frame->opc_start); int penalty = aligned ? MATCHCOURSE_PENALTY/2 : 0; int distance = penalty + cdist(rc.row, rc.col, nalt, target, p, srow, scol); new_cand(frame, nb, nalt, distance); tracelog(trace, "Adding candidate move to (%d, %d, %d) bearing %s " "distance=%d\n", rc.row, rc.col, nalt, bearings[nb].longname, distance); } } assert(frame->n_cand <= 15); if (frame->n_cand == 0) { tracelog(trace, "Warning: Can't find safe path for plane '%c'\n", p->id); *alt = -1; return; } tracelog(trace, "Checking %d candidate moves against %d blocking planes.\n", frame->n_cand, n_blp); for (int i = 0; i < frame->n_cand; i++) { for (int j = 0; j < n_blp; j++) { if (frame->cand[i].bearing != blocking_planes[j].bearing || p->isjet != blocking_planes[j].isjet) { tracelog(trace, "Not applying matchcourse penalty: " "%s c_bearing %s vs. %s b_bearing %s " "(%d/%d vs. %d/%d)\n", p->isjet ? "jet" : "prop", bearings[frame->cand[i].bearing].shortname, blocking_planes[j].isjet ? "jet" : "prop", bearings[blocking_planes[j].bearing].shortname, frame->cand[i].bearing, p->isjet, blocking_planes[j].bearing, blocking_planes[j].isjet); continue; } int da = abs(frame->cand[i].alt - blocking_planes[j].alt); if (da == 0) { tracelog(trace, "Applying matchcourse penalty to plane %c " "bearing %s.\n", p->id, bearings[blocking_planes[j].bearing].longname); frame->cand[i].distance += MATCHCOURSE_PENALTY; } else if (da == 1) { tracelog(trace, "Applying minor matchcourse penalty to " "plane %c bearing %s.\n", p->id, bearings[blocking_planes[j].bearing].longname); frame->cand[i].distance += MATCHCOURSE_PENALTY/10; } else { tracelog(trace, "Not applying matchcourse penalty: " "c_alt %d vs. b_alt %d\n", frame->cand[i].alt, blocking_planes[j].alt); } } } qsort(frame->cand, frame->n_cand, sizeof(*frame->cand), distcmp); int old_dist = cdist(srow, scol, *alt, target, p, srow, scol); if (frame->cand[frame->n_cand-1].distance > old_dist) { // We're being pushed away from the destination. Apply the // alt change bonus and re-sort. for (int i = 0; i < frame->n_cand; i++) { for (int j = 0; j < n_blp; j++) { if (*alt == blocking_planes[j].alt && frame->cand[i].alt != blocking_planes[j].alt) frame->cand[i].distance -= CHANGEALT_BONUS; } } qsort(frame->cand, frame->n_cand, sizeof(*frame->cand), distcmp); } *bearing = frame->cand[frame->n_cand-1].bearing; *alt = frame->cand[frame->n_cand-1].alt; }
bool Plane::Move(int time) { if (time >= nextMove) { fuel--; nextMove += speed; if (circling) { switch(heading) { int n; case east: n = cdist(position.x+1, position.y); if (n > circleDist) { n = cdist(position.x+1, position.y+1); if (n > circleDist) heading = northeast; else heading = southeast; } break; case southeast: n = cdist(position.x+1, position.y+1); if (n > circleDist) { n = cdist(position.x, position.y+1); if (n > circleDist) heading = east; else heading = south; } break; case south: n = cdist(position.x, position.y+1); if (n > circleDist) { n = cdist(position.x+1, position.y+1); if (n > circleDist) heading = southwest; else heading = southeast; } break; case southwest: n = cdist(position.x-1, position.y+1); if (n > circleDist) { n = cdist(position.x-1, position.y); if (n > circleDist) heading = south; else heading = west; } break; case west: n = cdist(position.x-1, position.y); if (n > circleDist) { n = cdist(position.x-1, position.y+1); if (n > circleDist) heading = northwest; else heading = southwest; } break; case northwest: n = cdist(position.x-1, position.y-1); if (n > circleDist) { n = cdist(position.x-1, position.y); if (n > circleDist) heading = north; else heading = west; } break; case north: n = cdist(position.x, position.y-1); if (n > circleDist) { n = cdist(position.x+1, position.y-1); if (n > circleDist) heading = northwest; else heading = northeast; } break; case northeast: n = cdist(position.x+1, position.y-1); if (n > circleDist) { n = cdist(position.x+1, position.y); if (n > circleDist) heading = north; else heading = west; } break; } } else if (rotate < 0) { rotate++; heading = heading-1; if (heading < 0) heading += 8; } else if (rotate > 0) { rotate --; heading = heading + 1; if (heading >= 8) heading -= 8; } switch(heading) { case east: MoveDelta (1, 0); break; case southeast: MoveDelta (1, 1); break; case south: MoveDelta (0, 1); break; case southwest: MoveDelta (-1, 1); break; case west: MoveDelta (-1, 0); break; case northwest: MoveDelta (-1, -1); break; case north: MoveDelta (0, -1); break; case northeast: MoveDelta (1, -1); break; } return true; } return false; }
static double ApproxEarthMoversMetric( vector<double> &dd, int wi, int hi ) { // find powers of 2 that are big enough int w = CeilPow2( wi ), h = CeilPow2( hi ); vector<double> d( w*h, 0.0 ); CopyRaster( &d[0], w, &dd[0], wi, wi, hi ); UnnormHaarTf2D( d, w, h ); // PrintVectorAsMat( stdout, d, 8 ); vector<int> cdist( w, 1 ); vector<int> rdist( h, 1 ); int mask; mask = w >> 1; // only works for w = 2^n for( int x = 1; x < w; ++x ) { for( int tmp = x; !(tmp & mask); tmp <<= 1 ) cdist[x] <<= 1; } mask = h >> 1; // only works for h = 2^n for( int y = 1; y < h; ++y ) { for( int tmp = y; !(tmp & mask); tmp <<= 1 ) rdist[y] <<= 1; } // use of 1 as lower bound is right; // do not care about sums, only differences double approx = 0.0; // approximate Earth Mover's metric for( int x = 1; x < w; ++x ) { for( int y = 1; y < h; ++y ) { int dx = cdist[x]; int dy = rdist[y]; double dist = min( dx, dy ); if( dist < 0 ) { printf( "x %d, y %d, dx %d, dy %d, dist %f, d[x+w*y] %f\n", x, y, dx, dy, dist, d[x + w*y] ); } approx += dist * abs( d[x + w*y] ); } } // this should never happen if( approx < 0 ) { printf( "##Negative metric? wi=%d, hi=%d" "\nColumn dists: ", wi, hi ); for( int x = 1; x < w; ++x ) printf( "%d ", cdist[x] ); printf( "\nRow dists: " ); for( int y = 1; y < h; ++y ) printf( "%d ", rdist[y] ); printf( "\n" ); exit( 42 ); } // Normalize: // - divide by 2 deltas per move unit. // - divide by N pixels (per pixel cost). // - divide by Poisson noise on area N. int N = wi * hi; approx /= 2.0 * N * sqrt( N ); printf( "Approximate EM metric %f for %d points.\n", approx, N ); return approx; }