xabbo-scripts/Tetris2.csx
Administrator b6c31a7feb Add 165 scripts from desktop collection + update README
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>
2026-03-16 09:47:56 +01:00

631 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
int sameCommandDelay = 75;
int verifyTimeout = 250;
int downDelay = 50;
int landingTimeout = 400;
int botPosX = 18;
int botPosY = 28;
int waitAreaMinX = 5;
int waitAreaMaxX = 8;
int waitAreaMinY = 19;
int waitAreaMaxY = 22;
int walkToX = 8;
int walkToY = 23;
const int FIELD_MIN_X = 11;
const int FIELD_MAX_X = 20;
const int FIELD_MIN_Y = 3;
const int FIELD_MAX_Y = 22;
const int SPAWN_MIN_X = 14;
const int SPAWN_MAX_X = 17;
const int SPAWN_MIN_Y = 3;
const int SPAWN_MAX_Y = 5;
const int CTRL_LEFT = 256117487;
const int CTRL_DOWN = 256117486;
const int CTRL_RIGHT = 256117485;
const int CTRL_ROTATE = 256117484;
DateTime lastLeftCmd = DateTime.MinValue;
DateTime lastRightCmd = DateTime.MinValue;
DateTime lastRotateCmd = DateTime.MinValue;
DateTime lastDownCmd = DateTime.MinValue;
DateTime lastMoveDetected = DateTime.MinValue;
DateTime spawnTime = DateTime.MinValue;
int pendingLeftFromX = -1;
int pendingRightFromX = -1;
int pendingRotateFromRot = -1;
int targetX = -1;
int targetRot = -1;
int currentX = -1;
int currentRot = -1;
char currentPiece = '?';
bool pieceActive = false;
bool positioned = false;
HashSet<int> pieceIds = new HashSet<int>();
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);
}
var shapes = new Dictionary<string, int[]> {
["I0"] = new[] {0,0,1,0,2,0,3,0}, ["I1"] = new[] {0,0,0,1,0,2,0,3},
["O0"] = new[] {0,0,1,0,0,1,1,1},
["T0"] = new[] {0,0,1,0,2,0,1,1}, ["T1"] = new[] {1,0,0,1,1,1,1,2},
["T2"] = new[] {1,0,0,1,1,1,2,1}, ["T3"] = new[] {0,0,0,1,1,1,0,2},
["S0"] = new[] {1,0,2,0,0,1,1,1}, ["S1"] = new[] {0,0,0,1,1,1,1,2},
["Z0"] = new[] {0,0,1,0,1,1,2,1}, ["Z1"] = new[] {1,0,0,1,1,1,0,2},
["L0"] = new[] {0,0,0,1,0,2,1,2}, ["L1"] = new[] {0,0,1,0,2,0,0,1},
["L2"] = new[] {0,0,1,0,1,1,1,2}, ["L3"] = new[] {2,0,0,1,1,1,2,1},
["J0"] = new[] {1,0,1,1,0,2,1,2}, ["J1"] = new[] {0,0,0,1,1,1,2,1},
["J2"] = new[] {0,0,1,0,0,1,0,2}, ["J3"] = new[] {0,0,1,0,2,0,2,1}
};
HashSet<string> ignoredItems = new HashSet<string> {
"Large Block 18", "Großer Bauklotz 18",
"Large Block 13", "Großer Bauklotz 13"
};
bool IsBlockItem(string name)
{
if (string.IsNullOrEmpty(name)) return false;
if (ignoredItems.Contains(name)) return false;
return name.StartsWith("Großer Bauklotz") || name.StartsWith("Large Block");
}
bool IsInField(int x, int y) => x >= FIELD_MIN_X && x <= FIELD_MAX_X && y >= FIELD_MIN_Y && y <= FIELD_MAX_Y;
bool IsInSpawn(int x, int y) => x >= SPAWN_MIN_X && x <= SPAWN_MAX_X && y >= SPAWN_MIN_Y && y <= SPAWN_MAX_Y;
void SendLeft()
{
pendingLeftFromX = currentX;
Send(Out["ClickFurni"], CTRL_LEFT, 0);
lastLeftCmd = DateTime.Now;
}
void SendRight()
{
pendingRightFromX = currentX;
Send(Out["ClickFurni"], CTRL_RIGHT, 0);
lastRightCmd = DateTime.Now;
}
void SendRotate()
{
pendingRotateFromRot = currentRot;
Send(Out["ClickFurni"], CTRL_ROTATE, 0);
lastRotateCmd = DateTime.Now;
}
void SendDown()
{
Send(Out["ClickFurni"], CTRL_DOWN, 0);
lastDownCmd = DateTime.Now;
}
bool CanLeft() => (DateTime.Now - lastLeftCmd).TotalMilliseconds >= sameCommandDelay;
bool CanRight() => (DateTime.Now - lastRightCmd).TotalMilliseconds >= sameCommandDelay;
bool CanRotate() => (DateTime.Now - lastRotateCmd).TotalMilliseconds >= sameCommandDelay;
bool CanDown() => (DateTime.Now - lastDownCmd).TotalMilliseconds >= downDelay;
string IdentifyShape(List<Point> positions)
{
if (positions.Count != 4) return "?";
int minX = positions.Min(p => p.X);
int minY = positions.Min(p => p.Y);
var norm = positions.Select(p => new Point(p.X - minX, p.Y - minY)).OrderBy(p => p.Y).ThenBy(p => p.X).ToList();
foreach (var kvp in shapes)
{
var pts = new List<Point>();
for (int i = 0; i < 8; i += 2) pts.Add(new Point(kvp.Value[i], kvp.Value[i+1]));
pts = pts.OrderBy(p => p.Y).ThenBy(p => p.X).ToList();
bool match = true;
for (int i = 0; i < 4; i++) if (!norm[i].Equals(pts[i])) { match = false; break; }
if (match) return kvp.Key;
}
return "?";
}
char GetPiece(string s) => s != "?" && s.Length >= 1 ? s[0] : '?';
int GetRot(string s) => s != "?" && s.Length >= 2 ? int.Parse(s.Substring(1)) : -1;
List<Point> GetShapePoints(char piece, int rot)
{
string key = $"{piece}{rot}";
if (!shapes.ContainsKey(key)) return new List<Point>();
var def = shapes[key];
var pts = new List<Point>();
for (int i = 0; i < 8; i += 2) pts.Add(new Point(def[i], def[i+1]));
return pts;
}
int GetMaxRotations(char piece)
{
if (piece == 'O') return 1;
if (piece == 'I' || piece == 'S' || piece == 'Z') return 2;
return 4;
}
HashSet<Point> GetFieldBlocks()
{
var result = new HashSet<Point>();
foreach (var item in FloorItems)
{
if (item == null) continue;
if (!IsBlockItem(item.GetName())) continue;
int x = item.Location.X;
int y = item.Location.Y;
if (IsInField(x, y) && !pieceIds.Contains((int)item.Id))
result.Add(new Point(x, y));
}
return result;
}
int SimulateDrop(char piece, int rot, int startX, HashSet<Point> field)
{
var shape = GetShapePoints(piece, rot);
if (shape.Count != 4) return -1;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y + 1; y++)
{
foreach (var p in shape)
{
int px = startX + p.X;
int py = y + p.Y;
if (py > FIELD_MAX_Y || field.Contains(new Point(px, py)))
return y - 1;
}
}
return FIELD_MAX_Y;
}
int CountHoles(HashSet<Point> field)
{
int holes = 0;
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
bool blockFound = false;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
if (field.Contains(new Point(x, y))) blockFound = true;
else if (blockFound) holes++;
}
}
return holes;
}
int CountHoleDepth(HashSet<Point> field)
{
int depth = 0;
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
int above = 0;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
if (field.Contains(new Point(x, y))) above++;
else if (above > 0) depth += above;
}
}
return depth;
}
int CountRowsWithHoles(HashSet<Point> field)
{
int count = 0;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
if (!field.Contains(new Point(x, y)))
{
for (int yy = FIELD_MIN_Y; yy < y; yy++)
{
if (field.Contains(new Point(x, yy))) { count++; goto nextRow; }
}
}
}
nextRow:;
}
return count;
}
int CountRowTransitions(HashSet<Point> field)
{
int t = 0;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
bool last = true;
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
bool cur = field.Contains(new Point(x, y));
if (cur != last) t++;
last = cur;
}
if (!last) t++;
}
return t;
}
int CountColTransitions(HashSet<Point> field)
{
int t = 0;
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
bool last = true;
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
bool cur = field.Contains(new Point(x, y));
if (cur != last) t++;
last = cur;
}
if (!last) t++;
}
return t;
}
int CountWellSums(HashSet<Point> field)
{
int wellSum = 0;
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
if (field.Contains(new Point(x, y))) continue;
bool lb = (x == FIELD_MIN_X) || field.Contains(new Point(x - 1, y));
bool rb = (x == FIELD_MAX_X) || field.Contains(new Point(x + 1, y));
if (lb && rb)
{
int d = 1;
for (int yy = y + 1; yy <= FIELD_MAX_Y; yy++)
{
if (field.Contains(new Point(x, yy))) break;
bool lb2 = (x == FIELD_MIN_X) || field.Contains(new Point(x - 1, yy));
bool rb2 = (x == FIELD_MAX_X) || field.Contains(new Point(x + 1, yy));
if (lb2 && rb2) d++;
else break;
}
wellSum += (d * (d + 1)) / 2;
}
}
}
return wellSum;
}
int[] GetHeights(HashSet<Point> field)
{
int[] h = new int[10];
for (int x = FIELD_MIN_X; x <= FIELD_MAX_X; x++)
{
for (int y = FIELD_MIN_Y; y <= FIELD_MAX_Y; y++)
{
if (field.Contains(new Point(x, y)))
{
h[x - FIELD_MIN_X] = FIELD_MAX_Y - y + 1;
break;
}
}
}
return h;
}
double ScorePlacement(char piece, int rot, int x, HashSet<Point> field)
{
var shape = GetShapePoints(piece, rot);
if (shape.Count != 4) return double.MinValue;
int landY = SimulateDrop(piece, rot, x, field);
if (landY < FIELD_MIN_Y) return double.MinValue;
foreach (var p in shape)
{
int px = x + p.X;
int py = landY + p.Y;
if (px < FIELD_MIN_X || px > FIELD_MAX_X || py > FIELD_MAX_Y)
return double.MinValue;
}
var newField = new HashSet<Point>(field);
var piecePoints = new HashSet<Point>();
foreach (var p in shape)
{
var pt = new Point(x + p.X, landY + p.Y);
newField.Add(pt);
piecePoints.Add(pt);
}
int linesCleared = 0;
int pieceCellsCleared = 0;
for (int row = FIELD_MIN_Y; row <= FIELD_MAX_Y; row++)
{
bool full = true;
for (int col = FIELD_MIN_X; col <= FIELD_MAX_X; col++)
if (!newField.Contains(new Point(col, row))) { full = false; break; }
if (full)
{
linesCleared++;
for (int col = FIELD_MIN_X; col <= FIELD_MAX_X; col++)
{
var pt = new Point(col, row);
if (piecePoints.Contains(pt)) pieceCellsCleared++;
newField.Remove(pt);
}
}
}
if (linesCleared > 0)
{
var dropped = new HashSet<Point>();
for (int row = FIELD_MAX_Y; row >= FIELD_MIN_Y; row--)
{
for (int col = FIELD_MIN_X; col <= FIELD_MAX_X; col++)
{
var pt = new Point(col, row);
if (newField.Contains(pt))
{
newField.Remove(pt);
int newRow = row;
while (newRow < FIELD_MAX_Y && !dropped.Contains(new Point(col, newRow + 1))) newRow++;
dropped.Add(new Point(col, newRow));
}
}
}
newField = dropped;
}
int pieceH = shape.Max(p => p.Y) + 1;
double landingHeight = (FIELD_MAX_Y - landY + 1) + (pieceH - 1) / 2.0;
double erodedPieceCells = linesCleared * pieceCellsCleared;
int rowTrans = CountRowTransitions(newField);
int colTrans = CountColTransitions(newField);
int holes = CountHoles(newField);
int wellSums = CountWellSums(newField);
int holeDepth = CountHoleDepth(newField);
int rowsWithHoles = CountRowsWithHoles(newField);
double score = 0;
score += -12.63 * landingHeight;
score += 6.60 * erodedPieceCells;
score += -9.22 * rowTrans;
score += -19.77 * colTrans;
score += -13.08 * holes;
score += -10.49 * wellSums;
score += -1.61 * holeDepth;
score += -24.04 * rowsWithHoles;
var heights = GetHeights(newField);
int maxH = heights.Max();
if (maxH >= 12)
{
score += linesCleared * 5000;
score -= maxH * 50;
}
return score;
}
(int, int) FindBestPlacement(char piece, HashSet<Point> field)
{
int bestX = 15, bestRot = 0;
double bestScore = double.MinValue;
int maxRot = GetMaxRotations(piece);
for (int rot = 0; rot < maxRot; rot++)
{
var shape = GetShapePoints(piece, rot);
if (shape.Count != 4) continue;
int w = shape.Max(p => p.X) + 1;
for (int px = FIELD_MIN_X; px <= FIELD_MAX_X - w + 1; px++)
{
double s = ScorePlacement(piece, rot, px, field);
if (s > bestScore) { bestScore = s; bestX = px; bestRot = rot; }
}
}
return (bestX, bestRot);
}
(List<Point> positions, int minX, int minY, string shape, char piece, int rot) GetPieceState()
{
var positions = new List<Point>();
foreach (var item in FloorItems)
{
if (item == null) continue;
if (!pieceIds.Contains((int)item.Id)) continue;
positions.Add(new Point(item.Location.X, item.Location.Y));
}
if (positions.Count != 4) return (positions, -1, -1, "?", '?', -1);
int minX = positions.Min(p => p.X);
int minY = positions.Min(p => p.Y);
string shape = IdentifyShape(positions);
char piece = GetPiece(shape);
int rot = GetRot(shape);
return (positions, minX, minY, shape, piece, rot);
}
OnIntercept(In["ObjectUpdate"], e => {
var p = e.Packet;
int id = p.ReadInt();
if (pieceIds.Contains(id))
lastMoveDetected = DateTime.Now;
});
Log("TETRIS BOT");
Log($"Play position: ({botPosX},{botPosY})");
while (Run)
{
Delay(1);
if (Self == null) continue;
int myX = Self.Location.X;
int myY = Self.Location.Y;
if (myX >= waitAreaMinX && myX <= waitAreaMaxX && myY >= waitAreaMinY && myY <= waitAreaMaxY)
{
Move(walkToX, walkToY);
Delay(500);
continue;
}
if (myX != botPosX || myY != botPosY)
{
pieceIds.Clear();
pieceActive = false;
currentPiece = '?';
targetX = -1;
targetRot = -1;
positioned = false;
Delay(100);
continue;
}
if (!pieceActive)
{
var spawnBlocks = new List<(int id, int x, int y)>();
foreach (var item in FloorItems)
{
if (item == null) continue;
if (!IsBlockItem(item.GetName())) continue;
int x = item.Location.X;
int y = item.Location.Y;
if (IsInSpawn(x, y))
spawnBlocks.Add(((int)item.Id, x, y));
}
if (spawnBlocks.Count == 4)
{
pieceIds.Clear();
foreach (var b in spawnBlocks)
pieceIds.Add(b.id);
var positions = spawnBlocks.Select(b => new Point(b.x, b.y)).ToList();
string shape = IdentifyShape(positions);
currentPiece = GetPiece(shape);
currentRot = GetRot(shape);
currentX = positions.Min(p => p.X);
if (currentPiece != '?')
{
var field = GetFieldBlocks();
var (bx, br) = FindBestPlacement(currentPiece, field);
targetX = bx;
targetRot = br;
positioned = false;
pieceActive = true;
spawnTime = DateTime.Now;
lastMoveDetected = DateTime.Now;
pendingLeftFromX = -1;
pendingRightFromX = -1;
pendingRotateFromRot = -1;
Log($"{currentPiece}{currentRot} @ X{currentX} -> X{targetX} r{targetRot}");
}
}
continue;
}
var state = GetPieceState();
if (state.positions.Count != 4)
{
pieceIds.Clear();
pieceActive = false;
currentPiece = '?';
targetX = -1;
targetRot = -1;
positioned = false;
continue;
}
int newX = state.minX;
int newRot = state.rot != -1 ? state.rot : currentRot;
if (newX != currentX)
{
if (pendingLeftFromX != -1 && newX < pendingLeftFromX)
pendingLeftFromX = -1;
if (pendingRightFromX != -1 && newX > pendingRightFromX)
pendingRightFromX = -1;
currentX = newX;
}
if (newRot != currentRot)
{
pendingRotateFromRot = -1;
currentRot = newRot;
}
double timeSinceMove = (DateTime.Now - lastMoveDetected).TotalMilliseconds;
if (timeSinceMove > landingTimeout)
{
pieceIds.Clear();
pieceActive = false;
currentPiece = '?';
targetX = -1;
targetRot = -1;
positioned = false;
continue;
}
int maxRot = GetMaxRotations(currentPiece);
int rotNeeded = (targetRot - currentRot + maxRot) % maxRot;
int xDiff = targetX - currentX;
if (rotNeeded == 0 && xDiff == 0)
positioned = true;
if (positioned)
{
if (CanDown())
SendDown();
continue;
}
double timeSinceLeft = (DateTime.Now - lastLeftCmd).TotalMilliseconds;
double timeSinceRight = (DateTime.Now - lastRightCmd).TotalMilliseconds;
double timeSinceRotate = (DateTime.Now - lastRotateCmd).TotalMilliseconds;
bool leftPending = pendingLeftFromX != -1 && timeSinceLeft < verifyTimeout;
bool rightPending = pendingRightFromX != -1 && timeSinceRight < verifyTimeout;
bool rotatePending = pendingRotateFromRot != -1 && timeSinceRotate < verifyTimeout;
bool leftTimeout = pendingLeftFromX != -1 && timeSinceLeft >= verifyTimeout;
bool rightTimeout = pendingRightFromX != -1 && timeSinceRight >= verifyTimeout;
bool rotateTimeout = pendingRotateFromRot != -1 && timeSinceRotate >= verifyTimeout;
if (rotNeeded > 0)
{
if (rotateTimeout && CanRotate())
{
SendRotate();
}
else if (!rotatePending && CanRotate())
{
SendRotate();
}
}
if (xDiff < 0)
{
if (leftTimeout && CanLeft())
{
SendLeft();
}
else if (!leftPending && CanLeft())
{
SendLeft();
}
}
else if (xDiff > 0)
{
if (rightTimeout && CanRight())
{
SendRight();
}
else if (!rightPending && CanRight())
{
SendRight();
}
}
}