图标识别应用两则 (#2154)

This commit is contained in:
FishmanTheMurloc
2025-09-06 01:17:05 +08:00
committed by GitHub
parent 2f55dd0502
commit 37fcde080f
32 changed files with 384 additions and 244 deletions

View File

@@ -14,6 +14,10 @@ public partial class AutoArtifactSalvageConfig : ObservableObject
Output = hasATK && hasDEF;
})(ArtifactStat);";
// JavaScript
[ObservableProperty]
private string _artifactSetFilter = "";
// 正则表达式
[Obsolete]
[ObservableProperty]

View File

@@ -7,6 +7,7 @@ using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.GetGridIcons;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers;
@@ -16,6 +17,7 @@ using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.ML.OnnxRuntime;
using OpenCvSharp;
using System;
using System.Collections.Frozen;
@@ -51,6 +53,8 @@ public class AutoArtifactSalvageTask : ISoloTask
private readonly string? javaScript;
private readonly string? artifactSetFilter;
private readonly int? maxNumToCheck;
private readonly RecognitionFailurePolicy? recognitionFailurePolicy;
@@ -65,6 +69,7 @@ public class AutoArtifactSalvageTask : ISoloTask
{
this.star = param.Star;
this.javaScript = param.JavaScript;
this.artifactSetFilter = param.ArtifactSetFilter;
this.maxNumToCheck = param.MaxNumToCheck;
this.recognitionFailurePolicy = param.RecognitionFailurePolicy;
this.logger = logger ?? App.GetLogger<AutoArtifactSalvageTask>();
@@ -228,7 +233,11 @@ public class AutoArtifactSalvageTask : ISoloTask
}
}
Bv.ClickWhiteConfirmButton(ra4);
using var quickSelectConfirmBtn = ra4.Find(ElementAssets.Instance.BtnWhiteConfirm);
if (quickSelectConfirmBtn.IsExist())
{
quickSelectConfirmBtn.Click();
}
await Delay(1500, ct);
@@ -264,6 +273,38 @@ public class AutoArtifactSalvageTask : ISoloTask
// 分解5星
if (javaScript != null)
{
if (!string.IsNullOrWhiteSpace(this.artifactSetFilter))
{
// 其实是点击筛选按钮……快速选择确认的这个按钮正好和筛选按钮位置重合,摆烂直接用了
quickSelectConfirmBtn.Click();
await Delay(400, ct);
// 点击所属套装
ra5.ClickTo(315, 205);
await Delay(1000, ct);
// 遍历套装Grid勾选套装
using InferenceSession session = GridIconsAccuracyTestTask.LoadModel(out Dictionary<string, float[]> prototypes);
ArtifactSetFilterScreen gridScreen = new ArtifactSetFilterScreen(new GridParams(new Rect(40, 100, 1300, 852), 2, 3, 40, 40, 0.024), this.logger, this.ct);
await foreach (ImageRegion itemRegion in gridScreen)
{
using Mat img125 = GetGridIconsTask.CropResizeArtifactSetFilterGridIcon(itemRegion);
(string predName, _) = GridIconsAccuracyTestTask.Infer(img125, session, prototypes);
if (this.artifactSetFilter.Contains(predName))
{
itemRegion.Click();
await Delay(100, ct);
}
}
// 点击确认筛选
using var confirmFilterBtnRegion = CaptureToRectArea();
Bv.ClickWhiteConfirmButton(confirmFilterBtnRegion);
await Delay(1500, ct);
// 点击确认
using var confirmBtnRegion = CaptureToRectArea();
Bv.ClickWhiteConfirmButton(confirmBtnRegion);
await Delay(600, ct);
}
// 逐一点选查看面板筛选
await Salvage5Star();
logger.LogInformation("筛选完毕,请复查并手动分解");
}

View File

@@ -9,16 +9,18 @@ namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage
{
public class AutoArtifactSalvageTaskParam : BaseTaskParam<AutoArtifactSalvageTask>
{
public AutoArtifactSalvageTaskParam(int star, string? javaScript, int? maxNumToCheck, RecognitionFailurePolicy? recognitionFailurePolicy, CultureInfo? cultureInfo = null, IStringLocalizer<AutoArtifactSalvageTask>? stringLocalizer = null) : base(cultureInfo, stringLocalizer)
public AutoArtifactSalvageTaskParam(int star, string? javaScript, string? artifactSetFilter, int? maxNumToCheck, RecognitionFailurePolicy? recognitionFailurePolicy, CultureInfo? cultureInfo = null, IStringLocalizer<AutoArtifactSalvageTask>? stringLocalizer = null) : base(cultureInfo, stringLocalizer)
{
Star = star;
JavaScript = javaScript;
ArtifactSetFilter = artifactSetFilter;
MaxNumToCheck = maxNumToCheck;
RecognitionFailurePolicy = recognitionFailurePolicy;
}
public int Star { get; set; }
public string? JavaScript { get; set; }
public string? ArtifactSetFilter { get; set; }
public int? MaxNumToCheck { get; set; }
public RecognitionFailurePolicy? RecognitionFailurePolicy { get; set; }
}

View File

@@ -1312,6 +1312,6 @@ public class AutoDomainTask : ISoloTask
star = 4;
}
await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star, javaScript: null, maxNumToCheck: null, recognitionFailurePolicy: null)).Start(_ct);
await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star, javaScript: null, artifactSetFilter: null, maxNumToCheck: null, recognitionFailurePolicy: null)).Start(_ct);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,30 +1,35 @@
using BetterGenshinImpact.GameTask.Common;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BehaviourTree;
using BehaviourTree.Composites;
using BehaviourTree.FluentBuilder;
using BehaviourTree;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.View.Drawable;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using Vanara.PInvoke;
using System.Linq;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
using BetterGenshinImpact.GameTask.Common.Job;
using Fischless.WindowsInput;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Core.Recognition.ONNX;
using static Vanara.PInvoke.User32;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using System.Globalization;
using Microsoft.Extensions.Localization;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.GetGridIcons;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.View.Drawable;
using Compunet.YoloSharp;
using Fischless.WindowsInput;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.ML.OnnxRuntime;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Vanara.PInvoke;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.GameTask.AutoFishing
{
@@ -51,6 +56,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
this._ct = ct;
IOcrService ocrService = OcrFactory.Paddle;
using InferenceSession session = GridIconsAccuracyTestTask.LoadModel(out Dictionary<string, float[]> prototypes);
Blackboard blackboard = new Blackboard(_predictor, this.Sleep, AutoFishingAssets.Instance);
@@ -64,7 +70,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
.PushLeaf(() => new TurnAround("转圈圈调整视角", blackboard, _logger, param.SaveScreenshotOnKeyTick, input))
.PushLeaf(() => new FindFishTimeout("找到鱼", 20, blackboard, _logger, param.SaveScreenshotOnKeyTick))
.End()
.PushLeaf(() => new EnterFishingMode("进入钓鱼模式", blackboard, _logger, param.SaveScreenshotOnKeyTick, input, cultureInfo: param.GameCultureInfo, stringLocalizer: param.StringLocalizer))
.PushLeaf(() => new EnterFishingMode("进入钓鱼模式", blackboard, _logger, param.SaveScreenshotOnKeyTick, input, session, prototypes, cultureInfo: param.GameCultureInfo, stringLocalizer: param.StringLocalizer))
.UntilFailed(@"\")
.Sequence("一直钓鱼直到没鱼")
.AlwaysSucceed(@"\")
@@ -79,7 +85,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
.End()
.PushLeaf(() => new FindFishTimeout("确认初始状态和找到鱼", 10, blackboard, _logger, param.SaveScreenshotOnKeyTick))
.End()
.PushLeaf(() => new ChooseBait("选择鱼饵", blackboard, _logger, param.SaveScreenshotOnKeyTick, TaskContext.Instance().SystemInfo, input))
.PushLeaf(() => new ChooseBait("选择鱼饵", blackboard, _logger, param.SaveScreenshotOnKeyTick, TaskContext.Instance().SystemInfo, input, session, prototypes))
.MySimpleParallel("抛竿直到成功或出错", policy: SimpleParallelPolicy.OnlyOneMustSucceed)
.UntilSuccess("重复抛竿")
.Sequence("-")
@@ -345,6 +351,8 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
private readonly IInputSimulator input;
private readonly Blackboard blackboard;
private readonly InferenceSession session;
private readonly Dictionary<string, float[]> prototypes;
private readonly TimeProvider timeProvider;
private DateTimeOffset? pressFWaitEndTime;
private DateTimeOffset? clickWhiteConfirmButtonWaitEndTime;
@@ -352,11 +360,13 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
private readonly string fishingLocalizedString;
public EnterFishingMode(string name, Blackboard blackboard, ILogger logger, bool saveScreenshotOnTerminate,
IInputSimulator input, TimeProvider? timeProvider = null, CultureInfo? cultureInfo = null, IStringLocalizer? stringLocalizer = null) : base(name,
IInputSimulator input, InferenceSession session, Dictionary<string, float[]> prototypes, TimeProvider? timeProvider = null, CultureInfo? cultureInfo = null, IStringLocalizer? stringLocalizer = null) : base(name,
logger, saveScreenshotOnTerminate)
{
this.blackboard = blackboard;
this.input = input;
this.session = session;
this.prototypes = prototypes;
this.timeProvider = timeProvider ?? TimeProvider.System;
this.fishingLocalizedString = stringLocalizer == null ? "钓鱼" : stringLocalizer.WithCultureGet(cultureInfo, "钓鱼");
}
@@ -380,7 +390,17 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
clickWhiteConfirmButtonWaitEndTime < timeProvider.GetLocalNow()) &&
Bv.ClickWhiteConfirmButton(imageRegion))
{
logger.LogInformation("点击开始钓鱼");
Mat subMat = imageRegion.SrcMat.SubMat(new Rect((int)(0.824 * imageRegion.Width), (int)(0.669 * imageRegion.Height), (int)(0.065 * imageRegion.Width), (int)(0.065 * imageRegion.Width)));
using Mat resized = subMat.Resize(new Size(125, 125));
(string predName, _) = GridIconsAccuracyTestTask.Infer(resized, this.session, this.prototypes);
if (predName.TryGetEnumValueFromDescription(out this.blackboard.selectedBait))
{
logger.LogInformation("点击开始钓鱼,当前鱼饵为{bait}", this.blackboard.selectedBait.Value.GetDescription());
}
else
{
logger.LogInformation("点击开始钓鱼,当前鱼饵未识别");
}
this.blackboard.pitchReset = true;

View File

@@ -1,26 +1,30 @@
using BehaviourTree;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
using BetterGenshinImpact.GameTask.GetGridIcons;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using BetterGenshinImpact.View.Drawable;
using Compunet.YoloSharp;
using Fischless.WindowsInput;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using Microsoft.ML.OnnxRuntime;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using static Vanara.PInvoke.User32;
using Color = System.Drawing.Color;
using Pen = System.Drawing.Pen;
using System.Linq;
using Fischless.WindowsInput;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.Model;
using System.Globalization;
using Compunet.YoloSharp;
using Microsoft.Extensions.Localization;
namespace BetterGenshinImpact.GameTask.AutoFishing
{
@@ -66,11 +70,11 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
blackboard.fishpond = fishpond;
string[] chooseBaitfailuresIgnoredBaits = blackboard.chooseBaitFailures.GroupBy(f => f).Where(g => g.Count() >= ChooseBait.MAX_FAILED_TIMES).Select(g => g.Key).ToArray();
string[] throwRodNoTargetFishfailuresIgnoredBaits = blackboard.throwRodNoBaitFishFailures.GroupBy(f => f).Where(g => g.Count() >= ThrowRod.MAX_NO_BAIT_FISH_TIMES).Select(g => g.Key).ToArray();
BaitType[] chooseBaitfailuresIgnoredBaits = blackboard.chooseBaitFailures.GroupBy(f => f).Where(g => g.Count() >= ChooseBait.MAX_FAILED_TIMES).Select(g => g.Key).ToArray();
BaitType[] throwRodNoTargetFishfailuresIgnoredBaits = blackboard.throwRodNoBaitFishFailures.GroupBy(f => f).Where(g => g.Count() >= ThrowRod.MAX_NO_BAIT_FISH_TIMES).Select(g => g.Key).ToArray();
logger.LogInformation("定位到鱼塘:" + string.Join('、', fishpond.Fishes.GroupBy(f => f.FishType)
.Select(g => $"{g.Key.ChineseName}{g.Count()}条" + ((chooseBaitfailuresIgnoredBaits.Contains(g.Key.BaitName) || throwRodNoTargetFishfailuresIgnoredBaits.Contains(g.Key.BaitName)) ? "(忽略)" : ""))
.Select(g => $"{g.Key.ChineseName}{g.Count()}条" + ((chooseBaitfailuresIgnoredBaits.Contains(g.Key.BaitType) || throwRodNoTargetFishfailuresIgnoredBaits.Contains(g.Key.BaitType)) ? "(忽略)" : ""))
));
int i = 0;
foreach (var fish in fishpond.Fishes)
@@ -80,8 +84,8 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
blackboard.Sleep(1000);
drawContent.ClearAll();
if (blackboard.fishpond.Fishes.Any(f =>
!chooseBaitfailuresIgnoredBaits.Contains(f.FishType.BaitName)
&& !throwRodNoTargetFishfailuresIgnoredBaits.Contains(f.FishType.BaitName)))
!chooseBaitfailuresIgnoredBaits.Contains(f.FishType.BaitType)
&& !throwRodNoTargetFishfailuresIgnoredBaits.Contains(f.FishType.BaitType)))
{
return BehaviourStatus.Succeeded;
}
@@ -100,6 +104,8 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
private readonly ISystemInfo systemInfo;
private readonly IInputSimulator input;
private readonly InferenceSession session;
private readonly Dictionary<string, float[]> prototypes;
private readonly Blackboard blackboard;
private readonly TimeProvider timeProvider;
private DateTimeOffset? chooseBaitUIOpenWaitEndTime; // 等待选鱼饵界面出现并尝试找鱼饵的结束时间
@@ -110,11 +116,13 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
/// </summary>
/// <param name="name"></param>
/// <param name="autoFishingTrigger"></param>
public ChooseBait(string name, Blackboard blackboard, ILogger logger, bool saveScreenshotOnTerminat, ISystemInfo systemInfo, IInputSimulator input, TimeProvider? timeProvider = null) : base(name, logger, saveScreenshotOnTerminat)
public ChooseBait(string name, Blackboard blackboard, ILogger logger, bool saveScreenshotOnTerminat, ISystemInfo systemInfo, IInputSimulator input, InferenceSession session, Dictionary<string, float[]> prototypes, TimeProvider? timeProvider = null) : base(name, logger, saveScreenshotOnTerminat)
{
this.blackboard = blackboard;
this.systemInfo = systemInfo;
this.input = input;
this.session = session;
this.prototypes = prototypes;
this.timeProvider = timeProvider ?? TimeProvider.System;
}
@@ -122,7 +130,7 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
if (this.Status == BehaviourStatus.Ready)
{
if (blackboard.fishpond.Fishes.Any(f => f.FishType.BaitName == blackboard.selectedBaitName)) // 如果该种鱼没钓完就不用换饵
if (blackboard.fishpond.Fishes.Any(f => f.FishType.BaitType == blackboard.selectedBait)) // 如果该种鱼没钓完就不用换饵
{
return BehaviourStatus.Succeeded;
}
@@ -136,72 +144,86 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
return BehaviourStatus.Running;
}
blackboard.selectedBaitName = blackboard.fishpond.Fishes.GroupBy(f => f.FishType.BaitName)
blackboard.selectedBait = blackboard.fishpond.Fishes.GroupBy(f => f.FishType.BaitType)
.Where(b => !blackboard.chooseBaitFailures.GroupBy(f => f).Where(g => g.Count() >= MAX_FAILED_TIMES).Any(g => g.Key == b.Key)) // 不能是已经失败两次的饵
.OrderByDescending(g => g.Count()).First().Key; // 选择最多鱼吃的饵料
logger.LogInformation("选择鱼饵 {Text}", BaitType.FromName(blackboard.selectedBaitName).ChineseName);
logger.LogInformation("选择鱼饵 {Text}", blackboard.selectedBait.GetDescription());
// 寻找鱼饵
var ro = new RecognitionObject
{
Name = "ChooseBait",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFishing", $"bait\\{blackboard.selectedBaitName}.png", systemInfo),
Threshold = 0.8,
Use3Channels = true,
DrawOnWindow = false
}.InitTemplate();
using ImageRegion singleRowGrid = imageRegion.DeriveCrop(0.28 * imageRegion.Width, 0.37 * imageRegion.Height, 0.45 * imageRegion.Width, 0.22 * imageRegion.Height);
using Mat grey = singleRowGrid.SrcMat.CvtColor(ColorConversionCodes.BGR2GRAY);
using Mat canny = grey.Canny(20, 40);
using var resRa = imageRegion.Find(ro);
if (resRa.IsEmpty())
{
if (timeProvider.GetLocalNow() >= chooseBaitUIOpenWaitEndTime)
Cv2.FindContours(canny, out Point[][] contours, out _, RetrievalModes.External, ContourApproximationModes.ApproxSimple, null);
contours = contours
.Where(c =>
{
logger.LogWarning("没有找到目标鱼饵");
input.Keyboard.KeyPress(VK.VK_ESCAPE);
Rect r = Cv2.BoundingRect(c);
if (r.Width < 0.065 * imageRegion.Width * 0.80) // 剔除太小的
{
return false;
}
if (r.Height == 0)
{
return false;
}
return Math.Abs((float)r.Width / r.Height - 0.81) < 0.05; // 按形状筛选
}).ToArray();
IEnumerable<Rect> boxes = contours.Select(Cv2.BoundingRect);
foreach (Rect box in boxes)
{
using ImageRegion resRa = singleRowGrid.DeriveCrop(box);
using Mat img125 = resRa.SrcMat.GetGridIcon();
(string predName, _) = GridIconsAccuracyTestTask.Infer(img125, this.session, this.prototypes);
if (predName == blackboard.selectedBait.GetDescription())
{
resRa.Click();
blackboard.Sleep(700);
// 可能重复点击,所以固定界面点击下
imageRegion.ClickTo((int)(imageRegion.Width * 0.675), (int)(imageRegion.Height / 3d));
blackboard.Sleep(200);
// 点击确定
using var ra = imageRegion.Find(new RecognitionObject
{
Name = "BtnWhiteConfirm",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "btn_white_confirm.png", systemInfo),
Use3Channels = true
}.InitTemplate());
if (ra.IsExist())
{
ra.Click();
}
blackboard.chooseBaitUIOpening = false;
logger.LogInformation("退出换饵界面");
blackboard.Sleep(500); // 等待界面切换
blackboard.chooseBaitFailures.Add(blackboard.selectedBaitName);
if (blackboard.chooseBaitFailures.Count(f => f == blackboard.selectedBaitName) >= MAX_FAILED_TIMES)
{
logger.LogWarning($"本次将忽略{BaitType.FromName(blackboard.selectedBaitName).ChineseName}");
}
blackboard.selectedBaitName = string.Empty;
return BehaviourStatus.Failed;
return BehaviourStatus.Succeeded;
}
else
}
if (timeProvider.GetLocalNow() >= chooseBaitUIOpenWaitEndTime)
{
logger.LogWarning("没有找到目标鱼饵");
input.Keyboard.KeyPress(VK.VK_ESCAPE);
blackboard.chooseBaitUIOpening = false;
logger.LogInformation("退出换饵界面");
blackboard.chooseBaitFailures.Add(blackboard.selectedBait.Value);
if (blackboard.chooseBaitFailures.Count(f => f == blackboard.selectedBait) >= MAX_FAILED_TIMES)
{
return BehaviourStatus.Running;
logger.LogWarning($"本次将忽略{blackboard.selectedBait.GetDescription()}");
}
blackboard.selectedBait = null;
return BehaviourStatus.Failed;
}
else
{
resRa.Click();
blackboard.Sleep(700);
// 可能重复点击,所以固定界面点击下
imageRegion.ClickTo((int)(imageRegion.Width * 0.675), (int)(imageRegion.Height / 3d));
blackboard.Sleep(200);
// 点击确定
using var ra = imageRegion.Find(new RecognitionObject
{
Name = "BtnWhiteConfirm",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "btn_white_confirm.png", systemInfo),
Use3Channels = true
}.InitTemplate());
if (ra.IsExist())
{
ra.Click();
}
blackboard.chooseBaitUIOpening = false;
logger.LogInformation("退出换饵界面");
blackboard.Sleep(500); // 等待界面切换
return BehaviourStatus.Running;
}
return BehaviourStatus.Succeeded;
}
}
@@ -374,10 +396,10 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
// 找到落点最近的鱼
currentFish = null;
string[] ignoredBaits = blackboard.throwRodNoBaitFishFailures.GroupBy(f => f).Where(g => g.Count() >= MAX_NO_BAIT_FISH_TIMES).Select(g => g.Key).ToArray();
BaitType[] ignoredBaits = blackboard.throwRodNoBaitFishFailures.GroupBy(f => f).Where(g => g.Count() >= MAX_NO_BAIT_FISH_TIMES).Select(g => g.Key).ToArray();
var list = fishpond.Fishes
.Where(f => !ignoredBaits.Contains(f.FishType.BaitName)) // 不能是已经失败两次的饵;
.Where(f => f.FishType.BaitName == blackboard.selectedBaitName).OrderByDescending(f => f.Confidence)
.Where(f => !ignoredBaits.Contains(f.FishType.BaitType)) // 不能是已经失败两次的饵;
.Where(f => f.FishType.BaitType == blackboard.selectedBait).OrderByDescending(f => f.Confidence)
.ToList();
if (list.Count > 0)
{
@@ -393,13 +415,17 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
// 没有找到鱼饵适用鱼,重新选择鱼饵
blackboard.throwRodNoBaitFish = true;
blackboard.throwRodNoBaitFishFailures.Add(blackboard.selectedBaitName);
if (blackboard.throwRodNoBaitFishFailures.Count(f => f == blackboard.selectedBaitName) >= MAX_NO_BAIT_FISH_TIMES)
if (blackboard.selectedBait == null)
{
logger.LogWarning($"本次将忽略{BaitType.FromName(blackboard.selectedBaitName).ChineseName}");
throw new NullReferenceException();
}
blackboard.throwRodNoBaitFishFailures.Add(blackboard.selectedBait.Value);
if (blackboard.throwRodNoBaitFishFailures.Count(f => f == blackboard.selectedBait) >= MAX_NO_BAIT_FISH_TIMES)
{
logger.LogWarning("本次将忽略{bait}", blackboard.selectedBait.GetDescription());
}
blackboard.selectedBaitName = string.Empty;
blackboard.selectedBait = null;
logger.LogInformation("没有找到鱼饵适用鱼");
input.Mouse.LeftButtonUp();
blackboard.Sleep(2000);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
@@ -18,9 +18,9 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
public bool abort = false;
/// <summary>
/// 已选择的鱼饵
/// 已选择的鱼饵类型
/// </summary>
public string selectedBaitName = string.Empty;
public BaitType? selectedBait = null;
/// <summary>
/// 鱼塘
@@ -44,9 +44,9 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
/// <summary>
/// 抛竿无目标鱼失败列表
/// 失败一次就加入一次鱼饵,列表中同名鱼饵的数量代表该种失败了几次
/// 失败一次就加入一次鱼饵类型,列表中同名鱼饵的数量代表该种失败了几次
/// </summary>
public List<string> throwRodNoBaitFishFailures = new List<string>();
public List<BaitType> throwRodNoBaitFishFailures = new List<BaitType>();
/// <summary>
/// 拉条位置的识别框
@@ -61,9 +61,9 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
/// <summary>
/// 选鱼饵失败列表
/// 失败一次就加入一次鱼饵,列表中同名鱼饵的数量代表该种失败了几次
/// 失败一次就加入一次鱼饵类型,列表中同名鱼饵的数量代表该种失败了几次
/// </summary>
public List<string> chooseBaitFailures = new List<string>();
public List<BaitType> chooseBaitFailures = new List<BaitType>();
/// <summary>
/// 镜头俯仰是否被行为重置
@@ -104,12 +104,12 @@ namespace BetterGenshinImpact.GameTask.AutoFishing
{
abort = false;
throwRodNoTargetTimes = 0;
throwRodNoBaitFishFailures = new List<string>();
throwRodNoBaitFishFailures = new List<BaitType>();
fishBoxRect = default;
chooseBaitUIOpening = false;
chooseBaitFailures = new List<string>();
chooseBaitFailures = new List<BaitType>();
pitchReset = true;
selectedBaitName = string.Empty;
selectedBait = null;
}
}
}

View File

@@ -1,54 +1,25 @@
using System.Collections.Generic;
using System.ComponentModel;
namespace BetterGenshinImpact.GameTask.AutoFishing.Model;
public class BaitType
public enum BaitType
{
public static readonly BaitType FruitPasteBait = new("fruit paste bait", "果酿饵");
public static readonly BaitType RedrotBait = new("redrot bait", "赤糜饵");
public static readonly BaitType FalseWormBait = new("false worm bait", "蠕虫假饵");
public static readonly BaitType FakeFlyBait = new("fake fly bait", "飞蝇假饵");
public static readonly BaitType SugardewBait = new("sugardew bait", "甘露饵");
public static readonly BaitType SourBait = new("sour bait", "酸桔饵");
public static readonly BaitType FlashingMaintenanceMekBait = new("flashing maintenance mek bait", "维护机关频闪诱饵");
public static readonly BaitType SpinelgrainBait = new("spinelgrain bait", "澄晶果粒饵");
public static readonly BaitType EmberglowBait = new("emberglow bait", "温火饵");
public static IEnumerable<BaitType> Values
{
get
{
yield return FruitPasteBait;
yield return RedrotBait;
yield return FalseWormBait;
yield return FakeFlyBait;
yield return SugardewBait;
yield return SourBait;
yield return FlashingMaintenanceMekBait;
yield return SpinelgrainBait;
yield return EmberglowBait;
}
}
public string Name { get; private set; }
public string ChineseName { get; private set; }
private BaitType(string name, string chineseName)
{
Name = name;
ChineseName = chineseName;
}
public static BaitType FromName(string name)
{
foreach (var type in Values)
{
if (type.Name == name)
{
return type;
}
}
throw new KeyNotFoundException($"BaitType {name} not found");
}
[Description("果酿饵")]
FruitPasteBait,
[Description("赤糜饵")]
RedrotBait,
[Description("蠕虫假饵")]
FalseWormBait,
[Description("飞蝇假饵")]
FakeFlyBait,
[Description("甘露饵")]
SugardewBait,
[Description("酸桔饵")]
SourBait,
[Description("维护机关频闪诱饵")]
FlashingMaintenanceMekBait,
[Description("澄晶果粒饵")]
SpinelgrainBait,
[Description("温火饵")]
EmberglowBait
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
namespace BetterGenshinImpact.GameTask.AutoFishing.Model;
@@ -9,27 +9,27 @@ namespace BetterGenshinImpact.GameTask.AutoFishing.Model;
/// </summary>
public class BigFishType
{
public static readonly BigFishType Medaka = new("medaka", "fruit paste bait", "花鳉", 0);
public static readonly BigFishType LargeMedaka = new("large medaka", "fruit paste bait", "大花鳉", 1);
public static readonly BigFishType Stickleback = new("stickleback", "redrot bait", "棘鱼", 2);
public static readonly BigFishType Koi = new("koi", "fake fly bait", "假龙", 3);
public static readonly BigFishType KoiHead = new("koi head", "fake fly bait", "假龙头", 3);
public static readonly BigFishType Butterflyfish = new("butterflyfish", "false worm bait", "蝶鱼", 4);
public static readonly BigFishType Pufferfish = new("pufferfish", "fake fly bait", "炮鲀", 5);
public static readonly BigFishType Medaka = new("medaka", BaitType.FruitPasteBait, "花鳉", 0);
public static readonly BigFishType LargeMedaka = new("large medaka", BaitType.FruitPasteBait, "大花鳉", 1);
public static readonly BigFishType Stickleback = new("stickleback", BaitType.RedrotBait, "棘鱼", 2);
public static readonly BigFishType Koi = new("koi", BaitType.FakeFlyBait, "假龙", 3);
public static readonly BigFishType KoiHead = new("koi head", BaitType.FakeFlyBait, "假龙头", 3);
public static readonly BigFishType Butterflyfish = new("butterflyfish", BaitType.FalseWormBait, "蝶鱼", 4);
public static readonly BigFishType Pufferfish = new("pufferfish", BaitType.FakeFlyBait, "炮鲀", 5);
public static readonly BigFishType Ray = new("ray", "fake fly bait", "鳐", 6);
public static readonly BigFishType Ray = new("ray", BaitType.FakeFlyBait, "鳐", 6);
// public static readonly BigFishType FormaloRay = new("formalo ray", "fake fly bait", "佛玛洛鳐");
// public static readonly BigFishType DivdaRay = new("divda ray", "fake fly bait", "迪芙妲鳐");
public static readonly BigFishType Angler = new("angler", "sugardew bait", "角鲀", 7);
public static readonly BigFishType AxeMarlin = new("axe marlin", "sugardew bait", "斧枪鱼", 8);
public static readonly BigFishType HeartfeatherBass = new("heartfeather bass", "sour bait", "心羽鲈", 9);
public static readonly BigFishType MaintenanceMek = new("maintenance mek", "flashing maintenance mek bait", "维护机关", 10);
public static readonly BigFishType Unihornfish = new("unihornfish", "spinelgrain bait", "独角鱼", 10);
public static readonly BigFishType Sunfish = new("sunfish", "spinelgrain bait", "翻车鲀", 7);
public static readonly BigFishType Rapidfish = new("rapidfish", "spinelgrain bait", "斗士急流鱼", 9);
public static readonly BigFishType PhonyUnihornfish = new("phony unihornfish", "emberglow bait", "燃素独角鱼", 10);
public static readonly BigFishType MagmaRapidfish = new("magma rapidfish", "emberglow bait", "炽岩斗士急流鱼", 9);
// public static readonly BigFishType FormaloRay = new("formalo ray", "飞蝇假饵", "佛玛洛鳐");
// public static readonly BigFishType DivdaRay = new("divda ray", "飞蝇假饵", "迪芙妲鳐");
public static readonly BigFishType Angler = new("angler", BaitType.SugardewBait, "角鲀", 7);
public static readonly BigFishType AxeMarlin = new("axe marlin", BaitType.SugardewBait, "斧枪鱼", 8);
public static readonly BigFishType HeartfeatherBass = new("heartfeather bass", BaitType.SourBait, "心羽鲈", 9);
public static readonly BigFishType MaintenanceMek = new("maintenance mek", BaitType.FlashingMaintenanceMekBait, "维护机关", 10);
public static readonly BigFishType Unihornfish = new("unihornfish", BaitType.SpinelgrainBait, "独角鱼", 10);
public static readonly BigFishType Sunfish = new("sunfish", BaitType.SpinelgrainBait, "翻车鲀", 7);
public static readonly BigFishType Rapidfish = new("rapidfish", BaitType.SpinelgrainBait, "斗士急流鱼", 9);
public static readonly BigFishType PhonyUnihornfish = new("phony unihornfish", BaitType.EmberglowBait, "燃素独角鱼", 10);
public static readonly BigFishType MagmaRapidfish = new("magma rapidfish", BaitType.EmberglowBait, "炽岩斗士急流鱼", 9);
public static IEnumerable<BigFishType> Values
@@ -59,15 +59,15 @@ public class BigFishType
}
public string Name { get; private set; }
public string BaitName { get; private set; }
public BaitType BaitType { get; private set; }
public string ChineseName { get; private set; }
public int NetIndex { get; private set; }
private BigFishType(string name, string baitName, string chineseName, int netIndex)
private BigFishType(string name, BaitType baitType, string chineseName, int netIndex)
{
Name = name;
BaitName = baitName;
BaitType = baitType;
ChineseName = chineseName;
NetIndex = netIndex;
}

View File

@@ -609,7 +609,7 @@ public class AutoStygianOnslaughtTask : ISoloTask
star = 4;
}
await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star, javaScript: null, maxNumToCheck: null, recognitionFailurePolicy: null)).Start(_ct);
await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star, javaScript: null, artifactSetFilter: null, maxNumToCheck: null, recognitionFailurePolicy: null)).Start(_ct);
}

View File

@@ -3,6 +3,7 @@ using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers.Extensions;
@@ -198,13 +199,8 @@ public class GetGridIconsTask : ISoloTask
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
double scale = TaskContext.Instance().SystemInfo.AssetScale;
double width = 60;
double height = 60; // 宽高缩放似乎不一致似乎在2.05:2.15之间,但不知道怎么测定
Rect iconRect = new Rect((int)(itemRegion.Width / 2 - 237 * scale - width / 2), (int)(itemRegion.Height / 2 - height / 2), (int)width, (int)height);
Mat crop = itemRegion.SrcMat.SubMat(iconRect);
using Mat resize = crop.Resize(new Size(125, 125));
resize.ToBitmap().Save(fs, System.Drawing.Imaging.ImageFormat.Png);
using Mat img125 = CropResizeArtifactSetFilterGridIcon(itemRegion);
img125.ToBitmap().Save(fs, System.Drawing.Imaging.ImageFormat.Png);
}
logger.LogInformation("图片保存成功:{Text}", fileName);
}
@@ -230,6 +226,16 @@ public class GetGridIconsTask : ISoloTask
}
}
internal static Mat CropResizeArtifactSetFilterGridIcon(ImageRegion itemRegion, ISystemInfo? systemInfo = null)
{
double scale = (systemInfo ?? TaskContext.Instance().SystemInfo).AssetScale;
double width = 60;
double height = 60; // 宽高缩放似乎不一致似乎在2.05:2.15之间,但不知道怎么测定
Rect iconRect = new Rect((int)(itemRegion.Width / 2 - 237 * scale - width / 2), (int)(itemRegion.Height / 2 - height / 2), (int)width, (int)height);
Mat crop = itemRegion.SrcMat.SubMat(iconRect);
return crop.Resize(new Size(125, 125));
}
/// <summary>
/// OCR检测★字符很不稳定因此用cv
/// 非常简陋的色彩检测,请传入聚焦的图像,勿带入可能的干扰

View File

@@ -111,7 +111,6 @@ public class GridIconsAccuracyTestTask : ISoloTask
{
itemRegion.Click();
Task task1 = Delay(300, ct);
var sadf = task1.Status;
// 用模型推理得到的结果
Task<(string, int)> task2 = Task.Run(() =>
@@ -161,7 +160,7 @@ public class GridIconsAccuracyTestTask : ISoloTask
/// <param name="mat"></param>
/// <param name="session"></param>
/// <param name="prototypes"></param>
/// <returns></returns>
/// <returns>(预测名称, 预测星级)</returns>
/// <exception cref="Exception"></exception>
public static (string, int) Infer(Mat mat, InferenceSession session, Dictionary<string, float[]> prototypes)
{

View File

@@ -215,7 +215,7 @@ namespace BetterGenshinImpact.GameTask.Model.GameUI
{
return false;
}
return Math.Abs((float)r.Width / r.Height - 0.8) < 0.05; // 按形状筛选
return Math.Abs((float)r.Width / r.Height - 0.81) < 0.05; // 按形状筛选
}).ToArray();
IEnumerable<Rect> boxes = contours.Select(Cv2.BoundingRect);

View File

@@ -1,8 +1,10 @@
using BetterGenshinImpact.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using BetterGenshinImpact.Model;
using System.Reflection;
namespace BetterGenshinImpact.Helpers.Extensions;
@@ -18,6 +20,33 @@ public static class EnumExtensions
?.Description ?? value.ToString();
}
public static bool TryGetEnumValueFromDescription<T>(this string description, [NotNullWhen(true)] out T? result) where T : struct, Enum
{
result = null;
if (string.IsNullOrEmpty(description))
return false;
var type = typeof(T);
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
is DescriptionAttribute attribute)
{
if (attribute.Description.Equals(description, StringComparison.OrdinalIgnoreCase))
{
var value = field.GetValue(null);
if (value is T enumValue)
{
result = enumValue;
return true;
}
return false;
}
}
}
return false;
}
public static int GetOrder(this Enum value)
{
return value.GetType()

View File

@@ -2377,6 +2377,38 @@
Text="{Binding Config.AutoArtifactSalvageConfig.JavaScript, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
TextWrapping="Wrap">
按套装筛选
</ui:TextBlock>
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
利用游戏自带的筛选功能先行筛选
<LineBreak/>
一般填写套装内生之花名,可填入多个名称;留空则不用
</ui:TextBlock>
<ui:TextBox Grid.Row="1"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="400"
Margin="0,0,36,0"
Text="{Binding Config.AutoArtifactSalvageConfig.ArtifactSetFilter, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />

View File

@@ -28,7 +28,7 @@ public partial class OcrDialog
this.widthRatio = widthRatio;
this.heightRatio = heightRatio;
this.javaScript = javaScript;
this.autoArtifactSalvageTask = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName)));
this.autoArtifactSalvageTask = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, null, new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName)));
InitializeComponent();

View File

@@ -584,7 +584,7 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
Config.HotKeyConfig.Test1HotkeyType,
(_, _) =>
{
Task.Run(async () => { await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star: 4, null, null, null)).Start(new CancellationToken()); });
Task.Run(async () => { await new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(star: 4, null, null, null, null)).Start(new CancellationToken()); });
}
));

View File

@@ -537,6 +537,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
.RunSoloTaskAsync(new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(
int.Parse(Config.AutoArtifactSalvageConfig.MaxArtifactStar),
Config.AutoArtifactSalvageConfig.JavaScript,
Config.AutoArtifactSalvageConfig.ArtifactSetFilter,
Config.AutoArtifactSalvageConfig.MaxNumToCheck,
Config.AutoArtifactSalvageConfig.RecognitionFailurePolicy
)));

View File

@@ -98,7 +98,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
CultureInfo cultureInfo = new CultureInfo("zh-Hans");
//
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
string result = PaddleResultDic.GetOrAdd(screenshot, screenshot_ =>
{
using Mat mat = new Mat(@$"..\..\..\Assets\AutoArtifactSalvage\{screenshot_}");
@@ -203,7 +203,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
*/
//
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
ArtifactStat result = sut.GetArtifactStat(mat, paddle.Get(cultureInfo.Name), out string _);
//
@@ -237,7 +237,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
CultureInfo cultureInfo = new CultureInfo("zh-Hans");
//
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
AutoArtifactSalvageTask sut = new AutoArtifactSalvageTask(new AutoArtifactSalvageTaskParam(5, null, null, null, null, cultureInfo, this.stringLocalizer), new FakeLogger());
ArtifactStat artifact = sut.GetArtifactStat(mat, paddle.Get(), out string _);
bool result = IsMatchJavaScript(artifact, js);

View File

@@ -1,4 +1,4 @@
using BehaviourTree;
using BehaviourTree;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
using BetterGenshinImpact.GameTask.Model.Area;
@@ -17,7 +17,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
[Theory]
[InlineData(@"20250225101300361_ChooseBait_Succeeded.png", new string[] { "medaka", "butterflyfish", "butterflyfish", "pufferfish" })]
[InlineData(@"20250226161354285_ChooseBait_Succeeded.png", new string[] { "medaka", "medaka" })]
[InlineData(@"20250226161354285_ChooseBait_Succeeded.png", new string[] { "medaka", "medaka" })] // todo 更新用例
[InlineData(@"202503160917566615@900p.png", new string[] { "pufferfish" })]
/// <summary>
/// 测试各种选取鱼饵,结果为成功
@@ -35,7 +35,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
};
//
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator());
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), this.session, this.prototypes);
BehaviourStatus actual = sut.Tick(imageRegion);
//
@@ -69,11 +69,11 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
FakeTimeProvider fakeTimeProvider = new FakeTimeProvider(dateTime);
//
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), fakeTimeProvider);
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), this.session, this.prototypes, fakeTimeProvider);
BehaviourStatus actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.True(blackboard.chooseBaitUIOpening);
Assert.Equal(BehaviourStatus.Running, actual);
@@ -84,7 +84,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.False(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.NotNull(blackboard.selectedBait);
Assert.True(blackboard.chooseBaitUIOpening);
Assert.Equal(BehaviourStatus.Running, actual);
@@ -95,7 +95,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.False(blackboard.chooseBaitUIOpening);
Assert.Equal(BehaviourStatus.Failed, actual);
}
@@ -124,15 +124,15 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
#region 1
//
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), fakeTimeProvider);
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), this.session, this.prototypes, fakeTimeProvider);
BehaviourStatus actual = sut.Tick(imageRegion);
fakeTimeProvider.SetUtcNow(dateTime.AddSeconds(3));
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == "fake fly bait"));
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == BaitType.FakeFlyBait));
#endregion
#region 2
@@ -146,9 +146,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == "fake fly bait").Count());
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == BaitType.FakeFlyBait).Count());
Assert.False(blackboard.abort);
#endregion
@@ -165,9 +165,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == "spinelgrain bait"));
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == BaitType.SpinelgrainBait));
#endregion
#region sunfish受到遮挡medaka再次出现4medaka
@@ -183,9 +183,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.False(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.NotNull(blackboard.selectedBait); // todo 更新用例
Assert.Equal(BehaviourStatus.Succeeded, actual);
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == "spinelgrain bait"));
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == BaitType.SpinelgrainBait));
#endregion
#region sunfish再次出现5
@@ -201,9 +201,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == "spinelgrain bait").Count());
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == BaitType.SpinelgrainBait).Count());
#endregion
}
@@ -230,15 +230,15 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
#region 1
//
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), fakeTimeProvider);
ChooseBait sut = new ChooseBait("-", blackboard, new FakeLogger(), false, systemInfo, new FakeInputSimulator(), this.session, this.prototypes, fakeTimeProvider);
BehaviourStatus actual = sut.Tick(imageRegion);
fakeTimeProvider.SetUtcNow(dateTime.AddSeconds(3));
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == "fake fly bait"));
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == BaitType.FakeFlyBait));
#endregion
#region koi受到遮挡2
@@ -254,9 +254,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == "spinelgrain bait"));
Assert.Single(blackboard.chooseBaitFailures.Where(f => f == BaitType.SpinelgrainBait));
Assert.False(blackboard.abort);
#endregion
@@ -273,9 +273,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == "fake fly bait").Count());
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == BaitType.FakeFlyBait).Count());
#endregion
#region 4
@@ -291,9 +291,9 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
actual = sut.Tick(imageRegion);
//
Assert.True(String.IsNullOrEmpty(blackboard.selectedBaitName));
Assert.Null(blackboard.selectedBait);
Assert.Equal(BehaviourStatus.Failed, actual);
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == "spinelgrain bait").Count());
Assert.Equal(2, blackboard.chooseBaitFailures.Where(f => f == BaitType.SpinelgrainBait).Count());
#endregion
}
}

View File

@@ -1,8 +1,9 @@
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.AutoFishing;
using BehaviourTree;
using BetterGenshinImpact.GameTask.Model.Area;
using Microsoft.Extensions.Time.Testing;
using OpenCvSharp;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -38,10 +39,10 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
}
[Theory]
[InlineData("20250225101257889_GetFishpond_Succeeded.png", new string[] { "fruit paste bait", "fruit paste bait", "redrot bait", "redrot bait" }, new string[] { "false worm bait", "false worm bait", "fake fly bait", "fake fly bait" })]
[InlineData("20250225101257889_GetFishpond_Succeeded.png", new BaitType[] { BaitType.FruitPasteBait, BaitType.FruitPasteBait, BaitType.RedrotBait, BaitType.RedrotBait }, new BaitType[] { BaitType.FalseWormBait, BaitType.FalseWormBait, BaitType.FakeFlyBait, BaitType.FakeFlyBait })]
/// 测试鱼的鱼饵均在失败列表中且被忽略,结果为运行中
/// </summary>
public void GetFishpondTest_AllIgnored_ShouldBeRunning(string screenshot1080p, IEnumerable<string> chooseBaitfailures, IEnumerable<string> throwRodNoTargetFishfailures)
public void GetFishpondTest_AllIgnored_ShouldBeRunning(string screenshot1080p, IEnumerable<BaitType> chooseBaitfailures, IEnumerable<BaitType> throwRodNoTargetFishfailures)
{
//
Mat mat = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");

View File

@@ -1,4 +1,4 @@
using BehaviourTree;
using BehaviourTree;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.Model.Area.Converter;
using BetterGenshinImpact.GameTask.Model.Area;
@@ -6,18 +6,19 @@ using Microsoft.Extensions.Time.Testing;
using OpenCvSharp;
using BehaviourTree.Composites;
using BehaviourTree.FluentBuilder;
using BetterGenshinImpact.GameTask.AutoFishing.Model;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
public partial class BehavioursTests
{
[Theory]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", "false worm bait")]
[InlineData(@"20250226162217468_ThrowRod_Succeeded.png", "fruit paste bait")]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", BaitType.FalseWormBait)]
[InlineData(@"20250226162217468_ThrowRod_Succeeded.png", BaitType.FruitPasteBait)]
/// <summary>
/// 测试各种抛竿,结果为成功
/// </summary>
public void ThrowRodTest_VariousFish_ShouldSuccess(string screenshot1080p, string selectedBaitName)
public void ThrowRodTest_VariousFish_ShouldSuccess(string screenshot1080p, BaitType selectedBait)
{
//
Mat mat = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
@@ -25,7 +26,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
var blackboard = new Blackboard(Predictor, sleep: i => { })
{
selectedBaitName = selectedBaitName
selectedBait = selectedBait
};
FakeTimeProvider fakeTimeProvider = new FakeTimeProvider();
@@ -40,12 +41,12 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
}
[Theory]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", "redrot bait")]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", "fake fly bait")]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", BaitType.RedrotBait)]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", BaitType.FakeFlyBait)]
/// <summary>
/// 测试各种抛竿未满足HutaoFisher判定结果为运行中
/// </summary>
public void ThrowRodTest_VariousFish_ShouldFail(string screenshot1080p, string selectedBaitName)
public void ThrowRodTest_VariousFish_ShouldFail(string screenshot1080p, BaitType selectedBait)
{
//
Mat mat = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
@@ -53,7 +54,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
var blackboard = new Blackboard(Predictor, sleep: i => { })
{
selectedBaitName = selectedBaitName
selectedBait = selectedBait
};
FakeTimeProvider fakeTimeProvider = new FakeTimeProvider();
@@ -68,11 +69,11 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
}
[Theory]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", "flashing maintenance mek bait")]
[InlineData(@"20250225101304534_ThrowRod_Succeeded.png", BaitType.FlashingMaintenanceMekBait)]
/// <summary>
/// 测试各种抛竿,无鱼饵适用鱼,结果为失败
/// </summary>
public void ThrowRodTest_NoBaitFish_ShouldFail(string screenshot1080p, string selectedBaitName)
public void ThrowRodTest_NoBaitFish_ShouldFail(string screenshot1080p, BaitType selectedBait)
{
//
Mat mat = new Mat(@$"..\..\..\Assets\AutoFishing\{screenshot1080p}");
@@ -80,7 +81,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
var blackboard = new Blackboard(Predictor, sleep: i => { })
{
selectedBaitName = selectedBaitName
selectedBait = selectedBait
};
FakeTimeProvider fakeTimeProvider = new FakeTimeProvider();
@@ -119,7 +120,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
var blackboard = new Blackboard(Predictor, sleep: i => { })
{
selectedBaitName = "fake fly bait"
selectedBait = GameTask.AutoFishing.Model.BaitType.FakeFlyBait
};
//

View File

@@ -2,7 +2,9 @@ using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.UnitTest.CoreTests.RecognitionTests.OCRTests;
using BetterGenshinImpact.UnitTest.GameTaskTests.GetGridIconsTests;
using Microsoft.Extensions.Localization;
using Microsoft.ML.OnnxRuntime;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
{
@@ -15,12 +17,17 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
private readonly PaddleFixture paddle;
private readonly IStringLocalizer<AutoFishingTask> stringLocalizer;
private readonly InferenceSession session;
private readonly Dictionary<string, float[]> prototypes;
public BehavioursTests(PaddleFixture paddle, TorchFixture torch, LocalizationFixture localization)
public BehavioursTests(PaddleFixture paddle, TorchFixture torch, LocalizationFixture localization, GridIconModelFixture iconModel)
{
this.paddle = paddle;
this.useTorch = torch.UseTorch;
this.stringLocalizer = localization.CreateStringLocalizer<AutoFishingTask>();
this.session = iconModel.modelLoader.Value.session;
this.prototypes = iconModel.modelLoader.Value.prototypes;
}
private IOcrService OcrService => paddle.Get();