xabbo-scripts/Scripts/Snake Auto (Campaign) v8.csx
Administrator 7a548130a3 Move all scripts into Scripts/ subfolder
Keeps the repo root clean - only README.md visible on landing page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 09:49:37 +01:00

171 lines
4.5 KiB
C#

/// @name Snake Auto (Campaign) v8
using System;
using System.Linq;
using System.Collections.Generic;
const long HEAD_ID = 769407982;
const int SNAKE_KIND = 5478;
static readonly int[] TARGET_KINDS = { 6559, 3666 };
const int GAME_MIN_X = 20;
const int GAME_MAX_X = 65;
const int GAME_MIN_Y = 20;
const int GAME_MAX_Y = 65;
// Direction -> plate ids (kept from your latest calibration)
const int CTRL_UP = 769406820;
const int CTRL_DOWN = 769407116;
const int CTRL_LEFT = 769407385;
const int CTRL_RIGHT = 769407085;
static readonly string[] DIRS = { "UP", "DOWN", "LEFT", "RIGHT" };
DateTime lastCmd = DateTime.MinValue;
DateTime lastDbg = DateTime.MinValue;
int curDir = -1;
(int x, int y) prevHead = (-1, -1);
bool InBoard(int x, int y)
=> x >= GAME_MIN_X && x <= GAME_MAX_X && y >= GAME_MIN_Y && y <= GAME_MAX_Y;
void Dbg(string m, bool force = false)
{
if (!force && (DateTime.Now - lastDbg).TotalMilliseconds < 220) return;
Log($"dbg: {m}");
lastDbg = DateTime.Now;
}
void Next(int x, int y, int d, out int nx, out int ny)
{
nx = x + (d == 3 ? 1 : d == 2 ? -1 : 0);
ny = y + (d == 1 ? 1 : d == 0 ? -1 : 0);
}
int Opp(int d) => d ^ 1;
int DirFromDelta(int dx, int dy)
{
if (Math.Abs(dx) >= Math.Abs(dy)) return dx < 0 ? 2 : 3;
return dy < 0 ? 0 : 1;
}
int BtnForDir(int d)
=> d == 0 ? CTRL_UP : d == 1 ? CTRL_DOWN : d == 2 ? CTRL_LEFT : CTRL_RIGHT;
bool TryGetBtnPos(int id, out int x, out int y)
{
var b = GetFloorItem(id);
if (b == null) { x = 0; y = 0; return false; }
x = b.Location.X;
y = b.Location.Y;
return true;
}
void Cmd(int d, string reason, (int x, int y) head, (int x, int y)? target)
{
if ((DateTime.Now - lastCmd).TotalMilliseconds < 120) return;
int id = BtnForDir(d);
if (TryGetBtnPos(id, out int bx, out int by)) Move(bx, by);
Send(Out["ClickFurni"], id, 0);
lastCmd = DateTime.Now;
if (target.HasValue)
Dbg($"{reason} head=({head.x},{head.y}) target=({target.Value.x},{target.Value.y}) dir={DIRS[d]} btn={id}", true);
else
Dbg($"{reason} head=({head.x},{head.y}) dir={DIRS[d]} btn={id}", true);
}
bool Bad(int x, int y, HashSet<(int x, int y)> body)
{
if (!InBoard(x, y)) return true;
return body.Contains((x, y));
}
int PickDir((int x, int y) head, (int x, int y) target, HashSet<(int x, int y)> body)
{
var order = new List<int>();
int dx = target.x - head.x;
int dy = target.y - head.y;
if (Math.Abs(dx) >= Math.Abs(dy))
{
order.Add(dx < 0 ? 2 : 3);
order.Add(dy < 0 ? 0 : 1);
}
else
{
order.Add(dy < 0 ? 0 : 1);
order.Add(dx < 0 ? 2 : 3);
}
order.AddRange(new[] { 0, 1, 2, 3 });
foreach (int d in order.Distinct())
{
if (curDir >= 0 && d == Opp(curDir)) continue;
Next(head.x, head.y, d, out int nx, out int ny);
if (!Bad(nx, ny, body)) return d;
}
return curDir >= 0 ? curDir : 0;
}
Log("Snake Auto (Campaign) v8 started");
Log($"target kinds: {string.Join(",", TARGET_KINDS)}");
Log($"buttons U={CTRL_UP} D={CTRL_DOWN} L={CTRL_LEFT} R={CTRL_RIGHT}");
while (Run)
{
Delay(20);
var headItem = GetFloorItem(HEAD_ID);
if (headItem == null)
{
Dbg("head missing");
continue;
}
var head = (headItem.Location.X, headItem.Location.Y);
if (!InBoard(head.Item1, head.Item2))
{
Dbg($"head out of board ({head.Item1},{head.Item2})");
continue;
}
if (prevHead.x != -1)
{
int hdx = head.Item1 - prevHead.x;
int hdy = head.Item2 - prevHead.y;
if (hdx != 0 || hdy != 0) curDir = DirFromDelta(hdx, hdy);
}
prevHead = head;
var body = FloorItems
.Where(i => i != null && i.Kind == SNAKE_KIND)
.Where(i => InBoard(i.Location.X, i.Location.Y))
.Select(i => (i.Location.X, i.Location.Y))
.Where(p => !(p.Item1 == head.Item1 && p.Item2 == head.Item2))
.ToHashSet();
var targets = FloorItems
.Where(i => i != null && TARGET_KINDS.Contains((int)i.Kind))
.Where(i => InBoard(i.Location.X, i.Location.Y))
.Select(i => (x: i.Location.X, y: i.Location.Y, kind: (int)i.Kind, id: (long)i.Id))
.ToList();
if (targets.Count == 0)
{
Dbg("no_target_hold", true);
continue;
}
var target = targets
.OrderBy(t => Math.Abs(t.x - head.Item1) + Math.Abs(t.y - head.Item2))
.First();
int dsel = PickDir(head, (target.x, target.y), body);
Cmd(dsel, $"seek k={target.kind} id={target.id}", head, (target.x, target.y));
}