Added scripts from C:\Users\ploet\Desktop\Habbo\Xabbo Scripte: - 39 KI/Chatbot scripts (ChatGPT, Gemini, Grok, DeepSeek, Ollama) - Game solvers (Domino, Dodgeball, Obsidian Maze, IceBall) - Collision avoidance bots (5 versions) - Plant/breeding automation (12 scripts) - Trading tools, packet debuggers, room utilities - Navigation & teleport helpers Removed: 9 files (3 empty, 2 trivial, 4 cross-duplicates) Updated README with full categorized index of all 230+ scripts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
250 lines
10 KiB
C#
250 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Xabbo.Core;
|
|
|
|
public struct Point : IEquatable<Point>
|
|
{
|
|
public int X { get; }
|
|
public int Y { get; }
|
|
public Point(int x, int y) { X = x; Y = 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 override string ToString() => $"({X}, {Y})";
|
|
}
|
|
|
|
// --- Configuration ---
|
|
string targetFurniName = "Number Tile Dark";
|
|
int minX = 16; int minY = 28;
|
|
int maxX = 54; int maxY = 62;
|
|
int stepDelayMilliseconds = 2500;
|
|
// --- End Configuration ---
|
|
|
|
Log("Dominosa Hybrid Solver v6 (Robust Reconciliation) Initialized.");
|
|
|
|
// ================================================================= //
|
|
// PHASE 1: FULL BOARD ANALYSIS
|
|
// ================================================================= //
|
|
Log("Phase 1: Analyzing board state...");
|
|
|
|
var numberTiles = new Dictionary<Point, IFloorItem>();
|
|
var connectionTiles = new Dictionary<Point, (Point, Point)>();
|
|
var floorMap = new Dictionary<Point, List<IFloorItem>>();
|
|
|
|
try
|
|
{
|
|
if (FloorItems == null) { Log("ERROR: Cannot access FloorItems."); return; }
|
|
foreach (IFloorItem item in FloorItems)
|
|
{
|
|
if (item == null) continue;
|
|
var p = new Point(item.Location.X, item.Location.Y);
|
|
if (!floorMap.ContainsKey(p)) floorMap[p] = new List<IFloorItem>();
|
|
floorMap[p].Add(item);
|
|
}
|
|
|
|
var numberTileLocations = new HashSet<Point>();
|
|
foreach (var item in floorMap.SelectMany(kvp => kvp.Value))
|
|
{
|
|
if (item.GetName() != targetFurniName) continue;
|
|
int x = item.Location.X;
|
|
int y = item.Location.Y;
|
|
if (x >= minX && x <= maxX && y >= minY && y <= maxY)
|
|
{
|
|
var p = new Point(x, y);
|
|
numberTiles[p] = item;
|
|
numberTileLocations.Add(p);
|
|
}
|
|
}
|
|
|
|
foreach (Point p1 in numberTileLocations)
|
|
{
|
|
Point p2_horiz = new Point(p1.X + 4, p1.Y);
|
|
if (numberTileLocations.Contains(p2_horiz)) connectionTiles[new Point(p1.X + 2, p1.Y)] = (p1, p2_horiz);
|
|
Point p2_vert = new Point(p1.X, p1.Y + 4);
|
|
if (numberTileLocations.Contains(p2_vert)) connectionTiles[new Point(p1.X, p1.Y + 2)] = (p1, p2_vert);
|
|
}
|
|
}
|
|
catch (Exception ex) { Log($"ERROR during analysis: {ex.Message}"); return; }
|
|
|
|
Log($"Analysis complete. Found {numberTiles.Count} numbers and {connectionTiles.Count} connections.");
|
|
|
|
Func<Point, bool> isGapTileConnected = (p) => {
|
|
if (floorMap.TryGetValue(p, out var stack))
|
|
return stack.Any(f => f.GetName() == "Dark Tile" && Math.Abs(f.Location.Z - 0.25) < 0.001);
|
|
return false;
|
|
};
|
|
|
|
var activeConnections = new HashSet<Point>();
|
|
foreach (var c in connectionTiles)
|
|
{
|
|
bool isHorizontal = numberTiles.ContainsKey(new Point(c.Key.X - 2, c.Key.Y));
|
|
if (isHorizontal)
|
|
{
|
|
if (isGapTileConnected(new Point(c.Key.X - 1, c.Key.Y)) && isGapTileConnected(c.Key) && isGapTileConnected(new Point(c.Key.X + 1, c.Key.Y)))
|
|
activeConnections.Add(c.Key);
|
|
}
|
|
else
|
|
{
|
|
if (isGapTileConnected(new Point(c.Key.X, c.Key.Y - 1)) && isGapTileConnected(c.Key) && isGapTileConnected(new Point(c.Key.X, c.Key.Y + 1)))
|
|
activeConnections.Add(c.Key);
|
|
}
|
|
}
|
|
Log($"Detected {activeConnections.Count} currently active connections on the board.");
|
|
|
|
// ================================================================= //
|
|
// PHASE 2: CALCULATING THE IDEAL SOLUTION (HYBRID SOLVER)
|
|
// ================================================================= //
|
|
var idealConnections = new HashSet<Point>();
|
|
var pairedTiles = new HashSet<Point>();
|
|
var usedDominoes = new HashSet<(int, int)>();
|
|
|
|
Log("Phase 2.1: Finding forced moves (deterministic pass)...");
|
|
int deterministicMoves = 0;
|
|
while (true)
|
|
{
|
|
bool moveMadeThisIteration = false;
|
|
foreach (var tilePos in numberTiles.Keys.Where(p => !pairedTiles.Contains(p)))
|
|
{
|
|
var possiblePartners = new List<Point>();
|
|
Point[] neighborChecks = { new Point(tilePos.X - 4, tilePos.Y), new Point(tilePos.X + 4, tilePos.Y), new Point(tilePos.X, tilePos.Y - 4), new Point(tilePos.X, tilePos.Y + 4) };
|
|
foreach (var partnerPos in neighborChecks)
|
|
{
|
|
if (numberTiles.ContainsKey(partnerPos) && !pairedTiles.Contains(partnerPos))
|
|
{
|
|
var domino = (Math.Min(numberTiles[tilePos].State, numberTiles[partnerPos].State), Math.Max(numberTiles[tilePos].State, numberTiles[partnerPos].State));
|
|
if (!usedDominoes.Contains(domino)) possiblePartners.Add(partnerPos);
|
|
}
|
|
}
|
|
if (possiblePartners.Count == 1)
|
|
{
|
|
var partnerPos = possiblePartners.First();
|
|
var domino = (Math.Min(numberTiles[tilePos].State, numberTiles[partnerPos].State), Math.Max(numberTiles[tilePos].State, numberTiles[partnerPos].State));
|
|
var connection = connectionTiles.First(kvp => (kvp.Value.Item1.Equals(tilePos) && kvp.Value.Item2.Equals(partnerPos)) || (kvp.Value.Item1.Equals(partnerPos) && kvp.Value.Item2.Equals(tilePos))).Key;
|
|
idealConnections.Add(connection); pairedTiles.Add(tilePos); pairedTiles.Add(partnerPos); usedDominoes.Add(domino);
|
|
moveMadeThisIteration = true; deterministicMoves++; break;
|
|
}
|
|
}
|
|
if (moveMadeThisIteration) continue;
|
|
var dominoPossibilities = new Dictionary<(int, int), List<Point>>();
|
|
foreach (var c in connectionTiles)
|
|
{
|
|
Point p1 = c.Value.Item1; Point p2 = c.Value.Item2;
|
|
if (!pairedTiles.Contains(p1) && !pairedTiles.Contains(p2))
|
|
{
|
|
var domino = (Math.Min(numberTiles[p1].State, numberTiles[p2].State), Math.Max(numberTiles[p1].State, numberTiles[p2].State));
|
|
if (!usedDominoes.Contains(domino))
|
|
{
|
|
if (!dominoPossibilities.ContainsKey(domino)) dominoPossibilities[domino] = new List<Point>();
|
|
dominoPossibilities[domino].Add(c.Key);
|
|
}
|
|
}
|
|
}
|
|
var forcedDomino = dominoPossibilities.FirstOrDefault(kvp => kvp.Value.Count == 1);
|
|
if (!forcedDomino.Equals(default(KeyValuePair<(int, int), List<Point>>)))
|
|
{
|
|
var connection = forcedDomino.Value.First();
|
|
var (p1, p2) = connectionTiles[connection];
|
|
var domino = forcedDomino.Key;
|
|
idealConnections.Add(connection); pairedTiles.Add(p1); pairedTiles.Add(p2); usedDominoes.Add(domino);
|
|
moveMadeThisIteration = true; deterministicMoves++; continue;
|
|
}
|
|
if (!moveMadeThisIteration) break;
|
|
}
|
|
Log($"Deterministic pass found {deterministicMoves} moves.");
|
|
|
|
bool success = false;
|
|
if (pairedTiles.Count == numberTiles.Count)
|
|
{
|
|
Log("Puzzle solved deterministically. No recursion needed.");
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
Log($"Phase 2.2: Starting recursive backtracking on remaining {numberTiles.Count - pairedTiles.Count} tiles...");
|
|
var precomputedNeighbors = numberTiles.Keys.ToDictionary(p => p, p => new Point[] { new Point(p.X - 4, p.Y), new Point(p.X + 4, p.Y), new Point(p.X, p.Y - 4), new Point(p.X, p.Y + 4) }.Where(n => numberTiles.ContainsKey(n)).ToList());
|
|
bool SolveRecursive()
|
|
{
|
|
if (pairedTiles.Count == numberTiles.Count) return true;
|
|
var firstUnpaired = numberTiles.Keys.First(p => !pairedTiles.Contains(p));
|
|
foreach (var neighbor in precomputedNeighbors[firstUnpaired])
|
|
{
|
|
if (pairedTiles.Contains(neighbor)) continue;
|
|
var domino = (Math.Min(numberTiles[firstUnpaired].State, numberTiles[neighbor].State), Math.Max(numberTiles[firstUnpaired].State, numberTiles[neighbor].State));
|
|
if (usedDominoes.Contains(domino)) continue;
|
|
var connection = connectionTiles.First(kvp => (kvp.Value.Item1.Equals(firstUnpaired) && kvp.Value.Item2.Equals(neighbor)) || (kvp.Value.Item1.Equals(neighbor) && kvp.Value.Item2.Equals(firstUnpaired))).Key;
|
|
pairedTiles.Add(firstUnpaired); pairedTiles.Add(neighbor); usedDominoes.Add(domino); idealConnections.Add(connection);
|
|
if (SolveRecursive()) return true;
|
|
idealConnections.Remove(connection); usedDominoes.Remove(domino); pairedTiles.Remove(neighbor); pairedTiles.Remove(firstUnpaired);
|
|
}
|
|
return false;
|
|
}
|
|
success = SolveRecursive();
|
|
}
|
|
|
|
Log($"Calculation complete. Success: {success}. Ideal solution has {idealConnections.Count} steps.");
|
|
|
|
// ================================================================= //
|
|
// PHASE 3: RECONCILIATION AND EXECUTION (ROBUST VERSION)
|
|
// ================================================================= //
|
|
if (!success) { Log("CRITICAL ERROR: The solver could not find any valid solution for the board."); return; }
|
|
Log("Phase 3: Reconciling current state with ideal solution...");
|
|
|
|
// --- THE FIX: Manually calculate the differences to avoid LINQ bugs ---
|
|
var movesToDisconnect = new List<Point>();
|
|
foreach(Point p in activeConnections)
|
|
{
|
|
if (!idealConnections.Contains(p)) movesToDisconnect.Add(p);
|
|
}
|
|
|
|
var movesToConnect = new List<Point>();
|
|
foreach(Point p in idealConnections)
|
|
{
|
|
if (!activeConnections.Contains(p)) movesToConnect.Add(p);
|
|
}
|
|
|
|
int correctCount = activeConnections.Count - movesToDisconnect.Count;
|
|
|
|
Log($"Found {movesToDisconnect.Count} incorrect connections to UNDO.");
|
|
Log($"Found {movesToConnect.Count} missing connections to MAKE.");
|
|
Log($"Found {correctCount} connections that are already correct.");
|
|
|
|
if (movesToDisconnect.Count == 0 && movesToConnect.Count == 0)
|
|
{
|
|
Log("\nBoard is already solved! No action needed.");
|
|
return;
|
|
}
|
|
|
|
if (movesToDisconnect.Any())
|
|
{
|
|
Log("\n--- [PLAN: UNDO MOVES] ---");
|
|
movesToDisconnect.ForEach(p => Log($" -> Click {p} to disconnect"));
|
|
Log("------------------------\nExecuting... Please wait.");
|
|
Delay(2000);
|
|
for (int i = 0; i < movesToDisconnect.Count; i++)
|
|
{
|
|
var p = movesToDisconnect[i];
|
|
Log($"Undoing {i + 1}/{movesToDisconnect.Count}: MoveTo {p}");
|
|
Send(Out["MoveAvatar"], p.X, p.Y);
|
|
Delay(stepDelayMilliseconds);
|
|
}
|
|
Log("Disconnections complete.");
|
|
}
|
|
|
|
if (movesToConnect.Any())
|
|
{
|
|
Log("\n--- [PLAN: MAKE MOVES] ---");
|
|
movesToConnect.ForEach(p => Log($" -> Click {p} to connect"));
|
|
Log("------------------------\nExecuting... Please wait.");
|
|
Delay(2000);
|
|
for (int i = 0; i < movesToConnect.Count; i++)
|
|
{
|
|
var p = movesToConnect[i];
|
|
Log($"Connecting {i + 1}/{movesToConnect.Count}: MoveTo {p}");
|
|
Send(Out["MoveAvatar"], p.X, p.Y);
|
|
Delay(stepDelayMilliseconds);
|
|
}
|
|
Log("Connections complete.");
|
|
}
|
|
|
|
Log("\nReconciliation complete. Puzzle should be solved."); |