Habbo Hotel automation scripts including: - Game solvers (Snake, Color Puzzle, Tetris, Flappy Bird, Flood-IT) - Room utilities (Autogate, One-Way Door, Furni Scanner) - Bot tools (Heal Bot, Pet Trainer, User Collector) - Trading & economy (Furni-Matic, Seed Trade, Trade Spam) Cleaned up: removed 5 duplicates and 2 broken scripts, renamed 37 gibberish filenames to descriptive names.
419 lines
12 KiB
C#
419 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
// ============================================================
|
||
// COLOR PUZZLE AUTO-SOLVER (Loopover 4x4)
|
||
// Liest Grid + Ziel aus dem Raum, loest per BFS/IDA*,
|
||
// klickt die Pfeil-Buttons automatisch.
|
||
// ============================================================
|
||
|
||
const int TILE_KIND = 3696;
|
||
const int ARROW_KIND = 17851;
|
||
const int GRID_X_MIN = 36;
|
||
const int GRID_X_MAX = 39;
|
||
const int GRID_Y_MIN = 27;
|
||
const int GRID_Y_MAX = 30;
|
||
const int CLICK_DELAY = 700;
|
||
const int BFS_MAX_NODES = 8_000_000;
|
||
const int IDA_MAX_SEC = 15;
|
||
|
||
// Set true if arrows push tiles INTO the grid (opposite direction)
|
||
const bool REVERSE_ARROWS = false;
|
||
|
||
int GetState(dynamic item)
|
||
{
|
||
try { return int.Parse(item.State?.ToString() ?? "0"); }
|
||
catch { return 0; }
|
||
}
|
||
|
||
int GetKind(dynamic item)
|
||
{
|
||
try { return (int)item.Kind; }
|
||
catch { return -1; }
|
||
}
|
||
|
||
Log("=== Color Puzzle Auto-Solver ===");
|
||
|
||
// ── 1. Read puzzle grid from room ──────────────────────────
|
||
int[,] grid = new int[4, 4];
|
||
bool[,] gridFound = new bool[4, 4];
|
||
|
||
foreach (var item in FloorItems)
|
||
{
|
||
if (item == null) continue;
|
||
if (GetKind(item) != TILE_KIND) continue;
|
||
int x = item.Location.X, y = item.Location.Y;
|
||
double z = item.Location.Z;
|
||
if (x < GRID_X_MIN || x > GRID_X_MAX) continue;
|
||
if (y < GRID_Y_MIN || y > GRID_Y_MAX) continue;
|
||
if (z < 18.4) continue;
|
||
int col = x - GRID_X_MIN;
|
||
int row = y - GRID_Y_MIN;
|
||
grid[row, col] = GetState(item);
|
||
gridFound[row, col] = true;
|
||
}
|
||
|
||
int foundCount = 0;
|
||
for (int r = 0; r < 4; r++)
|
||
for (int c = 0; c < 4; c++)
|
||
if (gridFound[r, c]) foundCount++;
|
||
|
||
if (foundCount < 16)
|
||
{
|
||
Log($"ERROR: Nur {foundCount}/16 Grid-Tiles gefunden!");
|
||
Log("Bist du im richtigen Raum?");
|
||
return;
|
||
}
|
||
|
||
Log("Aktuelles Grid:");
|
||
for (int r = 0; r < 4; r++)
|
||
Log($" Row {r}: [{grid[r,0]}, {grid[r,1]}, {grid[r,2]}, {grid[r,3]}]");
|
||
|
||
// ── 2. Define *fixed* target pattern (ignore room indicators) ──────────
|
||
// Mapping from Scanner (Floor:3696):
|
||
// State 1 = grün, State 2 = rot, State 3 = blau, State 0 = bunt (3‑Farben‑Tile)
|
||
// Gewünschtes Endbild (von oben nach unten):
|
||
// Row 0: alles grün (1)
|
||
// Row 1: alles rot (2)
|
||
// Row 2: alles blau (3)
|
||
// Row 3: alles bunt (0)
|
||
|
||
int[,] target = new int[4, 4];
|
||
for (int c = 0; c < 4; c++)
|
||
{
|
||
target[0, c] = 1; // grün
|
||
target[1, c] = 2; // rot
|
||
target[2, c] = 3; // blau
|
||
target[3, c] = 0; // bunt
|
||
}
|
||
|
||
Log("Ziel-Grid (fest vorgegeben):");
|
||
for (int r = 0; r < 4; r++)
|
||
Log($" Row {r}: [{target[r,0]}, {target[r,1]}, {target[r,2]}, {target[r,3]}]");
|
||
|
||
// ── 3. Read arrow button IDs ──────────────────────────────
|
||
var arrowIds = new Dictionary<string, long>();
|
||
|
||
foreach (var item in FloorItems)
|
||
{
|
||
if (item == null) continue;
|
||
if (GetKind(item) != ARROW_KIND) continue;
|
||
int x = item.Location.X, y = item.Location.Y;
|
||
|
||
if (y == GRID_Y_MIN - 1 && x >= GRID_X_MIN && x <= GRID_X_MAX)
|
||
arrowIds[$"up_{x - GRID_X_MIN}"] = item.Id;
|
||
else if (y == GRID_Y_MAX + 1 && x >= GRID_X_MIN && x <= GRID_X_MAX)
|
||
arrowIds[$"down_{x - GRID_X_MIN}"] = item.Id;
|
||
else if (x == GRID_X_MIN - 1 && y >= GRID_Y_MIN && y <= GRID_Y_MAX)
|
||
arrowIds[$"left_{y - GRID_Y_MIN}"] = item.Id;
|
||
else if (x == GRID_X_MAX + 1 && y >= GRID_Y_MIN && y <= GRID_Y_MAX)
|
||
arrowIds[$"right_{y - GRID_Y_MIN}"] = item.Id;
|
||
}
|
||
|
||
Log($"Arrow-Buttons gefunden: {arrowIds.Count}/16");
|
||
if (arrowIds.Count < 16)
|
||
{
|
||
Log("WARN: Nicht alle 16 Pfeile gefunden!");
|
||
foreach (var kv in arrowIds) Log($" {kv.Key} = {kv.Value}");
|
||
}
|
||
|
||
// ── 4. State encoding (2 bits per cell, 32 bits total) ────
|
||
// Bits 0-1: grid[0,0], Bits 2-3: grid[0,1], ... Bits 30-31: grid[3,3]
|
||
// Row r = bits [r*8 .. r*8+7]
|
||
|
||
uint Encode(int[,] g)
|
||
{
|
||
uint s = 0;
|
||
for (int r = 0; r < 4; r++)
|
||
for (int c = 0; c < 4; c++)
|
||
s |= ((uint)(g[r, c] & 3)) << (2 * (r * 4 + c));
|
||
return s;
|
||
}
|
||
|
||
// ── 5. Bit-manipulation move functions ────────────────────
|
||
// RowLeft: [c0,c1,c2,c3] → [c1,c2,c3,c0] = rotate byte RIGHT by 2
|
||
uint RowLeft(uint s, int r)
|
||
{
|
||
int sh = r * 8;
|
||
uint row = (s >> sh) & 0xFFu;
|
||
uint rot = ((row >> 2) | (row << 6)) & 0xFFu;
|
||
return (s & ~(0xFFu << sh)) | (rot << sh);
|
||
}
|
||
|
||
// RowRight: [c0,c1,c2,c3] → [c3,c0,c1,c2] = rotate byte LEFT by 2
|
||
uint RowRight(uint s, int r)
|
||
{
|
||
int sh = r * 8;
|
||
uint row = (s >> sh) & 0xFFu;
|
||
uint rot = ((row << 2) | (row >> 6)) & 0xFFu;
|
||
return (s & ~(0xFFu << sh)) | (rot << sh);
|
||
}
|
||
|
||
// ColUp: [r0,r1,r2,r3] → [r1,r2,r3,r0]
|
||
uint ColUp(uint s, int c)
|
||
{
|
||
int b = c * 2;
|
||
uint v0 = (s >> b) & 3u;
|
||
uint v1 = (s >> (b + 8)) & 3u;
|
||
uint v2 = (s >> (b + 16)) & 3u;
|
||
uint v3 = (s >> (b + 24)) & 3u;
|
||
uint mask = ~(3u << b | 3u << (b + 8) | 3u << (b + 16) | 3u << (b + 24));
|
||
return (s & mask) | (v1 << b) | (v2 << (b + 8)) | (v3 << (b + 16)) | (v0 << (b + 24));
|
||
}
|
||
|
||
// ColDown: [r0,r1,r2,r3] → [r3,r0,r1,r2]
|
||
uint ColDown(uint s, int c)
|
||
{
|
||
int b = c * 2;
|
||
uint v0 = (s >> b) & 3u;
|
||
uint v1 = (s >> (b + 8)) & 3u;
|
||
uint v2 = (s >> (b + 16)) & 3u;
|
||
uint v3 = (s >> (b + 24)) & 3u;
|
||
uint mask = ~(3u << b | 3u << (b + 8) | 3u << (b + 16) | 3u << (b + 24));
|
||
return (s & mask) | (v3 << b) | (v0 << (b + 8)) | (v1 << (b + 16)) | (v2 << (b + 24));
|
||
}
|
||
|
||
// Move encoding: 0-3=RowLeft(0-3), 4-7=RowRight(0-3), 8-11=ColUp(0-3), 12-15=ColDown(0-3)
|
||
uint ApplyMove(uint s, int m)
|
||
{
|
||
if (m < 4) return RowLeft(s, m);
|
||
if (m < 8) return RowRight(s, m - 4);
|
||
if (m < 12) return ColUp(s, m - 8);
|
||
return ColDown(s, m - 12);
|
||
}
|
||
|
||
int InverseMove(int m)
|
||
{
|
||
if (m < 4) return m + 4;
|
||
if (m < 8) return m - 4;
|
||
if (m < 12) return m + 4;
|
||
return m - 4;
|
||
}
|
||
|
||
string MoveName(int m)
|
||
{
|
||
if (m < 4) return $"Row{m} LEFT";
|
||
if (m < 8) return $"Row{m-4} RIGHT";
|
||
if (m < 12) return $"Col{m-8} UP";
|
||
return $"Col{m-12} DOWN";
|
||
}
|
||
|
||
// ── 6. Solve ──────────────────────────────────────────────
|
||
uint startState = Encode(grid);
|
||
uint goalState = Encode(target);
|
||
|
||
if (startState == goalState)
|
||
{
|
||
Log("Puzzle ist bereits geloest!");
|
||
return;
|
||
}
|
||
|
||
List<int> solution = null;
|
||
|
||
// ── 6a. BFS ───────────────────────────────────────────────
|
||
Log($"Starte BFS (max {BFS_MAX_NODES:N0} Nodes)...");
|
||
var startBfs = DateTime.Now;
|
||
|
||
var visited = new Dictionary<uint, (uint parent, int move)>();
|
||
var queue = new Queue<uint>();
|
||
visited[startState] = (startState, -1);
|
||
queue.Enqueue(startState);
|
||
|
||
bool solved = false;
|
||
int nodesExplored = 0;
|
||
|
||
while (queue.Count > 0 && !solved && nodesExplored < BFS_MAX_NODES)
|
||
{
|
||
uint current = queue.Dequeue();
|
||
nodesExplored++;
|
||
|
||
if (nodesExplored % 2_000_000 == 0)
|
||
Log($" BFS: {nodesExplored:N0} States, Queue: {queue.Count:N0}");
|
||
|
||
for (int m = 0; m < 16; m++)
|
||
{
|
||
uint next = ApplyMove(current, m);
|
||
if (visited.ContainsKey(next)) continue;
|
||
visited[next] = (current, m);
|
||
if (next == goalState)
|
||
{
|
||
solved = true;
|
||
break;
|
||
}
|
||
queue.Enqueue(next);
|
||
}
|
||
}
|
||
|
||
if (solved)
|
||
{
|
||
solution = new List<int>();
|
||
uint s = goalState;
|
||
while (s != startState)
|
||
{
|
||
var (parent, move) = visited[s];
|
||
solution.Add(move);
|
||
s = parent;
|
||
}
|
||
solution.Reverse();
|
||
var bfsTime = (DateTime.Now - startBfs).TotalMilliseconds;
|
||
Log($"BFS Loesung: {solution.Count} Moves in {bfsTime:F0}ms ({nodesExplored:N0} States)");
|
||
}
|
||
else
|
||
{
|
||
Log($"BFS: Keine Loesung in {nodesExplored:N0} Nodes.");
|
||
visited.Clear();
|
||
visited = null;
|
||
queue.Clear();
|
||
queue = null;
|
||
|
||
// ── 6b. IDA* Fallback ─────────────────────────────────
|
||
Log($"Starte IDA* (max {IDA_MAX_SEC}s)...");
|
||
var startIda = DateTime.Now;
|
||
|
||
int Heuristic(uint st)
|
||
{
|
||
int mis = 0;
|
||
for (int i = 0; i < 16; i++)
|
||
{
|
||
int sv = (int)((st >> (i * 2)) & 3u);
|
||
int gv = (int)((goalState >> (i * 2)) & 3u);
|
||
if (sv != gv) mis++;
|
||
}
|
||
return (mis + 3) / 4;
|
||
}
|
||
|
||
List<int> bestSol = null;
|
||
int bestLen = 30;
|
||
bool timeout = false;
|
||
|
||
bool DFS(uint state, List<int> moves, int maxDepth)
|
||
{
|
||
if (timeout) return false;
|
||
if ((DateTime.Now - startIda).TotalSeconds > IDA_MAX_SEC)
|
||
{
|
||
timeout = true;
|
||
return false;
|
||
}
|
||
|
||
if (state == goalState)
|
||
{
|
||
if (moves.Count < bestLen)
|
||
{
|
||
bestLen = moves.Count;
|
||
bestSol = new List<int>(moves);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
int h = Heuristic(state);
|
||
if (moves.Count + h > maxDepth) return false;
|
||
if (moves.Count >= bestLen - 1) return false;
|
||
|
||
int lastInv = moves.Count > 0 ? InverseMove(moves[moves.Count - 1]) : -1;
|
||
|
||
bool found = false;
|
||
for (int m = 0; m < 16; m++)
|
||
{
|
||
if (m == lastInv) continue;
|
||
uint next = ApplyMove(state, m);
|
||
moves.Add(m);
|
||
if (DFS(next, moves, maxDepth)) found = true;
|
||
moves.RemoveAt(moves.Count - 1);
|
||
if (timeout) break;
|
||
}
|
||
return found;
|
||
}
|
||
|
||
int startH = Heuristic(startState);
|
||
for (int depth = startH; depth <= 20 && !timeout; depth++)
|
||
{
|
||
Log($" IDA* Tiefe {depth}...");
|
||
DFS(startState, new List<int>(), depth);
|
||
if (bestSol != null) break;
|
||
}
|
||
|
||
if (bestSol != null)
|
||
{
|
||
solution = bestSol;
|
||
var idaTime = (DateTime.Now - startIda).TotalMilliseconds;
|
||
Log($"IDA* Loesung: {solution.Count} Moves in {idaTime:F0}ms");
|
||
}
|
||
else
|
||
{
|
||
Log("ERROR: Keine Loesung gefunden!");
|
||
Log("Moegliche Gruende:");
|
||
Log(" - Puzzle-State hat sich geaendert");
|
||
Log(" - Target-Zuordnung ist falsch");
|
||
return;
|
||
}
|
||
}
|
||
|
||
// ── 7. Show solution ──────────────────────────────────────
|
||
Log("Loesungs-Schritte:");
|
||
for (int i = 0; i < solution.Count; i++)
|
||
Log($" {i+1}. {MoveName(solution[i])}");
|
||
|
||
// ── 8. Execute moves via ClickFurni ──────────────────────
|
||
Log("Fuehre Moves aus...");
|
||
|
||
foreach (int m in solution)
|
||
{
|
||
string dir;
|
||
int idx;
|
||
|
||
if (REVERSE_ARROWS)
|
||
{
|
||
// Reversed: solver says LEFT → click RIGHT arrow (push from right)
|
||
if (m < 4) { dir = "right"; idx = m; }
|
||
else if (m < 8) { dir = "left"; idx = m - 4; }
|
||
else if (m < 12) { dir = "down"; idx = m - 8; }
|
||
else { dir = "up"; idx = m - 12; }
|
||
}
|
||
else
|
||
{
|
||
// Normal: solver says LEFT → click LEFT arrow
|
||
if (m < 4) { dir = "left"; idx = m; }
|
||
else if (m < 8) { dir = "right"; idx = m - 4; }
|
||
else if (m < 12) { dir = "up"; idx = m - 8; }
|
||
else { dir = "down"; idx = m - 12; }
|
||
}
|
||
|
||
string key = $"{dir}_{idx}";
|
||
if (!arrowIds.ContainsKey(key))
|
||
{
|
||
Log($"ERROR: Arrow '{key}' nicht gefunden!");
|
||
return;
|
||
}
|
||
|
||
long arrowId = arrowIds[key];
|
||
Log($" Click: {MoveName(m)} -> {key} (ID: {arrowId})");
|
||
Send(Out["ClickFurni"], (int)arrowId, 0);
|
||
Delay(CLICK_DELAY);
|
||
}
|
||
|
||
// ── 9. Verify final grid ────────────────────────────────────
|
||
int[,] finalGrid = new int[4, 4];
|
||
bool[,] finalFound = new bool[4, 4];
|
||
|
||
foreach (var item in FloorItems)
|
||
{
|
||
if (item == null) continue;
|
||
if (GetKind(item) != TILE_KIND) continue;
|
||
int x = item.Location.X, y = item.Location.Y;
|
||
double z = item.Location.Z;
|
||
if (x < GRID_X_MIN || x > GRID_X_MAX) continue;
|
||
if (y < GRID_Y_MIN || y > GRID_Y_MAX) continue;
|
||
if (z < 18.4) continue;
|
||
int col = x - GRID_X_MIN;
|
||
int row = y - GRID_Y_MIN;
|
||
finalGrid[row, col] = GetState(item);
|
||
finalFound[row, col] = true;
|
||
}
|
||
|
||
Log("Finales Grid nach Ausfuehrung:");
|
||
for (int r = 0; r < 4; r++)
|
||
Log($" Row {r}: [{finalGrid[r,0]}, {finalGrid[r,1]}, {finalGrid[r,2]}, {finalGrid[r,3]}]");
|
||
|
||
Log("=== Puzzle geloest (internes Ziel erreicht) ===");
|