using System; using System.Collections.Generic; using System.Linq; using System.Globalization; // ================================================ // ULTRA SMART COLLISION AVOIDANCE // Tracks ALL movement and predicts collisions // ================================================ public struct Point : IEquatable { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } public static implicit operator Point((int x, int y) tuple) => new Point(tuple.x, tuple.y); public bool Equals(Point other) => X == other.X && Y == other.Y; public override bool Equals(object obj) => obj is Point other && Equals(other); public override int GetHashCode() => HashCode.Combine(X, Y); public static bool operator ==(Point left, Point right) => left.Equals(right); public static bool operator !=(Point left, Point right) => !(left == right); public override string ToString() => $"({X},{Y})"; } public class Tile { public int X { get; set; } public int Y { get; set; } public double Z { get; set; } public Point XY => new Point(X, Y); public Tile(int x, int y, double z = 0.0) { X = x; Y = y; Z = z; } } public class MovingThreat { public long Id { get; set; } public Point Position { get; set; } public Point PreviousPosition { get; set; } public Point Velocity { get; set; } public DateTime LastUpdate { get; set; } public Queue Trail { get; set; } = new Queue(10); public double Speed { get; set; } } Log("ULTRA SMART AVOIDANCE STARTED"); // HARDCODED WALKABLE TILES HashSet walkable = new HashSet { (13,13),(14,13),(15,13),(16,13),(17,13),(18,13),(19,13),(20,13),(21,13),(22,13),(23,13),(24,13),(25,13), (13,14),(17,14),(21,14),(25,14), (13,15),(14,15),(15,15),(17,15),(18,15),(20,15),(21,15),(23,15),(24,15),(25,15), (13,16),(15,16),(18,16),(20,16),(23,16),(25,16), (13,17),(15,17),(16,17),(17,17),(18,17),(19,17),(20,17),(21,17),(22,17),(23,17),(25,17), (13,18),(15,18),(19,18),(23,18),(25,18), (13,19),(14,19),(15,19),(17,19),(18,19),(19,19),(20,19),(21,19),(23,19),(24,19),(25,19), (13,20),(15,20),(16,20),(17,20),(21,20),(22,20),(23,20),(25,20), (12,21),(13,21),(17,21),(18,21),(19,21),(20,21),(21,21),(25,21),(26,21), (13,22),(15,22),(16,22),(17,22),(21,22),(22,22),(23,22),(25,22), (13,23),(14,23),(15,23),(17,23),(18,23),(19,23),(20,23),(21,23),(23,23),(24,23),(25,23), (13,24),(15,24),(19,24),(23,24),(25,24), (13,25),(15,25),(16,25),(17,25),(18,25),(19,25),(20,25),(21,25),(22,25),(23,25),(25,25), (13,26),(15,26),(18,26),(20,26),(23,26),(25,26), (13,27),(14,27),(15,27),(17,27),(18,27),(20,27),(21,27),(23,27),(24,27),(25,27), (13,28),(17,28),(21,28),(25,28), (13,29),(14,29),(15,29),(16,29),(17,29),(18,29),(19,29),(20,29),(21,29),(22,29),(23,29),(24,29),(25,29) }; // Pre-calculate neighbors Dictionary> neighbors = new Dictionary>(); Point[] dirs = { (0,1), (0,-1), (1,0), (-1,0), (1,1), (1,-1), (-1,1), (-1,-1) }; foreach(var tile in walkable) { var n = new List(); foreach(var d in dirs) { Point p = new Point(tile.X + d.X, tile.Y + d.Y); if(walkable.Contains(p)) n.Add(p); } neighbors[tile] = n; } // Movement tracking Dictionary threats = new Dictionary(); Tile targetTile = null; Point lastMoveCommand = default(Point); DateTime lastMoveTime = DateTime.MinValue; Point myLastPosition = default(Point); Point myCurrentPosition = default(Point); Queue myMovementHistory = new Queue(5); int stuckCounter = 0; HashSet dangerZonesNextFrame = new HashSet(); // Get current position Point GetMyPosition() { if (Self == null) return default(Point); if (targetTile != null) return targetTile.XY; if (!lastMoveCommand.Equals(default(Point)) && (DateTime.UtcNow - lastMoveTime).TotalMilliseconds < 250) return lastMoveCommand; if (Self.Location != null) return new Point(Self.Location.X, Self.Location.Y); return default(Point); } // Execute move void DoMove(int x, int y) { Move(x, y); lastMoveCommand = new Point(x, y); lastMoveTime = DateTime.UtcNow; targetTile = null; } // Predict where threats will be in N frames HashSet PredictDangerZones(int framesAhead) { var zones = new HashSet(); foreach(var threat in threats.Values) { // Current position is dangerous zones.Add(threat.Position); // Predict based on velocity if(!threat.Velocity.Equals(default(Point))) { for(int i = 1; i <= framesAhead; i++) { Point predicted = new Point( threat.Position.X + threat.Velocity.X * i, threat.Position.Y + threat.Velocity.Y * i ); if(walkable.Contains(predicted)) zones.Add(predicted); } } // Add all adjacent tiles for frame 1-2 (they could move there) if(framesAhead >= 1 && neighbors.ContainsKey(threat.Position)) { foreach(var n in neighbors[threat.Position]) zones.Add(n); } // For frame 2+, add 2-tile radius if(framesAhead >= 2) { foreach(var d1 in dirs) { Point p1 = new Point(threat.Position.X + d1.X, threat.Position.Y + d1.Y); if(walkable.Contains(p1)) { zones.Add(p1); foreach(var d2 in dirs) { Point p2 = new Point(p1.X + d2.X, p1.Y + d2.Y); if(walkable.Contains(p2)) zones.Add(p2); } } } } } return zones; } // Find safest position Point FindSafestTile(Point current, HashSet danger) { if(!danger.Any()) return walkable.First(); double bestScore = double.MinValue; Point bestTile = current; foreach(var tile in walkable) { if(danger.Contains(tile)) continue; double score = 0; // Distance from all danger zones double minDist = double.MaxValue; foreach(var d in danger) { double dist = Math.Abs(tile.X - d.X) + Math.Abs(tile.Y - d.Y); minDist = Math.Min(minDist, dist); score += dist; } // Heavily weight minimum distance score += minDist * 100; // Prefer tiles with more escape routes if(neighbors.ContainsKey(tile)) { int escapes = neighbors[tile].Count(n => !danger.Contains(n)); score += escapes * 10; } // Small penalty for distance from current (prefer closer safe spots) double currentDist = Math.Abs(tile.X - current.X) + Math.Abs(tile.Y - current.Y); score -= currentDist * 0.5; if(score > bestScore) { bestScore = score; bestTile = tile; } } return bestTile; } // Get best immediate move Point GetBestMove(Point current, Point goal, HashSet danger1, HashSet danger2, HashSet danger3) { if(!neighbors.ContainsKey(current)) return current; // Check if stuck if(myMovementHistory.Count >= 3) { var last3 = myMovementHistory.TakeLast(3).ToArray(); if(last3[0] == last3[2] && last3[0] != last3[1]) { stuckCounter++; if(stuckCounter > 1) { // Force escape in any safe direction var anyMove = neighbors[current] .Where(n => !danger1.Contains(n)) .OrderBy(n => danger2.Contains(n) ? 1 : 0) .FirstOrDefault(); if(!anyMove.Equals(default(Point))) { stuckCounter = 0; return anyMove; } } } else stuckCounter = 0; } double bestScore = double.MinValue; Point bestMove = current; foreach(var next in neighbors[current]) { // NEVER go to immediate danger if(danger1.Contains(next)) continue; // NEVER backtrack to previous position if(!myLastPosition.Equals(default(Point)) && next.Equals(myLastPosition)) continue; double score = 0; // Heavy penalty for predicted danger if(danger2.Contains(next)) score -= 1000; if(danger3.Contains(next)) score -= 500; // Distance to goal double goalDist = Math.Abs(next.X - goal.X) + Math.Abs(next.Y - goal.Y); score -= goalDist * 10; // Distance from all current threats foreach(var threat in threats.Values) { double dist = Math.Abs(next.X - threat.Position.X) + Math.Abs(next.Y - threat.Position.Y); score += dist * 20; } // Bonus for tiles with escape routes if(neighbors.ContainsKey(next)) { int escapes = neighbors[next].Count(n => !danger1.Contains(n) && !danger2.Contains(n)); score += escapes * 50; } if(score > bestScore) { bestScore = score; bestMove = next; } } return bestMove; } // Room entry OnEnteredRoom(e => { Log($"Entered room {RoomId}"); threats.Clear(); myMovementHistory.Clear(); myLastPosition = default(Point); stuckCounter = 0; }); // Track ALL wired movements OnIntercept(In["WiredMovements"], e => { var packet = e.Packet; int count = packet.ReadInt(); for(int i = 0; i < count; i++) { packet.ReadInt(); int fromX = packet.ReadInt(); int fromY = packet.ReadInt(); int toX = packet.ReadInt(); int toY = packet.ReadInt(); packet.ReadString(); // fromHeight packet.ReadString(); // toHeight int id = packet.ReadInt(); packet.ReadInt(); packet.ReadInt(); long furniId = id; Point newPos = new Point(toX, toY); Point oldPos = new Point(fromX, fromY); // Track this threat if(!threats.ContainsKey(furniId)) { threats[furniId] = new MovingThreat { Id = furniId }; } var threat = threats[furniId]; threat.PreviousPosition = threat.Position; threat.Position = newPos; threat.Velocity = new Point(toX - fromX, toY - fromY); threat.LastUpdate = DateTime.UtcNow; threat.Trail.Enqueue(newPos); if(threat.Trail.Count > 10) threat.Trail.Dequeue(); // Calculate speed if((threat.LastUpdate - DateTime.UtcNow).TotalSeconds < 1) { threat.Speed = Math.Sqrt(Math.Pow(threat.Velocity.X, 2) + Math.Pow(threat.Velocity.Y, 2)); } } }); // Track bot position OnIntercept(In["UserUpdate"], e => { if(Self == null) return; var packet = e.Packet; int numUpdates = packet.ReadInt(); for(int i = 0; i < numUpdates; i++) { int entityIndex = packet.ReadInt(); int x = packet.ReadInt(); int y = packet.ReadInt(); string zStr = packet.ReadString(); packet.ReadInt(); // headRot packet.ReadInt(); // bodyRot string action = packet.ReadString(); if(entityIndex == Self.Index) { myLastPosition = myCurrentPosition; myCurrentPosition = new Point(x, y); myMovementHistory.Enqueue(myCurrentPosition); if(myMovementHistory.Count > 5) myMovementHistory.Dequeue(); // Parse target if(action.Contains("/mv")) { var parts = action.Split(new[] {' ', '/', ','}, StringSplitOptions.RemoveEmptyEntries); if(parts.Length >= 4 && parts[0] == "mv") { if(int.TryParse(parts[1], out int mvX) && int.TryParse(parts[2], out int mvY) && double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double mvZ)) { targetTile = new Tile(mvX, mvY, mvZ); lastMoveCommand = default(Point); } } } else if(action.EndsWith("//") && !action.Contains("/mv")) { targetTile = null; } } } }); // Main loop while(Run) { try { Point myPos = GetMyPosition(); if(myPos.Equals(default(Point))) { Delay(20); continue; } if(threats.Any()) { // Predict danger zones var danger1Frame = PredictDangerZones(1); var danger2Frame = PredictDangerZones(2); var danger3Frame = PredictDangerZones(3); // Find safest destination Point safeGoal = FindSafestTile(myPos, danger1Frame); // Get best immediate move Point nextMove = GetBestMove(myPos, safeGoal, danger1Frame, danger2Frame, danger3Frame); // Execute if different from current if(!nextMove.Equals(myPos)) { DoMove(nextMove.X, nextMove.Y); } } } catch(Exception ex) { Log($"Error: {ex.Message}"); } Delay(20); // 50Hz update rate } Log("Bot stopped");