The International Simutrans Forum

Development => Patches & Projects => Considered Patches => Topic started by: Dwachs on May 30, 2009, 01:41:08 PM

Title: AI: build depot
Post by: Dwachs on May 30, 2009, 01:41:08 PM
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:
(http://simutrans-germany.com/files/upload/ai-depot.png)

This is only implemented and tested for rail and road transport. Also the passenger AI does not no this new stuff yet.
Title: Re: AI: build depot
Post by: prissi on May 31, 2009, 09:10:12 PM
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.
Title: Re: AI: build depot
Post by: Dwachs on June 01, 2009, 09:15:45 AM
patch against 2500.

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 strasse: 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::strasse; break;
+ case track_wt:  bt = wegbauer_t::schiene; break;
+ case water_wt:  bt = wegbauer_t::wasser;  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 @@

class karte_t;
class ware_besch_t;
+class haus_besch_t;
+class way_obj_besch_t;

/**
  * bauplatz_mit_strasse_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) {
Title: Re: AI: build depot
Post by: prissi on June 17, 2009, 08:03:41 PM
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 class 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. (http://simutrans-germany.com/files/upload/simscr03.png). In this case not harm was done, but it could have been much worse.