千里眼
代码已更新以面对AbleDogs
呜呼!最终击败了网猫!我对现有代码进行了扩展(对Geobits进行了信用!),并进行了一些小修改以创建此将来的预测包。谁知道猎物将移动到哪里的掠食者,无所不及!
从我做过的两次测试中,我的背包总能击败Netcats。但这在没有其他背包的情况下效果不佳,因为如果附近有其他猎物,预测仍然会失败。
也许我可以包括《文明野兽》的the俩,以在头几千回合中大幅减少猎物的数量。
在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)在9270转中消耗了916猎物(0.099猎物/转)
EcoCamel.pl(3)在8118回合中消耗了73个猎物(0.009个猎物/回合)
网猫(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窃取猎物获得的。
不幸的是,由于EcoCamel的中心骆驼,文明的野兽正在失去。
而且EcoCamel(饥饿临界值为500)非常有效,它吃得足以生存到最后。
同样,使用此更新的《千里眼》,该游戏也几乎无法达到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;
}
}