千里眼
AbleDogsに対応するために更新されたコード
うわー!最終的にそのNetcatsを打ち負かす!この将来の予測パックを作成するために、若干の修正を加えて既存のコードを拡張しました(Geobitsにクレジット!)。獲物がどこに移動するかを知っている捕食者に勝るものはありません!
私が行った2つのテストから、私のパックは常にNetcatに勝ちました。ただし、他のパックが存在しない場合は、近くに他の獲物が多すぎると予測が失敗するため、これはあまりよくありません。
たぶん、最初の数千ターンの間に獲物の数を大幅に減らすためにCivilizedBeastsのトリックを含めることができます。
5.21分で完了
千里眼(1):ターン9270:スコア100
EcoCamel.pl(3):ターン8118:スコア80
Netcats(0):ターン6111:スコア64
RubyVultures.rb(5):ターン4249:スコア51
RubySpiders.rb(4):ターン3495:スコア40
CivilizedBeasts(2):ターン3176:スコア32
ChaserPack(6):ターン2492:スコア25
私のパックの名前から、どの戦略を使用するかを知っているはずです= D
編集:
- 同じ獲物を追跡しないようにパック管理システムを更新しました(また、最適な一致を見つけようとしました!)
- 獲物の数が少ないときのさまようプロセスを改善します(これは勝利のために重要です!)。
以前のバージョンがちょうど角で立ち往生した特殊なケースを改善します。
- 捕食者検出アルゴリズムのバグを修正しました(今では非常に正確です!)
- 含まれる餌食
flock[ALIGN]
要因
- 食物が不足している場合、ペットとして1つの獲物を保管してください
- パックが獲物を集める場所に巣穴を作ります
- 近くの捕食者を誘惑して獲物を追いかけます
各パックが何匹の獲物を食べるかを数えましたが、結果は次のとおりです。
Clairvoyant(1)は9270ターンで916の獲物を消費しました(0.099獲物/ターン)
EcoCamel.pl(3)は、8118ターンで73獲物を消費しました(0.009獲物/ターン)
Netcats(0)は6111ターンで563の獲物を消費しました(0.092獲物/ターン)
RubyVultures.rb(5)は4249ターンで77の獲物を消費しました(0.018獲物/ターン)
RubySpiders.rb(4)は3495ターンで293獲物を消費しました(0.084獲物/ターン)
CivilizedBeasts(2)は3176ターンで10獲物を消費しました(0.003獲物/ターン)
ChaserPack(6)は2492ターンで43獲物を消費しました(0.017獲物/ターン)
私のパックは非常に攻撃的であり、916カウントのほとんどは、RubySpidersのようにNetcatsから獲物を盗むことから得られると思います。
CivilizedBeastsは、残念ながらEcoCamelの中央のラクダのために負けています。
そして、EcoCamel(空腹クリティカル500)は非常に効率的で、最後まで生き残るのに十分なだけ食べます。
また、この更新されたClairvoyantでは、ゲームはかろうじて10,000ターンに達します。
コード:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.TreeSet;
public class Clairvoyant extends GenericPack {
private static final double MAX_SPEED = 6.1;
private TreeSet<Animal> foods = new TreeSet<Animal>(new AnimalComparator());
private TreeSet<Animal> predators = new TreeSet<Animal>(new AnimalComparator());
private XY abattoirCorner;
private double abattoirRadius = 100;
private MyMember[] myMembers = new MyMember[100];
public class AnimalComparator implements Comparator<Animal>{
@Override
public int compare(Animal arg0, Animal arg1) {
if(arg0.x < arg1.x){
return -1;
} else if (arg0.x > arg1.x){
return 1;
} else {
if(arg0.y < arg1.y){
return -1;
} else if(arg0.y > arg1.y){
return 1;
} else {
return 0;
}
}
}
}
public class MyMember extends Member{
public XY target;
public XY herdPos;
public double herdRadius;
public boolean mayEat;
public XY pos;
public ArrayList<MyAnimal> closestPreys;
public boolean outdated;
public MyMember(int id) {
super(id);
this.pos = new XY(x, y);
closestPreys = new ArrayList<MyAnimal>();
mayEat = true;
outdated = true;
}
public MyMember(Member member){
super(member.id);
this.pos = new XY(x, y);
closestPreys = new ArrayList<MyAnimal>();
mayEat = true;
outdated = true;
}
public MyMember(Member member, Animal target){
super(member.id);
this.target = new XY(target.x, target.y);
this.pos = new XY(x, y);
closestPreys = new ArrayList<MyAnimal>();
mayEat = true;
outdated = true;
}
public void reset(Member me){
x = me.x;
y = me.y;
pos = new XY(x, y);
closestPreys.clear();
mayEat = true;
outdated = true;
}
}
public class MyAnimal extends Animal{
public ArrayList<MyMember> chasers;
public XY pos;
public boolean resolved;
public MyAnimal(double x, double y){
super(x, y);
pos = new XY(x, y);
chasers = new ArrayList<MyMember>();
resolved = false;
}
public MyAnimal(Animal ani){
super(ani.x, ani.y);
pos = new XY(x, y);
chasers = new ArrayList<MyMember>();
resolved = false;
}
}
public static void main(String[] args){
new Clairvoyant().run();
}
public Clairvoyant(){
for(int i=0; i<100; i++){
nextIdx[i] = 0;
}
int cornerIdx = (int)Math.floor(Math.random()*4);
switch (cornerIdx){
case 0: abattoirCorner = new XY(0,0); break;
case 1: abattoirCorner = new XY(500,0); break;
case 2: abattoirCorner = new XY(500,500); break;
case 3: abattoirCorner = new XY(0,500); break;
}
}
@Override
public void respond(){
updateData();
goToTarget();
}
private void updateData(){
for(int i=0; i<100; i++){
if(myMembers[i]!=null){
myMembers[i].pos = null;
}
}
foods.clear();
predators.clear();
for(Member me: members){
foods.addAll(me.foods);
predators.addAll(me.others);
predators.add(new Animal(me.x, me.y));
if(myMembers[me.id] != null){
myMembers[me.id].reset(me);
} else {
myMembers[me.id] = new MyMember(me);
}
}
for(int i=0; i<100; i++){
if(myMembers[i]!=null && myMembers[i].pos == null){
myMembers[i] = null;
}
}
TreeSet<MyAnimal> closestPreys = new TreeSet<MyAnimal>(new AnimalComparator());
for(int i=0; i<100; i++){
if (myMembers[i]==null) continue;
MyMember me = myMembers[i];
ArrayList<Animal> animals = findClosest(foods, me.pos, members.size());
boolean first = true;
for(Animal ani: animals){
MyAnimal myAni = new MyAnimal(ani);
if(closestPreys.contains(ani)){
myAni = closestPreys.ceiling(myAni);
} else {
closestPreys.add(myAni);
}
if(first){
myAni.chasers.add(me);
first = false;
}
me.closestPreys.add(myAni);
}
}
performMatching();
for(int i=0; i<100; i++){
if (myMembers[i] == null) continue;
MyMember me = myMembers[i];
if(!me.outdated) continue;
if(me.closestPreys.size() == 0) continue;
MyAnimal closestPrey = me.closestPreys.get(0);
if(closestPrey.resolved) continue;
if(closestPrey.chasers.size() > 1){
MyMember hungriest = me;
MyMember closest = me;
for(MyMember otherMe: closestPrey.chasers){
if(sqDist(closestPrey.pos, otherMe) < sqDist(closestPrey.pos, closest)){
closest = otherMe;
}
if(otherMe.hunger < hungriest.hunger){
hungriest = otherMe;
}
}
if(hungriest.hunger > 200){ // Nobody's critically hungry, the closest takes the prey
closest.target = closestPrey.pos;
closest.mayEat = true;
closest.herdPos = abattoirCorner;
closest.herdRadius = abattoirRadius;
closest.outdated = false;
} else {
if(hungriest == closest){
closest.target = closestPrey.pos;
closest.mayEat = true;
closest.herdPos = abattoirCorner;
closest.herdRadius = abattoirRadius;
closest.outdated = false;
} else {
closest.target = closestPrey.pos;
closest.mayEat = false;
closest.herdPos = hungriest.pos;
closest.herdRadius = 0;
closest.outdated = false;
hungriest.target = closestPrey.pos;
hungriest.mayEat = true;
hungriest.herdPos = abattoirCorner;
hungriest.herdRadius = 10;
hungriest.outdated = false;
}
}
closestPrey.resolved = true;
} else {
me.target = closestPrey.pos;
me.herdPos = abattoirCorner;
me.herdRadius = abattoirRadius;
me.mayEat = true;
me.outdated = false;
}
}
for(int i=0; i<100; i++){
if (myMembers[i] == null) continue;
MyMember me = myMembers[i];
if(me.outdated){
me.target = null;
me.outdated = false;
}
}
}
private void goToTarget(){
for(Member me: members){
MyMember mem = myMembers[me.id];
if(mem.target == null){
wander(me, 2*(me.id%2)-1);
continue;
} else {
nextIdx[me.id] = 0;
XY[] nearestHostile = new XY[100];
for(Animal other: me.others){
XY otherPos = new XY(other.x, other.y);
boolean isMember = false;
for(Member otherMember: members){
if(other.x==otherMember.x && other.y==otherMember.y){
isMember = true;
break;
}
}
if(!isMember){
if(nearestHostile[me.id] == null || XY.sqDistance(mem.pos, otherPos) < XY.sqDistance(mem.pos, nearestHostile[me.id])){
nearestHostile[me.id] = otherPos;
}
}
}
// Go towards the target by predicting its next position
XY target = predictNextPos(mem.target, me);
me.dx = (target.x - me.x);
me.dy = (target.y - me.y);
// Try to herd the target to our abattoir if this member is not too hungry
// and if there is no other hostile predator who is closer to the target than us
// This will make the other hostile predator to keep targeting this target, while
// it is certain that we will get the target.
// This is a win situation for us, since it will make the other predator wasting his turn.
if((me.hunger <= 200 && XY.sqDistance(mem.target, mem.pos) > 400) || me.hunger <= 50 ||
(nearestHostile[me.id] != null && Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) < Math.sqrt(XY.sqDistance(mem.target, mem.pos)))){
continue;
}
// Don't eat if not threatened nor hungry
if(me.hunger > 50 && (nearestHostile[me.id] == null ||
Math.sqrt(XY.sqDistance(mem.target, nearestHostile[me.id])) > Math.sqrt(XY.sqDistance(mem.target, mem.pos)) + 6)){
mem.mayEat = false;
}
// Herd to abattoir corner
double distFromHerd = Math.sqrt(XY.sqDistance(target, mem.herdPos));
XY oppositeAbattoirCorner = new XY(500-abattoirCorner.x, 500-abattoirCorner.y);
double distFromOpposite = Math.sqrt(XY.sqDistance(target, oppositeAbattoirCorner));
if((me.dx*me.dx+me.dy*me.dy > 64 && distFromHerd > mem.herdRadius && distFromOpposite > abattoirRadius)
|| (preyCount < 5*predCount)){
double herdDistance = 4*(distFromHerd-mem.herdRadius)/(Island.SIZE-mem.herdRadius);
if(!mem.mayEat) herdDistance = 4;
XY gradient = target.minus(abattoirCorner);
me.dx += gradient.x*herdDistance/distFromHerd;
me.dy += gradient.y*herdDistance/distFromHerd;
}
}
}
}
private void performMatching(){
for(int i=0; i<100; i++){
if (myMembers[i] == null) continue;
MyMember me = myMembers[i];
if(me.closestPreys.size()==0) continue;
MyAnimal closestPrey = me.closestPreys.get(0);
if(closestPrey.chasers.size() > 1){
resolveConflict(closestPrey);
}
}
}
private void resolveConflict(MyAnimal prey){
ArrayList<MyMember> chasers = prey.chasers;
MyMember winner = null;
double closestDist = Double.MAX_VALUE;
for(MyMember me: chasers){
if(sqDist(prey.pos, me) < closestDist){
closestDist = sqDist(prey.pos, me);
winner = me;
}
}
for(int i=chasers.size()-1; i>=0; i--){
MyMember me = chasers.get(i);
if(me!=winner){
me.closestPreys.get(0).chasers.remove(me);
me.closestPreys.add(me.closestPreys.remove(0));
me.closestPreys.get(0).chasers.add(me);
}
}
}
private Animal findClosest(Collection<Animal> preys, XY me){
Animal target = null;
double cDist = Double.MAX_VALUE;
double x, y, sqDist;
for (Animal food : preys) {
x = food.x - me.x;
y = food.y - me.y;
sqDist = x * x + y * y + Double.MIN_NORMAL;
if (sqDist < cDist) {
cDist = sqDist;
target = food;
}
}
return target;
}
private ArrayList<Animal> findClosest(Collection<Animal> preys, XY me, int num){
ArrayList<Animal> result = new ArrayList<Animal>();
for(Animal food: preys){
int addIdx = -1;
for(int i=0; i<num && i<result.size(); i++){
Animal regFood = result.get(i);
if(sqDist(me, food) < sqDist(me, regFood)){
addIdx = i;
break;
}
}
if(addIdx == -1){
result.add(food);
} else {
result.add(addIdx, food);
}
if(result.size() > num){
result.remove(num);
}
}
return result;
}
private Member findClosestToTarget(Collection<Member> members, Animal target){
Member member = null;
double cDist = Double.MAX_VALUE;
double x, y, sqDist;
for (Member me : members) {
x = me.x - target.x;
y = me.y - target.y;
sqDist = x * x + y * y + Double.MIN_NORMAL;
if (sqDist < cDist) {
cDist = sqDist;
member = me;
}
}
return member;
}
private static final XY[] CHECKPOINTS = new XY[]{
new XY(49.5,49.5),
new XY(450.5,49.5),
new XY(450.5,100),
new XY(49.5,100),
new XY(49.5,150),
new XY(450.5,150),
new XY(450.5,200),
new XY(49.5,200),
new XY(49.5,250),
new XY(450.5,250),
new XY(450.5,300),
new XY(49.5,300),
new XY(49.5,350),
new XY(450.5,350),
new XY(450.5,400),
new XY(49.5,400),
new XY(49.5,450.5),
new XY(450.5,450.5)};
private int[] nextIdx = new int[100];
private int advanceIdx(int idx, int sign, int amount){
return sign*(((Math.abs(idx)+CHECKPOINTS.length-1+sign*amount) % CHECKPOINTS.length) + 1);
}
private void wander(Member me, int sign) {
if(preyCount > 20*predCount){
if (me.dx == 0 && me.dy == 0) {
me.dx = 250 - me.x;
me.dy = 250 - me.y;
return;
}
double lx, ly, px, py;
lx = me.dx / 4;
ly = me.dy / 4;
boolean dir = Math.random() < 0.5 ? true : false;
px = dir ? ly : -ly;
py = dir ? -lx : lx;
me.dx += px;
me.dy += py;
} else {
if(nextIdx[me.id]==0){
XY farthest = new XY(2000,2000);
int farthestIdx = -1;
for(int i=0; i<CHECKPOINTS.length; i++){
if(sign*sqDist(CHECKPOINTS[i], me) > sign*sqDist(farthest, me)){
farthest = CHECKPOINTS[i];
farthestIdx = i+1;
}
}
nextIdx[me.id] = farthestIdx*sign;
for(Member mem: members){
if(mem.id == me.id) continue;
if(nextIdx[mem.id]==nextIdx[me.id]){
nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 5);
}
}
}
if(sqDist(CHECKPOINTS[Math.abs(nextIdx[me.id])-1],me) < 1){
nextIdx[me.id] = advanceIdx(nextIdx[me.id], sign, 1);
}
me.setDirection(CHECKPOINTS[Math.abs(nextIdx[me.id])-1].x-me.x,
CHECKPOINTS[Math.abs(nextIdx[me.id])-1].y-me.y);
}
}
private double sqDist(XY me, Animal target){
double dx = me.x-target.x;
double dy = me.y-target.y;
return dx*dx + dy*dy + Double.MIN_NORMAL;
}
private double sqDist(XY me, Member target){
double dx = me.x-target.x;
double dy = me.y-target.y;
return dx*dx + dy*dy + Double.MIN_NORMAL;
}
private double sqDist(Animal target, Member me){
double dx = me.x-target.x;
double dy = me.y-target.y;
return dx*dx + dy*dy + Double.MIN_NORMAL;
}
private List<Animal> getNeighbors(double radius, XY pos, Collection<Animal> candidates) {
List<Animal> neighbors = new ArrayList<Animal>();
for(Animal neighbor: candidates){
if(sqDist(pos, neighbor) < radius * radius){
neighbors.add(neighbor);
}
}
return neighbors;
}
final double[] weights = { 1, 1, 0.96, 2, 4 };
double weightSum;
static final int ALIGN = 0;
static final int SEPARATE = 1;
static final int COHESION = 2;
static final int FLEE = 3;
static final int WALL = 4;
static final int VISIBLE = 30;
static final int VISIBLE_PRED = 50;
private HashMap<Member, List<Animal>> prevPreys = new HashMap<Member, List<Animal>>();
private XY matchPreys(List<Animal> prevs, List<Animal> curs, XY prey){
XY result = new XY();
double sqDist = 0;
Animal candidate;
XY otherPos;
for(Animal otherPrey: curs){
otherPos = new XY(otherPrey.x, otherPrey.y);
sqDist = XY.sqDistance(prey, otherPos);
if(sqDist > VISIBLE * VISIBLE)
continue;
candidate = findClosest(getNeighbors(6, otherPos, prevs), prey);
if(candidate == null){
return null;
}
result.add(otherPos.x-candidate.x, otherPos.y-candidate.y);
}
return result;
}
private XY predictNextPos(XY prey, Member me) {
List<Animal> preys = getNeighbors(VISIBLE_PRED, prey, foods);
List<Animal> preds = getNeighbors(VISIBLE, prey, predators);
XY flock[] = new XY[weights.length];
for (int i = 0; i < weights.length; i++)
flock[i] = new XY();
double dx, dy, dist, sqDist;
for (Animal otherPrey : preys) {
sqDist = XY.sqDistance(prey, new XY(otherPrey.x, otherPrey.y));
if(sqDist > VISIBLE * VISIBLE)
continue;
dx = otherPrey.x - prey.x;
dy = otherPrey.y - prey.y;
flock[COHESION].add(dx*sqDist, dy*sqDist);
flock[SEPARATE].add(-dx*(1d/sqDist), -dy*(1d/sqDist));
flock[ALIGN].add(new XY(prey.x-me.x,prey.y-me.y));
}
if(sqDist(prey, me) < 400){
if(prevPreys.get(me) == null){
prevPreys.put(me, preys);
} else {
XY flockAlign = matchPreys(prevPreys.get(me), preys, prey);
if(flockAlign == null){
prevPreys.put(me , null);
} else {
flock[ALIGN] = flockAlign;
prevPreys.put(me, preys);
}
}
}
flock[ALIGN].unitize().multiply(5);
flock[COHESION].unitize().multiply(5);
flock[SEPARATE].unitize().multiply(5);
for (Animal predator : preds){
flock[FLEE].add(prey.x-predator.x, prey.y-predator.y);
}
dx = Island.CENTER.x - prey.x;
dy = Island.CENTER.y - prey.y;
dist = Math.max(Math.abs(dx), Math.abs(dy));
if(dist > 240){
flock[WALL].x = dx * dist;
flock[WALL].y = dy * dist;
flock[WALL].unitize().multiply(5);
}
XY vec = new XY();
vec.x = 0;
vec.y = 0;
for (int i = 0; i < flock.length; i++) {
flock[i].multiply(weights[i]);
vec.add(flock[i]);
}
limitSpeed(vec);
return vec.add(prey);
}
private XY limitSpeed(XY move) {
if (move.x*move.x+move.y*move.y > MAX_SPEED*MAX_SPEED)
move.unitize().multiply(MAX_SPEED);
return move;
}
}