Ants AI, language matters
    bearophile 
    bearophileHUGS at lycos.com
       
    Sun Dec 25 09:07:46 PST 2011
    
    
  
Ants AI Challenge sponsored by Google is now finished, 24 people have played this contest using D2:
http://aichallenge.org/language_profile.php?language=D
The best D entry, by Minthos, with rank 439 (over about 8000 entries!):
http://aichallenge.org/profile.php?user=4823
Minthos D2 code:
http://files.minthos.com/code/minthos.dbot.dm02.tgz
Some low-level comments on Minthos code, about D, and not about game strategy or other high level things.
His code is:
> ...
> ...
My code is the one without those >.
-------------------------------
pqueue.d module: sometimes more collections are needed.
-------------------------------
Missed !in
> assert(!(hill in myAnts));
-------------------------------
Missed foreach() and maybe more:
> struct pfNode{
> 	Loc pos;
> 	pfNode* prev;
> 	float cost;
> 	bool visited;
> 	bool water;
> }
>
> ...
>
> pfNode[][] nodes = new pfNode[][](map.rows, map.cols);
> pfNode*[] openList;
> // initialize data structure
> for(int y = 0; y < map.rows; y++){
>     for(int x = 0; x < map.cols; x++){
>         nodes[y][x].water = map.water[y][x];
>         nodes[y][x].visited = false;
>         nodes[y][x].pos = Loc(y, x);
>         nodes[y][x].prev = null;
>     }
> }
This is a bit sad because of no named arguments yet in D (add braces as desired):
foreach (y; row; map)
    foreach (x, ref el; row)
        el = PfNode(/*pos*/ Loc(y, x), 
                    /*prev*/ null,
                    /*cost*/ el.cost,
                    /*visited*/ false,
                    /*water*/ map.water[y][x]);
-------------------------------
It's usually better to think of pre/post increments as returning void, and avoid code like this (but there is _far_ worse C/D code around):
> lokeLars = path[index--];
-------------------------------
Sad need(?) to use GC.disable:
> void main(string[] args) {
> 	version(unittest) {
> 		// We don't run the bot or wait for input in this case
> 	} else {
> 		GC.disable();
> 		MyBot b = new MyBot();
> 		b.name = args[0];
> 		Ants.run(b);
> 	}
> }
-------------------------------
Missed AA.byKey(), and required care to use "ref" to avoid bad bugs here:
> foreach(ref hill; map.myHills.keys){
> 	if(engine.manhattan(ant.pos, hill) < 5){
> 		goto ignore;
> 	}
> }
Often a return or a named break/continue are better in D than that goto.
-------------------------------
Improving switch to make it work on structs avoids such not nice code (http://d.puremagic.com/issues/show_bug.cgi?id=596 ):
> struct Direction {
> 	char key;
> 	int row;
> 	int col;
> }
> 
> immutable Direction[4] AIM = [
> 	{'n', -1, 0},
> 	{'e', 0, 1},
> 	{'s', 1, 0},
> 	{'w', 0, -1}
> ];
> 
> ...
> 
> Direction directionLeft(Direction d){
> 	for(int i = 0; i < 4; i++){
> 		if(d == AIM[i]){
> 			return AIM[(i + 1) % 4];
> 		}
> 	}
> 	assert(0);
> }
> 
> Direction oppositeDirection(Direction d){
> 	if(d == AIM[0]) return AIM[2];
> 	if(d == AIM[1])	return AIM[3];
> 	if(d == AIM[2])	return AIM[0];
> 	if(d == AIM[3])	return AIM[1];
> 	assert(0);
> }
I am thinking about something like:
Direction oppositeDirection(in Direction d) pure nothrow {
    final switch (d) {
        case AIM0: return AIM2;
        case AIM1: return AIM3;
        case AIM2: return AIM0;
        case AIM3: return AIM1;
    }
}
Hopefully the "final switch" too becomes usable here if AIM array becomes an enum of 4 items.
But I can't even create an enum of structs, maybe I am doing something wrong:
import std.typecons;
// Error: need member function opCmp() for struct Foo to compare
struct Foo { int x, y; }
// Error: template std.typecons.Tuple!(int,"x",int,"y").Tuple.opCmp(R) if (isTuple!(R)) does not match any function template declaration
alias Tuple!(int,"x", int,"y") Foo;
// Error: Integer constant expression expected instead of Foo(1,1)
const struct Foo {
    int x, y;
    int opCmp(const ref Foo other) const pure nothrow {
        return 1;
    }
}
enum MyE : Foo { A = Foo(1, 1),
                 B = Foo(2, 2),
                 C = Foo(3, 3) }
void main() {}
-------------------------------
There's a bit of need of std.random.choice, as in Python:
> Direction randomDirection(){
> 	return AIM[uniform(0, 3)];
> }
Using $ it becomes a bit better (and maybe removes a bug, because AIM length is 4, while uniform on default doesn't return the right extrema):
Direction randomDirection() {
    return AIM[uniform(0, $)];
}
But with a choice() it becomes less bug-prone and more clear:
Direction randomDirection() nothrow {
	return choice(AIM);
}
-------------------------------
> void clearArray(ref bool[][] a)
> {
> 	for(int x = 0; x < cols; x++){
> 		for(int y = 0; y < rows; y++){
> 			a[y][x] = false;
> 		}
> 	}
> }
Seems better:
void clearArray(bool[][] a) pure nothrow {
    foreach (row; a)
        a[] = false;
}
a is a headconst array, its size must not change inside clearArray().
-------------------------------
There's some need for a fast boolean matrix data structure in Phobos:
http://d.puremagic.com/issues/show_bug.cgi?id=6697
> // unpermanent stuff
> bool[][] explored;
> bool[][] vision;
> bool[][] water;
> bool[][] land;
> float[][] threat;
> float[][] crowd;
> int[Loc] waypoints;
> int[Loc] staleWaypoints;
> Loc[] bestGuesses;
-------------------------------
Bye,
bearophile
    
    
More information about the Digitalmars-d
mailing list