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

Reply via email to