Skip to main content
Topic: AI: build depot (Read 14733 times) previous topic - next topic

AI: build depot

Here is a patch that lets the goods-ai build depots:

http://simutrans-germany.com/~patches/download.php?file=ai-depot-2497.patch

It looks something like that:


This is only implemented and tested for rail and road transport. Also the p****enger AI does not no this new stuff yet.
Parsley, sage, rosemary, and maggikraut.

Re: AI: build depot

Reply #1
Unfourtunately, it is not working against the code you just submitted ... Since I am not sure, how this works with you new code there, would you mind to update it.

 

Re: AI: build depot

Reply #2
patch against 2500.

Code: [Select]
Index: bauer/wegbauer.cc
===================================================================
--- bauer/wegbauer.cc (revision 2500)
+++ bauer/wegbauer.cc (working copy)
@@ -56,6 +56,7 @@
 #include "../dings/crossing.h"
 #include "../dings/leitung2.h"
 #include "../dings/groundobj.h"
+#include "../dings/label.h"
 
 #include "../vehicle/movingobj.h"
 
@@ -546,7 +547,7 @@
  return false;
  }
  //sint16 height = welt->lookup(to->get_pos().get_2d())->get_kartenboden()->get_hoehe()+Z_TILE_STEP;
- sint16 height = to->get_hoehe()+Z_TILE_STEP;
+ sint8 height = to->get_hoehe()+Z_TILE_STEP;
  grund_t *to2 = welt->lookup(koord3d(to->get_pos().get_2d(),height));
  if(to2) {
  if(to2->get_weg_nr(0)) {
@@ -622,7 +623,7 @@
  return false;
  }
  // calculate costs
- *costs = str ? 0 : welt->get_einstellungen()->way_count_straight;
+ *costs = str ? ((bautyp&depot_flag)!=0) : welt->get_einstellungen()->way_count_straight;
  if((str==NULL  &&  to->hat_wege())  ||  (str  &&  to->has_two_ways())) {
  *costs += 4; // avoid crossings
  }
@@ -973,6 +974,7 @@
 /* this routine uses A* to calculate the best route
  * beware: change the cost and you will mess up the system!
  * (but you can try, look at simuconf.tab)
+ * depot-search uses A* with useless lower bound=0
  */
 long
 wegbauer_t::intern_calc_route(const vector_tpl<koord3d> &start, const vector_tpl<koord3d> &ziel)
@@ -988,7 +990,7 @@
  for( uint32 i = 0; i < ziel.get_count(); i++ ) {
  has_target_ground |= welt->lookup(ziel[i]) != NULL;
  }
- if( !has_target_ground ) {
+ if( !has_target_ground && ((bautyp&depot_flag)==0) ) {
  return -1;
  }
 
@@ -1045,7 +1047,12 @@
 
  tmp->parent = NULL;
  tmp->gr = gr;
- tmp->f = calc_distance(start[i], mini, maxi);
+ if ((bautyp&depot_flag)==0) {
+ tmp->f = calc_distance(start[i], mini, maxi);
+ }
+ else {
+ tmp->f = tmp->g + 1; // f=g means: ready
+ }
  tmp->g = 0;
  tmp->dir = 0;
  tmp->count = 0;
@@ -1066,6 +1073,8 @@
  // to speed up search, but may not find all shortest ways
  uint32 min_dist = 99999999;
 
+ bool ready=false;
+
 //DBG_MESSAGE("route_t::itern_calc_route()","calc route from %d,%d,%d to %d,%d,%d",ziel.x, ziel.y, ziel.z, start.x, start.y, start.z);
  do {
  route_t::ANode *test_tmp = queue.pop();
@@ -1084,10 +1093,14 @@
 #ifdef DEBUG_ROUTES
 DBG_DEBUG("insert to close","(%i,%i,%i)  f=%i",gr->get_pos().x,gr->get_pos().y,gr->get_pos().z,tmp->f);
 #endif
-
+ // maximum cost reached?
+ if (tmp->g > maximum) {
+ break;
+ }
  // already there
- if(  ziel.is_contained(gr_pos)  ||  tmp->g>maximum) {
+ if( ((bautyp&depot_flag)==0) ? ziel.is_contained(gr_pos) :  tmp->f==tmp->g) {
  // we added a target to the closed list: we are finished
+ ready = true;
  break;
  }
 
@@ -1186,25 +1199,39 @@
  current_dir = ribi_typ( gr->get_pos().get_2d(), to->get_pos().get_2d() );
  }
 
- const uint32 new_dist = calc_distance( to->get_pos(), mini, maxi );
+ uint32 new_dist;
+ if ((bautyp&depot_flag)==0) {
+ new_dist = calc_distance( to->get_pos(), mini, maxi );
 
- // special check for kinks at the end
- if(new_dist==0  &&  current_dir!=tmp->dir) {
- // discourage turn on last tile
- new_g += welt->get_einstellungen()->way_count_double_curve;
- }
+ // special check for kinks at the end
+ if(new_dist==0  &&  current_dir!=tmp->dir) {
+ // discourage turn on last tile
+ new_g += welt->get_einstellungen()->way_count_double_curve;
+ }
 
- if(new_dist<min_dist) {
- min_dist = new_dist;
+ if(new_dist<min_dist) {
+ min_dist = new_dist;
+ }
+ else if(new_dist>min_dist+50) {
+ // skip, if too far from current minimum tile
+ // will not find some ways, but will be much faster ...
+ // also it will avoid too big detours, which is probably also not the way, the builder intended
+ continue;
+ }
  }
- else if(new_dist>min_dist+50) {
- // skip, if too far from current minimum tile
- // will not find some ways, but will be much faster ...
- // also it will avoid too big detours, which is probably also not the way, the builder intended
- continue;
+ else {
+ if (check_for_depot(gr, to)) {
+ new_dist = 0;
+ // extra cost if we have to build a depot ourselves
+ if (to->get_depot()==NULL) {
+ new_g += welt->get_einstellungen()->way_count_double_curve;
+ }
+ }
+ else {
+ new_dist = 1;
+ }
  }
 
-
  const uint32 new_f = new_g+new_dist;
 
  if((step&0x03)==0) {
@@ -1240,10 +1267,10 @@
 
  route_t::RELEASE_NODE();
 
-//DBG_DEBUG("reached","%i,%i",tmp->pos.x,tmp->pos.y);
+// DBG_DEBUG("reached","%s cost %d",tmp->gr->get_pos().get_str(),tmp->g);
  // target reached?
- if( !ziel.is_contained(gr->get_pos())  || step >=route_t::MAX_STEP  ||  tmp->parent==NULL) {
- dbg->warning("wegbauer_t::intern_calc_route()","Too many steps (%i>=max %i) in route (too long/complex)",step,route_t::MAX_STEP);
+ if(!ready) {
+ dbg->warning("wegbauer_t::intern_calc_route()","No route found. Steps %i (max %i). Cost %i (max %i)",step,route_t::MAX_STEP, (tmp?tmp->g:-1), maximum);
  return -1;
  }
  else {
@@ -1251,7 +1278,7 @@
  // reached => construct route
  while(tmp != NULL) {
  route.append(tmp->gr->get_pos());
-//DBG_DEBUG("add","%i,%i",tmp->pos.x,tmp->pos.y);
+// DBG_DEBUG("add","(%s)",tmp->gr->get_pos().get_str());
  tmp = tmp->parent;
  }
 
@@ -1263,7 +1290,53 @@
  return -1;
 }
 
+bool wegbauer_t::check_for_depot(const grund_t *from, const grund_t *to) const
+{
+ // dist>1 => tunnel or bridge end
+ if (koord_distance(from->get_pos(), to->get_pos())>1) {
+ return false;
+ }
+ // no slopes
+ if (to->get_grund_hang()!=hang_t::flach) {
+ return false;
+ }
+ // depots on ground for non-elevated ways
+ if ( (bautyp&elevated_flag)==0 && !to->ist_karten_boden()) {
+ return false;
+ }
+ // check for own depot building
+ depot_t *dp = to->get_depot();
+ if(dp) {
+ return (dp->get_besitzer()==sp);
+ }
+ // check for powerline
+ leitung_t* lt = to->find<leitung_t>();
+ if (lt && !check_owner(sp, lt->get_besitzer())) {
+ return false;
+ }
+ // check for marker
+ label_t* lb = to->find<label_t>();
+ if (lb && !check_owner(sp, lb->get_besitzer())) {
+ return false;
+ }
 
+ weg_t *w;
+ switch (bautyp&bautyp_mask) {
+ case str****e: w = to->get_weg(road_wt);
+ case schiene: w = to->get_weg(track_wt);
+ case monorail: w = to->get_weg(besch->get_wtyp());
+ default: w = NULL;
+ }
+ // free end - no crossing
+ if (w && check_owner(sp, w->get_besitzer())) {
+ ribi_t::ribi r = ribi_typ( from->get_pos().get_2d(), to->get_pos().get_2d());
+ return ((r & w->get_ribi_unmasked()) == r);
+ }
+ else {
+ // free tile
+ return(!to->hat_wege());
+ }
+}
 
 void
 wegbauer_t::intern_calc_straight_route(const koord3d start, const koord3d ziel)
@@ -1497,23 +1570,25 @@
  long cost2 = intern_calc_route(start, ziel);
  INT_CHECK("wegbauer 1165");
 
- if(cost2<0) {
- // not sucessful: try backwards
- intern_calc_route(ziel,start);
- return;
- }
+ if ((bautyp&depot_flag)==0) {
+ if(cost2<0) {
+ // not sucessful: try backwards
+ intern_calc_route(ziel,start);
+ return;
+ }
 
 #ifdef REVERSE_CALC_ROUTE_TOO
- vector_tpl<koord3d> route2(0);
- swap(route, route2);
- long cost = intern_calc_route(ziel, start);
- INT_CHECK("wegbauer 1165");
+ vector_tpl<koord3d> route2(0);
+ swap(route, route2);
+ long cost = intern_calc_route(ziel, start);
+ INT_CHECK("wegbauer 1165");
 
- // the cheaper will survive ...
- if(  cost2 < cost  ||  cost < 0  ) {
- swap(route, route2);
+ // the cheaper will survive ...
+ if(  cost2 < cost  ||  cost < 0  ) {
+ swap(route, route2);
+ }
+#endif
  }
-#endif
  max_n = route.get_count() - 1;
 
  }
@@ -1632,10 +1707,11 @@
 
 
 
-/* returns the amount needed to built this way
+/* returns the amount needed to built this way
+ * or the monthly maintenance for the items to be built
  * author prissi
  */
-sint64 wegbauer_t::calc_costs()
+sint64 wegbauer_t::calc_costs(bool compute_maintenance)
 {
  sint64 costs=0;
 
@@ -1653,19 +1729,21 @@
  //nothing to be done
  }
  else if(besch->get_wtyp()!=powerline_wt  ||  gr->get_leitung()==NULL) {
- costs += besch->get_preis();
+ costs += compute_maintenance ? besch->get_wartung() :  besch->get_preis();
  // eventually we have to remove trees
- for(  uint8 i=0;  i<gr->get_top();  i++  ) {
- ding_t *dt = gr->obj_bei(i);
- switch(dt->get_typ()) {
- case ding_t::baum:
- costs -= welt->get_einstellungen()->cst_remove_tree;
- break;
- case ding_t::groundobj:
- costs += ((groundobj_t *)dt)->get_besch()->get_preis();
- break;
+ if (!compute_maintenance) {
+ for(  uint8 i=0;  i<gr->get_top();  i++  ) {
+ ding_t *dt = gr->obj_bei(i);
+ switch(dt->get_typ()) {
+ case ding_t::baum:
+ costs -= welt->get_einstellungen()->cst_remove_tree;
+ break;
+ case ding_t::groundobj:
+ costs += ((groundobj_t *)dt)->get_besch()->get_preis();
+ break;
 
- default: break;
+ default: break;
+ }
  }
  }
  }
@@ -1694,12 +1772,12 @@
 
  if(start->get_grund_hang()==0  ||  start->get_grund_hang()==hang_typ(zv*(-1))) {
  // bridge
- costs += bruecke_besch->get_preis()*(koord_distance(route[i], route[i+1])+1);
+ costs += (compute_maintenance ? bruecke_besch->get_wartung() : bruecke_besch->get_preis() )*(koord_distance(route[i], route[i+1])+1);
  continue;
  }
  else {
  // tunnel
- costs += tunnel_besch->get_preis()*(koord_distance(route[i], route[i+1])+1);
+ costs += (compute_maintenance ? tunnel_besch->get_wartung() : tunnel_besch->get_preis() )*(koord_distance(route[i], route[i+1])+1);
  continue;
  }
  }
@@ -1707,7 +1785,13 @@
 
  // check next tile
  }
- DBG_MESSAGE("wegbauer_t::calc_costs()","construction estimate: %f",costs/100.0);
+ if (compute_maintenance) {
+ costs <<= (welt->ticks_bits_per_tag-18);
+ DBG_MESSAGE("wegbauer_t::calc_costs()","maintenance estimate: %d",costs);
+ }
+ else {
+ DBG_MESSAGE("wegbauer_t::calc_costs()","construction estimate: %f",costs/100.0);
+ }
  return costs;
 }
 
Index: bauer/wegbauer.h
===================================================================
--- bauer/wegbauer.h (revision 2500)
+++ bauer/wegbauer.h (working copy)
@@ -68,6 +68,7 @@
  bautyp_mask=255,
  bot_flag=0x100, // do not connect to other ways
  elevated_flag=0x200, // elevated structure
+ depot_flag=0x400, // depot
  tunnel_flag=0x800 // underground structure
  };
 
@@ -202,11 +203,17 @@
  /* returns the amount needed to built this way
  * author prissi
  */
- sint64 calc_costs();
+ sint64 calc_costs( bool compute_maintenance=false );
 
  bool check_crossing(const koord zv, const grund_t *bd,waytype_t wtyp, const spieler_t *sp) const;
  bool check_for_leitung(const koord zv, const grund_t *bd) const;
 
+ /* can we build a depot at to?
+ *
+ * @author dwachs
+ */
+ bool check_for_depot(const grund_t *from, const grund_t *to) const;
+
  void baue();
 };
 
Index: player/ai.cc
===================================================================
--- player/ai.cc (revision 2500)
+++ player/ai.cc (working copy)
@@ -513,3 +513,119 @@
 
 
 
+// builds depot near starting location
+bool ai_t::build_depot(const haus_besch_t *depot, const weg_besch_t *way, const way_obj_besch_t *wob, koord3d &start)
+{
+ if (!welt->ist_in_kartengrenzen(start.get_2d()) || depot==NULL || way==NULL) {
+ return false;
+ }
+
+ wegbauer_t::bautyp_t bt;
+ switch (way->get_wtyp()) {
+ case road_wt:   bt = wegbauer_t::str****e; break;
+ case track_wt:  bt = wegbauer_t::schiene; break;
+ case water_wt:  bt = wegbauer_t::w****er;  break;
+ default: return false;
+ }
+
+ wegbauer_t bauigel(welt, this);
+ bauigel.route_fuer( (wegbauer_t::bautyp_t)(bt | wegbauer_t::depot_flag), way, NULL, NULL);
+
+ // we won't destroy cities (and save the money)
+ bauigel.set_keep_existing_faster_ways(true);
+ bauigel.set_keep_city_roads(true);
+ bauigel.set_maximum(10000);
+
+ bauigel.calc_route(start, start);
+ //sp->log->message( "ait_road_connectf_t::build_depot","start search at (%s)", start_pos.get_str());
+ //sp->log->message( "ait_road_connectf_t::build_depot",".. in direction (%s)", (start_pos+koord(w_ribi)).get_str());
+ koord3d deppos;
+
+ if(bauigel.max_n > 0) {
+ deppos = (bauigel.get_route())[0];
+ if (!welt->lookup(deppos)->ist_karten_boden()) {
+ dbg->warning( "ai_t::build_depot","no route found to depot location on ground");
+ return false;
+ }
+ bauigel.baue();
+ dbg->message( "ai_t::build_depot","found depot location (%s)", deppos.get_str());
+ }
+ else {
+ dbg->warning( "ai_t::build_depot","no route found to depot location");
+ return false;
+ }
+ // build overhead wires
+ if (wob) {
+ wkz_wayobj_t wkz;
+ wkz.default_param = wob->get_name();
+ wkz.init( welt, this );
+ wkz.work( welt, this, start );
+ wkz.work( welt, this, deppos );
+ wkz.exit( welt, this );
+ }
+ // build depot
+ depot_t *dp = welt->lookup(deppos)->get_depot();
+ if (!dp) {
+ if (!call_general_tool(WKZ_DEPOT, deppos.get_2d(), depot->get_name())) {
+ dbg->warning( "ait_road_connectf_t::build_depot","build depot failed at %s", deppos.get_str());
+ return(false);
+ }
+ }
+ start = deppos;
+ return true;
+}
+
+bool ai_t::remove_depot(koord3d start)
+{
+ grund_t *gr = welt->lookup(start);
+ if (gr==NULL) {
+ return false;
+ }
+ depot_t *dp = gr->get_depot();
+ if (!dp || !check_owner(dp->get_besitzer(), this)) {
+ return false;
+ }
+ // delete depot
+ wkz_remover_t wkz;
+ wkz.init(welt, this);
+ while(gr->get_depot()) {
+ if (wkz.work(welt, this, start)!=NULL) return false;
+ }
+
+ // find connecting way until first junction / station / ....
+ waytype_t wt = gr->get_weg_nr(0)->get_waytype();
+ ribi_t::ribi ribi = gr->get_weg(wt)->get_ribi_unmasked();
+
+ for (uint8 i=0; i<100; i++) {
+ grund_t *to;
+ if (! gr->get_neighbour(to, wt, ribi)  || to->get_halt().is_bound() || !check_owner(to->get_weg(wt)->get_besitzer(), this)) {
+ break;
+ }
+ gr = to;
+ ribi = gr->get_weg(wt)->get_ribi_unmasked() & (~ ribi_t::rueckwaerts(ribi));
+ if (! ribi_t::ist_einfach(ribi)) {
+ break;
+ }
+ }
+ koord3d end = gr->get_pos();
+
+ // .. and delete it
+ if (start != end) {
+ char param[16];
+ sprintf( param, "%i", wt );
+ wkz_wayremover_t wkz2;
+ wkz2.default_param = param;
+ wkz2.init(welt, this);
+ wkz2.work(welt, this, start);
+ if (wkz2.work(welt, this, end)) {
+ return false;
+ }
+ }
+ // check end tile
+ gr = welt->lookup(end);
+ if (gr->get_weg(wt) && ribi_t::ist_einfach(gr->get_weg(wt)->get_ribi_unmasked())) {
+ return gr->remove_everything_from_way(this, wt, ribi_t::keine);
+ }
+ return true;
+}
+
Index: player/ai.h
===================================================================
--- player/ai.h (revision 2500)
+++ player/ai.h (working copy)
@@ -16,6 +16,8 @@
 
 cl**** karte_t;
 cl**** ware_besch_t;
+cl**** haus_besch_t;
+cl**** way_obj_besch_t;
 
 /**
  * bauplatz_mit_str****e_sucher_t:
@@ -66,6 +68,10 @@
 
  // builds a round between those two places or returns false
  bool create_simple_road_transport(koord platz1, koord size1, koord platz2, koord size2, const weg_besch_t *road );
+
+ // builds depot near starting location
+ bool build_depot(const haus_besch_t *depot, const weg_besch_t *way, const way_obj_besch_t *wob, koord3d &start);
+ bool remove_depot(koord3d start);
 };
 
 #endif
Index: player/ai_goods.cc
===================================================================
--- player/ai_goods.cc (revision 2500)
+++ player/ai_goods.cc (working copy)
@@ -454,11 +454,14 @@
  start_location = 1;
  }
 
- // calculate vehicle start position
+ // build depot
  koord3d startpos=(start_location==0)?pos1:pos2;
- ribi_t::ribi w_ribi = welt->lookup(startpos)->get_weg_ribi_unmasked(road_wt);
- // now start all vehicle one field before, so they load immediately
- startpos = welt->lookup(koord(startpos.get_2d())+koord(w_ribi))->get_kartenboden()->get_pos();
+ if (!build_depot(hausbauer_t::get_random_station(haus_besch_t::depot, road_wt, welt->get_timeline_year_month(), 0), road_weg, NULL, startpos)) {
+ // if building depot failed calculate vehicle start position
+ ribi_t::ribi w_ribi = welt->lookup(startpos)->get_weg_ribi_unmasked(road_wt);
+ // now start all vehicle one field before, so they load immediately
+ startpos = welt->lookup(koord(startpos.get_2d())+koord(w_ribi))->get_kartenboden()->get_pos();
+ }
 
  // since 86.01 we use lines for road vehicles ...
  schedule_t *fpl=new autofahrplan_t();
@@ -505,9 +508,10 @@
  koord3d pos2 = welt->lookup(platz2)->get_kartenboden()->get_pos();
 
  // probably need to electrify the track?
+ const way_obj_besch_t *e = NULL;
  if(  rail_engine->get_engine_type()==vehikel_besch_t::electric  ) {
  // we need overhead wires
- const way_obj_besch_t *e = wayobj_t::wayobj_search(track_wt,overheadlines_wt,welt->get_timeline_year_month());
+ e = wayobj_t::wayobj_search(track_wt,overheadlines_wt,welt->get_timeline_year_month());
  wkz_wayobj_t wkz;
  wkz.default_param = e->get_name();
  wkz.init( welt, this );
@@ -516,9 +520,12 @@
  wkz.exit( welt, this );
  }
 
- koord diff1( sgn(size1.x), sgn(size1.y) );
- vehikel_t* v = vehikelbauer_t::baue(pos1+size1-diff1, this, NULL, rail_engine);
+ // build depot
+ koord3d startpos = pos1;
+ build_depot(hausbauer_t::get_random_station(haus_besch_t::depot, track_wt, welt->get_timeline_year_month(), 0), rail_weg, e, startpos);
 
+ vehikel_t* v = vehikelbauer_t::baue(startpos, this, NULL, rail_engine);
+
  // V.Meyer: give the new convoi name from first vehicle
  cnv->set_name(rail_engine->get_name());
  cnv->add_vehikel( v );
@@ -530,7 +537,7 @@
  */
  for(int i = 0; i < anz_vehikel; i++) {
  // use the vehicle we searched before
- vehikel_t* v = vehikelbauer_t::baue(pos1+size1-diff1, this, NULL, rail_vehicle);
+ vehikel_t* v = vehikelbauer_t::baue(startpos, this, NULL, rail_vehicle);
  cnv->add_vehikel( v );
  }
 
@@ -544,6 +551,7 @@
  cnv->set_schedule(fpl);
  welt->sync_add( cnv );
  cnv->start();
+
 }
 
 
@@ -1183,12 +1191,13 @@
  linehandle_t line = cnv->get_line();
  DBG_MESSAGE("ai_goods_t::do_ki()","%s retires convoi %s!", get_name(), cnv->get_name());
 
- koord3d start_pos, end_pos;
+ koord3d start_pos, end_pos, dep_pos;
  schedule_t *fpl = cnv->get_schedule();
  if(fpl  &&  fpl->get_count()>1) {
  start_pos = fpl->eintrag[0].pos;
  end_pos = fpl->eintrag[1].pos;
  }
+ dep_pos = cnv->get_home_depot();
 
  cnv->self_destruct();
  cnv->step(); // to really get rid of it
@@ -1217,6 +1226,21 @@
  // delete harbour
  call_general_tool( WKZ_REMOVER, water_stop, NULL );
  }
+ // depot unused?
+ bool remove_dep = true;
+ if (line.is_bound()) {
+ vector_tpl<linehandle_t> lines;
+ simlinemgmt.get_lines( line.is_bound() ? line->get_linetype() : simline_t::trainline, &lines );
+ for (vector_tpl<linehandle_t>::const_iterator iter = lines.begin(), end = lines.end(); iter != end; iter++) {
+ linehandle_t line = *iter;
+ if (line->count_convoys()>0 && line->get_convoy(0)->get_home_depot()==dep_pos) {
+ remove_dep = false;
+ break;
+ }
+ }
+ }
+ // delete depot
+ if (remove_dep) remove_depot(dep_pos);
  }
 
  if(wt==track_wt) {
Parsley, sage, rosemary, and maggikraut.

Re: AI: build depot

Reply #3
Well, rewriting the wayfinder for the depot search in not really nice, from the standpoint of cleanlyness imho. I would rather suggest an externsion like find_route, similar as for convois, wiht an appropriate cl**** to check destination conditions. The may be also needed for further AI-route searching developments.

In one situation there was also a depot build by crossing lines. . In this case not harm was done, but it could have been much worse.