hi!
i am a student and as a project i want to make an AI Bot for the crossfire who can play the game just like a human player.
i want to know if its possible at all? and if it is then do i have to change or add to client code only?
i think i need to access the client code which gets the latest game stat from the server and then send back the actions to server based on the human player input. i also need to access the client code which handles the player inputs and also the data structures containing the player info.
can some one let me know in which source files to look for above things.
thanks
AI BOT
Moderator: Board moderators
-
- Forum Aficionado
- Posts: 1994
- Joined: Tue Apr 29, 2003 5:55 pm
- Location: Minnesota, USA
- Contact:
Re: AI BOT
Hello asi,asi wrote:hi!
i am a student and as a project i want to make an AI Bot for the crossfire who can play the game just like a human player.
i want to know if its possible at all?
I'll answer what I can; yes - an AI bot is possible and there's at least two that I know of in use. I'm not involved in their development, so I can't elaborate any more on them other then to acknowledge that they exist.

These bots were designed to visit various shops to purchase specific or specified items. They are also designed to avoid or reduce conflicting with other players (for instance, move out of their way, only bump into them once, etc.)
Hopefully someone else will be able to answer your more technical questions.
"Put another, more succinct way: don't complain, contribute. It's more satisfying in the long run, and it's more constructive."
Eric Meyer
Eric Meyer
From the owner of one such bot, I know that it takes ALOT of careful effort and planning to be 100% certain that it won't kill or blocks players, while at the same time avoiding getting messed up as techolous said. (One hint, is that it is a good idea for a bot to try to use word of recall if it finds itself trapped or lost)
a good project; it's important to keep in mind what you want it to do.
If the purpose is to enter levels and clean them of mobs/ treasure, it requires a good amount of code. Ideas:
* start with Beginners 1 area
* the bot builds its own copy of the map, adding all that are in line of sight, and not yet present in its map. this map also senses where there are un-explored hallways and doors (if edge of vision has no visible wall, mark as "un-explored" until a wall is observed)
* the main objectives: generators, treasures. these are un-moving and easy to associate
with each tile of the map.
* have closest -to-farthest and clockwise for example as the order to pursue these
* then deal with roaming monsters;
*if their numbers or level exceeds some amount they might have to be dealth with before the generators/treasures.
*if it is high enough, retreat to a one-block passage and /brace /attack until the threat level drops
* if it is considerably higher, i.e. level 1 versus an ogre, or health level and damage taken per second past a certain point, retreat to exit
* there should not be separation between the point of retreat and the exit
* if no one-block tunnel is available, a corner of the room is okay
* the attack vs defend issue might not be easy for a bot to judge
* treasure might be picked selectively, depending on what characters can carry.
- A character will not want to go below ____ speed from picking loot until monsters are all cleared.
- pickup mode 7 is a good way to get just lightweight valuables. keys and food are also good all the time
- if burdened, and more bronze things are found, do not take them. if iron or gems are found, drop a bronze item.
* treasure chests and doors pose a problem for a bot:
search 10? times for traps, disarm if one is found
* after visible treasures and monsters, next objectives are doorways / hallways not explored
* categorize doors as keyed/breakable, keyed/unbreakable, switched, floorplated
* unfortunately, matching switches / floorplates to doors is not obvious to a bot
* in beginners 1, closest switch works for most; this does not always apply
* (temporary) activate all switches left over that have no buttons attached
* destructable walls: these should be mined next
* if map is free of unexplored halls, generators, treasures (or overloaded),
monsters, then "done" and run for exit
this might be fun to test on beginners 2, that it searches for traps, and flees properly after opening the door to the skeletons. Unfortunately, a bot cannot know which doors and levers not to activate. It is unlikely that a bot knows which areas to visit. A bot is unlikely to answer riddles.
* ranged weapon fighting: a later concern. especially with spells that have splash-damage, and maintaining distance on pursuing foes
* the boulders in beginners 1 represent another challenge: in the extreme case, you need to create a sokoban bot
* if a bot enters a crowded floor and is surrounded and booted off the stairs, a reasonable goal is to have it hack its way back to the stairs
* selling items: - drop gold to ID cursed items
- try on all equipment that is not cursed
- if there are +1, etc items then evaluate if better than what you have
- equip best equipment
- possibly, "lock" other "good" items not to sell
- hope that quest items are "locked" too
- sell the remaining weapons / armor / monster parts / gems
If the purpose is to enter levels and clean them of mobs/ treasure, it requires a good amount of code. Ideas:
* start with Beginners 1 area
* the bot builds its own copy of the map, adding all that are in line of sight, and not yet present in its map. this map also senses where there are un-explored hallways and doors (if edge of vision has no visible wall, mark as "un-explored" until a wall is observed)
* the main objectives: generators, treasures. these are un-moving and easy to associate
with each tile of the map.
* have closest -to-farthest and clockwise for example as the order to pursue these
* then deal with roaming monsters;
*if their numbers or level exceeds some amount they might have to be dealth with before the generators/treasures.
*if it is high enough, retreat to a one-block passage and /brace /attack until the threat level drops
* if it is considerably higher, i.e. level 1 versus an ogre, or health level and damage taken per second past a certain point, retreat to exit
* there should not be separation between the point of retreat and the exit
* if no one-block tunnel is available, a corner of the room is okay
* the attack vs defend issue might not be easy for a bot to judge
* treasure might be picked selectively, depending on what characters can carry.
- A character will not want to go below ____ speed from picking loot until monsters are all cleared.
- pickup mode 7 is a good way to get just lightweight valuables. keys and food are also good all the time
- if burdened, and more bronze things are found, do not take them. if iron or gems are found, drop a bronze item.
* treasure chests and doors pose a problem for a bot:
search 10? times for traps, disarm if one is found
* after visible treasures and monsters, next objectives are doorways / hallways not explored
* categorize doors as keyed/breakable, keyed/unbreakable, switched, floorplated
* unfortunately, matching switches / floorplates to doors is not obvious to a bot
* in beginners 1, closest switch works for most; this does not always apply
* (temporary) activate all switches left over that have no buttons attached
* destructable walls: these should be mined next
* if map is free of unexplored halls, generators, treasures (or overloaded),
monsters, then "done" and run for exit
this might be fun to test on beginners 2, that it searches for traps, and flees properly after opening the door to the skeletons. Unfortunately, a bot cannot know which doors and levers not to activate. It is unlikely that a bot knows which areas to visit. A bot is unlikely to answer riddles.
* ranged weapon fighting: a later concern. especially with spells that have splash-damage, and maintaining distance on pursuing foes
* the boulders in beginners 1 represent another challenge: in the extreme case, you need to create a sokoban bot
* if a bot enters a crowded floor and is surrounded and booted off the stairs, a reasonable goal is to have it hack its way back to the stairs
* selling items: - drop gold to ID cursed items
- try on all equipment that is not cursed
- if there are +1, etc items then evaluate if better than what you have
- equip best equipment
- possibly, "lock" other "good" items not to sell
- hope that quest items are "locked" too
- sell the remaining weapons / armor / monster parts / gems
at any given time, monitor these conditions and handle in ascending order:
1) escape to exit if very strong monsters is found AND it has path to player
OR rate of health drop is > ___ OR health < ____
2) if moderately strong monsters, reduce numbers from safe position that has path to exit
3) defeat nearest generators, ignoring weaker monsters
4) obtain nearest treasures of good value / weight
5) defeat weaker monsters, if there are one or two left ignore
6) pursue un-explored areas
7) if "done" fill up on treasures of moderate value / weight, then exit
there is a risk of some endless loops i.e. retreat from swarm of monsters, diminish them / then attack generator / but retreat from swarm of monsters.
A machine can predict perfectly that you could survive __ seconds with monsters on all sides. A smart bot would break the loop by determining that the swarm condition does not go away, and use spells or retreat for the exit. Another approach is to create another layer of combat between defensive fighting and generator assault, and that is to sweep the edge of the swarm. The mobs will refuse to fight a defended player. It is too dangerous to have 8 mobs and tunnel to the generator, but safe enough to fight 3-5 on the edge until the threat is diminished further.
the client's map only stores visible things, the bot's map needs records of things already seen. Other structs to do would be a pathfinding to exit, and pathfinding to next current objective; the server code includes example pathfinding code.
it is possible to get hurt and then find too many foes between you and the exit when it is too late. The above pseudo code tries to prevent this by removing generators and doing a sweep of rooms before entering new ones, but it is possible for levels to be designed that spill monsters into rooms you have already visited... not predictable.
the scenario of the stairs down into the crowded room, in which monsters displace you into the swarm, seems to be guaranteed death.
One solution might be never to put the bot into dungeon floors where it could not survive 8 of the strongest foes on that floor. This can be predicted on random levels, based on the fact that they might add mobs of increasing level on each floor.
the floor traps are a concern, because it is too tedious for a bot to search all over for these, but humans might have a more intuitive notion of where these get placed
1) escape to exit if very strong monsters is found AND it has path to player
OR rate of health drop is > ___ OR health < ____
2) if moderately strong monsters, reduce numbers from safe position that has path to exit
3) defeat nearest generators, ignoring weaker monsters
4) obtain nearest treasures of good value / weight
5) defeat weaker monsters, if there are one or two left ignore
6) pursue un-explored areas
7) if "done" fill up on treasures of moderate value / weight, then exit
there is a risk of some endless loops i.e. retreat from swarm of monsters, diminish them / then attack generator / but retreat from swarm of monsters.
A machine can predict perfectly that you could survive __ seconds with monsters on all sides. A smart bot would break the loop by determining that the swarm condition does not go away, and use spells or retreat for the exit. Another approach is to create another layer of combat between defensive fighting and generator assault, and that is to sweep the edge of the swarm. The mobs will refuse to fight a defended player. It is too dangerous to have 8 mobs and tunnel to the generator, but safe enough to fight 3-5 on the edge until the threat is diminished further.
the client's map only stores visible things, the bot's map needs records of things already seen. Other structs to do would be a pathfinding to exit, and pathfinding to next current objective; the server code includes example pathfinding code.
it is possible to get hurt and then find too many foes between you and the exit when it is too late. The above pseudo code tries to prevent this by removing generators and doing a sweep of rooms before entering new ones, but it is possible for levels to be designed that spill monsters into rooms you have already visited... not predictable.
the scenario of the stairs down into the crowded room, in which monsters displace you into the swarm, seems to be guaranteed death.
One solution might be never to put the bot into dungeon floors where it could not survive 8 of the strongest foes on that floor. This can be predicted on random levels, based on the fact that they might add mobs of increasing level on each floor.
the floor traps are a concern, because it is too tedious for a bot to search all over for these, but humans might have a more intuitive notion of where these get placed
Hi
Basilisk thanks for your really helpfull post. currently i am designing the structure of my Bot and i find your reply very usefull for that.
You have mentioned in ur post that server code has a path finding code, can u please tell me exactly where is it? i tried to find it last night but apparently i could not.
I also have some confusions how and where client is storing map info and how come i can read it?
I have checked the maps which are in the server installation directory, they are in a text file format. Do i have to do some thing similer for my Bot maps?
thanks
Basilisk thanks for your really helpfull post. currently i am designing the structure of my Bot and i find your reply very usefull for that.
You have mentioned in ur post that server code has a path finding code, can u please tell me exactly where is it? i tried to find it last night but apparently i could not.
I also have some confusions how and where client is storing map info and how come i can read it?
I have checked the maps which are in the server installation directory, they are in a text file format. Do i have to do some thing similer for my Bot maps?
thanks
sorry for the wait, I have various projects.
The server and client code is powerful, and it does take some time to build a model of it or trace the functions / data structures, but I can help.
In the client, the map data is stored in a struct called "struct Map the_map"
in /common/mapdata.c. the format of the information is outlined in mapdata.h.
We are most interested in the_map.cells[mx][my],
or in the_map.heads[layer].face.
This structure "the_map" is written to, using various functions in mapdata.c,
but most importantly mapdata_scroll and mapdata_newmap.
The network code in commands.c parses the server's data for
text messages that are literally "map_scroll" followed by this packed
data (which we need not worry about) decoded by mapdata_scroll etc.
to add / modify indexes of the_map. Some of this data is from "FaceCmd"
which parses the face data from the server.
The structure "the_map" is read by mapdata_face to extract the face number.
In x11/x11.c, gen_draw_face calls mapdata_face, and uses the face number
to read indexes of pixmaps[face].
gen_draw_face writes these pixmaps to a screen buffer.
display_mapcell calls gen_draw_face once for each "layer" of an x,y position.
After writing several layers to the buffer, it copies the buffer to the correct screen coordinates and moves to the next x,y position.
display_mapcell and the resulting chain are called whenever mapdata_scroll happens.
the_map[ ] struct is modified constantly during gameplay.
pixmaps[ ] struct does not change as often. It is built quickly when first connecting to a server, or when entering a map with new artwork. It is described in x11/x11.h.
It is written to by create_and_rescale_image_from_data in /common/image.c
by reading from struct Image_Cache[ ] in /common/image.c
Image_Cache[ ] is written to by the server "ImageCmd". A number of functions in /common/image.c read or modify this cache (and no new images are requested if the image has an entry in the cache).
On a different subject, /common/player.c contains a list of requests that a client can send to the server. Right now, in x11/x11.c the keyboard commands are being used to call those player.c commands. None of that has to be disabled, but a new function might be added (the bot input) that also calls the player.c commands.
Here is the dangerous part. The server does not send the data any information on identities of map cells. All it sends are the pixmap and darkness level. Collision detection, monster AI, your character's inventory, your character's stats are all on the server.
The pixmaps themselves are not the best basis for a bot to identify a map cell as a wall.
A verbal description is best.
Image_Cache[ ] consists of a cache entry number and a "Cache Entry" struct.
The "Cache Entry" struct contains a file name as well as the actual data.
This file name is the description that you want.
Here is a first try (I still need to test):
int testpixnum;
char *cname;
testpixnum = mapdata_face(ax,ay,layer);
cname = facetoname(testpixnum);
Unfortunately, the current map struct only keeps track of what you currently see.
If you want to keep records of these objects, you would want to make a new array
of dimensions [x][y][layer] and populate it with the "cname" strings, where x and y can be
32 x 32 and layers is between 0 and 3.
It does not have to be as fancy as the mapstruct.
Then "if teststruct[x][y][layer] = dunwall.111 | dunwall.113 | ..... { treat as wall }"
If you are not sure of the exact names, try to "printf" the cnames onto the window that you called cfclient from (not the application window, the one that was there before it where all the sound warnings print).
The server and client code is powerful, and it does take some time to build a model of it or trace the functions / data structures, but I can help.
In the client, the map data is stored in a struct called "struct Map the_map"
in /common/mapdata.c. the format of the information is outlined in mapdata.h.
We are most interested in the_map.cells[mx][my],
or in the_map.heads[layer].face.
This structure "the_map" is written to, using various functions in mapdata.c,
but most importantly mapdata_scroll and mapdata_newmap.
The network code in commands.c parses the server's data for
text messages that are literally "map_scroll" followed by this packed
data (which we need not worry about) decoded by mapdata_scroll etc.
to add / modify indexes of the_map. Some of this data is from "FaceCmd"
which parses the face data from the server.
The structure "the_map" is read by mapdata_face to extract the face number.
In x11/x11.c, gen_draw_face calls mapdata_face, and uses the face number
to read indexes of pixmaps[face].
gen_draw_face writes these pixmaps to a screen buffer.
display_mapcell calls gen_draw_face once for each "layer" of an x,y position.
After writing several layers to the buffer, it copies the buffer to the correct screen coordinates and moves to the next x,y position.
display_mapcell and the resulting chain are called whenever mapdata_scroll happens.
the_map[ ] struct is modified constantly during gameplay.
pixmaps[ ] struct does not change as often. It is built quickly when first connecting to a server, or when entering a map with new artwork. It is described in x11/x11.h.
It is written to by create_and_rescale_image_from_data in /common/image.c
by reading from struct Image_Cache[ ] in /common/image.c
Image_Cache[ ] is written to by the server "ImageCmd". A number of functions in /common/image.c read or modify this cache (and no new images are requested if the image has an entry in the cache).
On a different subject, /common/player.c contains a list of requests that a client can send to the server. Right now, in x11/x11.c the keyboard commands are being used to call those player.c commands. None of that has to be disabled, but a new function might be added (the bot input) that also calls the player.c commands.
Here is the dangerous part. The server does not send the data any information on identities of map cells. All it sends are the pixmap and darkness level. Collision detection, monster AI, your character's inventory, your character's stats are all on the server.
The pixmaps themselves are not the best basis for a bot to identify a map cell as a wall.
A verbal description is best.
Image_Cache[ ] consists of a cache entry number and a "Cache Entry" struct.
The "Cache Entry" struct contains a file name as well as the actual data.
This file name is the description that you want.
Here is a first try (I still need to test):
int testpixnum;
char *cname;
testpixnum = mapdata_face(ax,ay,layer);
cname = facetoname(testpixnum);
Unfortunately, the current map struct only keeps track of what you currently see.
If you want to keep records of these objects, you would want to make a new array
of dimensions [x][y][layer] and populate it with the "cname" strings, where x and y can be
32 x 32 and layers is between 0 and 3.
It does not have to be as fancy as the mapstruct.
Then "if teststruct[x][y][layer] = dunwall.111 | dunwall.113 | ..... { treat as wall }"
If you are not sure of the exact names, try to "printf" the cnames onto the window that you called cfclient from (not the application window, the one that was there before it where all the sound warnings print).
comment about walls : the same image can represent a wall, a breakable wall, a wall who need to be activated, a false wall you can pass through... and these infos are not in the client, but in the map files.
It's the same about many images : doors, floor (sea tiles you can pass over), dragons (monster or teleporter), statues with monster face... you can't rely on image name.
It's the same about many images : doors, floor (sea tiles you can pass over), dragons (monster or teleporter), statues with monster face... you can't rely on image name.