anchor.diff:      Collision(this.a, this.b, this.t);
   
       String toString() { return "$a will collide $b at time $t"; }
  -}
  -
  -class Velocity {
  -    final Point first;
  -    final Point second;
  -
  -    num dx;
  -    num dy;
  -
  -    Velocity(this.first, this.second){
  -        dx = second.x - first.x;
  -        dy = second.y - first.y;
  -    }
  -
  -    num get dist  => sqrt(dx * dx + dy * dy);
  -    num get dist2 => dx * dx + dy * dy;
  -
  -    String toString() { return "$first to $second dx: $dx dy $dy dist $dist"; }
  +    
  +    bool ignorable(Collision other) {
  +        return other.t == 0 &&
  +        ((a.id == other.a.id && b.id == other.b.id) ||
  +         (a.id == other.b.id && b.id == other.a.id));
  +    }
  +    
  +    int get hashCode {
  +        int result = 17;
  +        result = 37 * result + a.id + b.id;
  +        result = 37 * result + t;
  +        return result;
  +    }
  +
  +    // You should generally implement operator == if you
  +    // override hashCode.
  +    bool operator ==(other) {
  +        if (other is! Collision) return false;
  +        Collision col = other;
  +        return col.t == t && 
  +            ((col.a.id == a.id && col.b.id == b.id) ||
  +             (col.b.id == a.id && col.a.id == b.id) );
  +    }
  +}
  +
  +class Move {
  +    num angle; //damped to -18.0 to 18
  +    int thrust; //damped to -1 to 100 (-1 means activate shield)
  +    
  +    double SHIELD_PROB = .05;
  +    
  +    Move(this.angle, this.thrust);
  +    
  +    String toString() => "Move: ${angle.truncate()} $thrust";
  +    
  +    Move clone() => new Move(this.angle, this.thrust);
  +    
  +    bool get shield => thrust < 0;
  +        
  +    void mutate(num amplitude) {
  +        num ramin = this.angle - 36.0 * amplitude;
  +        num ramax = this.angle + 36.0 * amplitude;
  +    
  +        if (ramin < -18.0) {
  +            ramin = -18.0;
  +        }
  +    
  +        if (ramax > 18.0) {
  +            ramax = 18.0;
  +        }
  +    
  +        angle = rand.nextDouble() * (ramax - ramin) + ramin;
  +    
  +        if (!this.shield && rand.nextDouble() < SHIELD_PROB) {
  +            this.thrust = -1;
  +        } else {
  +            int pmin = this.thrust - (100 * amplitude).truncate();
  +            int pmax = this.thrust + (100 * amplitude).truncate();
  +    
  +            if (pmin < 0) {
  +                pmin = 0;
  +            }
  +    
  +            if (pmax > 0) {
  +                pmax = 100;
  +            }
  +    
  +            this.thrust = rand.nextInt(pmax - pmin) + pmin;
  +    
  +            // this.shield = false;
  +        }
  +    }
  +}
  +
  +class Solution {
  +    List<Move> moves0;
  +    List<Move> moves1;
  +    List<Move> moves2;
  +    List<Move> moves3;
  +    
  +    num MOVE_MUTATE_RATE = .5;
  +    
  +    
  +    Solution(this.moves0, this.moves1, this.moves2, this.moves3);
  +    
  +    Solution clone() =>
  +        new Solution(
  +            moves0.map((move) => move.clone()).toList(), 
  +            moves1.map((move) => move.clone()).toList(), 
  +            moves2.map((move) => move.clone()).toList(), 
  +            moves3.map((move) => move.clone()).toList()
  +            );
  +    
  +    num score(List<Pod> startPods) {
  +        var pods = clonePods(startPods);
  +        
  +        // Play out the turns
  +        for (int i = 0; i < moves0.length; ++i) {
  +            // Apply the moves to the pods before playing
  +            
  +            pods[0].apply(moves0[i]);
  +            pods[1].apply(moves1[i]);
  +            pods[2].apply(moves2[i]);
  +            pods[3].apply(moves3[i]);
  +    
  +            play(pods, checkpoints);
  +        }
  +    
  +        // Compute the score
  +        num result = evaluation(pods);
  +    
  +        return result;
  +    }
  +    
  +    num evaluation(List<Pod> pods){
  +        if (pods[2].checked >= cpGoal ||
  +            pods[3].checked >= cpGoal ||
  +            pods[0].timeout <= 1 ||
  +            pods[1].timeout <= 1){
  +            return double.NEGATIVE_INFINITY;
  +        }
  +        if (pods[0].checked >= cpGoal ||
  +            pods[1].checked >= cpGoal ||
  +            pods[2].timeout <= 1 ||
  +            pods[3].timeout <= 1){
  +            return double.INFINITY;    
  +        }
  +        
  +        Pod myRunner = pods[0].score() > pods[1].score() ? pods[0] : pods[1];
  +        Pod myHarasser = myRunner.partner;
  +        
  +        Pod oppRunner = pods[2].score() > pods[3].score() ? pods[2] : pods[3];
  +        Pod oppHarasser = oppRunner.partner;
  +        
  +        num score = 0;
  +        score += (myRunner.score() - oppRunner.score()) * 2;
  +        // score += (myHarasser.score() - oppRunner.score());
  +        
  +        score -= myHarasser.distance(checkpoints[oppRunner.nextCP]);
  +        score -= myHarasser.diffAngle(oppRunner);
  +        score -= oppRunner.diffAngle(checkpoints[oppRunner.nextCP]) - 
  +             oppRunner.diffAngle(myHarasser);
  +        // score += myRunner.timeout;
  +        // score -= oppRunner.timeout;
  +        
  +        return score;
  +        
  +    }
  +    
  +    void mutate(){
  +        for(int i = 0; i < moves0.length; i++){
  +            if(rand.nextDouble() < MOVE_MUTATE_RATE){
  +                moves0[i].mutate(.5);
  +            }
  +            if(rand.nextDouble() < MOVE_MUTATE_RATE){
  +                moves1[i].mutate(.5);
  +            }
  +        }
  +    }
   }
   
   int thrustSigmoid(num angle){
  
positive.code:     }
  
      void bounceWithCheckpoint(Checkpoint u){
          this.timeout = 100;
          if(u.id == this.nextCP){
              this.nextCP =
              (this.nextCP + 1) % checkpoints.length;
          }
  
      }
  }
  
  class Checkpoint extends Unit {
      Checkpoint(num id, num x, num y) : super(id, x, y, 0, 0){
          radius = 600;
      }
  
      int get hashCode {
  
negative.code:         checkpoints.add(new Checkpoint(i, checkpointX, checkpointY));
      }
  
      List<Pod> pods = new List(4);
  
      // game loop
      while (true) {
          if(pods[0] == null){
              for(int index = 0; index < 4; index ++){
                  inputs = stdin.readLineSync().split(' ')
                      .map((s) => int.parse(s)).toList();
                  pods[index] = new Pod(index, inputs[0], inputs[1],
                      inputs[2], inputs[3], inputs[4], inputs[5]);
              }
              
              pods[0].partner = pods[1];
              pods[1].partner = pods[0];
      
              pods[2].partner = pods[3];
              pods[3].partner = pods[2];
          } else {
              for(int index = 0; index < 4; index ++){
                  inputs = stdin.readLineSync().split(' ')
                      .map((s) => int.parse(s)).toList();
                  pods[index].x = inputs[0];
                  pods[index].y = inputs[1];
                  pods[index].vx = inputs[2];
                  pods[index].vy = inputs[3];
                  pods[index].angle = inputs[4];
                  pods[index].nextCP = inputs[5];
              }
              
          }
  
  
          //update checked count
          [0, 1, 2, 3].forEach((index) {
              if(pods[index].nextCP != lastCPs[index]){
                  pods[index].checked++;
                  pods[index].timeout = 100;
                  pods[index].partner.timeout = 100;
              }
              
              lastCPs[index] = pods[index].nextCP;
          });
  
          // Write an action using print()
          // To debug: stderr.writeln('Debug messages...');
          printPods(pods);
  
          // You have to output the target position
          // followed by the power (0 <= thrust <= 100)
          // i.e.: "x y thrust"
  
          Solution gen = genetic(pods, 6, 95, lastSolution);
          
          stderr.writeln("Genetic eval: ${gen.score(pods)}");
          
          stderr.writeln("Move 0 ${gen.moves0[0]}");
          stderr.writeln("Move 1 ${gen.moves1[0]}");
          
          pods[0].output(gen.moves0[0]);
          pods[1].output(gen.moves1[0]);
          
          [0, 1, 2, 3].forEach((index) {
              pods[index].timeout -= 1;
          });
          
          [0, 1].forEach((index) {
              Pod pod = pods[index];
          
              if(pod.shield){
                  if(pod.shieldTimeout <= 0){
                      pod.shield = false;
                      pod.shieldTimeout = 0;
                  } else {
                      pod.shieldTimeout--;
                  }
              }
          });
          
  
          // [0, 1].forEach((index) {
          //     Point nextCP = checkpoints[pods[index].nextCP];
          //     //calc outer point on next checkpoint
          //     //vector from previous to next next cp
          //     int prevCPid = pods[index].nextCP - 1;
          //     if(prevCPid < 0 ){ prevCPid = cpCount - 1; }
          //     int nextNextCPid = pods[index].nextCP + 1;
          //     if(nextNextCPid >= cpCount  ){ nextNextCPid = 0; }
              
          //     num dx = checkpoints[prevCPid].x - checkpoints[nextNextCPid].x;
          //     num dy = checkpoints[prevCPid].y - checkpoints[nextNextCPid].y; 
          //     num length = sqrt(dx * dx + dy * dy);
          //     num scaleddx = (490 / length) * dx;
          //     num scaleddy = (490 / length) * dy;
          //     // Point target = new Point(
          //     //     nextCP.x - pods[index].vx * 3,
          //     //     nextCP.y - pods[index].vy * 3);
          //     Point target = new Point(
          //         nextCP.x + scaleddx.round() - pods[index].vx * 3,
          //         nextCP.y + scaleddy.round() - pods[index].vy * 3);
          //     bool shield = false;
          //     Collision col1 = pods[index].collision(pods[2]);
          //     Collision col2 = pods[index].collision(pods[3]);
          //     // Collision col3 = pods[index].collision(checkpoints[pods[index].nextCP]);
          //     if(col1 != null || col2 != null){
          //         shield = true;
          //     }
              
          //     num nextCheckpointAngle = pods[index].diffAngle(target);
          //     num nextCheckpointDist2 = pods[index].distance2(target);
          //     stderr.writeln("$index Next CP angle: $nextCheckpointAngle");
          //     stderr.writeln("$index Next CP Dist2: $nextCheckpointDist2");
              
              
          //     if(shield){
          //         print("${target.x} ${target.y} SHIELD SHIELDS UP!");
          //     // } else if (col3 != null){
          //     //     //going to collide with next CP next turn
          //     //     Point nextNextCP = checkpoints[nextNextCPid];
          //     //     print("${nextNextCP.x} ${nextNextCP.y} 100");
                  
          //     } else if(!pods[index].boostUsed &&
          //         nextCheckpointAngle.abs() < 2 &&
          //         nextCheckpointDist2 > (6000 * 6000)){
          //         pods[index].boostUsed = true;
          //         print("${target.x} ${target.y} BOOST BOOSTERS ENGAGED!");
          //     } else if(nextCheckpointAngle.abs() > 20){
          //          int thrust = thrustSigmoid(nextCheckpointAngle);
          //          print("${target.x} ${target.y} 20");
          //     } else if( nextCheckpointDist2 < 700 * 700){
          //         print("${target.x} ${target.y} 50");
          //     } else if( nextCheckpointDist2 < 650 * 650){
          //         print("${target.x} ${target.y} 30");
          //     } else {
          //         print("${target.x} ${target.y} 100");
          //     }
          // });
  
          turn++;
      }
  }
  
  