using System; using System.Collections.Generic; using System.Linq; using System.Globalization; 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 Duck { public long id { get; set; } public Point pos { get; set; } public Point lastpos { get; set; } public Point vel { get; set; } public DateTime lastseen { get; set; } public Queue trail { get; set; } = new Queue(10); public double spd { get; set; } } HashSet tiles = 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) }; Dictionary> adj = new Dictionary>(); Point[] dirs = { (0,1), (0,-1), (1,0), (-1,0), (1,1), (1,-1), (-1,1), (-1,-1) }; foreach(var t in tiles) { var n = new List(); foreach(var d in dirs) { Point p = new Point(t.X + d.X, t.Y + d.Y); if(tiles.Contains(p)) n.Add(p); } adj[t] = n; } Dictionary ducks = new Dictionary(); Tile tgt = null; Point lastcmd = default(Point); DateTime cmdtime = DateTime.MinValue; Point prev = default(Point); Point curr = default(Point); Queue hist = new Queue(5); int stuck = 0; HashSet danger = new HashSet(); Point dest = default(Point); bool forcedest = false; DateTime desttime = DateTime.MinValue; Point getpos() { if (Self == null) return default(Point); if (tgt != null) return tgt.XY; if (!lastcmd.Equals(default(Point)) && (DateTime.UtcNow - cmdtime).TotalMilliseconds < 250) return lastcmd; if (Self.Location != null) return new Point(Self.Location.X, Self.Location.Y); return default(Point); } void go(int x, int y) { Move(x, y); lastcmd = new Point(x, y); cmdtime = DateTime.UtcNow; tgt = null; } HashSet predict(int frames) { var zones = new HashSet(); foreach(var d in ducks.Values) { zones.Add(d.pos); if(!d.vel.Equals(default(Point))) { for(int i = 1; i <= frames; i++) { Point pred = new Point( d.pos.X + d.vel.X * i, d.pos.Y + d.vel.Y * i ); if(tiles.Contains(pred)) zones.Add(pred); } } if(frames >= 1 && adj.ContainsKey(d.pos)) { foreach(var n in adj[d.pos]) zones.Add(n); } if(frames >= 2) { foreach(var d1 in dirs) { Point p1 = new Point(d.pos.X + d1.X, d.pos.Y + d1.Y); if(tiles.Contains(p1)) { zones.Add(p1); foreach(var d2 in dirs) { Point p2 = new Point(p1.X + d2.X, p1.Y + d2.Y); if(tiles.Contains(p2)) zones.Add(p2); } } } } } return zones; } Point findsafe(Point me, HashSet bad) { if(!bad.Any()) return tiles.First(); double best = double.MinValue; Point spot = me; foreach(var t in tiles) { if(bad.Contains(t)) continue; double score = 0; double mindist = double.MaxValue; foreach(var b in bad) { double d = Math.Abs(t.X - b.X) + Math.Abs(t.Y - b.Y); mindist = Math.Min(mindist, d); score += d; } score += mindist * 100; if(adj.ContainsKey(t)) { int exits = adj[t].Count(n => !bad.Contains(n)); score += exits * 10; } double dist = Math.Abs(t.X - me.X) + Math.Abs(t.Y - me.Y); score -= dist * 0.5; if(score > best) { best = score; spot = t; } } return spot; } Point pathto(Point me, Point goal, HashSet d1, HashSet d2, HashSet d3) { if(!adj.ContainsKey(me)) return me; if(hist.Count >= 3) { var last3 = hist.TakeLast(3).ToArray(); if(last3[0] == last3[2] && last3[0] != last3[1]) { stuck++; if(stuck > 2) { var esc = adj[me] .Where(n => !d1.Contains(n)) .OrderBy(n => Guid.NewGuid()) .FirstOrDefault(); if(!esc.Equals(default(Point))) { stuck = 0; return esc; } } } else stuck = 0; } double best = double.MinValue; Point move = me; foreach(var n in adj[me]) { if(d1.Contains(n)) continue; double score = 0; if(d2.Contains(n)) score -= 800; if(d3.Contains(n)) score -= 400; double dist = Math.Abs(n.X - goal.X) + Math.Abs(n.Y - goal.Y); score -= dist * 100; foreach(var duck in ducks.Values) { double dd = Math.Abs(n.X - duck.pos.X) + Math.Abs(n.Y - duck.pos.Y); score += dd * 10; } if(adj.ContainsKey(n)) { int safe = adj[n].Count(x => !d1.Contains(x)); score += safe * 20; if(safe == 0 && d2.Contains(n)) score -= 2000; } if(!prev.Equals(default(Point)) && n.Equals(prev)) score -= 50; if(score > best) { best = score; move = n; } } return move; } Point getmove(Point me, Point goal, HashSet d1, HashSet d2, HashSet d3) { if(!adj.ContainsKey(me)) return me; if(hist.Count >= 3) { var last3 = hist.TakeLast(3).ToArray(); if(last3[0] == last3[2] && last3[0] != last3[1]) { stuck++; if(stuck > 1) { var any = adj[me] .Where(n => !d1.Contains(n)) .OrderBy(n => d2.Contains(n) ? 1 : 0) .FirstOrDefault(); if(!any.Equals(default(Point))) { stuck = 0; return any; } } } else stuck = 0; } double best = double.MinValue; Point move = me; foreach(var n in adj[me]) { if(d1.Contains(n)) continue; if(!prev.Equals(default(Point)) && n.Equals(prev)) continue; double score = 0; if(d2.Contains(n)) score -= 1000; if(d3.Contains(n)) score -= 500; double dist = Math.Abs(n.X - goal.X) + Math.Abs(n.Y - goal.Y); score -= dist * 10; foreach(var duck in ducks.Values) { double dd = Math.Abs(n.X - duck.pos.X) + Math.Abs(n.Y - duck.pos.Y); score += dd * 20; } if(adj.ContainsKey(n)) { int exits = adj[n].Count(x => !d1.Contains(x) && !d2.Contains(x)); score += exits * 50; } if(score > best) { best = score; move = n; } } return move; } OnIntercept(Out["MoveAvatar"], e => { var pkt = e.Packet; int x = pkt.ReadInt(); int y = pkt.ReadInt(); dest = new Point(x, y); forcedest = true; desttime = DateTime.UtcNow; }); OnEnteredRoom(e => { ducks.Clear(); hist.Clear(); prev = default(Point); stuck = 0; forcedest = false; dest = default(Point); }); OnIntercept(In["WiredMovements"], e => { var pkt = e.Packet; int cnt = pkt.ReadInt(); for(int i = 0; i < cnt; i++) { pkt.ReadInt(); int fx = pkt.ReadInt(); int fy = pkt.ReadInt(); int tx = pkt.ReadInt(); int ty = pkt.ReadInt(); pkt.ReadString(); pkt.ReadString(); int id = pkt.ReadInt(); pkt.ReadInt(); pkt.ReadInt(); long fid = id; Point newp = new Point(tx, ty); Point oldp = new Point(fx, fy); if(!ducks.ContainsKey(fid)) { ducks[fid] = new Duck { id = fid }; } var d = ducks[fid]; d.lastpos = d.pos; d.pos = newp; d.vel = new Point(tx - fx, ty - fy); d.lastseen = DateTime.UtcNow; d.trail.Enqueue(newp); if(d.trail.Count > 10) d.trail.Dequeue(); if((d.lastseen - DateTime.UtcNow).TotalSeconds < 1) { d.spd = Math.Sqrt(Math.Pow(d.vel.X, 2) + Math.Pow(d.vel.Y, 2)); } } }); OnIntercept(In["UserUpdate"], e => { if(Self == null) return; var pkt = e.Packet; int num = pkt.ReadInt(); for(int i = 0; i < num; i++) { int idx = pkt.ReadInt(); int x = pkt.ReadInt(); int y = pkt.ReadInt(); string z = pkt.ReadString(); pkt.ReadInt(); pkt.ReadInt(); string act = pkt.ReadString(); if(idx == Self.Index) { prev = curr; curr = new Point(x, y); hist.Enqueue(curr); if(hist.Count > 5) hist.Dequeue(); if(forcedest && curr.Equals(dest)) { forcedest = false; } if(act.Contains("/mv")) { var parts = act.Split(new[] {' ', '/', ','}, StringSplitOptions.RemoveEmptyEntries); if(parts.Length >= 4 && parts[0] == "mv") { if(int.TryParse(parts[1], out int mx) && int.TryParse(parts[2], out int my) && double.TryParse(parts[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double mz)) { tgt = new Tile(mx, my, mz); lastcmd = default(Point); } } } else if(act.EndsWith("//") && !act.Contains("/mv")) { tgt = null; } } } }); while(Run) { try { Point me = getpos(); if(me.Equals(default(Point))) { Delay(20); continue; } if(ducks.Any() || forcedest) { var d1 = predict(1); var d2 = predict(2); var d3 = predict(3); Point goal; Point next = me; if(forcedest && tiles.Contains(dest)) { if((DateTime.UtcNow - desttime).TotalSeconds > 30) { forcedest = false; goal = findsafe(me, d1); next = getmove(me, goal, d1, d2, d3); } else { goal = dest; next = pathto(me, goal, d1, d2, d3); } } else { goal = findsafe(me, d1); next = getmove(me, goal, d1, d2, d3); } if(!next.Equals(me)) { go(next.X, next.Y); } } } catch(Exception ex) { } Delay(20); }