News:

Simutrans Sites
Know our official sites. Find tools and resources for Simutrans.

AI: build depot

Started by Dwachs, May 30, 2009, 01:41:08 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dwachs

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 passenger AI does not no this new stuff yet.
Parsley, sage, rosemary, and maggikraut.

prissi

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.

Dwachs

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) {
Parsley, sage, rosemary, and maggikraut.

prissi

#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 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. . In this case not harm was done, but it could have been much worse.