using System; using System.Collections.Generic; using System.Linq; const int GAME_MIN_X = 9; const int GAME_MAX_X = 28; const int GAME_MIN_Y = 11; const int GAME_MAX_Y = 30; const int BLOCKED_MIN_X = 17; const int BLOCKED_MAX_X = 20; const int BLOCKED_MIN_Y = 19; const int BLOCKED_MAX_Y = 22; const int KIND_SNAKE = 7100; const int KIND_FOOD = 5068; const int CTRL_UP = 2147418172; const int CTRL_RIGHT = 2147418173; const int CTRL_DOWN = 2147418174; const int CTRL_LEFT = 2147418175; List<(int x, int y)> body = new List<(int x, int y)>(); HashSet<(int x, int y)> prevSnake = new HashSet<(int x, int y)>(); (int x, int y) head = (-1, -1); string dir = "DOWN"; string targetDir = ""; DateTime lastCmd = DateTime.MinValue; int length = 0; int GetKind(dynamic item) { try { return (int)item.Kind; } catch { return -1; } } bool IsWall(int x, int y) => x < GAME_MIN_X || x > GAME_MAX_X || y < GAME_MIN_Y || y > GAME_MAX_Y; bool IsBlocked(int x, int y) => x >= BLOCKED_MIN_X && x <= BLOCKED_MAX_X && y >= BLOCKED_MIN_Y && y <= BLOCKED_MAX_Y; bool Bad(int x, int y) { if (IsWall(x, y)) return true; if (IsBlocked(x, y)) return true; for (int i = 0; i < body.Count - 1; i++) if (body[i].x == x && body[i].y == y) return true; return false; } bool BadFull(int x, int y) { if (IsWall(x, y)) return true; if (IsBlocked(x, y)) return true; foreach (var p in body) if (p.x == x && p.y == y) return true; return false; } (int, int) Next(int x, int y, string d) { if (d == "UP") return (x, y - 1); if (d == "DOWN") return (x, y + 1); if (d == "LEFT") return (x - 1, y); if (d == "RIGHT") return (x + 1, y); return (x, y); } string Opp(string d) => d == "UP" ? "DOWN" : d == "DOWN" ? "UP" : d == "LEFT" ? "RIGHT" : "LEFT"; int Fill(int sx, int sy) { if (BadFull(sx, sy)) return 0; var v = new HashSet<(int, int)> { (sx, sy) }; var q = new Queue<(int, int)>(); q.Enqueue((sx, sy)); int c = 0; while (q.Count > 0 && c < 200) { var (x, y) = q.Dequeue(); c++; foreach (var (nx, ny) in new[] { (x, y-1), (x, y+1), (x-1, y), (x+1, y) }) if (!v.Contains((nx, ny)) && !BadFull(nx, ny)) { v.Add((nx, ny)); q.Enqueue((nx, ny)); } } return c; } void Cmd(string d) { if (d == "UP") Send(Out["ClickFurni"], CTRL_UP, 0); else if (d == "DOWN") Send(Out["ClickFurni"], CTRL_DOWN, 0); else if (d == "LEFT") Send(Out["ClickFurni"], CTRL_LEFT, 0); else if (d == "RIGHT") Send(Out["ClickFurni"], CTRL_RIGHT, 0); lastCmd = DateTime.Now; } string Best(int hx, int hy, int fx, int fy, string cur) { string opp = Opp(cur); var results = new List<(string d, bool safe, int dist, int space)>(); foreach (var d in new[] { "UP", "DOWN", "LEFT", "RIGHT" }) { if (d == opp) continue; var (nx, ny) = Next(hx, hy, d); bool hitBody = false; for (int i = 0; i < body.Count - 1; i++) if (body[i].x == nx && body[i].y == ny) { hitBody = true; break; } if (IsWall(nx, ny) || IsBlocked(nx, ny) || hitBody) continue; int space = Fill(nx, ny); int dist = Math.Abs(fx - nx) + Math.Abs(fy - ny); bool safe = space >= length + 3; results.Add((d, safe, dist, space)); } if (results.Count == 0) return cur; var safeOpts = results.Where(r => r.safe).ToList(); if (safeOpts.Count > 0) return safeOpts.OrderBy(r => r.dist).First().d; return results.OrderByDescending(r => r.space).First().d; } Log("═══════════════════════════════════════════"); Log(" SNAKE BOT V7 - Self Body Tracking"); Log("═══════════════════════════════════════════"); while (Run) { Delay(10); var snake = new HashSet<(int x, int y)>(); foreach (var item in FloorItems) { if (item == null) continue; int x = item.Location.X; int y = item.Location.Y; if (IsWall(x, y)) continue; int k = GetKind(item); if (k == KIND_SNAKE) snake.Add((x, y)); } if (snake.Count == 0) { body.Clear(); head = (-1, -1); continue; } length = snake.Count; var newP = snake.Except(prevSnake).ToList(); var removed = prevSnake.Except(snake).ToList(); bool moved = newP.Count == 1; if (moved) { var nh = newP[0]; if (head.x != -1) { int dx = nh.x - head.x; int dy = nh.y - head.y; if (dy == -1) dir = "UP"; else if (dy == 1) dir = "DOWN"; else if (dx == -1) dir = "LEFT"; else if (dx == 1) dir = "RIGHT"; } head = nh; body.Insert(0, head); while (body.Count > length) body.RemoveAt(body.Count - 1); targetDir = ""; } else if (prevSnake.Count == 0) { head = snake.OrderByDescending(p => p.y).First(); dir = "UP"; body = snake.OrderByDescending(p => p.y).ToList(); Log($"[INIT] Len:{length} Head:X{head.x},Y{head.y}"); } prevSnake = new HashSet<(int x, int y)>(snake); if (head.x == -1) continue; int fx = -1, fy = -1; foreach (var item in FloorItems) { if (item == null) continue; if (GetKind(item) == KIND_FOOD) { fx = item.Location.X; fy = item.Location.Y; break; } } if (fx == -1) continue; if (moved || targetDir == "") { targetDir = Best(head.x, head.y, fx, fy, dir); if (targetDir != dir) Log($"[{length}] X{head.x},Y{head.y} {dir}->{targetDir} F:X{fx},Y{fy}"); } var (nx, ny) = Next(head.x, head.y, dir); bool danger = IsWall(nx, ny) || IsBlocked(nx, ny); for (int i = 0; i < body.Count - 1 && !danger; i++) if (body[i].x == nx && body[i].y == ny) danger = true; if (danger) { targetDir = Best(head.x, head.y, fx, fy, dir); Cmd(targetDir); Delay(3); Cmd(targetDir); Delay(3); Cmd(targetDir); Log($"[!!!] X{head.x},Y{head.y} DANGER! ->{targetDir}"); } else if ((DateTime.Now - lastCmd).TotalMilliseconds > 20) { Cmd(targetDir); } }