diff --git a/DDApp/DDApplication/MainWindow.axaml.cs b/DDApp/DDApplication/MainWindow.axaml.cs index c3be011..f5f69b6 100644 --- a/DDApp/DDApplication/MainWindow.axaml.cs +++ b/DDApp/DDApplication/MainWindow.axaml.cs @@ -66,6 +66,8 @@ public partial class MainWindow : Window private DateTimeOffset _lastStatsAt = DateTimeOffset.MinValue; private DateTimeOffset _lastStatusAt = DateTimeOffset.MinValue; private (int X, int Y)? _nearestEnemy; + private (int X, int Y)? _lastObservedEnemyRelative; + private (int X, int Y)? _enemyVelocityEstimate; private Direction _lastMoveDirection = Direction.North; private Direction _escapeDirection = Direction.North; private int _recentDamageTicks; @@ -402,11 +404,20 @@ public partial class MainWindow : Window { var scan = await _client.ScanAsync(_playerToken, ct); MarkUsed(BotAction.Scan); + var previousEnemy = _nearestEnemy; var sx = scan.DifferenceToNearestPlayer?.X; var sy = scan.DifferenceToNearestPlayer?.Y; _nearestEnemy = sx.HasValue && sy.HasValue ? (sx.Value, sy.Value) : null; if (_nearestEnemy.HasValue) { + if (previousEnemy.HasValue) + { + _enemyVelocityEstimate = ( + _nearestEnemy.Value.X - previousEnemy.Value.X, + _nearestEnemy.Value.Y - previousEnemy.Value.Y); + } + + _lastObservedEnemyRelative = _nearestEnemy; AddHeat(_x + _nearestEnemy.Value.X, _y + _nearestEnemy.Value.Y, 5.5); } } @@ -459,11 +470,31 @@ public partial class MainWindow : Window _pressureStreak = Math.Max(_pressureStreak - 1, 0); } - var underPressure = hpRatio < PressureHpThreshold(profile) || _recentDamageTicks > 0 || closeThreats >= PressureThreatThreshold(profile) || _survivalTicks > 0 || _pressureStreak >= 2; - _lastUnderPressure = underPressure; + var underPressure = hpRatio < PressureHpThreshold(profile) || _recentDamageTicks > 0 || closeThreats >= PressureThreatThreshold(profile) || _survivalTicks > 0 || _pressureStreak >= 2; + _lastUnderPressure = underPressure; + var predictedEnemy = PredictEnemyRelative(); - // Teleport if we can and enemies are nearby - aggressive repositioning for kills - if (Ready(BotAction.Teleport) && closeThreats >= 1 && _nearestEnemy.HasValue) + var bestSight = _lastPeek + .Where(x => x.Value.Executed && x.Value.PlayersInSight > 0) + .OrderBy(x => x.Value.SightedPlayerDistance ?? int.MaxValue) + .FirstOrDefault(); + + if (Ready(BotAction.Hit) && immediateDir.HasValue && closeThreats >= 1) + { + var dir = immediateDir.Value; + return new BotDecision( + "hit-lock", + $"Enemy committed in close range at {dir}; preemptive melee pressure.", + async ct => + { + var response = await _client.HitAsync(_playerToken, (int)dir, ct); + MarkUsed(BotAction.Hit); + return response.Executed; + }); + } + + // Teleport if we can and enemies are nearby - aggressive repositioning for kills + if (Ready(BotAction.Teleport) && closeThreats >= 1 && _nearestEnemy.HasValue) { var targetX = _nearestEnemy.Value.X; var targetY = _nearestEnemy.Value.Y; @@ -487,10 +518,28 @@ public partial class MainWindow : Window }); } - var specialThreatThreshold = SpecialThreatThreshold(profile); - var specialHpFloor = SpecialHpFloor(profile); + if (Ready(BotAction.Dash) && TryGetAggressiveDashDirection(predictedEnemy, bestSight, out var dashDirection, out var dashReason)) + { + return new BotDecision( + "dash-aggro", + dashReason, + async ct => + { + var response = await _client.DashAsync(_playerToken, (int)dashDirection, ct); + MarkUsed(BotAction.Dash); + if (response.Executed) + { + ApplyMovement(dashDirection, Math.Max(response.BlocksDashed, 1)); + } - if (Ready(BotAction.Special) && closeThreats >= specialThreatThreshold) + return response.Executed; + }); + } + + var specialThreatThreshold = SpecialThreatThreshold(profile); + var specialHpFloor = SpecialHpFloor(profile); + + if (Ready(BotAction.Special) && closeThreats >= specialThreatThreshold && (hpRatio >= specialHpFloor || closeThreats > specialThreatThreshold)) { return new BotDecision( "specialattack", @@ -575,26 +624,18 @@ public partial class MainWindow : Window }); } - var bestSight = _lastPeek - .Where(x => x.Value.Executed && x.Value.PlayersInSight > 0) - .OrderBy(x => x.Value.SightedPlayerDistance ?? int.MaxValue) - .FirstOrDefault(); - - // More aggressive shooting - if (bestSight.Value is not null && Ready(BotAction.Shoot)) - { - var dir = bestSight.Key; - var dist = bestSight.Value.SightedPlayerDistance ?? 99; - return new BotDecision( - "shoot", - $"Enemy at {dir} ({dist} dist); ranged kill.", - async ct => - { - var response = await _client.ShootAsync(_playerToken, (int)dir, ct); - MarkUsed(BotAction.Shoot); - return response.Executed; - }); - } + if (Ready(BotAction.Shoot) && TryGetPredictiveShotDirection(bestSight.Key, bestSight.Value, predictedEnemy, out var shotDirection, out var shotReason)) + { + return new BotDecision( + "shoot", + shotReason, + async ct => + { + var response = await _client.ShootAsync(_playerToken, (int)shotDirection, ct); + MarkUsed(BotAction.Shoot); + return response.Executed; + }); + } var moveDirection = GetBestMoveDirection(chase: true); if (Ready(BotAction.Move)) @@ -641,8 +682,7 @@ public partial class MainWindow : Window foreach (var dir in scored.Keys.ToArray()) { var score = 0.0; - // Always chase aggressively - ignore radar safety - score += scored[dir] * 0.5; + score += scored[dir] * 0.5; if (_lastPeek.TryGetValue(dir, out var peek) && peek.Executed) { @@ -656,8 +696,8 @@ public partial class MainWindow : Window if (_nearestEnemy.HasValue) { - var targetDir = DirectionFromVector(_nearestEnemy.Value.X, _nearestEnemy.Value.Y); - // Always move toward nearest enemy + var targetVector = PredictEnemyRelative() ?? _nearestEnemy.Value; + var targetDir = DirectionFromVector(targetVector.X, targetVector.Y); if (dir == targetDir) { score += 12.0; @@ -927,6 +967,96 @@ public partial class MainWindow : Window _ => hpRatio < 0.02, }; + private (int X, int Y)? PredictEnemyRelative() + { + if (!_nearestEnemy.HasValue) + { + return _lastObservedEnemyRelative; + } + + if (_enemyVelocityEstimate is null) + { + return _nearestEnemy; + } + + return ( + _nearestEnemy.Value.X + _enemyVelocityEstimate.Value.X, + _nearestEnemy.Value.Y + _enemyVelocityEstimate.Value.Y); + } + + private bool TryGetPredictiveShotDirection( + Direction? sightDirection, + PeekEntitiesAsyncResponse? sightResponse, + (int X, int Y)? predictedEnemy, + out Direction direction, + out string reason) + { + if (sightDirection is null || sightResponse is null) + { + if (predictedEnemy.HasValue) + { + direction = DirectionFromVector(predictedEnemy.Value.X, predictedEnemy.Value.Y); + reason = $"Predicted enemy lane -> {direction}; taking lead shot."; + return true; + } + + direction = Direction.North; + reason = string.Empty; + return false; + } + + var currentDir = sightDirection.Value; + var dist = sightResponse.SightedPlayerDistance ?? 99; + if (predictedEnemy.HasValue && dist >= 2) + { + var predictedDir = DirectionFromVector(predictedEnemy.Value.X, predictedEnemy.Value.Y); + if (predictedDir != currentDir) + { + direction = predictedDir; + reason = $"Enemy moving toward {predictedDir}; lead shot instead of static line {currentDir}."; + return true; + } + } + + direction = currentDir; + reason = $"Enemy at {currentDir} ({dist} dist); ranged kill."; + return true; + } + + private bool TryGetAggressiveDashDirection( + (int X, int Y)? predictedEnemy, + KeyValuePair? bestSight, + out Direction direction, + out string reason) + { + if (_nearestEnemy.HasValue) + { + var target = predictedEnemy ?? _nearestEnemy.Value; + var dist = Math.Abs(target.X) + Math.Abs(target.Y); + if (dist >= 2 && dist <= 6) + { + direction = DirectionFromVector(target.X, target.Y); + reason = $"Aggressive gap-close toward enemy at {direction} (predicted distance {dist})."; + return true; + } + } + + if (bestSight.HasValue && bestSight.Value.Value.Executed && bestSight.Value.Value.PlayersInSight > 0) + { + var dist = bestSight.Value.Value.SightedPlayerDistance ?? 99; + if (dist >= 2 && dist <= 5) + { + direction = bestSight.Value.Key; + reason = $"Enemy visible at {direction} ({dist} dist); dash to force close-range combat."; + return true; + } + } + + direction = Direction.North; + reason = string.Empty; + return false; + } + private void MarkVisit(int x, int y, double amount) { var key = (x, y); diff --git a/DDApp/DDApplication/multiplayer.txt b/DDApp/DDApplication/multiplayer.txt new file mode 100644 index 0000000..e22caee --- /dev/null +++ b/DDApp/DDApplication/multiplayer.txt @@ -0,0 +1,288 @@ +Digital Dojo Menü // + + * //Coden lernen + o //Programmier-Roadmap + o //Programmierschule + o //Übungsräume + o //Katas + o //Tutorials + * //Dojo Game + o //Besiege unseren Bot + o //Spieleinstieg + o //Levels & Bots + o //Leaderboard + o //Multiplayer Modus + o //Tipps + * //Programmier-Challenge + * //Bewerben + * //Feedback + * // <#> + Ausloggen + +/ + +/ +/ + +/ + +//Teilen //Facebook // +Instagram //LinkedIn + //E-Mail + + + Der Multiplayer Modus + + + Dojo Game + + + //Multiplayer Modus + +------------------------------------------------------------------------ + +Der Mutiplayer Modus basiert auf einem *Lobby System*. Erstelle eine +Lobby, lade Freunde ein oder spiele mit anderen Usern! Alle +Möglichkeiten stehen dir offen. + +Desto mehr Spieler in einer Lobby sind, mit welchen du zuvor noch nicht +viel gespielt hast, desto mehr Punkte werden dir für eine gute +Platzierung gutgeschrieben. Der Mehrspieler Modus funktioniert mit den +selben API-Aufrufen +wie der Singleplayer - es werden lediglich Bots durch Spieler ersetzt. +Du kannst daher deinen Einzelspieler Code auch für Mehrspieler Matches +verwenden. + + +*Viel Spaß!* + + + // Lobby Manager + +------------------------------------------------------------------------ +Lobby-Key + +/ + +/ +// + +//Starten +//Verlassen +//Schließen +// tikaiz1 ADMIN +// Bot +// Bot +// Bot +// Bot +// Bot + + + // Dein derzeitiger Spielstand + + +Deine Tötungen: *21* + +Deine Tode: *43* + +Dein Leben: *(7/10)* + + + // Server Status + +Server - Adresse: game-dd.countit.at + +Port: 80 (optional) + +Status: Online + + + //Wichtige Informationen + +------------------------------------------------------------------------ + + + //Der Start + +Um eine Lobby zu starten, müssen mindestens die minimale Anzahl an +Spielern in einer Lobby sein - standardmäßig sind dies 2. Wir das Spiel +dann gestartet, wird die Lobby aus der Liste entfernt, da diese dann +nicht mehr benötigt wird. Spielern wird die eigene Lobby nach dem Start +nicht mehr angezeigt. + + + //Dein Algorithmus + +Dein Algorithmus kann schon laufen bevor deine Lobby gestartet wurde. +Dein Code wird, sobald eine Verbindung zu einem Spiel hergestellt wurde, +automatisch Befehle ausführen. +*Vorsicht:* Du darfst deinen Singleplayer Code nicht 1:1 übernehmen. +Wenn du ein Lobby Game spielst, darfst du zuvor keine Singleplayer Game +Instanz erstellen. + + + //Dein Spieler + +Dein Spieler ist im Multiplayer mit den selben Stärken und Fähigkeiten +ausgestattet wie im Singleplayer. In diesem Modus sind alle Spieler +gleich stark - es gibt keine unterschiedlichen Klassen, denn es geht +darum, wer den besten Code hat. + + + //Rangliste + +Unsere Rangliste wird durch Kills und an der Vielzahl der verschiedenen +Gegner in einem Match berechnet. Spielst du gegen neue Gegner oder +andere Spieler, mit welchen du noch nicht all zu viel gespielt hast, +bekommst du mehr Punkte als wenn du beispielsweise immer mit denselben 3 +Freunden spielst. + + + //Public/Private Lobby + +Eine öffentliche Lobby ist für jeden Nutzer zugänglich und kann mit +jedem Spieler gespielt werden. Eine geschlossene und private Lobby ist +nur für jene Spieler zugänglich, welche von einem bereits in der Lobby +vorhandenen Spieler den Lobby-Key o.a. Lobby-ID erhalten haben. Beim +Eintreten in eine private Lobby wird nach diesem Key verlangt. + + + //Lobby Admin + +Standardmäßig ist der Ersteller einer Lobby der Admin für die derzeitige +Sitzung. Verlässt dieser Nutzer die Lobby, wird durch ein Zufallsprinzip +ein neuer Admin ausgewählt. Das Spiel kann nur vom Lobby-Admin +geschlossen oder gestartet werden. + + + //Update Changelog + +Es sind seit dem neuesten Update nur 2 Spieler notwendig, um eine Lobby +zu starten. Zusätzlich wurde die Interkation mit dem User durch +verbesserte Nachrichten und Übersetzungen optimiert. + + + Übung macht den Meister! + +Du möchtest deine Skills noch ein wenig trainieren? Dann führe ein paar +Katas aus! + +Katas // + +/ + +/ + + + Dojo - virtueller Übungsraum + +Löse die Dojo-Aufgaben und werde Programmier-Profi! + +Zu den Dojos // +/ + +/ + + + Ultimative Coding-Roadmap + +Unsere Roadmap weist dir den Weg zum Coding-Profi! + +Mehr erfahren // +/ + +/ + + + Bewerbung bei COUNT IT + +Starte deine Karriere als Softwareentwickler*in bei COUNT IT. + +Zum Formular! // + + + Über Digital Dojo + +Das Digital Dojo ist der virtuelle Übungsraum von COUNT IT. + +Angehende Programmierer*innen, Code-Neulinge, Wiedereinsteiger*innen und +Fortgeschrittene finden hier das nötige Rüstzeug für ihre Karriere. + +Du möchtest deine Lehre bei COUNT IT starten? Dann bist du hier richtig +- besiege deine Gegner im Dojo Game und sichere dir deine Lehrstelle! + +Inspire your career. + + + Navigation + +Programmieren lernen News + +Programmierschule Partner + +Programmier-Challenge +Dojo Game + + +Datenschutz Karriere @ COUNT IT + +Impressum COUNT IT +Softwarehouse + + + Newsletter abonnieren + +Der COUNT IT Newsletter liefert viermal jährlich interessante +Neuigkeiten über das Unternehmen. Gleich anfordern! + +E-Mail-Adresse +Senden + +// // // + +// + +COUNT IT Group verwendet Cookies, um die Website bestmöglich an die +Bedürfnisse der User anpassen zu können. + + * Google + * Microsoft + * Ahrefs + * Statistik + +Akzeptieren +Individuelle EinstellungenIndividuelle Einstellungen ausblenden | +Datenschutz­erklärung + +Google + +Google Analytics und Optimize aggregiert Daten über unsere Besucher und +ihr Verhalten auf unserer Website. Wir nutzen diese Informationen zur +Verbesserung unserer Seite. + +Microsoft + +Microsoft aggregiert Daten über unsere Besucher und ihr Verhalten auf +unserer Website. Wir nutzen diese Informationen zur Verbesserung unserer +Seite. + +Ahrefs + +Ahrefs aggregiert Daten über unsere Besucher und ihr Verhalten auf +unserer Website. Wir nutzen diese Informationen zur Verbesserung unserer +Seite. + +Statistik + +Es werden grundlegend notwendige, anonymisierte Userdaten aufgezeichnet, +welche nur von uns für Auswertungen verwendet werden! + +Cookie Entscheidung speichern