diff --git a/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Line.java b/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Line.java index f8a516e..918e3db 100644 --- a/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Line.java +++ b/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Line.java @@ -1,5 +1,6 @@ package org.ndx.codingame.lib2d; +import java.util.ArrayList; import java.util.Collection; import org.ndx.codingame.lib2d.base.AbstractPoint; @@ -215,4 +216,73 @@ public Collection intersectionWith(Circle circle) { public boolean intersectsWith(Circle circle) { return circle.intersectsWith(this); } + + + public Collection intersectionWith(Line line) { + Collection returned = new ArrayList<>(); + double a = coeffs.a, + b = coeffs.b, + c = coeffs.c, + d = line.coeffs.a, + e = line.coeffs.b, + f = line.coeffs.c + ; + if(e*a!=b*d) { + if(coeffs.isHorizontalLine()) { + /* + * a*x+b*y+c = 0 + * d*x+e*y+f = 0 + * + * a*x = -(b*y+c) + * d*x = -(e*y+f) + * + * x = -(b*y+c)/a + * x = -(e*y+f)/d + * + * (b*y+c)/a=(e*y+f)/d + * + * d*(b*y+c)=a*(e*y+f) + * + * d*b*y+d*c=a*e*y+a*f + * + * (d*b-a*e)*y=a*f-d*c + * + * y=(a*f-d*c)/(d*b-a*e) + */ + double y = (a*f-d*c)/(d*b-a*e); + if(!line.coeffs.isHorizontalLine()) { + returned.add(new ContinuousPoint(line.coeffs.computeXFromY(y), y)); + } + } else { + /* + * a*x+b*y+c = 0 + * d*x+e*y+f = 0 + * + * b*y = -(a*x+c) + * e*y = -(d*x+f) + * + * y = -(a*x+c)/b + * y = -(d*x+f)/e + * + * (a*x+c)/b = (d*x+c)/e + * e*(a*x+c) = b*(d*x+f) + * + * e*a*x+e*c = b*d*x+b*c + * (e*a-b*d)*x = b*f-e*c + * x = (b*f-e*c)/(e*a-b*d) + */ + double x = (b*f-e*c)/(e*a-b*d); + if(line.coeffs.isVerticalLine()) { + returned.add(new ContinuousPoint(x, coeffs.computeYFromX(x))); + } else { + returned.add(new ContinuousPoint(x, line.coeffs.computeYFromX(x))); + } + } + } + return returned; + } + + public boolean intersectsWith(Line line) { + return !intersectionWith(line).isEmpty(); + } } diff --git a/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Segment.java b/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Segment.java index 4d5715b..1da27f0 100644 --- a/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Segment.java +++ b/libs/lib-2d/src/main/java/org/ndx/codingame/lib2d/Segment.java @@ -44,4 +44,29 @@ public boolean contains(ContinuousPoint point) { public Collection intersectionWith(Circle circle) { return super.intersectionWith(circle).stream().filter(this::contains).collect(Collectors.toSet()); } + + @Override + public Collection intersectionWith(Line line) { + return super.intersectionWith(line).stream() + .filter(p -> + p.x>=Math.min(first.x, second.x) && p.x<=Math.max(first.x, second.x) + && + p.y>=Math.min(first.y, second.y) && p.y<=Math.max(first.y, second.y) + ) + .collect(Collectors.toList()); + } + + public Collection intersectionWith(final Segment segment) { + return intersectionWith((Line) segment).stream() + .filter(p -> + p.x>=Math.min(segment.first.x, segment.second.x) && p.x<=Math.max(segment.first.x, segment.second.x) + && + p.y>=Math.min(segment.first.y, segment.second.y) && p.y<=Math.max(segment.first.y, segment.second.y) + ) + .collect(Collectors.toList()); + } + + public boolean intersectsWith(Segment segment) { + return !intersectionWith(segment).isEmpty(); + } } \ No newline at end of file diff --git a/libs/lib-2d/src/test/java/org/ndx/codingame/lib2d/LineTest.java b/libs/lib-2d/src/test/java/org/ndx/codingame/lib2d/LineTest.java index 7281abd..ecde050 100644 --- a/libs/lib-2d/src/test/java/org/ndx/codingame/lib2d/LineTest.java +++ b/libs/lib-2d/src/test/java/org/ndx/codingame/lib2d/LineTest.java @@ -26,6 +26,8 @@ public class LineTest { ContinuousPoint at_45_degrees = tested.pointAtAngle(first, 45, 1, PointBuilder.DEFAULT); assertThat(at_45_degrees.x).isEqualTo(1/sqrt(2), within(Algebra.ZERO)); assertThat(at_45_degrees.y).isEqualTo(1+1/sqrt(2), within(Algebra.ZERO)); + Line intersecting = from(0, 0).lineTo(2, 2); + assertThat(tested.intersectionWith(intersecting)).contains(at(1.0, 1)); } @Test public void can_compute_infos_on_vertical_line() { @@ -39,6 +41,8 @@ public class LineTest { assertThat(tested.project(at(0.0, 0))).isEqualTo(at(1, 0)); assertThat(tested.symetricOf(at(0.0, 0))).isEqualTo(at(2, 0)); assertThat(tested.pointAtNTimes(2)).isEqualTo(at(1, 2)); + Line intersecting = from(0, -1).lineTo(0, 1); + assertThat(tested.intersectionWith(intersecting)).isEmpty(); } @Test public void can_compute_infos_on_vertical_reverse_line() { diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/Player.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/Player.java index 031bcc9..4ca9077 100644 --- a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/Player.java +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/Player.java @@ -74,7 +74,7 @@ private static void chooseCaptain(Status status, List myTeam) { } else { if(status.getCaptain()==myCaptain.id) { // we change captain only when really deserved - if(Math.abs(myCaptain.position.x-wingman.position.x) Map getPositionsOf(Class) positionsOf.get(type); } + public Collection getSnaffles() { + return getCollectionOf(Snaffle.class); + } + public Snaffle findBestSnaffleFor(ContinuousPoint position) { return getPositionsOf(Snaffle.class).get(position.findNearestDistance2(getPositionsOf(Snaffle.class).keySet())); } @@ -74,12 +78,7 @@ public Collection getAllWizards() { } public Snaffle findBestSnaffleFor(Wizard wizard) { - SortedMap snaffles = null; - if(wizard.isAttacking()) { - snaffles = getSnafflesSortedByDistanceTo(wizard.getAttackedGoal().pointAtNTimes(0.5)); - } else { - snaffles = getSnafflesSortedByDistanceTo(wizard.getDefendedGoal().pointAtNTimes(0.5)); - } + SortedMap snaffles = sortSnafflesFor(wizard); SortedMap goodOnes = snaffles.headMap(wizard.position); ContinuousPoint key = null; if(goodOnes.isEmpty()) { @@ -105,6 +104,16 @@ public Snaffle findBestSnaffleFor(Wizard wizard) { return snaffles.get(key); } + public SortedMap sortSnafflesFor(Wizard wizard) { + SortedMap snaffles = null; + if(wizard.isAttacking()) { + snaffles = getSnafflesSortedByDistanceTo(wizard.getAttackedGoal().pointAtNTimes(0.5)); + } else { + snaffles = getSnafflesSortedByDistanceTo(wizard.getDefendedGoal().pointAtNTimes(0.5)); + } + return snaffles; + } + private SortedMap getSnafflesSortedByDistanceTo(ContinuousPoint goalCenter) { final Map snaffles = getPositionsOf(Snaffle.class); SortedMap sorted = new TreeMap<>(new AbstractPoint.PositionByDistanceTo(goalCenter)); diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Entity.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Entity.java index 995b332..5ba4d8f 100644 --- a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Entity.java +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Entity.java @@ -27,17 +27,19 @@ public int compare(Entity o1, Entity o2) { } public final ContinuousPoint position; + public final ContinuousPoint speed; public final Vector direction; public final int id; private Map circles = new TreeMap<>(); public Entity(int id, double x, double y, double vx, double vy) { this.id = id; position = new ContinuousPoint(x, y); + speed = new ContinuousPoint(vx, vy); direction = Geometry.from(position).vectorOf(vx, vy); } public Circle getCircle() { - return getExtendedCircle(getRadius()); + return getCircle(getRadius()); } protected abstract double getRadius(); @@ -48,7 +50,7 @@ public boolean isBetween(Wizard wizard, Segment goal) { return (int) Math.signum(position.getX()-wizard.position.getX())!=(int) Math.signum(position.getX()-goal.first.getX()); } - public Circle getExtendedCircle(Double radius) { + public Circle getCircle(Double radius) { if(!circles.containsKey(radius)) { circles.put(radius, Geometry.from(position).cirleOf(radius)); } diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Wizard.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Wizard.java index 32083cb..04db6cb 100644 --- a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Wizard.java +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/entities/Wizard.java @@ -79,7 +79,7 @@ private String throwInDirectionOf(List entities, Segment goal) { Iterator entity = toAvoid.iterator(); while(entity.hasNext() && found) { Entity tested = entity.next(); - found = !obstacleFinder.intersectsWith(tested.getExtendedCircle(tested.getRadius()+Snaffle.RADIUS*2)); + found = !obstacleFinder.intersectsWith(tested.getCircle(tested.getRadius()+Snaffle.RADIUS*2)); } } angle+=5; diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/AccioStatus.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/AccioStatus.java new file mode 100644 index 0000000..1475950 --- /dev/null +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/AccioStatus.java @@ -0,0 +1,7 @@ +package org.ndx.codingame.fantastic.spells; + +public class AccioStatus extends SpellStatus { + public AccioStatus() { + super(6); + } +} diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/ObliviateStatus.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/ObliviateStatus.java new file mode 100644 index 0000000..126efaa --- /dev/null +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/ObliviateStatus.java @@ -0,0 +1,10 @@ +package org.ndx.codingame.fantastic.spells; + +import org.ndx.codingame.fantastic.status.StatusElement; + +public class ObliviateStatus extends SpellStatus implements StatusElement { + + public ObliviateStatus() { + super(3); + } +} diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/Spell.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/Spell.java index 2db5de5..02a4380 100644 --- a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/Spell.java +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/Spell.java @@ -1,19 +1,50 @@ package org.ndx.codingame.fantastic.spells; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.ndx.codingame.fantastic.entities.Bludger; import org.ndx.codingame.fantastic.entities.Entities; import org.ndx.codingame.fantastic.entities.Entity; +import org.ndx.codingame.fantastic.entities.Snaffle; import org.ndx.codingame.fantastic.entities.Wizard; import org.ndx.codingame.fantastic.status.MagicStatus; import org.ndx.codingame.fantastic.status.Status; +import org.ndx.codingame.lib2d.Segment; +import org.ndx.codingame.lib2d.continuous.ContinuousPoint; public enum Spell { FLIPENDO(20) { }, ACCIO(20) { + @Override + public SpellContext shouldCastThat(Status status, Wizard wizard, Entities entities) { + if(!wizard.isAttacking()) { + AccioStatus spellStatus = status.get(AccioStatus.class); + if(spellStatus==null) { + spellStatus = new AccioStatus(); + status.set(spellStatus); + } + final Collection snaffles = entities.sortSnafflesFor(wizard).values(); + return snaffles.stream() + .filter(s -> { + Segment expected = new Segment(s.position, + new ContinuousPoint(s.position.x+s.speed.x*2, s.position.y+s.speed.y*2)); + return expected.intersectsWith(wizard.getDefendedGoal()); + }) + .findFirst() + .map(s -> new SpellContext(s)) + .orElse(SpellContext.NO); + } + return SpellContext.NO; + } + + @Override + public String cast(Status status, SpellContext context) { + status.get(AccioStatus.class).applyOn(context.entity); + return super.cast(status, context); + } }, PETRIFICUS(10) { }, @@ -23,22 +54,35 @@ public enum Spell { */ @Override public SpellContext shouldCastThat(Status status, Wizard wizard, Entities entities) { + ObliviateStatus obliviated = status.get(ObliviateStatus.class); + if(obliviated==null) { + obliviated = new ObliviateStatus(); + status.set(obliviated); + } for(Bludger b : entities.getBludgers()) { - List myTeamisNearBludger = entities.getAllWizards().stream() - .sorted(new Entity.ByDistanceTo(b)) - .limit(2) - .filter(w -> w.teamId==status.getTeam()) - .collect(Collectors.toList()); - if(myTeamisNearBludger.size()==2) { - System.err.println(String.format("The two nearest wizards of Bludger %s are mine !", b)); - if(myTeamisNearBludger.get(0).position.distance2To(b.position)<1000) { - System.err.println(String.format("And one is definitely too near")); - return new SpellContext(b); + if(!obliviated.isAppliedOn(b)) { + List myTeamisNearBludger = entities.getAllWizards().stream() + .sorted(new Entity.ByDistanceTo(b)) + .limit(2) + .filter(w -> w.teamId==status.getTeam()) + .collect(Collectors.toList()); + if(myTeamisNearBludger.size()==2) { + System.err.println(String.format("The two nearest wizards of Bludger %s are mine !", b)); + if(myTeamisNearBludger.get(0).position.distance2To(b.position)<1000) { + System.err.println(String.format("And one is definitely too near")); + return new SpellContext(b); + } } } } return SpellContext.NO; } + + @Override + public String cast(Status status, SpellContext context) { + status.get(ObliviateStatus.class).applyOn(context.entity); + return super.cast(status, context); + } }; private int required; diff --git a/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/SpellStatus.java b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/SpellStatus.java new file mode 100644 index 0000000..dc1912d --- /dev/null +++ b/multiplayer/fantastic-bits/src/main/java/org/ndx/codingame/fantastic/spells/SpellStatus.java @@ -0,0 +1,43 @@ +package org.ndx.codingame.fantastic.spells; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.ndx.codingame.fantastic.entities.Entity; +import org.ndx.codingame.fantastic.status.StatusElement; + +public class SpellStatus implements StatusElement { + private Map entities = new HashMap<>(); + + private final int duration; + + public SpellStatus(int duration) { + super(); + this.duration = duration; + } + + public void applyOn(Entity entity) { + entities.put(entity, duration); + } + + public boolean isAppliedOn(Entity entity) { + return entities.containsKey(entity); + } + + @Override + public void advanceOneTurn() { + List list = new ArrayList<>(entities.keySet()); + for (Entity entity : list) { + int durationOf = entities.get(entity); + if(durationOf>1) { + entities.put(entity, durationOf-1); + } else { + entities.remove(entity); + } + } + } + + +}