(svn r111) -Fix: converted all linebreaks to UNIX-linebreak (\n)

This commit is contained in:
truelight 2004-08-22 15:56:56 +00:00
parent 8644360264
commit 309ebe5f3f
9 changed files with 2746 additions and 2746 deletions

486
ai.h
View File

@ -1,243 +1,243 @@
#ifndef AI_H
#define AI_H
#include "aystar.h"
/*
* These defines can be altered to change the behavoir of the AI
*
* WARNING:
* This can also alter the AI in a negative way. I will never claim these settings
* are perfect, but don't change them if you don't know what the effect is.
*/
// How many times it the H multiplied. The higher, the more it will go straight to the
// end point. The lower, how more it will find the route with the lowest cost.
// also: the lower, the longer it takes before route is calculated..
#define AI_PATHFINDER_H_MULTIPLER 100
// How many loops may AyStar do before it stops
// 0 = infinite
#define AI_PATHFINDER_LOOPS_PER_TICK 5
// How long may the AI search for one route?
// 0 = infinite
// This number is the number of tiles tested.
// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks
// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts
// (that is: if the AI is on VERY FAST! :p
#define AI_PATHFINDER_MAX_SEARCH_NODES 5000
// If you enable this, the AI is not allowed to make 90degree turns
#define AI_PATHFINDER_NO_90DEGREES_TURN
// Below are defines for the g-calculation
// Standard penalty given to a tile
#define AI_PATHFINDER_PENALTY 150
// The penalty given to a tile that is going up
#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450
// Changing direction is a penalty, to prevent curved ways (with that: slow ways)
#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200
// Same penalty, only for when road already exists
#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50
// A diagonal track cost the same as a straigh, but a diagonal is faster... so give
// a bonus for using diagonal track
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
#define AI_PATHFINDER_DIAGONAL_BONUS 95
#else
#define AI_PATHFINDER_DIAGONAL_BONUS 75
#endif
// If a roadblock already exists, it gets a bonus
#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140
// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation
#define AI_PATHFINDER_CURVE_PENALTY 200
// Penalty a bridge gets per length
#define AI_PATHFINDER_BRIDGE_PENALTY 180
// The penalty for a bridge going up
#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000
// Tunnels are expensive...
// Because of that, every tile the cost is increased with 1/8th of his value
// This is also true if you are building a tunnel yourself
#define AI_PATHFINDER_TUNNEL_PENALTY 350
/*
* Ai_New defines
*/
// How long may we search cities and industry for a new route?
#define AI_LOCATE_ROUTE_MAX_COUNTER 200
// How many days must there be between building the first station and the second station
// within one city. This number is in days and should be more then 4 months.
#define AI_CHECKCITY_DATE_BETWEEN 180
// How many cargo is needed for one station in a city?
#define AI_CHECKCITY_CARGO_PER_STATION 60
// How much cargo must there not be used in a city before we can build a new station?
#define AI_CHECKCITY_NEEDED_CARGO 50
// When there is already a station which takes the same good and the rating of that
// city is higher then this numer, we are not going to attempt to build anything
// there
#define AI_CHECKCITY_CARGO_RATING 50
// But, there is a chance of 1 out of this number, that we do ;)
#define AI_CHECKCITY_CARGO_RATING_CHANCE 5
// If a city is too small to contain a station, there is a small chance
// that we still do so.. just to make the city bigger!
#define AI_CHECKCITY_CITY_CHANCE 5
// This number indicates for every unit of cargo, how many tiles two stations maybe be away
// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units
// of the cargo in the other station, both stations can be 96 units away from eachother, if the
// next number is 0.4.
#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4
#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7
// In whole tiles, the minimum distance for a truck route
#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30
// The amount of tiles in a square from -X to +X that is scanned for a station spot
// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot
// Safe values are between 15 and 5
#define AI_FINDSTATION_TILE_RANGE 10
// Building on normal speed goes very fast. Idle this amount of ticks between every
// building part. It is calculated like this: (4 - competitor_speed) * num + 1
// where competitor_speed is between 0 (very slow) to 4 (very fast)
#define AI_BUILDPATH_PAUSE 10
// Minimum % of reliabilty a vehicle has to have before the AI buys it
#define AI_VEHICLE_MIN_RELIABILTY 60
// The minimum amount of money a player should always have
#define AI_MINIMUM_MONEY 15000
// If the most cheap route is build, how much is it going to cost..
// This is to prevent the AI from trying to build a route which can not be paid for
#define AI_MINIMUM_BUS_ROUTE_MONEY 25000
#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000
// The minimum amount of money before we are going to repay any money
#define AI_MINIMUM_LOAN_REPAY_MONEY 40000
// How many repays do we do if we have enough money to do so?
// Every repay is 10000
#define AI_LOAN_REPAY 2
// How much income must we have before paying back a loan? Month-based (and looked at the last month)
#define AI_MINIMUM_INCOME_FOR_LOAN 7000
// If there is <num> time as much cargo in the station then the vehicle can handle
// reuse the station instead of building a new one!
#define AI_STATION_REUSE_MULTIPLER 2
// No more then this amount of vehicles per station..
#define AI_CHECK_MAX_VEHICLE_PER_STATION 10
// How many thick between building 2 vehicles
#define AI_BUILD_VEHICLE_TIME_BETWEEN 74
/*
* End of defines
*/
// This stops 90degrees curves
static const byte _illegal_curves[6] = {
255, 255, // Horz and vert, don't have the effect
5, // upleft and upright are not valid
4, // downright and downleft are not valid
2, // downleft and upleft are not valid
3, // upright and downright are not valid
};
static const TileIndexDiff _tiles_around[4] = {
TILE_XY(-1,0),
TILE_XY(0,1),
TILE_XY(1,0),
TILE_XY(0,-1),
};
enum {
AI_STATE_STARTUP = 0,
AI_STATE_FIRST_TIME,
AI_STATE_NOTHING,
AI_STATE_WAKE_UP,
AI_STATE_LOCATE_ROUTE,
AI_STATE_FIND_STATION,
AI_STATE_FIND_PATH,
AI_STATE_FIND_DEPOT,
AI_STATE_VERIFY_ROUTE,
AI_STATE_BUILD_STATION,
AI_STATE_BUILD_PATH,
AI_STATE_BUILD_DEPOT,
AI_STATE_BUILD_VEHICLE,
AI_STATE_GIVE_ORDERS,
AI_STATE_START_VEHICLE,
AI_STATE_REPAY_MONEY,
AI_STATE_ACTION_DONE,
AI_STATE_STOP, // Temporary function to stop the AI
};
// Used for tbt (train/bus/truck)
enum {
AI_TRAIN = 0,
AI_BUS,
AI_TRUCK,
};
enum {
AI_ACTION_NONE = 0,
AI_ACTION_BUS_ROUTE,
AI_ACTION_TRUCK_ROUTE,
AI_ACTION_REPAY_LOAN,
};
// Used for from_type/to_type
enum {
AI_NO_TYPE = 0,
AI_CITY,
AI_INDUSTRY,
};
#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry)
#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route
#define AI_STATION_RANGE TILE_XY(TILE_X_MAX, TILE_Y_MAX)
#define AI_PATHFINDER_NO_DIRECTION (byte)-1
// Flags used in user_data
#define AI_PATHFINDER_FLAG_BRIDGE 1
#define AI_PATHFINDER_FLAG_TUNNEL 2
// A macro for mp_street, where 0x20 is depot
// mp_tunnelbridge, where 0xf0 is a bridge, and 0x4/0x2 means: roadtunnel/bridge
#define AI_PATHFINDER_IS_ROAD(tile) ((IS_TILETYPE(tile, MP_STREET) && !(_map5[tile] & 0x20)) || \
(IS_TILETYPE(tile, MP_TUNNELBRIDGE) && \
(((_map5[tile] & 0x80) == 0 && (_map5[tile] & 0x4) == 0x4) || \
((_map5[tile] & 0x80) != 0 && (_map5[tile] & 0x2) == 0x2))))
typedef void AiNew_StateFunction(Player *p);
// ai_new.c
void AiNewDoGameLoop(Player *p);
// ai_pathfinder.c
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo);
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo);
// ai_shared.c
int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c);
int AiNew_GetRoadDirection(uint tile_a, uint tile_b, uint tile_c);
int AiNew_GetDirection(uint tile_a, uint tile_b);
// ai_build.c
bool AiNew_Build_CompanyHQ(Player *p, uint tile);
int AiNew_Build_Station(Player *p, byte type, uint tile, byte length, byte numtracks, byte direction, byte flag);
int AiNew_Build_Bridge(Player *p, uint tile_a, uint tile_b, byte flag);
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag);
int AiNew_PickVehicle(Player *p);
int AiNew_Build_Vehicle(Player *p, uint tile, byte flag);
int AiNew_Build_Depot(Player *p, uint tile, byte direction, byte flag);
#endif
#ifndef AI_H
#define AI_H
#include "aystar.h"
/*
* These defines can be altered to change the behavoir of the AI
*
* WARNING:
* This can also alter the AI in a negative way. I will never claim these settings
* are perfect, but don't change them if you don't know what the effect is.
*/
// How many times it the H multiplied. The higher, the more it will go straight to the
// end point. The lower, how more it will find the route with the lowest cost.
// also: the lower, the longer it takes before route is calculated..
#define AI_PATHFINDER_H_MULTIPLER 100
// How many loops may AyStar do before it stops
// 0 = infinite
#define AI_PATHFINDER_LOOPS_PER_TICK 5
// How long may the AI search for one route?
// 0 = infinite
// This number is the number of tiles tested.
// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks
// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts
// (that is: if the AI is on VERY FAST! :p
#define AI_PATHFINDER_MAX_SEARCH_NODES 5000
// If you enable this, the AI is not allowed to make 90degree turns
#define AI_PATHFINDER_NO_90DEGREES_TURN
// Below are defines for the g-calculation
// Standard penalty given to a tile
#define AI_PATHFINDER_PENALTY 150
// The penalty given to a tile that is going up
#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450
// Changing direction is a penalty, to prevent curved ways (with that: slow ways)
#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200
// Same penalty, only for when road already exists
#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50
// A diagonal track cost the same as a straigh, but a diagonal is faster... so give
// a bonus for using diagonal track
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
#define AI_PATHFINDER_DIAGONAL_BONUS 95
#else
#define AI_PATHFINDER_DIAGONAL_BONUS 75
#endif
// If a roadblock already exists, it gets a bonus
#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140
// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation
#define AI_PATHFINDER_CURVE_PENALTY 200
// Penalty a bridge gets per length
#define AI_PATHFINDER_BRIDGE_PENALTY 180
// The penalty for a bridge going up
#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000
// Tunnels are expensive...
// Because of that, every tile the cost is increased with 1/8th of his value
// This is also true if you are building a tunnel yourself
#define AI_PATHFINDER_TUNNEL_PENALTY 350
/*
* Ai_New defines
*/
// How long may we search cities and industry for a new route?
#define AI_LOCATE_ROUTE_MAX_COUNTER 200
// How many days must there be between building the first station and the second station
// within one city. This number is in days and should be more then 4 months.
#define AI_CHECKCITY_DATE_BETWEEN 180
// How many cargo is needed for one station in a city?
#define AI_CHECKCITY_CARGO_PER_STATION 60
// How much cargo must there not be used in a city before we can build a new station?
#define AI_CHECKCITY_NEEDED_CARGO 50
// When there is already a station which takes the same good and the rating of that
// city is higher then this numer, we are not going to attempt to build anything
// there
#define AI_CHECKCITY_CARGO_RATING 50
// But, there is a chance of 1 out of this number, that we do ;)
#define AI_CHECKCITY_CARGO_RATING_CHANCE 5
// If a city is too small to contain a station, there is a small chance
// that we still do so.. just to make the city bigger!
#define AI_CHECKCITY_CITY_CHANCE 5
// This number indicates for every unit of cargo, how many tiles two stations maybe be away
// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units
// of the cargo in the other station, both stations can be 96 units away from eachother, if the
// next number is 0.4.
#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4
#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7
// In whole tiles, the minimum distance for a truck route
#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30
// The amount of tiles in a square from -X to +X that is scanned for a station spot
// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot
// Safe values are between 15 and 5
#define AI_FINDSTATION_TILE_RANGE 10
// Building on normal speed goes very fast. Idle this amount of ticks between every
// building part. It is calculated like this: (4 - competitor_speed) * num + 1
// where competitor_speed is between 0 (very slow) to 4 (very fast)
#define AI_BUILDPATH_PAUSE 10
// Minimum % of reliabilty a vehicle has to have before the AI buys it
#define AI_VEHICLE_MIN_RELIABILTY 60
// The minimum amount of money a player should always have
#define AI_MINIMUM_MONEY 15000
// If the most cheap route is build, how much is it going to cost..
// This is to prevent the AI from trying to build a route which can not be paid for
#define AI_MINIMUM_BUS_ROUTE_MONEY 25000
#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000
// The minimum amount of money before we are going to repay any money
#define AI_MINIMUM_LOAN_REPAY_MONEY 40000
// How many repays do we do if we have enough money to do so?
// Every repay is 10000
#define AI_LOAN_REPAY 2
// How much income must we have before paying back a loan? Month-based (and looked at the last month)
#define AI_MINIMUM_INCOME_FOR_LOAN 7000
// If there is <num> time as much cargo in the station then the vehicle can handle
// reuse the station instead of building a new one!
#define AI_STATION_REUSE_MULTIPLER 2
// No more then this amount of vehicles per station..
#define AI_CHECK_MAX_VEHICLE_PER_STATION 10
// How many thick between building 2 vehicles
#define AI_BUILD_VEHICLE_TIME_BETWEEN 74
/*
* End of defines
*/
// This stops 90degrees curves
static const byte _illegal_curves[6] = {
255, 255, // Horz and vert, don't have the effect
5, // upleft and upright are not valid
4, // downright and downleft are not valid
2, // downleft and upleft are not valid
3, // upright and downright are not valid
};
static const TileIndexDiff _tiles_around[4] = {
TILE_XY(-1,0),
TILE_XY(0,1),
TILE_XY(1,0),
TILE_XY(0,-1),
};
enum {
AI_STATE_STARTUP = 0,
AI_STATE_FIRST_TIME,
AI_STATE_NOTHING,
AI_STATE_WAKE_UP,
AI_STATE_LOCATE_ROUTE,
AI_STATE_FIND_STATION,
AI_STATE_FIND_PATH,
AI_STATE_FIND_DEPOT,
AI_STATE_VERIFY_ROUTE,
AI_STATE_BUILD_STATION,
AI_STATE_BUILD_PATH,
AI_STATE_BUILD_DEPOT,
AI_STATE_BUILD_VEHICLE,
AI_STATE_GIVE_ORDERS,
AI_STATE_START_VEHICLE,
AI_STATE_REPAY_MONEY,
AI_STATE_ACTION_DONE,
AI_STATE_STOP, // Temporary function to stop the AI
};
// Used for tbt (train/bus/truck)
enum {
AI_TRAIN = 0,
AI_BUS,
AI_TRUCK,
};
enum {
AI_ACTION_NONE = 0,
AI_ACTION_BUS_ROUTE,
AI_ACTION_TRUCK_ROUTE,
AI_ACTION_REPAY_LOAN,
};
// Used for from_type/to_type
enum {
AI_NO_TYPE = 0,
AI_CITY,
AI_INDUSTRY,
};
#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry)
#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route
#define AI_STATION_RANGE TILE_XY(TILE_X_MAX, TILE_Y_MAX)
#define AI_PATHFINDER_NO_DIRECTION (byte)-1
// Flags used in user_data
#define AI_PATHFINDER_FLAG_BRIDGE 1
#define AI_PATHFINDER_FLAG_TUNNEL 2
// A macro for mp_street, where 0x20 is depot
// mp_tunnelbridge, where 0xf0 is a bridge, and 0x4/0x2 means: roadtunnel/bridge
#define AI_PATHFINDER_IS_ROAD(tile) ((IS_TILETYPE(tile, MP_STREET) && !(_map5[tile] & 0x20)) || \
(IS_TILETYPE(tile, MP_TUNNELBRIDGE) && \
(((_map5[tile] & 0x80) == 0 && (_map5[tile] & 0x4) == 0x4) || \
((_map5[tile] & 0x80) != 0 && (_map5[tile] & 0x2) == 0x2))))
typedef void AiNew_StateFunction(Player *p);
// ai_new.c
void AiNewDoGameLoop(Player *p);
// ai_pathfinder.c
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo);
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo);
// ai_shared.c
int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c);
int AiNew_GetRoadDirection(uint tile_a, uint tile_b, uint tile_c);
int AiNew_GetDirection(uint tile_a, uint tile_b);
// ai_build.c
bool AiNew_Build_CompanyHQ(Player *p, uint tile);
int AiNew_Build_Station(Player *p, byte type, uint tile, byte length, byte numtracks, byte direction, byte flag);
int AiNew_Build_Bridge(Player *p, uint tile_a, uint tile_b, byte flag);
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag);
int AiNew_PickVehicle(Player *p);
int AiNew_Build_Vehicle(Player *p, uint tile, byte flag);
int AiNew_Build_Depot(Player *p, uint tile, byte direction, byte flag);
#endif

View File

@ -1,257 +1,257 @@
#include "stdafx.h"
#include "ttd.h"
#include "command.h"
#include "ai.h"
#include "engine.h"
// Build HQ
// Params:
// tile : tile where HQ is going to be build
bool AiNew_Build_CompanyHQ(Player *p, uint tile) {
if (DoCommandByTile(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ) == CMD_ERROR)
return false;
DoCommandByTile(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
return true;
}
// Build station
// Params:
// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station
// tile : tile where station is going to be build
// length : in case of AI_TRAIN: length of station
// numtracks : in case of AI_TRAIN: tracks of station
// direction : the direction of the station
// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it)
int AiNew_Build_Station(Player *p, byte type, uint tile, byte length, byte numtracks, byte direction, byte flag) {
if (type == AI_TRAIN)
return DoCommandByTile(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
else if (type == AI_BUS)
return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_BUS_STATION);
else
return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRUCK_STATION);
}
// Builds a brdige. The second best out of the ones available for this player
// Params:
// tile_a : starting point
// tile_b : end point
// flag : flag passed to DoCommand
int AiNew_Build_Bridge(Player *p, uint tile_a, uint tile_b, byte flag) {
int bridge_type, bridge_len, type, type2;
// Find a good bridgetype (the best money can buy)
bridge_len = GetBridgeLength(tile_a, tile_b);
type = type2 = 0;
for (bridge_type = MAX_BRIDGES-1; bridge_type >= 0; bridge_type--) {
if (CheckBridge_Stuff(bridge_type, bridge_len)) {
type2 = type;
type = bridge_type;
// We found two bridges, exit
if (type2 != 0)
break;
}
}
// There is only one bridge that can be build..
if (type2 == 0 && type != 0) type2 = type;
// Now, simply, build the bridge!
if (p->ainew.tbt == AI_TRAIN)
return DoCommandByTile(tile_a, tile_b, (0<<8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
else
return DoCommandByTile(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
}
// Build the route part by part
// Basicly what this function do, is build that amount of parts of the route
// that go in the same direction. It sets 'part' to the last part of the route builded.
// The return value is the cost for the builded parts
//
// Params:
// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder
// part : Which part we need to build
//
// TODO: skip already builded road-pieces (e.g.: cityroad)
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag) {
int part = PathFinderInfo->position;
byte *route_extra = PathFinderInfo->route_extra;
TileIndex *route = PathFinderInfo->route;
int dir;
int old_dir = -1;
int cost = 0;
int res;
// We need to calculate the direction with the parent of the parent.. so we skip
// the first pieces and the last piece
if (part < 1) part = 1;
// When we are done, stop it
if (part >= PathFinderInfo->route_length - 1) { PathFinderInfo->position = -2; return 0; }
if (PathFinderInfo->rail_or_road) {
// Tunnel code
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
cost += DoCommandByTile(route[part], 0, 0, flag, CMD_BUILD_TUNNEL);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
}
// Bridge code
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
cost += AiNew_Build_Bridge(p, route[part], route[part-1], flag);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
}
// Build normal rail
// Keep it doing till we go an other way
if (route_extra[part-1] == 0 && route_extra[part] == 0) {
while (route_extra[part] == 0) {
// Get the current direction
dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]);
// Is it the same as the last one?
if (old_dir != -1 && old_dir != dir) break;
old_dir = dir;
// Build the tile
res = DoCommandByTile(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL);
if (res == CMD_ERROR) {
// Problem.. let's just abort it all!
p->ainew.state = AI_STATE_NOTHING;
return 0;
}
cost += res;
// Go to the next tile
part++;
// Check if it is still in range..
if (part >= PathFinderInfo->route_length - 1) break;
}
part--;
}
// We want to return the last position, so we go back one
PathFinderInfo->position = part;
} else {
// Tunnel code
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
cost += DoCommandByTile(route[part], 0x200, 0, flag, CMD_BUILD_TUNNEL);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
}
// Bridge code
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
cost += AiNew_Build_Bridge(p, route[part], route[part+1], flag);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
}
// Build normal road
// Keep it doing till we go an other way
// EnsureNoVehicle makes sure we don't build on a tile where a vehicle is. This way
// it will wait till the vehicle is gone..
if (route_extra[part-1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
// Get the current direction
dir = AiNew_GetRoadDirection(route[part-1], route[part], route[part+1]);
// Is it the same as the last one?
if (old_dir != -1 && old_dir != dir) break;
old_dir = dir;
// There is already some road, and it is a bridge.. don't build!!!
if (!IS_TILETYPE(route[part], MP_TUNNELBRIDGE)) {
// Build the tile
res = DoCommandByTile(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD);
// Currently, we ignore CMD_ERRORs!
if (res == CMD_ERROR && !IS_TILETYPE(route[part], MP_STREET) && (flag == DC_EXEC && !EnsureNoVehicle(route[part]))) {
// Problem.. let's just abort it all!
DEBUG(ai,0)("Darn, the route could not be builded.. aborting!");
p->ainew.state = AI_STATE_NOTHING;
return 0;
} else {
if (res != CMD_ERROR)
cost += res;
}
}
// Go to the next tile
part++;
// Check if it is still in range..
if (part >= PathFinderInfo->route_length - 1) break;
}
part--;
// We want to return the last position, so we go back one
}
if (!EnsureNoVehicle(route[part]) && flag == DC_EXEC) part--;
PathFinderInfo->position = part;
}
return cost;
}
// This functions tries to find the best vehicle for this type of cargo
// It returns vehicle_id or -1 if not found
int AiNew_PickVehicle(Player *p) {
if (p->ainew.tbt == AI_TRAIN) {
// Not supported yet
return -1;
} else {
int start, count, i, r = CMD_ERROR;
start = _cargoc.ai_roadveh_start[p->ainew.cargo];
count = _cargoc.ai_roadveh_count[p->ainew.cargo];
// Let's check it backwards.. we simply want to best engine available..
for (i=start+count-1;i>=start;i--) {
// Is it availiable?
// Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
if (!HASBIT(_engines[i].player_avail, _current_player) || _engines[i].reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
// Can we build it?
r = DoCommandByTile(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
if (r != CMD_ERROR) break;
}
// We did not find a vehicle :(
if (r == CMD_ERROR) { return -1; }
return i;
}
}
// Builds the best vehicle possible
int AiNew_Build_Vehicle(Player *p, uint tile, byte flag) {
int i = AiNew_PickVehicle(p);
if (i == -1) return CMD_ERROR;
if (p->ainew.tbt == AI_TRAIN) {
return CMD_ERROR;
} else {
return DoCommandByTile(tile, i, 0, flag, CMD_BUILD_ROAD_VEH);
}
}
int AiNew_Build_Depot(Player *p, uint tile, byte direction, byte flag) {
static const byte _roadbits_by_dir[4] = {2,1,8,4};
int r, r2;
if (p->ainew.tbt == AI_TRAIN) {
return DoCommandByTile(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
} else {
r = DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
if (r == CMD_ERROR) return r;
// Try to build the road from the depot
r2 = DoCommandByTile(tile + _tileoffs_by_dir[direction], _roadbits_by_dir[direction], 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
// If it fails, ignore it..
if (r2 == CMD_ERROR) return r;
return r + r2;
}
}
#include "stdafx.h"
#include "ttd.h"
#include "command.h"
#include "ai.h"
#include "engine.h"
// Build HQ
// Params:
// tile : tile where HQ is going to be build
bool AiNew_Build_CompanyHQ(Player *p, uint tile) {
if (DoCommandByTile(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ) == CMD_ERROR)
return false;
DoCommandByTile(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
return true;
}
// Build station
// Params:
// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station
// tile : tile where station is going to be build
// length : in case of AI_TRAIN: length of station
// numtracks : in case of AI_TRAIN: tracks of station
// direction : the direction of the station
// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it)
int AiNew_Build_Station(Player *p, byte type, uint tile, byte length, byte numtracks, byte direction, byte flag) {
if (type == AI_TRAIN)
return DoCommandByTile(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
else if (type == AI_BUS)
return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_BUS_STATION);
else
return DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRUCK_STATION);
}
// Builds a brdige. The second best out of the ones available for this player
// Params:
// tile_a : starting point
// tile_b : end point
// flag : flag passed to DoCommand
int AiNew_Build_Bridge(Player *p, uint tile_a, uint tile_b, byte flag) {
int bridge_type, bridge_len, type, type2;
// Find a good bridgetype (the best money can buy)
bridge_len = GetBridgeLength(tile_a, tile_b);
type = type2 = 0;
for (bridge_type = MAX_BRIDGES-1; bridge_type >= 0; bridge_type--) {
if (CheckBridge_Stuff(bridge_type, bridge_len)) {
type2 = type;
type = bridge_type;
// We found two bridges, exit
if (type2 != 0)
break;
}
}
// There is only one bridge that can be build..
if (type2 == 0 && type != 0) type2 = type;
// Now, simply, build the bridge!
if (p->ainew.tbt == AI_TRAIN)
return DoCommandByTile(tile_a, tile_b, (0<<8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
else
return DoCommandByTile(tile_a, tile_b, (0x80 << 8) + type2, flag | DC_AUTO, CMD_BUILD_BRIDGE);
}
// Build the route part by part
// Basicly what this function do, is build that amount of parts of the route
// that go in the same direction. It sets 'part' to the last part of the route builded.
// The return value is the cost for the builded parts
//
// Params:
// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder
// part : Which part we need to build
//
// TODO: skip already builded road-pieces (e.g.: cityroad)
int AiNew_Build_RoutePart(Player *p, Ai_PathFinderInfo *PathFinderInfo, byte flag) {
int part = PathFinderInfo->position;
byte *route_extra = PathFinderInfo->route_extra;
TileIndex *route = PathFinderInfo->route;
int dir;
int old_dir = -1;
int cost = 0;
int res;
// We need to calculate the direction with the parent of the parent.. so we skip
// the first pieces and the last piece
if (part < 1) part = 1;
// When we are done, stop it
if (part >= PathFinderInfo->route_length - 1) { PathFinderInfo->position = -2; return 0; }
if (PathFinderInfo->rail_or_road) {
// Tunnel code
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
cost += DoCommandByTile(route[part], 0, 0, flag, CMD_BUILD_TUNNEL);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
}
// Bridge code
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
cost += AiNew_Build_Bridge(p, route[part], route[part-1], flag);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
}
// Build normal rail
// Keep it doing till we go an other way
if (route_extra[part-1] == 0 && route_extra[part] == 0) {
while (route_extra[part] == 0) {
// Get the current direction
dir = AiNew_GetRailDirection(route[part-1], route[part], route[part+1]);
// Is it the same as the last one?
if (old_dir != -1 && old_dir != dir) break;
old_dir = dir;
// Build the tile
res = DoCommandByTile(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL);
if (res == CMD_ERROR) {
// Problem.. let's just abort it all!
p->ainew.state = AI_STATE_NOTHING;
return 0;
}
cost += res;
// Go to the next tile
part++;
// Check if it is still in range..
if (part >= PathFinderInfo->route_length - 1) break;
}
part--;
}
// We want to return the last position, so we go back one
PathFinderInfo->position = part;
} else {
// Tunnel code
if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
cost += DoCommandByTile(route[part], 0x200, 0, flag, CMD_BUILD_TUNNEL);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: tunnel could not be build!");
return 0;
}
return cost;
}
// Bridge code
if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
cost += AiNew_Build_Bridge(p, route[part], route[part+1], flag);
PathFinderInfo->position++;
// TODO: problems!
if (cost == CMD_ERROR) {
DEBUG(ai,0)("[AiNew - BuildPath] We have a serious problem: bridge could not be build!");
return 0;
}
return cost;
}
// Build normal road
// Keep it doing till we go an other way
// EnsureNoVehicle makes sure we don't build on a tile where a vehicle is. This way
// it will wait till the vehicle is gone..
if (route_extra[part-1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicle(route[part]))) {
// Get the current direction
dir = AiNew_GetRoadDirection(route[part-1], route[part], route[part+1]);
// Is it the same as the last one?
if (old_dir != -1 && old_dir != dir) break;
old_dir = dir;
// There is already some road, and it is a bridge.. don't build!!!
if (!IS_TILETYPE(route[part], MP_TUNNELBRIDGE)) {
// Build the tile
res = DoCommandByTile(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD);
// Currently, we ignore CMD_ERRORs!
if (res == CMD_ERROR && !IS_TILETYPE(route[part], MP_STREET) && (flag == DC_EXEC && !EnsureNoVehicle(route[part]))) {
// Problem.. let's just abort it all!
DEBUG(ai,0)("Darn, the route could not be builded.. aborting!");
p->ainew.state = AI_STATE_NOTHING;
return 0;
} else {
if (res != CMD_ERROR)
cost += res;
}
}
// Go to the next tile
part++;
// Check if it is still in range..
if (part >= PathFinderInfo->route_length - 1) break;
}
part--;
// We want to return the last position, so we go back one
}
if (!EnsureNoVehicle(route[part]) && flag == DC_EXEC) part--;
PathFinderInfo->position = part;
}
return cost;
}
// This functions tries to find the best vehicle for this type of cargo
// It returns vehicle_id or -1 if not found
int AiNew_PickVehicle(Player *p) {
if (p->ainew.tbt == AI_TRAIN) {
// Not supported yet
return -1;
} else {
int start, count, i, r = CMD_ERROR;
start = _cargoc.ai_roadveh_start[p->ainew.cargo];
count = _cargoc.ai_roadveh_count[p->ainew.cargo];
// Let's check it backwards.. we simply want to best engine available..
for (i=start+count-1;i>=start;i--) {
// Is it availiable?
// Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
if (!HASBIT(_engines[i].player_avail, _current_player) || _engines[i].reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
// Can we build it?
r = DoCommandByTile(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
if (r != CMD_ERROR) break;
}
// We did not find a vehicle :(
if (r == CMD_ERROR) { return -1; }
return i;
}
}
// Builds the best vehicle possible
int AiNew_Build_Vehicle(Player *p, uint tile, byte flag) {
int i = AiNew_PickVehicle(p);
if (i == -1) return CMD_ERROR;
if (p->ainew.tbt == AI_TRAIN) {
return CMD_ERROR;
} else {
return DoCommandByTile(tile, i, 0, flag, CMD_BUILD_ROAD_VEH);
}
}
int AiNew_Build_Depot(Player *p, uint tile, byte direction, byte flag) {
static const byte _roadbits_by_dir[4] = {2,1,8,4};
int r, r2;
if (p->ainew.tbt == AI_TRAIN) {
return DoCommandByTile(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
} else {
r = DoCommandByTile(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
if (r == CMD_ERROR) return r;
// Try to build the road from the depot
r2 = DoCommandByTile(tile + _tileoffs_by_dir[direction], _roadbits_by_dir[direction], 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
// If it fails, ignore it..
if (r2 == CMD_ERROR) return r;
return r + r2;
}
}

2442
ai_new.c

File diff suppressed because it is too large Load Diff

View File

@ -1,457 +1,457 @@
#include "stdafx.h"
#include "ttd.h"
#include "command.h"
#include "ai.h"
#define TEST_STATION_NO_DIR 0xFF
// Tests if a station can be build on the given spot
// TODO: make it train compatible
bool TestCanBuildStationHere(uint tile, byte dir) {
Player *p = DEREF_PLAYER(_current_player);
if (dir == TEST_STATION_NO_DIR) {
// TODO: currently we only allow spots that can be access from al 4 directions...
// should be fixed!!!
for (dir=0;dir<4;dir++) {
int res = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
if (res != CMD_ERROR)
return true;
}
return false;
} else {
int res = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
if (res == CMD_ERROR)
return false;
}
return true;
}
// Checks if a tile 'a' is between the tiles 'b' and 'c'
#define TILES_BETWEEN(a,b,c) (GET_TILE_X(a) >= GET_TILE_X(b) && GET_TILE_X(a) <= GET_TILE_X(c) && GET_TILE_Y(a) >= GET_TILE_Y(b) && GET_TILE_Y(a) <= GET_TILE_Y(c))
// Check if the current tile is in our end-area
int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
// It is not allowed to have a station on the end of a bridge or tunnel ;)
if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
if (IS_TILETYPE(current->path.node.tile, MP_CLEAR) || IS_TILETYPE(current->path.node.tile, MP_TREES))
if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile,AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
return AYSTAR_FOUND_END_NODE;
return AYSTAR_DONE;
}
// Calculates the hash
// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
uint AiPathFinder_Hash(uint key1, uint key2) {
return (GET_TILE_X(key1) & 0x1F) + ((GET_TILE_Y(key1) & 0x1F) << 5);
}
// Clear the memory of all the things
void AyStar_AiPathFinder_Free(AyStar *aystar) {
AyStarMain_Free(aystar);
free(aystar);
}
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
// This creates the AiPathFinder
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo) {
PathNode start_node;
uint x,y;
// Create AyStar
AyStar *result = malloc(sizeof(AyStar));
init_AyStar(result, AiPathFinder_Hash, 1 << 10);
// Set the function pointers
result->CalculateG = AyStar_AiPathFinder_CalculateG;
result->CalculateH = AyStar_AiPathFinder_CalculateH;
result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
result->free = AyStar_AiPathFinder_Free;
// Set some information
result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
result->max_path_cost = 0;
result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
// Set the user_data to the PathFinderInfo
result->user_target = PathFinderInfo;
// Set the start node
start_node.parent = NULL;
start_node.node.direction = 0;
start_node.node.user_data[0] = 0;
// Now we add all the starting tiles
for (x=GET_TILE_X(PathFinderInfo->start_tile_tl);x<=GET_TILE_X(PathFinderInfo->start_tile_br);x++) {
for (y=GET_TILE_Y(PathFinderInfo->start_tile_tl);y<=GET_TILE_Y(PathFinderInfo->start_tile_br);y++) {
start_node.node.tile = TILE_XY(x,y);
result->addstart(result, &start_node.node);
}
}
return result;
}
// To reuse AyStar we sometimes have to clean all the memory
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo) {
PathNode start_node;
uint x,y;
aystar->clear(aystar);
// Set the user_data to the PathFinderInfo
aystar->user_target = PathFinderInfo;
// Set the start node
start_node.parent = NULL;
start_node.node.direction = 0;
start_node.node.user_data[0] = 0;
start_node.node.tile = PathFinderInfo->start_tile_tl;
// Now we add all the starting tiles
for (x=GET_TILE_X(PathFinderInfo->start_tile_tl);x<=GET_TILE_X(PathFinderInfo->start_tile_br);x++) {
for (y=GET_TILE_Y(PathFinderInfo->start_tile_tl);y<=GET_TILE_Y(PathFinderInfo->start_tile_br);y++) {
if (!(IS_TILETYPE(TILE_XY(x,y), MP_CLEAR) || IS_TILETYPE(TILE_XY(x,y), MP_TREES))) continue;
if (!TestCanBuildStationHere(TILE_XY(x,y),TEST_STATION_NO_DIR)) continue;
start_node.node.tile = TILE_XY(x,y);
aystar->addstart(aystar, &start_node.node);
}
}
}
// The h-value, simple calculation
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, r2;
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
r = GetTileDist(current->tile, PathFinderInfo->end_tile_tl + _tiles_around[PathFinderInfo->end_direction]);
r2 = GetTileDist(current->tile, PathFinderInfo->end_tile_br + _tiles_around[PathFinderInfo->end_direction]);
} else {
// No direction, so just get the fastest route to the station
r = GetTileDist(current->tile, PathFinderInfo->end_tile_tl);
r2 = GetTileDist(current->tile, PathFinderInfo->end_tile_br);
}
// See if the bottomright is faster then the topleft..
if (r2 < r) r = r2;
return r * AI_PATHFINDER_H_MULTIPLER;
}
// We found the end.. let's get the route back and put it in an array
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int i = 0;
PathNode *parent = &current->path;
do {
PathFinderInfo->route_extra[i] = parent->node.user_data[0];
PathFinderInfo->route[i++] = parent->node.tile;
if (i > lengthof(PathFinderInfo->route)) {
// We ran out of space for the PathFinder
DEBUG(ai,0)("[AiPathFinder] Ran out of spacein the route[] array!!!");
PathFinderInfo->route_length = -1; // -1 indicates out of space
return;
}
parent = parent->parent;
} while (parent != NULL);
PathFinderInfo->route_length = i;
DEBUG(ai,1)("[Ai-PathFinding] Found route of %d nodes long in %d nodes of searching",i,Hash_Size(&aystar->ClosedListHash));
}
// What tiles are around us.
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current) {
int i, r, dir;
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
aystar->num_neighbours = 0;
// Go through all surrounding tiles and check if they are within the limits
for (i=0;i<4;i++) {
if (GET_TILE_X(_tiles_around[i] + current->path.node.tile) > 1 && GET_TILE_X(_tiles_around[i] + current->path.node.tile) < TILE_X_MAX - 1 &&
GET_TILE_Y(_tiles_around[i] + current->path.node.tile) > 1 && GET_TILE_Y(_tiles_around[i] + current->path.node.tile) < TILE_Y_MAX - 1) {
// We also directly test if the current tile can connect to this tile..
// We do this simply by just building the tile!
// If the next step is a bridge, we have to enter it the right way
if (!PathFinderInfo->rail_or_road && AI_PATHFINDER_IS_ROAD(current->path.node.tile + _tiles_around[i])) {
if (IS_TILETYPE(current->path.node.tile + _tiles_around[i], MP_TUNNELBRIDGE)) {
// An existing bridge... let's test the direction ;)
if ((_map5[current->path.node.tile + _tiles_around[i]] & 1) != (i & 1)) continue;
// This problem only is valid for tunnels:
// When the last tile was not yet a tunnel, check if we enter from the right side..
if (!IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE) && (_map5[current->path.node.tile + _tiles_around[i]] & 0x80) == 0) {
if ((i^2) != (_map5[current->path.node.tile + _tiles_around[i]] & 3)) continue;
}
}
}
// But also if we are on a bridge, we can only move a certain direction
if (!PathFinderInfo->rail_or_road && AI_PATHFINDER_IS_ROAD(current->path.node.tile)) {
if (IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE)) {
// An existing bridge/tunnel... let's test the direction ;)
if ((_map5[current->path.node.tile] & 1) != (i & 1)) continue;
}
}
if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
(AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
// We are a bridge/tunnel, how cool!!
// This means we can only point forward.. get the direction from the user_data
if (i != (current->path.node.user_data[0] >> 8)) continue;
}
dir = 0;
// First, check if we have a parent
if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
// If not, this means we are at the starting station
if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
// We do need a direction?
if (AiNew_GetDirection(current->path.node.tile, current->path.node.tile + _tiles_around[i]) != PathFinderInfo->start_direction)
// We are not pointing the right way, invalid tile
continue;
}
} else if (current->path.node.user_data[0] == 0) {
if (PathFinderInfo->rail_or_road) {
// Rail check
dir = AiNew_GetRailDirection(current->path.parent->node.tile, current->path.node.tile, current->path.node.tile + _tiles_around[i]);
r = DoCommandByTile(current->path.node.tile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
if (r == CMD_ERROR) continue;
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
if (current->path.parent->parent != NULL) {
// Check if we don't make a 90degree curve
int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, current->path.node.tile);
if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
continue;
}
}
#endif
} else {
// Road check
dir = AiNew_GetRoadDirection(current->path.parent->node.tile, current->path.node.tile, current->path.node.tile + _tiles_around[i]);
if (AI_PATHFINDER_IS_ROAD(current->path.node.tile)) {
if (IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE)) {
// We have a bridge, how nicely! We should mark it...
dir = 0;
} else {
// It already has road.. check if we miss any bits!
if ((_map5[current->path.node.tile] & dir) != dir) {
// We do miss some pieces :(
dir &= ~_map5[current->path.node.tile];
} else {
dir = 0;
}
}
}
// Only destruct things if it is MP_CLEAR of MP_TREES
if (dir != 0) {
r = DoCommandByTile(current->path.node.tile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
if (r == CMD_ERROR) continue;
}
}
}
// The tile can be connected
aystar->neighbours[aystar->num_neighbours].tile = _tiles_around[i] + current->path.node.tile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
aystar->neighbours[aystar->num_neighbours++].direction = 0;
}
}
// Next step, check for bridges and tunnels
if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
TileInfo ti;
// First we get the dir from this tile and his parent
int dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
// It means we can only walk with the track, so the bridge has to be in the same direction
TileIndex tile = current->path.node.tile;
TileIndex new_tile = tile;
FindLandscapeHeightByTile(&ti, tile);
// Bridges can only be build on land that is not flat
// And if there is a road or rail blocking
if (ti.tileh != 0 ||
(PathFinderInfo->rail_or_road && IS_TILETYPE(tile + _tiles_around[dir], MP_STREET)) ||
(!PathFinderInfo->rail_or_road && IS_TILETYPE(tile + _tiles_around[dir], MP_RAILWAY))) {
for (;;) {
new_tile += _tiles_around[dir];
// Precheck, is the length allowed?
if (!CheckBridge_Stuff(0,GetBridgeLength(tile, new_tile))) break;
// Check if we hit the station-tile.. we don't like that!
if (TILES_BETWEEN(new_tile,PathFinderInfo->end_tile_tl,PathFinderInfo->end_tile_br)) break;
// Try building the bridge..
r = DoCommandByTile(tile, new_tile, (0<<8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
if (r == CMD_ERROR) continue;
// We can build a bridge here.. add him to the neighbours
aystar->neighbours[aystar->num_neighbours].tile = new_tile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
aystar->neighbours[aystar->num_neighbours++].direction = 0;
// We can only have 12 neighbours, and we need 1 left for tunnels
if (aystar->num_neighbours == 11) break;
}
}
// Next, check for tunnels!
// Tunnels can only be build with tileh of 3, 6, 9 or 12, depending on the direction
// For now, we check both sides for this tile.. terraforming gives fuzzy result
if ((dir == 0 && ti.tileh == 12) ||
(dir == 1 && ti.tileh == 6) ||
(dir == 2 && ti.tileh == 3) ||
(dir == 3 && ti.tileh == 9)) {
// Now simply check if a tunnel can be build
r = DoCommandByTile(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
FindLandscapeHeightByTile(&ti, _build_tunnel_endtile);
if (r != CMD_ERROR && (ti.tileh == 3 || ti.tileh == 6 || ti.tileh == 9 || ti.tileh == 12)) {
aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
aystar->neighbours[aystar->num_neighbours++].direction = 0;
}
}
}
}
extern uint GetRailFoundation(uint tileh, uint bits);
extern uint GetRoadFoundation(uint tileh, uint bits);
extern uint GetBridgeFoundation(uint tileh, byte direction);
enum {
BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
};
// The most important function: it calculates the g-value
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, res = 0;
TileInfo ti, parent_ti;
// Gather some information about the tile..
FindLandscapeHeightByTile(&ti, current->tile);
FindLandscapeHeightByTile(&parent_ti, parent->path.node.tile);
// Check if we hit the end-tile
if (TILES_BETWEEN(current->tile,PathFinderInfo->end_tile_tl,PathFinderInfo->end_tile_br)) {
// We are at the end-tile, check if we had a direction or something...
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction)
// We are not pointing the right way, invalid tile
return AYSTAR_INVALID_NODE;
// If it was valid, drop out.. we don't build on the endtile
return 0;
}
// Give everything a small penalty
res += AI_PATHFINDER_PENALTY;
if (!PathFinderInfo->rail_or_road) {
// Road has the lovely advantage it can use other road... check if
// the current tile is road, and if so, give a good bonus
if (AI_PATHFINDER_IS_ROAD(current->tile)) {
res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
}
}
// We should give a penalty when the tile is going up or down.. this is one way to do so!
// Too bad we have to count it from the parent.. but that is not so bad
if (parent_ti.tileh != 0 && parent->path.parent != NULL) {
// Skip if the tile was from a bridge or tunnel
if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
if (PathFinderInfo->rail_or_road) {
r = GetRailFoundation(parent_ti.tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
if (r >= 15 || (r == 0 && (BRIDGE_NO_FOUNDATION & (1 << ti.tileh)))) {
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
}
} else {
if (!(AI_PATHFINDER_IS_ROAD(parent->path.node.tile) && IS_TILETYPE(parent->path.node.tile, MP_TUNNELBRIDGE))) {
r = GetRoadFoundation(parent_ti.tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
if (r >= 15 || r == 0)
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
}
}
}
}
// Are we part of a tunnel?
if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
// Tunnels are very expensive when build on long routes..
// Ironicly, we are using BridgeCode here ;)
r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
res += r + (r >> 8);
}
// Are we part of a bridge?
if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
// That means for every length a penalty
res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
// Check if we are going up or down, first for the starting point
// In user_data[0] is at the 8th bit the direction
if (!(BRIDGE_NO_FOUNDATION & (1 << parent_ti.tileh))) {
if (GetBridgeFoundation(parent_ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
// Second for the end point
if (!(BRIDGE_NO_FOUNDATION & (1 << ti.tileh))) {
if (GetBridgeFoundation(ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
if (parent_ti.tileh == 0)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
if (ti.tileh == 0)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
// To prevent the AI from taking the fastest way in tiles, but not the fastest way
// in speed, we have to give a good penalty to direction changing
// This way, we get almost the fastest way in tiles, and a very good speed on the track
if (!PathFinderInfo->rail_or_road) {
if (parent->path.parent != NULL &&
AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
// When road exists, we don't like turning, but its free, so don't be to piggy about it
if (AI_PATHFINDER_IS_ROAD(parent->path.node.tile))
res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
else
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
}
} else {
// For rail we have 1 exeption: diagonal rail..
// So we fetch 2 raildirection. That of the current one, and of the one before that
if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
// First, see if we are on diagonal path, that is better then straight path
if (dir1 > 1) { res -= AI_PATHFINDER_DIAGONAL_BONUS; }
// First see if they are different
if (dir1 != dir2) {
// dir 2 and 3 are 1 diagonal track, and 4 and 5.
if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
// It is not, so we changed of direction
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
}
if (parent->path.parent->parent->parent != NULL) {
int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
// Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
res += AI_PATHFINDER_CURVE_PENALTY;
}
}
}
}
}
// Res should never be below zero.. if so, make it zero!
if (res < 0) { res = 0; }
// Return our value
return res;
}
#include "stdafx.h"
#include "ttd.h"
#include "command.h"
#include "ai.h"
#define TEST_STATION_NO_DIR 0xFF
// Tests if a station can be build on the given spot
// TODO: make it train compatible
bool TestCanBuildStationHere(uint tile, byte dir) {
Player *p = DEREF_PLAYER(_current_player);
if (dir == TEST_STATION_NO_DIR) {
// TODO: currently we only allow spots that can be access from al 4 directions...
// should be fixed!!!
for (dir=0;dir<4;dir++) {
int res = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
if (res != CMD_ERROR)
return true;
}
return false;
} else {
int res = AiNew_Build_Station(p, p->ainew.tbt, tile, 1, 1, dir, DC_QUERY_COST);
if (res == CMD_ERROR)
return false;
}
return true;
}
// Checks if a tile 'a' is between the tiles 'b' and 'c'
#define TILES_BETWEEN(a,b,c) (GET_TILE_X(a) >= GET_TILE_X(b) && GET_TILE_X(a) <= GET_TILE_X(c) && GET_TILE_Y(a) >= GET_TILE_Y(b) && GET_TILE_Y(a) <= GET_TILE_Y(c))
// Check if the current tile is in our end-area
int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
// It is not allowed to have a station on the end of a bridge or tunnel ;)
if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
if (IS_TILETYPE(current->path.node.tile, MP_CLEAR) || IS_TILETYPE(current->path.node.tile, MP_TREES))
if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile,AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
return AYSTAR_FOUND_END_NODE;
return AYSTAR_DONE;
}
// Calculates the hash
// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
uint AiPathFinder_Hash(uint key1, uint key2) {
return (GET_TILE_X(key1) & 0x1F) + ((GET_TILE_Y(key1) & 0x1F) << 5);
}
// Clear the memory of all the things
void AyStar_AiPathFinder_Free(AyStar *aystar) {
AyStarMain_Free(aystar);
free(aystar);
}
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
// This creates the AiPathFinder
AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo) {
PathNode start_node;
uint x,y;
// Create AyStar
AyStar *result = malloc(sizeof(AyStar));
init_AyStar(result, AiPathFinder_Hash, 1 << 10);
// Set the function pointers
result->CalculateG = AyStar_AiPathFinder_CalculateG;
result->CalculateH = AyStar_AiPathFinder_CalculateH;
result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
result->free = AyStar_AiPathFinder_Free;
// Set some information
result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
result->max_path_cost = 0;
result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
// Set the user_data to the PathFinderInfo
result->user_target = PathFinderInfo;
// Set the start node
start_node.parent = NULL;
start_node.node.direction = 0;
start_node.node.user_data[0] = 0;
// Now we add all the starting tiles
for (x=GET_TILE_X(PathFinderInfo->start_tile_tl);x<=GET_TILE_X(PathFinderInfo->start_tile_br);x++) {
for (y=GET_TILE_Y(PathFinderInfo->start_tile_tl);y<=GET_TILE_Y(PathFinderInfo->start_tile_br);y++) {
start_node.node.tile = TILE_XY(x,y);
result->addstart(result, &start_node.node);
}
}
return result;
}
// To reuse AyStar we sometimes have to clean all the memory
void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo) {
PathNode start_node;
uint x,y;
aystar->clear(aystar);
// Set the user_data to the PathFinderInfo
aystar->user_target = PathFinderInfo;
// Set the start node
start_node.parent = NULL;
start_node.node.direction = 0;
start_node.node.user_data[0] = 0;
start_node.node.tile = PathFinderInfo->start_tile_tl;
// Now we add all the starting tiles
for (x=GET_TILE_X(PathFinderInfo->start_tile_tl);x<=GET_TILE_X(PathFinderInfo->start_tile_br);x++) {
for (y=GET_TILE_Y(PathFinderInfo->start_tile_tl);y<=GET_TILE_Y(PathFinderInfo->start_tile_br);y++) {
if (!(IS_TILETYPE(TILE_XY(x,y), MP_CLEAR) || IS_TILETYPE(TILE_XY(x,y), MP_TREES))) continue;
if (!TestCanBuildStationHere(TILE_XY(x,y),TEST_STATION_NO_DIR)) continue;
start_node.node.tile = TILE_XY(x,y);
aystar->addstart(aystar, &start_node.node);
}
}
}
// The h-value, simple calculation
static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, r2;
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
// The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
r = GetTileDist(current->tile, PathFinderInfo->end_tile_tl + _tiles_around[PathFinderInfo->end_direction]);
r2 = GetTileDist(current->tile, PathFinderInfo->end_tile_br + _tiles_around[PathFinderInfo->end_direction]);
} else {
// No direction, so just get the fastest route to the station
r = GetTileDist(current->tile, PathFinderInfo->end_tile_tl);
r2 = GetTileDist(current->tile, PathFinderInfo->end_tile_br);
}
// See if the bottomright is faster then the topleft..
if (r2 < r) r = r2;
return r * AI_PATHFINDER_H_MULTIPLER;
}
// We found the end.. let's get the route back and put it in an array
static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int i = 0;
PathNode *parent = &current->path;
do {
PathFinderInfo->route_extra[i] = parent->node.user_data[0];
PathFinderInfo->route[i++] = parent->node.tile;
if (i > lengthof(PathFinderInfo->route)) {
// We ran out of space for the PathFinder
DEBUG(ai,0)("[AiPathFinder] Ran out of spacein the route[] array!!!");
PathFinderInfo->route_length = -1; // -1 indicates out of space
return;
}
parent = parent->parent;
} while (parent != NULL);
PathFinderInfo->route_length = i;
DEBUG(ai,1)("[Ai-PathFinding] Found route of %d nodes long in %d nodes of searching",i,Hash_Size(&aystar->ClosedListHash));
}
// What tiles are around us.
static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current) {
int i, r, dir;
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
aystar->num_neighbours = 0;
// Go through all surrounding tiles and check if they are within the limits
for (i=0;i<4;i++) {
if (GET_TILE_X(_tiles_around[i] + current->path.node.tile) > 1 && GET_TILE_X(_tiles_around[i] + current->path.node.tile) < TILE_X_MAX - 1 &&
GET_TILE_Y(_tiles_around[i] + current->path.node.tile) > 1 && GET_TILE_Y(_tiles_around[i] + current->path.node.tile) < TILE_Y_MAX - 1) {
// We also directly test if the current tile can connect to this tile..
// We do this simply by just building the tile!
// If the next step is a bridge, we have to enter it the right way
if (!PathFinderInfo->rail_or_road && AI_PATHFINDER_IS_ROAD(current->path.node.tile + _tiles_around[i])) {
if (IS_TILETYPE(current->path.node.tile + _tiles_around[i], MP_TUNNELBRIDGE)) {
// An existing bridge... let's test the direction ;)
if ((_map5[current->path.node.tile + _tiles_around[i]] & 1) != (i & 1)) continue;
// This problem only is valid for tunnels:
// When the last tile was not yet a tunnel, check if we enter from the right side..
if (!IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE) && (_map5[current->path.node.tile + _tiles_around[i]] & 0x80) == 0) {
if ((i^2) != (_map5[current->path.node.tile + _tiles_around[i]] & 3)) continue;
}
}
}
// But also if we are on a bridge, we can only move a certain direction
if (!PathFinderInfo->rail_or_road && AI_PATHFINDER_IS_ROAD(current->path.node.tile)) {
if (IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE)) {
// An existing bridge/tunnel... let's test the direction ;)
if ((_map5[current->path.node.tile] & 1) != (i & 1)) continue;
}
}
if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
(AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
// We are a bridge/tunnel, how cool!!
// This means we can only point forward.. get the direction from the user_data
if (i != (current->path.node.user_data[0] >> 8)) continue;
}
dir = 0;
// First, check if we have a parent
if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
// If not, this means we are at the starting station
if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
// We do need a direction?
if (AiNew_GetDirection(current->path.node.tile, current->path.node.tile + _tiles_around[i]) != PathFinderInfo->start_direction)
// We are not pointing the right way, invalid tile
continue;
}
} else if (current->path.node.user_data[0] == 0) {
if (PathFinderInfo->rail_or_road) {
// Rail check
dir = AiNew_GetRailDirection(current->path.parent->node.tile, current->path.node.tile, current->path.node.tile + _tiles_around[i]);
r = DoCommandByTile(current->path.node.tile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
if (r == CMD_ERROR) continue;
#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
if (current->path.parent->parent != NULL) {
// Check if we don't make a 90degree curve
int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, current->path.node.tile);
if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
continue;
}
}
#endif
} else {
// Road check
dir = AiNew_GetRoadDirection(current->path.parent->node.tile, current->path.node.tile, current->path.node.tile + _tiles_around[i]);
if (AI_PATHFINDER_IS_ROAD(current->path.node.tile)) {
if (IS_TILETYPE(current->path.node.tile, MP_TUNNELBRIDGE)) {
// We have a bridge, how nicely! We should mark it...
dir = 0;
} else {
// It already has road.. check if we miss any bits!
if ((_map5[current->path.node.tile] & dir) != dir) {
// We do miss some pieces :(
dir &= ~_map5[current->path.node.tile];
} else {
dir = 0;
}
}
}
// Only destruct things if it is MP_CLEAR of MP_TREES
if (dir != 0) {
r = DoCommandByTile(current->path.node.tile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
if (r == CMD_ERROR) continue;
}
}
}
// The tile can be connected
aystar->neighbours[aystar->num_neighbours].tile = _tiles_around[i] + current->path.node.tile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
aystar->neighbours[aystar->num_neighbours++].direction = 0;
}
}
// Next step, check for bridges and tunnels
if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
TileInfo ti;
// First we get the dir from this tile and his parent
int dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
// It means we can only walk with the track, so the bridge has to be in the same direction
TileIndex tile = current->path.node.tile;
TileIndex new_tile = tile;
FindLandscapeHeightByTile(&ti, tile);
// Bridges can only be build on land that is not flat
// And if there is a road or rail blocking
if (ti.tileh != 0 ||
(PathFinderInfo->rail_or_road && IS_TILETYPE(tile + _tiles_around[dir], MP_STREET)) ||
(!PathFinderInfo->rail_or_road && IS_TILETYPE(tile + _tiles_around[dir], MP_RAILWAY))) {
for (;;) {
new_tile += _tiles_around[dir];
// Precheck, is the length allowed?
if (!CheckBridge_Stuff(0,GetBridgeLength(tile, new_tile))) break;
// Check if we hit the station-tile.. we don't like that!
if (TILES_BETWEEN(new_tile,PathFinderInfo->end_tile_tl,PathFinderInfo->end_tile_br)) break;
// Try building the bridge..
r = DoCommandByTile(tile, new_tile, (0<<8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
if (r == CMD_ERROR) continue;
// We can build a bridge here.. add him to the neighbours
aystar->neighbours[aystar->num_neighbours].tile = new_tile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
aystar->neighbours[aystar->num_neighbours++].direction = 0;
// We can only have 12 neighbours, and we need 1 left for tunnels
if (aystar->num_neighbours == 11) break;
}
}
// Next, check for tunnels!
// Tunnels can only be build with tileh of 3, 6, 9 or 12, depending on the direction
// For now, we check both sides for this tile.. terraforming gives fuzzy result
if ((dir == 0 && ti.tileh == 12) ||
(dir == 1 && ti.tileh == 6) ||
(dir == 2 && ti.tileh == 3) ||
(dir == 3 && ti.tileh == 9)) {
// Now simply check if a tunnel can be build
r = DoCommandByTile(tile, (PathFinderInfo->rail_or_road?0:0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
FindLandscapeHeightByTile(&ti, _build_tunnel_endtile);
if (r != CMD_ERROR && (ti.tileh == 3 || ti.tileh == 6 || ti.tileh == 9 || ti.tileh == 12)) {
aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
aystar->neighbours[aystar->num_neighbours++].direction = 0;
}
}
}
}
extern uint GetRailFoundation(uint tileh, uint bits);
extern uint GetRoadFoundation(uint tileh, uint bits);
extern uint GetBridgeFoundation(uint tileh, byte direction);
enum {
BRIDGE_NO_FOUNDATION = 1 << 0 | 1 << 3 | 1 << 6 | 1 << 9 | 1 << 12,
};
// The most important function: it calculates the g-value
static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
int r, res = 0;
TileInfo ti, parent_ti;
// Gather some information about the tile..
FindLandscapeHeightByTile(&ti, current->tile);
FindLandscapeHeightByTile(&parent_ti, parent->path.node.tile);
// Check if we hit the end-tile
if (TILES_BETWEEN(current->tile,PathFinderInfo->end_tile_tl,PathFinderInfo->end_tile_br)) {
// We are at the end-tile, check if we had a direction or something...
if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction)
// We are not pointing the right way, invalid tile
return AYSTAR_INVALID_NODE;
// If it was valid, drop out.. we don't build on the endtile
return 0;
}
// Give everything a small penalty
res += AI_PATHFINDER_PENALTY;
if (!PathFinderInfo->rail_or_road) {
// Road has the lovely advantage it can use other road... check if
// the current tile is road, and if so, give a good bonus
if (AI_PATHFINDER_IS_ROAD(current->tile)) {
res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
}
}
// We should give a penalty when the tile is going up or down.. this is one way to do so!
// Too bad we have to count it from the parent.. but that is not so bad
if (parent_ti.tileh != 0 && parent->path.parent != NULL) {
// Skip if the tile was from a bridge or tunnel
if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
if (PathFinderInfo->rail_or_road) {
r = GetRailFoundation(parent_ti.tileh, 1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
// Maybe is BRIDGE_NO_FOUNDATION a bit strange here, but it contains just the right information..
if (r >= 15 || (r == 0 && (BRIDGE_NO_FOUNDATION & (1 << ti.tileh)))) {
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
}
} else {
if (!(AI_PATHFINDER_IS_ROAD(parent->path.node.tile) && IS_TILETYPE(parent->path.node.tile, MP_TUNNELBRIDGE))) {
r = GetRoadFoundation(parent_ti.tileh, AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
if (r >= 15 || r == 0)
res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
}
}
}
}
// Are we part of a tunnel?
if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
// Tunnels are very expensive when build on long routes..
// Ironicly, we are using BridgeCode here ;)
r = AI_PATHFINDER_TUNNEL_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
res += r + (r >> 8);
}
// Are we part of a bridge?
if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
// That means for every length a penalty
res += AI_PATHFINDER_BRIDGE_PENALTY * GetBridgeLength(current->tile, parent->path.node.tile);
// Check if we are going up or down, first for the starting point
// In user_data[0] is at the 8th bit the direction
if (!(BRIDGE_NO_FOUNDATION & (1 << parent_ti.tileh))) {
if (GetBridgeFoundation(parent_ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
// Second for the end point
if (!(BRIDGE_NO_FOUNDATION & (1 << ti.tileh))) {
if (GetBridgeFoundation(ti.tileh, (current->user_data[0] >> 8) & 1) < 15)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
if (parent_ti.tileh == 0)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
if (ti.tileh == 0)
res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
}
// To prevent the AI from taking the fastest way in tiles, but not the fastest way
// in speed, we have to give a good penalty to direction changing
// This way, we get almost the fastest way in tiles, and a very good speed on the track
if (!PathFinderInfo->rail_or_road) {
if (parent->path.parent != NULL &&
AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
// When road exists, we don't like turning, but its free, so don't be to piggy about it
if (AI_PATHFINDER_IS_ROAD(parent->path.node.tile))
res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
else
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
}
} else {
// For rail we have 1 exeption: diagonal rail..
// So we fetch 2 raildirection. That of the current one, and of the one before that
if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
// First, see if we are on diagonal path, that is better then straight path
if (dir1 > 1) { res -= AI_PATHFINDER_DIAGONAL_BONUS; }
// First see if they are different
if (dir1 != dir2) {
// dir 2 and 3 are 1 diagonal track, and 4 and 5.
if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
// It is not, so we changed of direction
res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
}
if (parent->path.parent->parent->parent != NULL) {
int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
// Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
res += AI_PATHFINDER_CURVE_PENALTY;
}
}
}
}
}
// Res should never be below zero.. if so, make it zero!
if (res < 0) { res = 0; }
// Return our value
return res;
}

View File

@ -1,82 +1,82 @@
#include "stdafx.h"
#include "ttd.h"
#include "player.h"
#include "ai.h"
int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c) {
// 0 = vert
// 1 = horz
// 2 = dig up-left
// 3 = dig down-right
// 4 = dig down-left
// 5 = dig up-right
int x1, x2, x3;
int y1, y2, y3;
x1 = GET_TILE_X(tile_a);
x2 = GET_TILE_X(tile_b);
x3 = GET_TILE_X(tile_c);
y1 = GET_TILE_Y(tile_a);
y2 = GET_TILE_Y(tile_b);
y3 = GET_TILE_Y(tile_c);
if (y1 == y2 && y2 == y3) return 0;
if (x1 == x2 && x2 == x3) return 1;
if (y2 > y1) {
if (x2 > x3) return 2;
else return 4;
}
if (x2 > x1) {
if (y2 > y3) return 2;
else return 5;
}
if (y1 > y2) {
if (x2 > x3) return 5;
else return 3;
}
if (x1 > x2) {
if (y2 > y3) return 4;
else return 3;
}
return 0;
}
int AiNew_GetRoadDirection(uint tile_a, uint tile_b, uint tile_c) {
int x1, x2, x3;
int y1, y2, y3;
int r;
x1 = GET_TILE_X(tile_a);
x2 = GET_TILE_X(tile_b);
x3 = GET_TILE_X(tile_c);
y1 = GET_TILE_Y(tile_a);
y2 = GET_TILE_Y(tile_b);
y3 = GET_TILE_Y(tile_c);
r = 0;
if (x1 < x2) r += 8;
if (y1 < y2) r += 1;
if (x1 > x2) r += 2;
if (y1 > y2) r += 4;
if (x2 < x3) r += 2;
if (y2 < y3) r += 4;
if (x2 > x3) r += 8;
if (y2 > y3) r += 1;
return r;
}
// Get's the direction between 2 tiles seen from tile_a
int AiNew_GetDirection(uint tile_a, uint tile_b) {
if (GET_TILE_Y(tile_a) < GET_TILE_Y(tile_b)) return 1;
if (GET_TILE_Y(tile_a) > GET_TILE_Y(tile_b)) return 3;
if (GET_TILE_X(tile_a) < GET_TILE_X(tile_b)) return 2;
return 0;
}
#include "stdafx.h"
#include "ttd.h"
#include "player.h"
#include "ai.h"
int AiNew_GetRailDirection(uint tile_a, uint tile_b, uint tile_c) {
// 0 = vert
// 1 = horz
// 2 = dig up-left
// 3 = dig down-right
// 4 = dig down-left
// 5 = dig up-right
int x1, x2, x3;
int y1, y2, y3;
x1 = GET_TILE_X(tile_a);
x2 = GET_TILE_X(tile_b);
x3 = GET_TILE_X(tile_c);
y1 = GET_TILE_Y(tile_a);
y2 = GET_TILE_Y(tile_b);
y3 = GET_TILE_Y(tile_c);
if (y1 == y2 && y2 == y3) return 0;
if (x1 == x2 && x2 == x3) return 1;
if (y2 > y1) {
if (x2 > x3) return 2;
else return 4;
}
if (x2 > x1) {
if (y2 > y3) return 2;
else return 5;
}
if (y1 > y2) {
if (x2 > x3) return 5;
else return 3;
}
if (x1 > x2) {
if (y2 > y3) return 4;
else return 3;
}
return 0;
}
int AiNew_GetRoadDirection(uint tile_a, uint tile_b, uint tile_c) {
int x1, x2, x3;
int y1, y2, y3;
int r;
x1 = GET_TILE_X(tile_a);
x2 = GET_TILE_X(tile_b);
x3 = GET_TILE_X(tile_c);
y1 = GET_TILE_Y(tile_a);
y2 = GET_TILE_Y(tile_b);
y3 = GET_TILE_Y(tile_c);
r = 0;
if (x1 < x2) r += 8;
if (y1 < y2) r += 1;
if (x1 > x2) r += 2;
if (y1 > y2) r += 4;
if (x2 < x3) r += 2;
if (y2 < y3) r += 4;
if (x2 > x3) r += 8;
if (y2 > y3) r += 1;
return r;
}
// Get's the direction between 2 tiles seen from tile_a
int AiNew_GetDirection(uint tile_a, uint tile_b) {
if (GET_TILE_Y(tile_a) < GET_TILE_Y(tile_b)) return 1;
if (GET_TILE_Y(tile_a) > GET_TILE_Y(tile_b)) return 3;
if (GET_TILE_X(tile_a) < GET_TILE_X(tile_b)) return 2;
return 0;
}

542
aystar.c
View File

@ -1,271 +1,271 @@
/*
* This file has the core function for AyStar
* AyStar is a fast pathfinding routine and is used for things like
* AI_pathfinding and Train_pathfinding.
* For more information about AyStar (A* Algorithm), you can look at
* http://en.wikipedia.org/wiki/A-star_search_algorithm
*/
/*
* Friendly reminder:
* Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
* And when not free'd, it can cause system-crashes.
* Also remember that when you stop an algorithm before it is finished, your
* should call clear() yourself!
*/
#include "stdafx.h"
#include "ttd.h"
#include "aystar.h"
// This looks in the Hash if a node exists in ClosedList
// If so, it returns the PathNode, else NULL
PathNode *AyStarMain_ClosedList_IsInList(AyStar *aystar, AyStarNode *node) {
return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
}
// This adds a node to the ClosedList
// It makes a copy of the data
void AyStarMain_ClosedList_Add(AyStar *aystar, PathNode *node) {
// Add a node to the ClosedList
PathNode *new_node = malloc(sizeof(PathNode));
*new_node = *node;
Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
}
// Checks if a node is in the OpenList
// If so, it returns the OpenListNode, else NULL
OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, AyStarNode *node) {
return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
}
// Gets the best node from OpenList
// returns the best node, or NULL of none is found
// Also it deletes the node from the OpenList
OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar) {
// Return the item the Queue returns.. the best next OpenList item.
OpenListNode* res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
if (res != NULL)
Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
return res;
}
// Adds a node to the OpenList
// It makes a copy of node, and puts the pointer of parent in the struct
void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, AyStarNode *node, int f, int g, int userdata) {
// Add a new Node to the OpenList
OpenListNode* new_node = malloc(sizeof(OpenListNode));
new_node->g = g;
new_node->path.parent = parent;
new_node->path.node = *node;
Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node);
// Add it to the queue
aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f);
}
/*
* Checks one tile and calculate his f-value
* return values:
* AYSTAR_DONE : indicates we are done
*/
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
int new_f, new_g, new_h;
PathNode *closedlist_parent;
OpenListNode *check;
// Check the new node against the ClosedList
if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE;
// Calculate the G-value for this node
new_g = aystar->CalculateG(aystar, current, parent);
// If the value was INVALID_NODE, we don't do anything with this node
if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE;
// There should not be given any other error-code..
assert(new_g >= 0);
// Add the parent g-value to the new g-value
new_g += parent->g;
if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE;
// Calculate the h-value
new_h = aystar->CalculateH(aystar, current, parent);
// There should not be given any error-code..
assert(new_h >= 0);
// The f-value if g + h
new_f = new_g + new_h;
// Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList)
closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
// Check if this item is already in the OpenList
if ((check = AyStarMain_OpenList_IsInList(aystar, current)) != NULL) {
int i;
// Yes, check if this g value is lower..
if (new_g > check->g) return AYSTAR_DONE;
aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0);
// It is lower, so change it to this item
check->g = new_g;
check->path.parent = closedlist_parent;
/* Copy user data, will probably have changed */
for (i=0;i<lengthof(current->user_data);i++)
check->path.node.user_data[i] = current->user_data[i];
// Readd him in the OpenListQueue
aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
} else {
// A new node, add him to the OpenList
AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g, 0);
}
return AYSTAR_DONE;
}
/*
* This function is the core of AyStar. It handles one item and checks
* his neighbour items. If they are valid, they are added to be checked too.
* return values:
* AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
* has been found.
* AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
* reached.
* AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
* AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
*/
int AyStarMain_Loop(AyStar *aystar) {
int i, r;
// Get the best node from OpenList
OpenListNode *current = AyStarMain_OpenList_Pop(aystar);
// If empty, drop an error
if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
// Check for end node and if found, return that code
if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) {
if (aystar->FoundEndNode != NULL)
aystar->FoundEndNode(aystar, current);
free(current);
return AYSTAR_FOUND_END_NODE;
}
// Add the node to the ClosedList
AyStarMain_ClosedList_Add(aystar, &current->path);
// Load the neighbours
aystar->GetNeighbours(aystar, current);
// Go through all neighbours
for (i=0;i<aystar->num_neighbours;i++) {
// Check and add them to the OpenList if needed
r = aystar->checktile(aystar, &aystar->neighbours[i], current);
}
// Free the node
free(current);
if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes)
/* We've expanded enough nodes */
return AYSTAR_LIMIT_REACHED;
else
// Return that we are still busy
return AYSTAR_STILL_BUSY;
}
/*
* This function frees the memory it allocated
*/
void AyStarMain_Free(AyStar *aystar) {
aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
/* 2nd argument above is false, below is true, to free the values only
* once */
delete_Hash(&aystar->OpenListHash, true);
delete_Hash(&aystar->ClosedListHash, true);
#ifdef AYSTAR_DEBUG
printf("[AyStar] Memory free'd\n");
#endif
}
/*
* This function make the memory go back to zero
* This function should be called when you are using the same instance again.
*/
void AyStarMain_Clear(AyStar *aystar) {
// Clean the Queue, but not the elements within. That will be done by
// the hash.
aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
// Clean the hashes
clear_Hash(&aystar->OpenListHash, true);
clear_Hash(&aystar->ClosedListHash, true);
#ifdef AYSTAR_DEBUG
printf("[AyStar] Cleared AyStar\n");
#endif
}
/*
* This is the function you call to run AyStar.
* return values:
* AYSTAR_FOUND_END_NODE : indicates we found an end node.
* AYSTAR_NO_PATH : indicates that there was no path found.
* AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
* When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
* aystar->clear() is called. Note that when you stop the algorithm halfway,
* you should still call clear() yourself!
*/
int AyStarMain_Main(AyStar *aystar) {
int r, i = 0;
// Loop through the OpenList
// Quit if result is no AYSTAR_STILL_BUSY or is more then loops_per_tick
while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
#ifdef AYSTAR_DEBUG
if (r == AYSTAR_FOUND_END_NODE)
printf("[AyStar] Found path!\n");
else if (r == AYSTAR_EMPTY_OPENLIST)
printf("[AyStar] OpenList run dry, no path found\n");
else if (r == AYSTAR_LIMIT_REACHED)
printf("[AyStar] Exceeded search_nodes, no path found\n");
#endif
if (r != AYSTAR_STILL_BUSY)
/* We're done, clean up */
aystar->clear(aystar);
// Check result-value
if (r == AYSTAR_FOUND_END_NODE) return AYSTAR_FOUND_END_NODE;
// Check if we have some left in the OpenList
if (r == AYSTAR_EMPTY_OPENLIST || r == AYSTAR_LIMIT_REACHED) return AYSTAR_NO_PATH;
// Return we are still busy
return AYSTAR_STILL_BUSY;
}
/*
* Adds a node from where to start an algorithm. Multiple nodes can be added
* if wanted. You should make sure that clear() is called before adding nodes
* if the AyStar has been used before (though the normal main loop calls
* clear() automatically when the algorithm finishes
*/
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node) {
#ifdef AYSTAR_DEBUG
printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n", GET_TILE_X(start_node->tile), GET_TILE_Y(start_node->tile), start_node->direction);
#endif
AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, 0, 0);
}
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets) {
// Allocated the Hash for the OpenList and ClosedList
init_Hash(&aystar->OpenListHash, hash, num_buckets);
init_Hash(&aystar->ClosedListHash, hash, num_buckets);
// Set up our sorting queue
// BinaryHeap allocates a block of 1024 nodes
// When thatone gets full it reserves an otherone, till this number
// That is why it can stay this high
init_BinaryHeap(&aystar->OpenListQueue, 102400);
aystar->addstart = AyStarMain_AddStartNode;
aystar->main = AyStarMain_Main;
aystar->loop = AyStarMain_Loop;
aystar->free = AyStarMain_Free;
aystar->clear = AyStarMain_Clear;
aystar->checktile = AyStarMain_CheckTile;
}
/*
* This file has the core function for AyStar
* AyStar is a fast pathfinding routine and is used for things like
* AI_pathfinding and Train_pathfinding.
* For more information about AyStar (A* Algorithm), you can look at
* http://en.wikipedia.org/wiki/A-star_search_algorithm
*/
/*
* Friendly reminder:
* Call (AyStar).free() when you are done with Aystar. It reserves a lot of memory
* And when not free'd, it can cause system-crashes.
* Also remember that when you stop an algorithm before it is finished, your
* should call clear() yourself!
*/
#include "stdafx.h"
#include "ttd.h"
#include "aystar.h"
// This looks in the Hash if a node exists in ClosedList
// If so, it returns the PathNode, else NULL
PathNode *AyStarMain_ClosedList_IsInList(AyStar *aystar, AyStarNode *node) {
return (PathNode*)Hash_Get(&aystar->ClosedListHash, node->tile, node->direction);
}
// This adds a node to the ClosedList
// It makes a copy of the data
void AyStarMain_ClosedList_Add(AyStar *aystar, PathNode *node) {
// Add a node to the ClosedList
PathNode *new_node = malloc(sizeof(PathNode));
*new_node = *node;
Hash_Set(&aystar->ClosedListHash, node->node.tile, node->node.direction, new_node);
}
// Checks if a node is in the OpenList
// If so, it returns the OpenListNode, else NULL
OpenListNode *AyStarMain_OpenList_IsInList(AyStar *aystar, AyStarNode *node) {
return (OpenListNode*)Hash_Get(&aystar->OpenListHash, node->tile, node->direction);
}
// Gets the best node from OpenList
// returns the best node, or NULL of none is found
// Also it deletes the node from the OpenList
OpenListNode *AyStarMain_OpenList_Pop(AyStar *aystar) {
// Return the item the Queue returns.. the best next OpenList item.
OpenListNode* res = (OpenListNode*)aystar->OpenListQueue.pop(&aystar->OpenListQueue);
if (res != NULL)
Hash_Delete(&aystar->OpenListHash, res->path.node.tile, res->path.node.direction);
return res;
}
// Adds a node to the OpenList
// It makes a copy of node, and puts the pointer of parent in the struct
void AyStarMain_OpenList_Add(AyStar *aystar, PathNode *parent, AyStarNode *node, int f, int g, int userdata) {
// Add a new Node to the OpenList
OpenListNode* new_node = malloc(sizeof(OpenListNode));
new_node->g = g;
new_node->path.parent = parent;
new_node->path.node = *node;
Hash_Set(&aystar->OpenListHash, node->tile, node->direction, new_node);
// Add it to the queue
aystar->OpenListQueue.push(&aystar->OpenListQueue, new_node, f);
}
/*
* Checks one tile and calculate his f-value
* return values:
* AYSTAR_DONE : indicates we are done
*/
int AyStarMain_CheckTile(AyStar *aystar, AyStarNode *current, OpenListNode *parent) {
int new_f, new_g, new_h;
PathNode *closedlist_parent;
OpenListNode *check;
// Check the new node against the ClosedList
if (AyStarMain_ClosedList_IsInList(aystar, current) != NULL) return AYSTAR_DONE;
// Calculate the G-value for this node
new_g = aystar->CalculateG(aystar, current, parent);
// If the value was INVALID_NODE, we don't do anything with this node
if (new_g == AYSTAR_INVALID_NODE) return AYSTAR_DONE;
// There should not be given any other error-code..
assert(new_g >= 0);
// Add the parent g-value to the new g-value
new_g += parent->g;
if (aystar->max_path_cost != 0 && (uint)new_g > aystar->max_path_cost) return AYSTAR_DONE;
// Calculate the h-value
new_h = aystar->CalculateH(aystar, current, parent);
// There should not be given any error-code..
assert(new_h >= 0);
// The f-value if g + h
new_f = new_g + new_h;
// Get the pointer to the parent in the ClosedList (the currentone is to a copy of the one in the OpenList)
closedlist_parent = AyStarMain_ClosedList_IsInList(aystar, &parent->path.node);
// Check if this item is already in the OpenList
if ((check = AyStarMain_OpenList_IsInList(aystar, current)) != NULL) {
int i;
// Yes, check if this g value is lower..
if (new_g > check->g) return AYSTAR_DONE;
aystar->OpenListQueue.del(&aystar->OpenListQueue, check, 0);
// It is lower, so change it to this item
check->g = new_g;
check->path.parent = closedlist_parent;
/* Copy user data, will probably have changed */
for (i=0;i<lengthof(current->user_data);i++)
check->path.node.user_data[i] = current->user_data[i];
// Readd him in the OpenListQueue
aystar->OpenListQueue.push(&aystar->OpenListQueue, check, new_f);
} else {
// A new node, add him to the OpenList
AyStarMain_OpenList_Add(aystar, closedlist_parent, current, new_f, new_g, 0);
}
return AYSTAR_DONE;
}
/*
* This function is the core of AyStar. It handles one item and checks
* his neighbour items. If they are valid, they are added to be checked too.
* return values:
* AYSTAR_EMPTY_OPENLIST : indicates all items are tested, and no path
* has been found.
* AYSTAR_LIMIT_REACHED : Indicates that the max_nodes limit has been
* reached.
* AYSTAR_FOUND_END_NODE : indicates we found the end. Path_found now is true, and in path is the path found.
* AYSTAR_STILL_BUSY : indicates we have done this tile, did not found the path yet, and have items left to try.
*/
int AyStarMain_Loop(AyStar *aystar) {
int i, r;
// Get the best node from OpenList
OpenListNode *current = AyStarMain_OpenList_Pop(aystar);
// If empty, drop an error
if (current == NULL) return AYSTAR_EMPTY_OPENLIST;
// Check for end node and if found, return that code
if (aystar->EndNodeCheck(aystar, current) == AYSTAR_FOUND_END_NODE) {
if (aystar->FoundEndNode != NULL)
aystar->FoundEndNode(aystar, current);
free(current);
return AYSTAR_FOUND_END_NODE;
}
// Add the node to the ClosedList
AyStarMain_ClosedList_Add(aystar, &current->path);
// Load the neighbours
aystar->GetNeighbours(aystar, current);
// Go through all neighbours
for (i=0;i<aystar->num_neighbours;i++) {
// Check and add them to the OpenList if needed
r = aystar->checktile(aystar, &aystar->neighbours[i], current);
}
// Free the node
free(current);
if (aystar->max_search_nodes != 0 && Hash_Size(&aystar->ClosedListHash) >= aystar->max_search_nodes)
/* We've expanded enough nodes */
return AYSTAR_LIMIT_REACHED;
else
// Return that we are still busy
return AYSTAR_STILL_BUSY;
}
/*
* This function frees the memory it allocated
*/
void AyStarMain_Free(AyStar *aystar) {
aystar->OpenListQueue.free(&aystar->OpenListQueue, false);
/* 2nd argument above is false, below is true, to free the values only
* once */
delete_Hash(&aystar->OpenListHash, true);
delete_Hash(&aystar->ClosedListHash, true);
#ifdef AYSTAR_DEBUG
printf("[AyStar] Memory free'd\n");
#endif
}
/*
* This function make the memory go back to zero
* This function should be called when you are using the same instance again.
*/
void AyStarMain_Clear(AyStar *aystar) {
// Clean the Queue, but not the elements within. That will be done by
// the hash.
aystar->OpenListQueue.clear(&aystar->OpenListQueue, false);
// Clean the hashes
clear_Hash(&aystar->OpenListHash, true);
clear_Hash(&aystar->ClosedListHash, true);
#ifdef AYSTAR_DEBUG
printf("[AyStar] Cleared AyStar\n");
#endif
}
/*
* This is the function you call to run AyStar.
* return values:
* AYSTAR_FOUND_END_NODE : indicates we found an end node.
* AYSTAR_NO_PATH : indicates that there was no path found.
* AYSTAR_STILL_BUSY : indicates we have done some checked, that we did not found the path yet, and that we still have items left to try.
* When the algorithm is done (when the return value is not AYSTAR_STILL_BUSY)
* aystar->clear() is called. Note that when you stop the algorithm halfway,
* you should still call clear() yourself!
*/
int AyStarMain_Main(AyStar *aystar) {
int r, i = 0;
// Loop through the OpenList
// Quit if result is no AYSTAR_STILL_BUSY or is more then loops_per_tick
while ((r = aystar->loop(aystar)) == AYSTAR_STILL_BUSY && (aystar->loops_per_tick == 0 || ++i < aystar->loops_per_tick)) { }
#ifdef AYSTAR_DEBUG
if (r == AYSTAR_FOUND_END_NODE)
printf("[AyStar] Found path!\n");
else if (r == AYSTAR_EMPTY_OPENLIST)
printf("[AyStar] OpenList run dry, no path found\n");
else if (r == AYSTAR_LIMIT_REACHED)
printf("[AyStar] Exceeded search_nodes, no path found\n");
#endif
if (r != AYSTAR_STILL_BUSY)
/* We're done, clean up */
aystar->clear(aystar);
// Check result-value
if (r == AYSTAR_FOUND_END_NODE) return AYSTAR_FOUND_END_NODE;
// Check if we have some left in the OpenList
if (r == AYSTAR_EMPTY_OPENLIST || r == AYSTAR_LIMIT_REACHED) return AYSTAR_NO_PATH;
// Return we are still busy
return AYSTAR_STILL_BUSY;
}
/*
* Adds a node from where to start an algorithm. Multiple nodes can be added
* if wanted. You should make sure that clear() is called before adding nodes
* if the AyStar has been used before (though the normal main loop calls
* clear() automatically when the algorithm finishes
*/
void AyStarMain_AddStartNode(AyStar *aystar, AyStarNode *start_node) {
#ifdef AYSTAR_DEBUG
printf("[AyStar] Starting A* Algorithm from node (%d, %d, %d)\n", GET_TILE_X(start_node->tile), GET_TILE_Y(start_node->tile), start_node->direction);
#endif
AyStarMain_OpenList_Add(aystar, NULL, start_node, 0, 0, 0);
}
void init_AyStar(AyStar* aystar, Hash_HashProc hash, uint num_buckets) {
// Allocated the Hash for the OpenList and ClosedList
init_Hash(&aystar->OpenListHash, hash, num_buckets);
init_Hash(&aystar->ClosedListHash, hash, num_buckets);
// Set up our sorting queue
// BinaryHeap allocates a block of 1024 nodes
// When thatone gets full it reserves an otherone, till this number
// That is why it can stay this high
init_BinaryHeap(&aystar->OpenListQueue, 102400);
aystar->addstart = AyStarMain_AddStartNode;
aystar->main = AyStarMain_Main;
aystar->loop = AyStarMain_Loop;
aystar->free = AyStarMain_Free;
aystar->clear = AyStarMain_Clear;
aystar->checktile = AyStarMain_CheckTile;
}

View File

@ -1564,7 +1564,7 @@ Industry *CreateNewIndustry(uint tile, int type)
return i;
}
static const byte _numof_industry_table[4][12] = {
static const byte _numof_industry_table[4][12] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
@ -1574,8 +1574,8 @@ static const byte _numof_industry_table[4][12] = {
static void PlaceInitialIndustry(byte type, int amount)
{
int num = _numof_industry_table[_opt.diff.number_industries][amount];
if (_opt.diff.number_industries != 0)
if (_opt.diff.number_industries != 0)
{
assert(num > 0);
@ -1585,7 +1585,7 @@ static void PlaceInitialIndustry(byte type, int amount)
if (CreateNewIndustry(TILE_MASK(Random()), type) != NULL)
break;
} while (--i != 0);
} while (--num);
} while (--num);
}
}

View File

@ -1252,14 +1252,14 @@ void NetworkIPListInit() {
gethostname(hostname,250);
DEBUG(misc,2) ("[NET][IP] init for host %s", hostname);
he=gethostbyname((char *) hostname);
if (he == NULL) {
he = gethostbyname("localhost");
}
if (he == NULL) {
bcaddr = inet_addr("127.0.0.1");
he = gethostbyaddr(inet_ntoa(*(struct in_addr *) &bcaddr), sizeof(bcaddr), AF_INET);
if (he == NULL) {
he = gethostbyname("localhost");
}
if (he == NULL) {
bcaddr = inet_addr("127.0.0.1");
he = gethostbyaddr(inet_ntoa(*(struct in_addr *) &bcaddr), sizeof(bcaddr), AF_INET);
}
if (he == NULL) {

406
queue.h
View File

@ -1,203 +1,203 @@
#ifndef QUEUE_H
#define QUEUE_H
//#define NOFREE
//#define QUEUE_DEBUG
//#define HASH_DEBUG
typedef struct Queue Queue;
typedef bool Queue_PushProc(Queue* q, void* item, int priority);
typedef void* Queue_PopProc(Queue* q);
typedef bool Queue_DeleteProc(Queue* q, void* item, int priority);
typedef void Queue_ClearProc(Queue* q, bool free_values);
typedef void Queue_FreeProc(Queue* q, bool free_values);
typedef struct InsSortNode InsSortNode;
struct InsSortNode {
void* item;
int priority;
InsSortNode* next;
};
typedef struct BinaryHeapNode BinaryHeapNode;
struct BinaryHeapNode {
void* item;
int priority;
};
struct Queue{
/*
* Pushes an element into the queue, at the appropriate place for the queue.
* Requires the queue pointer to be of an appropriate type, of course.
*/
Queue_PushProc* push;
/*
* Pops the first element from the queue. What exactly is the first element,
* is defined by the exact type of queue.
*/
Queue_PopProc* pop;
/*
* Deletes the item from the queue. priority should be specified if
* known, which speeds up the deleting for some queue's. Should be -1
* if not known.
*/
Queue_DeleteProc* del;
/* Clears the queue, by removing all values from it. It's state is
* effectively reset. If free_items is true, each of the items cleared
* in this way are free()'d.
*/
Queue_ClearProc* clear;
/* Frees the queue, by reclaiming all memory allocated by it. After
* this it is no longer usable. If free_items is true, any remaining
* items are free()'d too.
*/
Queue_FreeProc* free;
union {
struct {
uint max_size;
uint size;
void** elements;
} stack;
struct {
uint max_size;
uint head; /* The index where the last element should be inserted */
uint tail; /* The index where the next element should be read */
void** elements;
} fifo;
struct {
InsSortNode* first;
} inssort;
struct {
uint max_size;
uint size;
uint blocks; /* The amount of blocks for which space is reserved in elements */
BinaryHeapNode** elements;
} binaryheap;
} data;
/* If true, this struct will be free'd when the
* Queue is deleted. */
bool freeq;
};
/* Initializes a stack and allocates internal memory. */
void init_Stack(Queue* q, uint max_size);
/* Allocate a new stack with a maximum of max_size elements. */
Queue* new_Stack(uint max_size);
/*
* Fifo
*/
/* Initializes a fifo and allocates internal memory for maximum of max_size
* elements */
void init_Fifo(Queue* q, uint max_size);
/* Allocate a new fifo and initializes it with a maximum of max_size elements. */
Queue* new_Fifo(uint max_size);
Queue* new_Fifo_in_buffer(uint max_size, void* buffer);
int build_Fifo(void* buffer, uint size);
/*
* Insertion Sorter
*/
/* Initializes a inssort and allocates internal memory. There is no maximum
* size */
void init_InsSort(Queue* q);
/* Allocate a new fifo and initializes it. There is no maximum size */
Queue* new_InsSort();
/*
* Binary Heap
* For information, see:
* http://www.policyalmanac.org/games/binaryHeaps.htm
*/
/* The amount of elements that will be malloc'd at a time */
#define BINARY_HEAP_BLOCKSIZE_BITS 10
/* Initializes a binary heap and allocates internal memory for maximum of
* max_size elements */
void init_BinaryHeap(Queue* q, uint max_size);
/* Allocate a new binary heap and initializes it with a maximum of max_size
* elements. */
Queue* new_BinaryHeap(uint max_size);
/*
* Hash
*/
typedef struct HashNode HashNode;
struct HashNode {
uint key1;
uint key2;
void* value;
HashNode* next;
};
/* Generates a hash code from the given key pair. You should make sure that
* the resulting range is clearly defined.
*/
typedef uint Hash_HashProc(uint key1, uint key2);
typedef struct Hash {
/* The hash function used */
Hash_HashProc* hash;
/* The amount of items in the hash */
uint size;
/* The number of buckets allocated */
uint num_buckets;
/* A pointer to an array of num_buckets buckets. */
HashNode* buckets;
/* A pointer to an array of numbuckets booleans, which will be true if
* there are any Nodes in the bucket */
bool* buckets_in_use;
/* If true, buckets will be freed in delete_hash */
bool freeb;
/* If true, the pointer to this struct will be freed in delete_hash */
bool freeh;
} Hash;
/* Call these function to manipulate a hash */
/* Deletes the value with the specified key pair from the hash and returns
* that value. Returns NULL when the value was not present. The value returned
* is _not_ free()'d! */
void* Hash_Delete(Hash* h, uint key1, uint key2);
/* Sets the value associated with the given key pair to the given value.
* Returns the old value if the value was replaced, NULL when it was not yet present. */
void* Hash_Set(Hash* h, uint key1, uint key2, void* value);
/* Gets the value associated with the given key pair, or NULL when it is not
* present. */
void* Hash_Get(Hash* h, uint key1, uint key2);
/* Call these function to create/destroy a hash */
/* Builds a new hash, with num_buckets buckets. Make sure that hash() always
* returns a hash less than num_buckets! Call delete_hash after use */
Hash* new_Hash(Hash_HashProc* hash, int num_buckets);
/* Builds a new hash in an existing struct. Make sure that hash() always
* returns a hash less than num_buckets! Call delete_hash after use */
void init_Hash(Hash* h, Hash_HashProc* hash, int num_buckets);
/*
* Deletes the hash and cleans up. Only cleans up memory allocated by new_Hash
* & friends. If free is true, it will call free() on all the values that
* are left in the hash.
*/
void delete_Hash(Hash* h, bool free_values);
/*
* Cleans the hash, but keeps the memory allocated
*/
void clear_Hash(Hash* h, bool free_values);
/*
* Gets the current size of the Hash
*/
uint Hash_Size(Hash* h);
#endif /* QUEUE_H */
#ifndef QUEUE_H
#define QUEUE_H
//#define NOFREE
//#define QUEUE_DEBUG
//#define HASH_DEBUG
typedef struct Queue Queue;
typedef bool Queue_PushProc(Queue* q, void* item, int priority);
typedef void* Queue_PopProc(Queue* q);
typedef bool Queue_DeleteProc(Queue* q, void* item, int priority);
typedef void Queue_ClearProc(Queue* q, bool free_values);
typedef void Queue_FreeProc(Queue* q, bool free_values);
typedef struct InsSortNode InsSortNode;
struct InsSortNode {
void* item;
int priority;
InsSortNode* next;
};
typedef struct BinaryHeapNode BinaryHeapNode;
struct BinaryHeapNode {
void* item;
int priority;
};
struct Queue{
/*
* Pushes an element into the queue, at the appropriate place for the queue.
* Requires the queue pointer to be of an appropriate type, of course.
*/
Queue_PushProc* push;
/*
* Pops the first element from the queue. What exactly is the first element,
* is defined by the exact type of queue.
*/
Queue_PopProc* pop;
/*
* Deletes the item from the queue. priority should be specified if
* known, which speeds up the deleting for some queue's. Should be -1
* if not known.
*/
Queue_DeleteProc* del;
/* Clears the queue, by removing all values from it. It's state is
* effectively reset. If free_items is true, each of the items cleared
* in this way are free()'d.
*/
Queue_ClearProc* clear;
/* Frees the queue, by reclaiming all memory allocated by it. After
* this it is no longer usable. If free_items is true, any remaining
* items are free()'d too.
*/
Queue_FreeProc* free;
union {
struct {
uint max_size;
uint size;
void** elements;
} stack;
struct {
uint max_size;
uint head; /* The index where the last element should be inserted */
uint tail; /* The index where the next element should be read */
void** elements;
} fifo;
struct {
InsSortNode* first;
} inssort;
struct {
uint max_size;
uint size;
uint blocks; /* The amount of blocks for which space is reserved in elements */
BinaryHeapNode** elements;
} binaryheap;
} data;
/* If true, this struct will be free'd when the
* Queue is deleted. */
bool freeq;
};
/* Initializes a stack and allocates internal memory. */
void init_Stack(Queue* q, uint max_size);
/* Allocate a new stack with a maximum of max_size elements. */
Queue* new_Stack(uint max_size);
/*
* Fifo
*/
/* Initializes a fifo and allocates internal memory for maximum of max_size
* elements */
void init_Fifo(Queue* q, uint max_size);
/* Allocate a new fifo and initializes it with a maximum of max_size elements. */
Queue* new_Fifo(uint max_size);
Queue* new_Fifo_in_buffer(uint max_size, void* buffer);
int build_Fifo(void* buffer, uint size);
/*
* Insertion Sorter
*/
/* Initializes a inssort and allocates internal memory. There is no maximum
* size */
void init_InsSort(Queue* q);
/* Allocate a new fifo and initializes it. There is no maximum size */
Queue* new_InsSort();
/*
* Binary Heap
* For information, see:
* http://www.policyalmanac.org/games/binaryHeaps.htm
*/
/* The amount of elements that will be malloc'd at a time */
#define BINARY_HEAP_BLOCKSIZE_BITS 10
/* Initializes a binary heap and allocates internal memory for maximum of
* max_size elements */
void init_BinaryHeap(Queue* q, uint max_size);
/* Allocate a new binary heap and initializes it with a maximum of max_size
* elements. */
Queue* new_BinaryHeap(uint max_size);
/*
* Hash
*/
typedef struct HashNode HashNode;
struct HashNode {
uint key1;
uint key2;
void* value;
HashNode* next;
};
/* Generates a hash code from the given key pair. You should make sure that
* the resulting range is clearly defined.
*/
typedef uint Hash_HashProc(uint key1, uint key2);
typedef struct Hash {
/* The hash function used */
Hash_HashProc* hash;
/* The amount of items in the hash */
uint size;
/* The number of buckets allocated */
uint num_buckets;
/* A pointer to an array of num_buckets buckets. */
HashNode* buckets;
/* A pointer to an array of numbuckets booleans, which will be true if
* there are any Nodes in the bucket */
bool* buckets_in_use;
/* If true, buckets will be freed in delete_hash */
bool freeb;
/* If true, the pointer to this struct will be freed in delete_hash */
bool freeh;
} Hash;
/* Call these function to manipulate a hash */
/* Deletes the value with the specified key pair from the hash and returns
* that value. Returns NULL when the value was not present. The value returned
* is _not_ free()'d! */
void* Hash_Delete(Hash* h, uint key1, uint key2);
/* Sets the value associated with the given key pair to the given value.
* Returns the old value if the value was replaced, NULL when it was not yet present. */
void* Hash_Set(Hash* h, uint key1, uint key2, void* value);
/* Gets the value associated with the given key pair, or NULL when it is not
* present. */
void* Hash_Get(Hash* h, uint key1, uint key2);
/* Call these function to create/destroy a hash */
/* Builds a new hash, with num_buckets buckets. Make sure that hash() always
* returns a hash less than num_buckets! Call delete_hash after use */
Hash* new_Hash(Hash_HashProc* hash, int num_buckets);
/* Builds a new hash in an existing struct. Make sure that hash() always
* returns a hash less than num_buckets! Call delete_hash after use */
void init_Hash(Hash* h, Hash_HashProc* hash, int num_buckets);
/*
* Deletes the hash and cleans up. Only cleans up memory allocated by new_Hash
* & friends. If free is true, it will call free() on all the values that
* are left in the hash.
*/
void delete_Hash(Hash* h, bool free_values);
/*
* Cleans the hash, but keeps the memory allocated
*/
void clear_Hash(Hash* h, bool free_values);
/*
* Gets the current size of the Hash
*/
uint Hash_Size(Hash* h);
#endif /* QUEUE_H */