public bool Intersect(ACar car, double extendRadius) { if (Type == ProjectileType.Washer) { return(Geom.ContainPoint(car.GetRect(-extendRadius), this)); } var r = Radius + extendRadius; if (GetDistanceTo2(car) > Geom.Sqr(r + Const.CarDiagonalHalfLength)) { return(false); } return(car.GetRectEx().Any(p => GetDistanceTo(p) < r)); }
public bool CheckUseOil() { if (self.RemainingOilCooldownTicks != 0 || world.Tick < Const.Game.InitialFreezeDurationTicks) { return(false); } if (self.EnginePower < 0) { return(false); } var slick = new AOilSlick(new ACar(self)); var rad = slick.Radius * 0.8; var result = false; for (var t = 0; t < MagicConst.OpponentsTicksPrediction; t++) { for (var i = 0; i < Opponents.Length; i++) { if (slick.GetDistanceTo2(OpponentsCars[i][t]) < rad * rad && (self.OilCanisterCount > 1 || Math.Abs(OpponentsCars[i][t].WheelTurn) > 0.2)) { result = true; } } } if (!result) { return(false); } foreach (var my in MyTeam) { if (my == null) { continue; } foreach (var pos in my) { if (pos.GetDistanceTo2(slick) < Geom.Sqr(slick.Radius)) { return(false); } } } return(true); }
public Moves Do(ACar car, Points pts) { // Проверка что данный путь был выбран if (_selectThisTick + 1 != MyStrategy.world.Tick) { _lastSuccessStack = null; } Self = car.Clone(); if (_lastCall == LastSuccess) { LastSuccess = _lastCall; } for (var t = 0; t < MyStrategy.world.Tick - _lastCall && _lastSuccessStack != null && _lastSuccessStack.Count > 0; t++) { _lastSuccessStack[0].Times--; _lastSuccessStack.Normalize(); } if (_lastSuccessStack != null && (_lastSuccessStack.Count == 0 || _useDist2 && _lastSuccessStack.ComputeTime() < 30)) { _lastSuccessStack = null; } _lastCall = MyStrategy.world.Tick; /* * Количество бонусов на расстоянии 0.5t * Если изменилось - пересчитывать сильно */ var bonusesCount05 = MyStrategy.Bonuses .Count(bonus => Self.GetDistanceTo(bonus) < Const.TileSize / 2); /* * Количество бонусов на расстоянии 2t * Если изменилось - чуть нужно пересчитать */ var bonusesCount2 = MyStrategy.Bonuses .Count( bonus => Self.GetDistanceTo(bonus) < Const.TileSize * 2 && MyStrategy.CellDistance(Self, bonus) <= 2); // Если был success на прошлом тике, то продолжаем. Или каждые _interval тиков. if (Const.Game.InitialFreezeDurationTicks < MyStrategy.world.Tick && bonusesCount05 == _bonusesCount05 && LastSuccess < MyStrategy.world.Tick - 1 && (MyStrategy.world.Tick - (LastSuccess + 1)) % _interval != 0) { _validateLastSuccessStack(); return(_lastSuccessStack); } /* * Смотрим на шины, которые на расстоянии не более 6 тайлов */ var prevProj = _projCandidates; _projCandidates = MyStrategy.Tires .Where( proj => Self.GetDistanceTo(proj[0]) <= Const.TileSize * 6 && MyStrategy.CellDistance(Self, proj[0]) <= 6) .ToArray(); var extended = MyStrategy.ExtendWaySegments(pts, 50); _bruteWayPoints = extended.GetRange(0, Math.Min(_waypointsCount, extended.Count)).ToArray(); if (LastStageMove.IsUseNitro && _turnsCount(_bruteWayPoints) > 1) { return(null); } #if DEBUG var bruteWayPoints = new Points(); bruteWayPoints.AddRange(_bruteWayPoints); Visualizer.SegmentsDrawQueue.Add(new object[] { Brushes.Brown, bruteWayPoints, 0.0 }); #endif _needDist = Const.TileSize * 0.5 - 3; _needDist2 = Const.TileSize - 3; _turnTo = _bruteWayPoints[_bruteWayPoints.Length - 1]; _turnTo2 = _bruteWayPoints[Math.Min(_bruteWayPoints.Length - 1, (int)(_bruteWayPoints.Length * 0.83))]; #if DEBUG Visualizer.CircleFillQueue.Add(new Tuple <Brush, ACircularUnit>(Brushes.OrangeRed, new ACircularUnit { X = _turnTo.X, Y = _turnTo.Y, Radius = 20 })); Visualizer.CircleFillQueue.Add(new Tuple <Brush, ACircularUnit>(Brushes.Orange, new ACircularUnit { X = _turnTo2.X, Y = _turnTo2.Y, Radius = 20 })); #endif _patterns = Patterns.Select(pt => new PathPattern { From = pt.From, To = pt.To, Step = pt.Step, Move = pt.Move.Clone() }).ToArray(); foreach (var p in _patterns) { if (p.Move.WheelTurn is TurnPattern) { var turnPattern = p.Move.WheelTurn as TurnPattern; if (turnPattern.Pattern == TurnPatternType.ToNext) { p.Move.WheelTurn = Self.GetAngleTo(_turnTo) < 0 ? -1 : 1; } else if (turnPattern.Pattern == TurnPatternType.FromNext) { p.Move.WheelTurn = Self.GetAngleTo(_turnTo) < 0 ? 1 : -1; } } } _movesStack = new Moves(); _bestMovesStack = new Moves(); _bestTime = MyStrategy.Infinity; _bestImportance = 0; /* * Смотрим на бонусы, которые на расстоянии не более 4t * TODO: уменьшить приоритет бонусов, которые может быть возьмет другой (в.т.ч тиммейт) */ _bonusCandidates = MyStrategy.Bonuses .Where( bonus => MyStrategy.world.Tick > 270 && // Не смотреть на бонусы при старте!!! Self.GetDistanceTo(bonus) <= Const.TileSize * 4 && MyStrategy.CellDistance(Self, bonus) <= 4 ) .ToArray(); /* * Смотрим на лужи, которые на расстоянии не более 5 тайлов */ var prevSlicks = _slickCandidates; _slickCandidates = MyStrategy.OilSlicks .Where( slick => Self.GetDistanceTo(slick) <= Const.TileSize * 5 && MyStrategy.CellDistance(Self, slick) <= 5 ) .ToArray(); /* * Пытаться объехать тех, которые * - Крашнулись * - Убиты * - Двигатель меньше чем на 0.5 мощности * - Двигаются по встречной * * - Если у меня нитро, или будет нитро * * - Своих */ var prevCars = _carCandidates; _carCandidates = MyStrategy.Others .Where(opp => opp[0].GetDistanceTo(Self) < Const.TileSize * 9) .Where( opp => opp[0].Original.IsTeammate || MyStrategy.IsCrashed(opp[0].Original) || !DurabilityObserver.IsActive(opp[0].Original) || opp[0].EnginePower < 0.5 || Self.RemainingNitroTicks > 0 || Math.Abs(Geom.GetAngleBetween(Self.Speed, opp[0].Speed)) > Math.PI / 2 ) .Where(opp => MyStrategy.CellDistance(Self, opp[0]) <= 9) // 9 - потому что он может ехать по встречке .ToArray(); if (_cache != null) { for (var k = 0; k < _patterns.Length; k++) { var range = (prevSlicks == null || prevCars == null || prevProj == null || _bonusesCount2 != bonusesCount2 || prevSlicks.Length != _slickCandidates.Length || prevCars.Length != _carCandidates.Length || prevProj.Length != _projCandidates.Length) ? (k == 0 ? 6 : 4) : (k == 0 ? 6 : 2); if (_bonusesCount05 != bonusesCount05 || Special && k == 0) { range = 10; } _patterns[k].From = Math.Max(0, _cache[k].Times - range); _patterns[k].To = Math.Min(_patterns[k].To * 9 / 7, _cache[k].Times + range); _patterns[k].Step = 2; } } _bonusesCount05 = bonusesCount05; _bonusesCount2 = bonusesCount2; var wayPointRequired = false; for (var i = _bruteWayPoints.Length - 1; i >= 0; i--) { if (_bruteWayPoints[i].GetDistanceTo2(_turnTo) < _needDist * _needDist) { for (var j = 0; j < i; j++) { wayPointRequired |= MyStrategy.GetNextWayPoint(Self.Original).Equals(MyStrategy.GetCell(_bruteWayPoints[j])); } break; } } _doRecursive(Self, 0, new PassedInfo { WayPoint = !wayPointRequired }); _cache = null; if (_bestTime == MyStrategy.Infinity) { return(_lastSuccessStack); } if (_bestMovesStack.ComputeTime() != _bestTime) { throw new Exception("ComputeTime != BestTime"); } LastSuccess = MyStrategy.world.Tick; _cache = _bestMovesStack.Clone(); if (_maxTicksInfo == null) { _maxTicksInfo = new int[_bestMovesStack.Count]; } for (var i = 0; i < _maxTicksInfo.Length; i++) { _maxTicksInfo[i] = Math.Max(_maxTicksInfo[i], _bestMovesStack[i].Times); } _bestMovesStack.Normalize(); _lastSuccessStack = _bestMovesStack.Clone(); return(_bestMovesStack); }
public static bool IntersectTail(Point p, double additionalMargin) { if (p.X < 0 || p.X >= Const.MapWidth || p.Y < 0 || p.Y >= Const.MapHeight) { return(true); } var cell = GetCell(p.X, p.Y); var tile = MyTiles[cell.I, cell.J]; if (tile.Type == TileType.Empty) { return(true); } var c = GetCenter(cell); var margin = Const.TileSize / 2 - Const.TileMargin; var lx = c.X - margin; var rx = c.X + margin; var ly = c.Y - margin; var ry = c.Y + margin; // внутри квадрата if (lx <= p.X && p.X <= rx && ly <= p.Y && p.Y <= ry) // optimization { return(false); } var cornerDist2 = Geom.Sqr(Const.TileMargin + Math.Max(-5, additionalMargin)); // в углу foreach (var corner in MyTiles[cell.I, cell.J].Parts) { if (corner.Type != TilePartType.Circle) { continue; } if (corner.Circle.GetDistanceTo2(p) < cornerDist2) { return(true); } } // по бокам if (p.X < lx + additionalMargin && !tile.IsFreeLeft) { return(true); } if (p.X > rx - additionalMargin && !tile.IsFreeRight) { return(true); } if (p.Y < ly + additionalMargin && !tile.IsFreeTop) { return(true); } if (p.Y > ry - additionalMargin && !tile.IsFreeBottom) { return(true); } return(false); }
public void Move(double enginePower, double wheelTurn, bool isBreak, bool useNitro, bool simpleMode) { if (RemainingInactiveTicks > 0 || MyStrategy.IsCrashed(Original)) { isBreak = false; useNitro = false; enginePower = 0; wheelTurn = WheelTurn; } useNitro = useNitro && Original.NitroChargeCount > 0; if (useNitro && RemainingNitroTicks == 0 && RemainingNitroCooldownTicks == 0) { RemainingNitroTicks = Const.Game.NitroDurationTicks; RemainingNitroCooldownTicks = Const.Game.UseNitroCooldownTicks; } var updateIterations = simpleMode ? 2 : 10; var frictionMultiplier = Math.Pow(1.0 - Const.Game.CarMovementAirFrictionFactor, 1.0 / updateIterations); var rotationFrictionMultiplier = Math.Pow(1.0 - Const.Game.CarRotationFrictionFactor, 1.0 / updateIterations); if (enginePower > 1 + MyStrategy.Eps || enginePower < -1 - MyStrategy.Eps) { throw new Exception("invalid enginePower " + enginePower); } if (wheelTurn > 1 + MyStrategy.Eps || wheelTurn < -1 - MyStrategy.Eps) { throw new Exception("invalid wheelTurn " + wheelTurn); } if (RemainingNitroTicks > 0) { EnginePower = 2.0; } else { EnginePower = enginePower > EnginePower ? Math.Min(EnginePower + Const.Game.CarEnginePowerChangePerTick, enginePower) : Math.Max(EnginePower - Const.Game.CarEnginePowerChangePerTick, enginePower); } WheelTurn = wheelTurn > WheelTurn ? Math.Min(WheelTurn + Const.Game.CarWheelTurnChangePerTick, wheelTurn) : Math.Max(WheelTurn - Const.Game.CarWheelTurnChangePerTick, wheelTurn); if (MyStrategy.world.Tick >= Const.Game.InitialFreezeDurationTicks) { // http://russianaicup.ru/forum/index.php?topic=394.msg3888#msg3888 var dir = Point.ByAngle(Angle); var baseAngSpd = AngularSpeed; // HACK AngularSpeed -= baseAngSpd; baseAngSpd = Const.Game.CarAngularSpeedFactor * WheelTurn * (Speed * dir); AngularSpeed += baseAngSpd; var carAcceleration = EnginePower >= 0 ? _carAccelerationUp : _carAccelerationDown; var accelerationDelta = dir * (carAcceleration * EnginePower / updateIterations); var lengthwiseMovementFrictionFactor = isBreak ? Const.Game.CarCrosswiseMovementFrictionFactor : Const.Game.CarLengthwiseMovementFrictionFactor; lengthwiseMovementFrictionFactor /= updateIterations; var crosswiseMovementFrictionFactor = Const.Game.CarCrosswiseMovementFrictionFactor / updateIterations; for (var i = 0; i < updateIterations; i++) { X += Speed.X / updateIterations; Y += Speed.Y / updateIterations; if (!isBreak) { Speed.X += accelerationDelta.X; Speed.Y += accelerationDelta.Y; } Speed.X *= frictionMultiplier; Speed.Y *= frictionMultiplier; var t1 = _limit(Speed.X * dir.X + Speed.Y * dir.Y, lengthwiseMovementFrictionFactor); var t2 = _limit(Speed.X * dir.Y - Speed.Y * dir.X, crosswiseMovementFrictionFactor); Speed.X = dir.X * t1 + dir.Y * t2; Speed.Y = dir.Y * t1 - dir.X * t2; Angle += AngularSpeed / updateIterations; dir = Point.ByAngle(Angle); AngularSpeed = baseAngSpd + (AngularSpeed - baseAngSpd) * rotationFrictionMultiplier; } Geom.AngleNormalize(ref Angle); if (RemainingNitroCooldownTicks > 0) { RemainingNitroCooldownTicks--; } if (RemainingNitroTicks > 0) { RemainingNitroTicks--; } if (RemainingInactiveTicks > 0) { RemainingInactiveTicks--; } } // clear rectangles cache _rect = null; _rectEx = null; }
public void Move() { if (!Exists) { return; } if (Type == ProjectileType.Washer) { X += Speed.X; Y += Speed.Y; if (X < 0 || Y < 0 || X > Const.MapWidth || Y > Const.MapHeight) { Exists = false; } return; } var reflected = false; for (int it = 0; it < UpdateIterations; it++) { X += Speed.X / UpdateIterations; Y += Speed.Y / UpdateIterations; // может выйти за пределы если тайлы unknown if (X < 1 || Y < 1 || X >= Const.MapWidth - 1 || Y >= Const.MapHeight - 1) { Exists = false; break; } if (reflected) { continue; } var currentCell = MyStrategy.GetCell(this); foreach (var part in MyStrategy.MyTiles[currentCell.I, currentCell.J].Parts) { Point e = null, n = null; // e - направление стены // n - нормаль от стены double en = 0, et = 0, d = 0, d1 = 0, d2 = 0; if (part.Type == TilePartType.Segment) { var pts = Geom.LineCircleIntersect(part.Start, part.End, this, this.Radius); if (pts.Length == 0) { continue; } e = (part.End - part.Start).Normalized(); n = ~e * -1; } else { if (GetDistanceTo2(part.Circle) <= Geom.Sqr(Radius + part.Circle.Radius)) { n = (this - part.Circle).Normalized(); e = ~n; } } if (e == null || n == null) { continue; } en = Speed * n; // норм. et = Speed * e; // танг. en *= -0.5; d = n.X * e.Y - n.Y * e.X; d1 = n.X * et - en * e.X; d2 = en * e.Y - n.Y * et; Speed.X = d2 / d; Speed.Y = d1 / d; reflected = true; break; } } if (Speed.Length <= Const.Game.TireDisappearSpeedFactor * Const.Game.TireInitialSpeed) { Exists = false; } }
public bool Intersect(ACar car, double safeMargin) { return(GetDistanceTo2(car) < Geom.Sqr(Radius + safeMargin)); }
public ATile(int i, int j, TileType type) : base(i, j) { Type = type; IsFreeLeft = _tileFreeLeft(type); IsFreeRight = _tileFreeRight(type); IsFreeTop = _tileFreeTop(type); IsFreeBottom = _tileFreeBottom(type); var margin = Const.TileMargin; var sy = new Point(0, margin); var sx = new Point(margin, 0); var ly = new Point(0, Const.TileSize); var lx = new Point(Const.TileSize, 0); var res = new List <TilePart>(); switch (type) { // Левый верхний угол case TileType.LeftHeadedT: case TileType.TopHeadedT: case TileType.RightBottomCorner: case TileType.Crossroads: case TileType.Unknown: res.Add(TilePart.GetCircle(Point.Zero, margin)); break; } switch (type) { // Правый верхний угол case TileType.RightHeadedT: case TileType.TopHeadedT: case TileType.LeftBottomCorner: case TileType.Crossroads: case TileType.Unknown: res.Add(TilePart.GetCircle(lx, margin)); break; } switch (type) { // Правый нижний угол case TileType.BottomHeadedT: case TileType.RightHeadedT: case TileType.LeftTopCorner: case TileType.Crossroads: case TileType.Unknown: res.Add(TilePart.GetCircle(lx + ly, margin)); break; } switch (type) { // Левый нижний угол case TileType.BottomHeadedT: case TileType.LeftHeadedT: case TileType.RightTopCorner: case TileType.Crossroads: case TileType.Unknown: res.Add(TilePart.GetCircle(ly, margin)); break; } switch (type) { // Верхняя полоса case TileType.BottomHeadedT: case TileType.Horizontal: case TileType.LeftTopCorner: case TileType.RightTopCorner: res.Add(TilePart.GetSegment(sy, lx + sy)); break; } switch (type) { // Нижняя полоса case TileType.TopHeadedT: case TileType.Horizontal: case TileType.LeftBottomCorner: case TileType.RightBottomCorner: res.Add(TilePart.GetSegment(ly - sy, lx + ly - sy)); break; } switch (type) { // Левая полоса case TileType.RightHeadedT: case TileType.Vertical: case TileType.LeftBottomCorner: case TileType.LeftTopCorner: res.Add(TilePart.GetSegment(sx, ly + sx)); break; } switch (type) { // Правая полоса case TileType.LeftHeadedT: case TileType.Vertical: case TileType.RightBottomCorner: case TileType.RightTopCorner: res.Add(TilePart.GetSegment(lx - sx, lx + ly - sx)); break; } var dx = Const.TileSize * j; var dy = Const.TileSize * i; foreach (var part in res) { if (part.Type == TilePartType.Circle) { part.Circle.X += dx; part.Circle.Y += dy; } else { part.Start.X += dx; part.Start.Y += dy; part.End.X += dx; part.End.Y += dy; if (Geom.VectorProduct(part.Start, part.End, MyStrategy.GetCenter(i, j)) > 0) { var tmp = part.Start; part.Start = part.End; part.End = tmp; } } } Parts = res.ToArray(); }
void Initialize() { if (!Const.Initialized) { Const.Initialized = true; Const.TileSize = Const.Game.TrackTileSize; Const.TileMargin = Const.Game.TrackTileMargin; Const.CarDiagonalHalfLength = Geom.Gypot(Const.Game.CarWidth, Const.Game.CarHeight) / 2; Const.BonusDiagonalHalfLength = Geom.Gypot(Const.Game.BonusSize / 2 - MagicConst.BonusSafeMargin, Const.Game.BonusSize / 2 - MagicConst.BonusSafeMargin); Const.MapWidth = Const.TileSize * world.Width; Const.MapHeight = Const.TileSize * world.Height; } Teammate = world.Cars.FirstOrDefault(car => car.IsTeammate && car.Id != self.Id); ComputedPath.Remove(self.Id); if (Teammate != null) { TeammateCar = ComputedPath.ContainsKey(Teammate.Id) ? ComputedPath[Teammate.Id] : null; } if (Players == null) // check for first call { MyTiles = new ATile[world.Height, world.Width]; for (var i = 0; i < world.Height; i++) { for (var j = 0; j < world.Width; j++) { MyTiles[i, j] = new ATile(i, j, TileType.Unknown); } } } // intialize tiles var t = world.TilesXY; for (var i = 0; i < world.Height; i++) { for (var j = 0; j < world.Width; j++) { if (MyTiles[i, j].Type == TileType.Unknown && t[j][i] != TileType.Unknown) { MyTiles[i, j] = new ATile(i, j, t[j][i]); } MyTiles[i, j].Weight = 0; } } // intialize waypoints var wp = world.Waypoints; Waypoints = new Cell[wp.Length]; for (var i = 0; i < Waypoints.Length; i++) { Waypoints[i] = new Cell(wp[i][1], wp[i][0]); } Players = new Dictionary <long, Player>(); foreach (var player in world.Players) { Players[player.Id] = player; } foreach (var car in world.Cars) { DurabilityObserver.Watch(car); } var tires = world.Projectiles.Where(x => x.Type == ProjectileType.Tire).ToArray(); Tires = new AProjectile[tires.Length][]; #if DEBUG var trajectories = Tires.Select(x => new Points()).ToArray(); #endif for (var i = 0; i < tires.Length; i++) { Tires[i] = new AProjectile[MagicConst.ProjectilePredictionTicks]; Tires[i][0] = new AProjectile(tires[i]); for (var j = 1; j < MagicConst.ProjectilePredictionTicks; j++) { Tires[i][j] = Tires[i][j - 1].Clone(); Tires[i][j].Move(); #if DEBUG trajectories[i].Add(new Point(Tires[i][j])); #endif } } #if DEBUG foreach (var tr in trajectories) { Visualizer.SegmentsDrawQueue.Add(new object[] { Brushes.Indigo, tr, 0.0 }); } #endif Bonuses = world.Bonuses.Select(b => new ABonus(b)).ToArray(); OilSlicks = world.OilSlicks.Select(s => new AOilSlick(s)).ToArray(); EnumerateNeigbours(GetCell(self), to => { var center = GetCenter(to); MyTiles[to.I, to.J].Weight += Math.Abs(self.GetAngleTo(center.X, center.Y)); }); foreach (var bonus in Bonuses) { var cell = GetCell(bonus); MyTiles[cell.I, cell.J].AddBonus(bonus); } foreach (var slick in OilSlicks) { var cell = GetCell(slick); MyTiles[cell.I, cell.J].AddSlick(slick); } Opponents = world.Cars.Where(car => !car.IsTeammate && !car.IsFinishedTrack).ToArray(); PrepareOpponentsPath(); if (OpponentsCars != null) { Others = OpponentsCars .Concat(TeammateCar == null ? new ACar[][] { } : new[] { TeammateCar }) .ToArray(); } ComputedPath.Clear(); if (self.TeammateIndex == 1 && OpponentsCars != null) { foreach (var opp in OpponentsCars) { ComputedPath[opp[0].Original.Id] = opp; } } }