
言語とスターターパック
プロジェクトの主催者は、ボットの開発用に12を超える言語を提供しているため、Javaを選択しました。 私はJavaをよく知っているとは言えませんが、それは起こりました(C ++は冗長で、PHPはクールではなく、C#は柔らかすぎ、JavaScriptはV8を扱うには長すぎます、Luaは構文に似ていません、Pascalは学校に似ています、Python -少し怖い)。 Java用スターターパックは、公式Webサイトまたはこの直接リンクからダウンロードできます。
始め方について
たぶん私はブレーキをかけているか、Javaを知らないのですが、最初はメインクラスでコードがどのように機能するかを理解できませんでした。
public void doTurn() { Ants ants = getAnts(); for (Tile myAnt : ants.getMyAnts()) { for (Aim direction : Aim.values()) { if (ants.getIlk(myAnt, direction).isPassable()) { ants.issueOrder(myAnt, direction); break; } } } }
スターターパックのコード全体を解析するのに約1時間かかりました(誰でも同じことを行うことをお勧めします。すぐに始める方法についての記事を読んでいないことをお勧めします)。 結果として、最初のサイクルは友好的なアリのセルを通過し、2番目のサイクルは4つの方向のいずれかにアリを移動しようとすることが判明しました(Aimの連続列挙)。
最初のステップで、1つの非常に良いルールに出会いました。2つのアリを同じセルに移動すると、両方とも死にます。 この問題を解決するために、Antsクラスに修正が加えられました。
占有されているセルのリストを追加しました。
public class Ants { private final Set<Tile> employed = new HashSet<Tile>(); //...
フレンドリーなアリの位置を掃除するとき、私は占有された位置の掃除を追加しました:
public void clearMyAnts() { employed.clear(); //...
そして最後に、issueOrderメソッドをほぼ完全に書き直しました。
public boolean issueOrder(Tile myAnt, Aim direction) { Order order = new Order(myAnt, direction); Tile newPosition = getTile(myAnt, direction); if(getIlk(myAnt, direction).isPassable() && !employed.contains(newPosition)) { orders.add(order); // , employed.add(newPosition); System.out.println(order); System.out.flush(); return true; } return false; }
メインクラスのコードはわずかに減少します。
public void doTurn() { Ants ants = getAnts(); for (Tile myAnt : ants.getMyAnts()) { for (Aim direction : Aim.values()) { if (ants.issueOrder(myAnt, direction)) break; } } }
少し個性
私たちは少し夢を見て、将来、アリのそれぞれが個人であり、いくつかのパラメーターの所有者になると想像します。 このような考慮事項の継続は、Antクラスになります。
public class Ant { protected Ants ants; protected Tile tile; protected int waitStep = 0; public Ant(Ants ants, Tile tile) { this.tile = tile; this.ants = ants; } public Tile getTile() { return tile; } // ( !) public boolean issueOrder(Aim direction) { if(ants.issueOrder(tile, direction)) { tile = ants.getTile(tile, direction); return true; } else return false; } // , public boolean waiting() { return waitStep-- > 0; } // public void think() { for(Aim direction : Aim.values()) { if(ants.issueOrder(direction)) break; } } }
次に、このクラスを機能させるために、いくつかの小さな変更を加える必要があります。 それはかなり退屈で単調ですが、それでも... MyBotメソッドから始めましょう:
public void doTurn() { getAnts().think(); // }
欠落しているメソッドをAntに追加し、myAntsリストのコンテンツタイプを変更します。
private final Set<Ant> myAnts = new HashSet<Ant>(); //... public void think() { for(Ant ant : myAnts) { if(ant.waiting()) // , continue; ant.think(); // } }
ここで、Antオブジェクトの追加と削除を注意深く監視する必要があります。 コードを理解した人は、Antsクラスのupdateメソッドを見た可能性が高く、このメソッドでは、フレンドリーなアリの出現と死を追跡できます(ただし、死はすべてのアリに共通です)。 この一般的な死は、いくつかの方法で修正することができます...私は2種類の死を追加することにしました。
Ilk列挙クラスに変更を加えましょう。
public enum Ilk { MY_DEAD, ENEMY_DEAD, // DEAD, //... public boolean isUnoccupied() { return this == LAND || this == MY_DEAD || this == ENEMY_DEAD; } }
BotクラスのremoveAntメソッドを調整します。
public void removeAnt(int row, int col, int owner) { ants.update(owner > 0 ? Ilk.ENEMY_DEAD : Ilk.MY_DEAD, new Tile(row, col)); }
これで、アリの「観察」を行う準備がすべて整いました。Atnsクラスのupdateメソッドを編集します。
public void update(Ilk ilk, Tile tile) { map[tile.getRow()][tile.getCol()] = ilk; switch (ilk) { case FOOD: foodTiles.add(tile); break; case MY_ANT: myAnts.add(new Ant(this, tile)); break; case ENEMY_ANT: enemyAnts.add(tile); break; case MY_DEAD: myAnts.remove(new Ant(this, tile)); } }
食べ物とでたらめ-小さな脳
さらに、すべてのステップをペイントしようとしても意味がありません。コードにコメントを付けてください。 Antクラスコードを変更して、アリの動作を変更します。
public class Ant { //... public void think() { if(!doFood()) // , doRandom(); // } protected boolean doRandom() { while(!issueOrder(Aim.getRandom())); // return true; } protected boolean doFood() { return moveToObject(ants.getFoodTiles()); } // protected boolean moveToObject(Set<Tile> objects) { Tile object = findObject(objects); if(object != null) { issueOrder(ants.getDirections(object, tile).get(0)); return true; } return false; } // protected Tile findObject(Set<Tile> objects) { Tile find = null; int distance = 0; int minDistance = ants.MAX_MAP_SIZE; for(Tile aspt : objects) { distance = ants.getDistance(aspt, tile); if(minDistance > distance) { find = aspt; minDistance = distance; } } return find; } }
このコードを書くときに、ランダムな方向を返す静的メソッドをAim列挙クラスに追加しました。
public enum Aim { private static final Random random = new Random(); //... public static Aim getRandom() { return values()[random.nextInt(values().length)]; } }
間違い
コードのどこかで台無しになったのかもしれませんが、AntsクラスのgetDirectionsメソッドにエラーが忍び込んでいるようです。
public List<Aim> getDirections(Tile t1, Tile t2) { List<Aim> directions = new ArrayList<Aim>(); if (t1.getRow() < t2.getRow()) { if (t2.getRow() - t1.getRow() >= rows / 2) { directions.add(Aim.SOUTH); // Aim.NORTH } else { directions.add(Aim.NORTH); // Aim.SOUTH } } else if (t1.getRow() > t2.getRow()) { if (t1.getRow() - t2.getRow() >= rows / 2) { directions.add(Aim.NORTH); // ... } else { directions.add(Aim.SOUTH); //... ( ) } } if (t1.getCol() < t2.getCol()) { if (t2.getCol() - t1.getCol() >= cols / 2) { directions.add(Aim.EAST); // ... } else { directions.add(Aim.WEST); //... ( ) } } else if (t1.getCol() > t2.getCol()) { if (t1.getCol() - t2.getCol() >= cols / 2) { directions.add(Aim.WEST); // ... } else { directions.add(Aim.EAST); //... ( ) } } return directions; }
長い間、私は何が間違っていたのか理解できませんでした...アリはどういうわけか間違って食べ物を探していました...これは事実であることが判明しました。
おわりに
そのようなゲームには独学が必要だとは思いません。 おそらく、一連の戦略+わずかな調整(グループサイズ、現在の戦略...他のパラメータはまだ公開しません)を用意する方が良いでしょう。 そのようなアリがどれほど効果的であるかはまだわかりませんが、これについては第2回の記事で書きます。
PSの自己トレーニングは必要ありません。 それは新しい要素が現れない閉じたシステムです。 それまでの間、自己学習の敵は学習しているので、敵の丘を攻略できます:)