improve_strategy <- function(){
  change = FALSE
  if(not.improving.turns %% (iteration.limit/10) == 0){
    np <<- np + np.step
    print(paste("new noise parameter:", np))
  }
  # if(not.improving.turns == (iteration.limit/4)){
  #   destruction.degree.type <<- "random-equal"
  #   line1 <<- c(line1, length(cost.vector))
  #   change = TRUE
  # }else if(not.improving.turns == (iteration.limit/2)){
  #   add.noise <<- TRUE
  #   destruction.degree.type <<- "random-low"
  #   line2 <<- c(line2, length(cost.vector))
  #   change = TRUE
  # }else if(not.improving.turns == (iteration.limit*3/4)){
  #   destruction.degree.type <<- "fixed"
  #   line3 <<- c(line3, length(cost.vector))
  #   change = TRUE
  # }
  
  # if(change == TRUE){
  #   print(paste("iterations incumbent sol not improved:", not.improving.turns,
  #               ", destruction type:", destruction.degree.type))
  #   print(paste("destroy probabilities:     ", paste(destroy.probs, collapse = "  -  ")))
  #   print(paste("repair probabilities:      ", paste(repair.probs, collapse = "  -  ")))
  #   print(paste("move probabilities:        ", paste(move.probs, collapse = "  -  ")))
  #   print(paste("temperature:", temperature))
  # }
}

include_drone_delivery <- function(){
  include.drone <<- TRUE
  print(paste(paste(rep("-",10), collapse = "-"), "starting of drone assignment",
              paste(rep("-",10), collapse = "-")))
  dline <<- length(cost.vector)
  
  vrp.cost <<- best.cost
  temp.no = move.number
  move.number <<- max.move
  move.probs <<- round(rep(1/move.number, move.number), 4)
  moves.used <<-       c(moves.used, numeric(move.number - temp.no))
  moves.feasible <<-   c(moves.feasible, numeric(move.number - temp.no))
  moves.successful <<- c(moves.successful, numeric(move.number - temp.no))
  moves.incumbent <<-  c(moves.incumbent, numeric(move.number - temp.no))
  moves.score <<- rep(0, move.number)
  moves.used.in.segment <<- rep(0, move.number)
  
  dest <<- 5
  repp <<- 3
  
  destroy.probs <<- round(rep(1/dest, dest), 4)
  # destroy.used <<- c(destroy.used, numeric(1))
  # destroy.feasible <<- c(destroy.feasible, numeric(1))
  # destroy.successful <<- c(destroy.successful, numeric(1))
  # destroy.incumbent <<- c(destroy.incumbent, numeric(1))
  destroy.score <<- rep(0, dest)
  destroy.used.in.segment <<- rep(0, dest)
  
  repair.probs <<- round(rep(1/repp, repp), 4)
  # repair.used <<- c(repair.used, numeric(1))
  # repair.feasible <<- c(repair.feasible, numeric(1))
  # repair.successful <<- c(repair.successful, numeric(1))
  # repair.incumbent <<- c(repair.incumbent, numeric(1))
  repair.score <<- rep(0, repp)
  repair.used.in.segment <<- rep(0, repp)
  
  destroy.used <<- destroy.used + destroy.used.in.segment
  destroy.score <<- rep(0, dest)
  destroy.used.in.segment <<- rep(0, dest)
  
  repair.used <<- repair.used + repair.used.in.segment
  repair.score <<- rep(0, repp)
  repair.used.in.segment <<- rep(0, repp)
  
  d.len <<- length(d.range)
  destruction.prob <<- rep(1/d.len, d.len)
  destruction.score <<- rep(0, d.len)
  destruction.used <<- rep(0, d.len)
}

incumbent_sol_update <- function(save.points = F){
  print(paste("noise", noise))
  incumbent.sol <<- TRUE
  moves.incumbent[move.operator] <<- moves.incumbent[move.operator] + 1
  destroy.incumbent[move.operator] <<- destroy.incumbent[move.operator] + 1
  repair.incumbent[move.operator] <<- repair.incumbent[move.operator] + 1
  
  # destroy.score[destroy.method] <<- destroy.score[destroy.method] + prob.weights[1] - prob.weights[2]
  repair.score[repair.method] <<- repair.score[repair.method] + prob.weights[1] - prob.weights[2]
  # if(destruction.degree<=max(d.range)){
  #   destruction.score[destruction.degree] <<- 
  #     destruction.score[destruction.degree] + prob.weights[1] - prob.weights[2]
  # }
  
  destruction.degree.type <<- "random-high"
  # d.factor <<- initial.d.factor
  # d.range <<- 1:floor(ncust*d.factor)
  tcost = cost.found.in.last.move - noise
  incumbent.cost <<- cost.found.in.last.move
  incumbent.truck.routes <<- truck.routes
  incumbent.drone.routes <<- drone.routes
  
  if(tcost<best.cost){
    not.improving.turns <<- 0
    np <<- 0
    last.solution.time <<- round(as.numeric(difftime(Sys.time(),
                                                     starting.time, units = "secs")))
    best.cost <<- tcost
    best.truck.routes <<- truck.routes
    best.drone.routes <<- drone.routes
    
    iline <<- c(iline, length(cost.vector))
    turn <<- turn + 1
    print(paste0("better route found at iteration: ", iteration,
                 " , at move: ", move.operator, " , cost: ", round(tcost,2)))
    plot_dronetruck_kvehicle(incumbent.truck.routes, incumbent.drone.routes)
    title(main = paste("cost:", round(tcost,2)),
          xlab = paste("move", move.operator, ",", "d", destruction.degree))
    if(turn %% 5 == 0){
      print(paste("destroy probabilities:     ", paste(destroy.probs, collapse = "  -  ")))
      print(paste("repair probabilities:      ", paste(repair.probs, collapse = "  -  ")))
      print(paste("move probabilities:        ", paste(move.probs, collapse = "  -  ")))
    }
  }
}

evaluate_move_result <- function(new.trs, new.drs, v){
  moves.used.in.segment[move.operator] <<- moves.used.in.segment[move.operator] + 1
  # if(destruction.degree<=max(d.range)){
  #   destruction.used[destruction.degree] <<- destruction.used[destruction.degree] + 1
  # }
  destroy.used.in.segment[destroy.method] <<- destroy.used.in.segment[destroy.method] + 1
  repair.used.in.segment[repair.method] <<- repair.used.in.segment[repair.method] + 1
  total.changes <<- total.changes + destruction.degree
  # check if route is still feasible after the move
  if(is_solution_feasible(new.trs, new.drs)){
    sol.feasibility <<- TRUE
    moves.feasible[move.operator] <<- moves.feasible[move.operator] + 1
    destroy.feasible[destroy.method] <<- destroy.feasible[destroy.method] + 1
    repair.feasible[repair.method] <<- repair.feasible[repair.method] + 1
    new.cost = find_solution_cost(new.trs, new.drs)
    # noise = 0 
    noise = round(sum(new.cost) * np * rnorm(1),4)
    cost.found.in.last.move <<- sum(new.cost) + noise
    noise <<- noise
    
    if(sum(new.cost) <= current.cost){
      destroy.score[destroy.method] <<- destroy.score[destroy.method] + prob.weights[2]
      repair.score[repair.method] <<- repair.score[repair.method] + prob.weights[2]
      moves.successful[move.operator] <<- moves.successful[move.operator] + 1
      # if(destruction.degree<=max(d.range)){
      #   destruction.score[destruction.degree] <<- destruction.score[destruction.degree] + prob.weights[2]
      # }
    }else{
      destroy.score[destroy.method] <<- destroy.score[destroy.method] + prob.weights[3]
      repair.score[repair.method] <<- repair.score[repair.method] + prob.weights[3]
      # if(destruction.degree<=max(d.range)){
      #   destruction.score[destruction.degree] <<- destruction.score[destruction.degree] + prob.weights[3]
      # }
    }
    route.costs <<- new.cost
  }else{
    sol.feasibility <<- FALSE
    destroy.score[destroy.method] <<- destroy.score[destroy.method] + prob.weights[4]
    repair.score[repair.method] <<- repair.score[repair.method] + prob.weights[4]
    # if(destruction.degree<=max(d.range)){
    #   destruction.score[destruction.degree] <<- destruction.score[destruction.degree] + prob.weights[4]
    # }
  }
}

update_after_segment <- function(){
  if(include.drone){
    update_probabilities(reaction.factor, min.prob)
  }
  moves.used <<- moves.used + moves.used.in.segment
  moves.score <<- rep(0, move.number)
  moves.used.in.segment <<- rep(0, move.number)
  
  destroy.used <<- destroy.used + destroy.used.in.segment
  destroy.score <<- rep(0, dest)
  destroy.used.in.segment <<- rep(0, dest)
  
  repair.used <<- repair.used + repair.used.in.segment
  repair.score <<- rep(0, repp)
  repair.used.in.segment <<- rep(0, repp)
  
  destruction.score <<- rep(0, d.len)
  destruction.used <<- rep(0, d.len)
  # destruction.degree = sample(d.range, 1, prob = d.range)
  # destruction.degree <<- choose_destruction_degree(destruction.degree.type)
}

apply_move <- function(move.operator, k, trs, drs, v, d){
  move.cost = 100000
  for(i in 1:k){
    tryCatch({
      if(move.operator == 1){
        ### select one customer and assign to another route
        result = relocate_customer(trs, drs, v, d)
      }else if(move.operator == 2){
        ### select two customers from different routes and
        ### relocate them into the other's route
        result = exchange_customers(trs, drs, v, d)
      }else if(move.operator == 3){
        ### select one node and insert to a new place
        result = truck_node_reinsertion(trs, drs, v, d)
      }else if(move.operator == 4){
        ### assign a customer from truck to drone
        result = truck_to_drone_exchange(trs, drs, v, d)
      }else{
        ### assign a customer from truck to drone
        result = drone_to_truck_exchange(trs, drs, v, d)
      }
    }, error = function(cond){
      if(i==1){
        return(list(trs,drs))
      }else{
        if(exists("nsch")){
          nsch <<- nsch
        }
        return(list(ntrs,ndrs))
      }
    })

    
    ctrs = result[[1]]
    cdrs = result[[2]]
    if(i == 1){
      ntrs = ctrs
      ndrs = cdrs
      if(is_solution_feasible(ctrs, cdrs)){
        move.cost = find_solution_cost(ctrs, cdrs, "sum")
        nsch = new.schedule
      }
    }else if(is_solution_feasible(ctrs, cdrs)){
      if(find_solution_cost(ctrs, cdrs, "sum") < move.cost){
        ntrs = ctrs
        ndrs = cdrs
        nsch = new.schedule
        move.cost = find_solution_cost(ctrs, cdrs, "sum")
      }
    }
  }
  nsch <<- nsch
  return(list(ntrs, ndrs))
}

assign_initial_values <- function(){
  nodes <<- nrow(df)
  ncust <<- nodes-2
  route.costs = find_solution_cost(truck.routes, drone.routes)
  
  # for(v in 1:vehicle.number){
  #   route.costs[v] = round(find_route_cost(truck.routes[[v]],
  #                                          drone.routes[[v]]), 4)
  #   #print(is_route_feasible(truck.routes[[v]], drone.routes[[v]]))
  # }
  route.costs <<- route.costs
  drone.routes <<- drone.routes
  current.cost <<- sum(route.costs)
  initial.cost <<- current.cost 
  print(paste("initial cost:         ", current.cost))
  
  initial.temperature <<- current.cost*init.temp.factor*temp.scaleup
  temperature <<- initial.temperature
  temperature.length <<- initial.temperature.length
  temp.counter <<- 0
  print(paste("initial temperature:  ", initial.temperature))
  print(paste("initial vehicle count:", vehicle.number))
  
  turn <<- 0
  iteration <<- 0
  not.improving.turns <<- 0
  np <<- 0
  
  incumbent.cost <<- current.cost
  incumbent.truck.routes <<- truck.routes
  incumbent.drone.routes <<- drone.routes
  
  best.cost <<- current.cost
  best.truck.routes <<- truck.routes
  best.drone.routes <<- drone.routes
  
  moves.score <<- numeric(move.number)
  moves.used <<- rep(0, move.number)
  moves.feasible <<- rep(0, move.number)
  moves.successful <<- rep(0, move.number)
  moves.incumbent <<- rep(0, move.number)
  moves.used.in.segment <<- rep(0, move.number)
  
  destroy.score <<- numeric(dest)
  destroy.used <<- rep(0, dest)
  destroy.feasible <<- rep(0, dest)
  destroy.successful <<- rep(0, dest)
  destroy.incumbent <<- rep(0, dest)
  destroy.used.in.segment <<- rep(0, dest)
  
  repair.score <<- numeric(repp)
  repair.used <<- rep(0, repp)
  repair.feasible <<- rep(0, repp)
  repair.successful <<- rep(0, repp)
  repair.incumbent <<- rep(0, repp)
  repair.used.in.segment <<- rep(0, repp)
  
  d.len <<- length(d.range)
  destruction.prob <<- rep(1/d.len, d.len)
  destruction.score <<- rep(0, d.len)
  destruction.used <<- rep(0, d.len)
}
