Allow AI hero slot culture cards
This commit is contained in:
parent
dc93fab7da
commit
15593c487b
@ -169,7 +169,7 @@ Classify the repeated action before patching:
|
|||||||
|
|
||||||
Known temporary filters from this iteration:
|
Known temporary filters from this iteration:
|
||||||
|
|
||||||
- `CommonActionType.BuyCultureCard` is disabled for AI generation.
|
- `CommonActionType.BuyCultureCard` is only generated for `SecondHero` and `ThirdHero` slot unlock cards; other culture cards remain disabled for AI generation.
|
||||||
- `UnitActionType.ToggleShenlan` is filtered because it is a debug/visual toggle with no action-point cost.
|
- `UnitActionType.ToggleShenlan` is filtered because it is a debug/visual toggle with no action-point cost.
|
||||||
- `PlayerActionType.FinishHeroTask` is filtered because it can execute repeatedly without observable AI turn progress.
|
- `PlayerActionType.FinishHeroTask` is filtered because it can execute repeatedly without observable AI turn progress.
|
||||||
|
|
||||||
|
|||||||
@ -491,8 +491,8 @@ Director 不直接推演行为结果,而是从合法 Action 池中选择。
|
|||||||
| 单位行为 | Capture、Examine、Gather、Recover、Upgrade、HeroUpgrade、CultureUnitUpgrade、ShipUpgrade、AbsorbMarker、英雄主动 |
|
| 单位行为 | Capture、Examine、Gather、Recover、Upgrade、HeroUpgrade、CultureUnitUpgrade、ShipUpgrade、AbsorbMarker、英雄主动 |
|
||||||
| 城市行为 | TrainUnit、CityLevelUpAction、CityAction、StartWonder、BuildWonder |
|
| 城市行为 | TrainUnit、CityLevelUpAction、CityAction、StartWonder、BuildWonder |
|
||||||
| 地块行为 | Gain、Build、GridMisc |
|
| 地块行为 | Gain、Build、GridMisc |
|
||||||
| 玩家行为 | LearnTech、PlayerAction;BuyCultureCard 暂不进入 AI 候选,等文化卡策略单独回归 |
|
| 玩家行为 | LearnTech、PlayerAction;BuyCultureCard 只开放英雄槽位卡,不开放普通文化卡 |
|
||||||
| 英雄管理 | SelectHero、TrainUnit:Giant 出场/复活、FinishHeroTask |
|
| 英雄管理 | SelectHero、TrainUnit:Giant 出场/复活、SecondHero/ThirdHero 槽位卡、FinishHeroTask |
|
||||||
|
|
||||||
UnitAttackAlly 只由 HeroPlaybook、支援战术或明确的友军互动规则使用,不进入 Fallback。友军目标动作如果落到兜底,通常说明它缺少英雄或支援规则。
|
UnitAttackAlly 只由 HeroPlaybook、支援战术或明确的友军互动规则使用,不进入 Fallback。友军目标动作如果落到兜底,通常说明它缺少英雄或支援规则。
|
||||||
|
|
||||||
|
|||||||
@ -386,7 +386,10 @@ SkillBase
|
|||||||
pool.PlayerActions.Add(action)
|
pool.PlayerActions.Add(action)
|
||||||
|
|
||||||
如果 id.ActionType == BuyCultureCard:
|
如果 id.ActionType == BuyCultureCard:
|
||||||
继续
|
如果 IsAIHeroSlotCultureCard(player, id.CultureCardType):
|
||||||
|
pool.HeroManagementActions.Add(action)
|
||||||
|
否则:
|
||||||
|
继续
|
||||||
|
|
||||||
如果 id.ActionType == PlayerAction:
|
如果 id.ActionType == PlayerAction:
|
||||||
如果 id.PlayerActionType in [SelectHero, FinishHeroTask]:
|
如果 id.PlayerActionType in [SelectHero, FinishHeroTask]:
|
||||||
@ -395,6 +398,23 @@ SkillBase
|
|||||||
pool.PlayerActions.Add(action)
|
pool.PlayerActions.Add(action)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
函数 IsAIHeroSlotCultureCard(player, cardType):
|
||||||
|
如果 player 没有 HeroData 或 CultureInfo:
|
||||||
|
返回 false
|
||||||
|
|
||||||
|
如果 player 已拥有 cardType:
|
||||||
|
返回 false
|
||||||
|
|
||||||
|
如果 cardType == SecondHero:
|
||||||
|
返回 MaxHeroCount == 1 且 HeroCount >= 1
|
||||||
|
|
||||||
|
如果 cardType == ThirdHero:
|
||||||
|
返回 MaxHeroCount == 2 且 HeroCount >= 2 且 已拥有 SecondHero
|
||||||
|
|
||||||
|
返回 false
|
||||||
|
```
|
||||||
|
|
||||||
### 4.3 危险动作过滤
|
### 4.3 危险动作过滤
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@ -1303,6 +1323,10 @@ SkillBase
|
|||||||
如果 candidate.IsValid:
|
如果 candidate.IsValid:
|
||||||
返回 candidate
|
返回 candidate
|
||||||
|
|
||||||
|
candidate = TryBuyHeroSlotCultureCard(ctx)
|
||||||
|
如果 candidate.IsValid:
|
||||||
|
返回 candidate
|
||||||
|
|
||||||
candidate = TryForceFinishHeroTask(ctx)
|
candidate = TryForceFinishHeroTask(ctx)
|
||||||
如果 candidate.IsValid:
|
如果 candidate.IsValid:
|
||||||
返回 candidate
|
返回 candidate
|
||||||
@ -1369,7 +1393,19 @@ SkillBase
|
|||||||
|
|
||||||
英雄出场仍然走 TrainUnitAction 的 CheckCan 和 Execute。AI 只负责更早选择这个合法动作,不直接改变英雄状态。
|
英雄出场仍然走 TrainUnitAction 的 CheckCan 和 Execute。AI 只负责更早选择这个合法动作,不直接改变英雄状态。
|
||||||
|
|
||||||
### 7.4 FinishHeroTask
|
### 7.4 BuyHeroSlotCultureCard
|
||||||
|
|
||||||
|
```text
|
||||||
|
函数 TryBuyHeroSlotCultureCard(ctx):
|
||||||
|
action = FindHeroSlotCultureCard(ctx.Player)
|
||||||
|
如果 action == None:
|
||||||
|
返回 None
|
||||||
|
|
||||||
|
score = ScoreCultureCard(ctx, action.CultureCardType)
|
||||||
|
返回 Candidate(action, HeroManagement, score, "购买英雄槽位卡")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.5 FinishHeroTask
|
||||||
|
|
||||||
```text
|
```text
|
||||||
函数 TryForceFinishHeroTask(ctx):
|
函数 TryForceFinishHeroTask(ctx):
|
||||||
@ -2336,20 +2372,20 @@ Byakuren / Miko / Zanmu:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
函数 ScoreCultureCard(ctx, action):
|
函数 ScoreCultureCard(ctx, action):
|
||||||
当前 AI 不生成 BuyCultureCard 动作。
|
如果 !IsAIHeroSlotCultureCard(ctx.Player, action.CultureCardType):
|
||||||
此函数只作为未来恢复文化卡策略时的评分入口。
|
返回 0
|
||||||
|
|
||||||
score = 300
|
score = 860
|
||||||
|
|
||||||
如果 前线吃紧 且文化卡提供即时战力:
|
如果 action.CultureCardType == SecondHero 且 MaxHeroCount == 1:
|
||||||
score += 120
|
|
||||||
|
|
||||||
如果 英雄体系成型 且文化卡强化英雄:
|
|
||||||
score += 100
|
|
||||||
|
|
||||||
如果 安全发展 且文化卡提供长期经济:
|
|
||||||
score += 80
|
score += 80
|
||||||
|
|
||||||
|
如果 action.CultureCardType == ThirdHero 且 MaxHeroCount == 2:
|
||||||
|
score += 70
|
||||||
|
|
||||||
|
如果 当前没有在场英雄:
|
||||||
|
score -= 120
|
||||||
|
|
||||||
返回 score
|
返回 score
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -292,6 +292,7 @@ namespace Logic.AI
|
|||||||
GeneratorActionIds(data, CommonActionType.LearnTech);
|
GeneratorActionIds(data, CommonActionType.LearnTech);
|
||||||
GeneratorActionIds(data, CommonActionType.StartWonder);
|
GeneratorActionIds(data, CommonActionType.StartWonder);
|
||||||
GeneratorActionIds(data, CommonActionType.PlayerAction);
|
GeneratorActionIds(data, CommonActionType.PlayerAction);
|
||||||
|
GeneratorActionIds(data, CommonActionType.BuyCultureCard);
|
||||||
foreach (var city in selfCities)
|
foreach (var city in selfCities)
|
||||||
{
|
{
|
||||||
data.TargetParam.CityData = city;
|
data.TargetParam.CityData = city;
|
||||||
@ -376,6 +377,7 @@ namespace Logic.AI
|
|||||||
GeneratorActionIds(data, CommonActionType.LearnTech);
|
GeneratorActionIds(data, CommonActionType.LearnTech);
|
||||||
GeneratorActionIds(data, CommonActionType.StartWonder);
|
GeneratorActionIds(data, CommonActionType.StartWonder);
|
||||||
GeneratorActionIds(data, CommonActionType.PlayerAction);
|
GeneratorActionIds(data, CommonActionType.PlayerAction);
|
||||||
|
GeneratorActionIds(data, CommonActionType.BuyCultureCard);
|
||||||
foreach (var city in selfCities)
|
foreach (var city in selfCities)
|
||||||
{
|
{
|
||||||
data.TargetParam.CityData = city;
|
data.TargetParam.CityData = city;
|
||||||
@ -453,9 +455,6 @@ namespace Logic.AI
|
|||||||
|
|
||||||
public static void GeneratorActionIds(AICalculatorData data, CommonActionType type)
|
public static void GeneratorActionIds(AICalculatorData data, CommonActionType type)
|
||||||
{
|
{
|
||||||
// AI暂不购买文化卡,避免隐藏/里程碑卡被当成普通候选行为反复执行。
|
|
||||||
if (type == CommonActionType.BuyCultureCard) return;
|
|
||||||
|
|
||||||
var actions = ActionLogicFactory.GetActionLogicByType(type);
|
var actions = ActionLogicFactory.GetActionLogicByType(type);
|
||||||
if (actions == null || actions.Count == 0) return;
|
if (actions == null || actions.Count == 0) return;
|
||||||
|
|
||||||
@ -832,6 +831,7 @@ namespace Logic.AI
|
|||||||
data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type);
|
data.TargetParam.MainObjectType = ActionLogicFactory.GetMainObjectType(type);
|
||||||
foreach (var action in actions)
|
foreach (var action in actions)
|
||||||
{
|
{
|
||||||
|
if (!IsAIHeroSlotCultureCard(data.TargetParam.PlayerData, action.ActionId.CultureCardType)) continue;
|
||||||
if (!action.CheckCan(data.TargetParam)) continue;
|
if (!action.CheckCan(data.TargetParam)) continue;
|
||||||
var param = data.TargetParam.GetCopyParam();
|
var param = data.TargetParam.GetCopyParam();
|
||||||
param.UnitData = null;
|
param.UnitData = null;
|
||||||
@ -846,6 +846,24 @@ namespace Logic.AI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsAIHeroSlotCultureCard(PlayerData player, CultureCardType cardType)
|
||||||
|
{
|
||||||
|
var heroData = player?.PlayerHeroData;
|
||||||
|
var cultureInfo = player?.PlayerCultureInfo;
|
||||||
|
if (heroData == null || cultureInfo == null) return false;
|
||||||
|
if (cultureInfo.CultureCardList != null && cultureInfo.CultureCardList.Contains(cardType)) return false;
|
||||||
|
|
||||||
|
return cardType switch
|
||||||
|
{
|
||||||
|
CultureCardType.SecondHero => heroData.MaxHeroCount == 1 && heroData.HeroCount >= 1,
|
||||||
|
CultureCardType.ThirdHero => heroData.MaxHeroCount == 2
|
||||||
|
&& heroData.HeroCount >= 2
|
||||||
|
&& cultureInfo.CultureCardList != null
|
||||||
|
&& cultureInfo.CultureCardList.Contains(CultureCardType.SecondHero),
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static void GenerateMoveActionsTowardAnchors(
|
private static void GenerateMoveActionsTowardAnchors(
|
||||||
AICalculatorData data,
|
AICalculatorData data,
|
||||||
List<ActionLogicBase> actions,
|
List<ActionLogicBase> actions,
|
||||||
|
|||||||
@ -191,6 +191,23 @@ namespace Logic.AI.Director
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AIActionBase FindHeroSlotCultureCard(PlayerData player)
|
||||||
|
{
|
||||||
|
AIActionBase best = null;
|
||||||
|
var bestType = CultureCardType.Max;
|
||||||
|
foreach (var action in HeroManagementActions)
|
||||||
|
{
|
||||||
|
var id = action.ActionLogic.ActionId;
|
||||||
|
if (id.ActionType != CommonActionType.BuyCultureCard) continue;
|
||||||
|
if (!AIActionGenerator.IsAIHeroSlotCultureCard(player, id.CultureCardType)) continue;
|
||||||
|
if (id.CultureCardType >= bestType) continue;
|
||||||
|
bestType = id.CultureCardType;
|
||||||
|
best = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<AIActionBase> GetCityActions(CityData city)
|
public IEnumerable<AIActionBase> GetCityActions(CityData city)
|
||||||
{
|
{
|
||||||
if (city == null) yield break;
|
if (city == null) yield break;
|
||||||
@ -321,9 +338,14 @@ namespace Logic.AI.Director
|
|||||||
PlayerActions.Add(action);
|
PlayerActions.Add(action);
|
||||||
break;
|
break;
|
||||||
case CommonActionType.LearnTech:
|
case CommonActionType.LearnTech:
|
||||||
case CommonActionType.BuyCultureCard:
|
|
||||||
PlayerActions.Add(action);
|
PlayerActions.Add(action);
|
||||||
break;
|
break;
|
||||||
|
case CommonActionType.BuyCultureCard:
|
||||||
|
if (AIActionGenerator.IsAIHeroSlotCultureCard(action.Param.PlayerData, id.CultureCardType))
|
||||||
|
{
|
||||||
|
HeroManagementActions.Add(action);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -347,6 +347,14 @@ namespace Logic.AI.Director
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var heroSlotCard = TryHeroSlotCultureCard(ctx, decision);
|
||||||
|
if (heroSlotCard.IsValid)
|
||||||
|
{
|
||||||
|
candidate = heroSlotCard;
|
||||||
|
decision.AddTrace($"HeroManagement: buy hero slot card {candidate.ActionId?.CultureCardType}.", ctx.Config.MaxCandidateTraceCount);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
var finishTask = ctx.ActionIndex.FindBestHeroTaskFinish(ctx.Player);
|
var finishTask = ctx.ActionIndex.FindBestHeroTaskFinish(ctx.Player);
|
||||||
candidate = ctx.ActionIndex.Candidate(finishTask, AIDirectorLane.HeroManagement, "HeroManagement.FinishLowestTask", 870f);
|
candidate = ctx.ActionIndex.Candidate(finishTask, AIDirectorLane.HeroManagement, "HeroManagement.FinishLowestTask", 870f);
|
||||||
AddTerms(candidate, ("base", 870f));
|
AddTerms(candidate, ("base", 870f));
|
||||||
@ -360,6 +368,16 @@ namespace Logic.AI.Director
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AIDirectorActionCandidate TryHeroSlotCultureCard(AIDirectorContext ctx, AIDirectorDecision decision)
|
||||||
|
{
|
||||||
|
var action = ctx.ActionIndex.FindHeroSlotCultureCard(ctx.Player);
|
||||||
|
var score = ScoreCultureCard(ctx, action?.ActionLogic?.ActionId?.CultureCardType ?? CultureCardType.None);
|
||||||
|
var candidate = ctx.ActionIndex.Candidate(action, AIDirectorLane.HeroManagement, "HeroManagement.BuyHeroSlotCard", score);
|
||||||
|
AddTerms(candidate, ("heroSlotCard", score));
|
||||||
|
RecordCandidate(ctx, decision, "BuyHeroSlotCard", candidate, action == null ? "NoAction" : (candidate.IsValid ? null : "CheckCanFailed"));
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
private AIDirectorActionCandidate FindBestHeroSpawn(AIDirectorContext ctx, AIDirectorDecision decision)
|
private AIDirectorActionCandidate FindBestHeroSpawn(AIDirectorContext ctx, AIDirectorDecision decision)
|
||||||
{
|
{
|
||||||
AIDirectorActionCandidate best = AIDirectorActionCandidate.None;
|
AIDirectorActionCandidate best = AIDirectorActionCandidate.None;
|
||||||
@ -746,10 +764,12 @@ namespace Logic.AI.Director
|
|||||||
|
|
||||||
private float ScoreCultureCard(AIDirectorContext ctx, CultureCardType card)
|
private float ScoreCultureCard(AIDirectorContext ctx, CultureCardType card)
|
||||||
{
|
{
|
||||||
var score = 300f;
|
if (!AIActionGenerator.IsAIHeroSlotCultureCard(ctx.Player, card)) return 0f;
|
||||||
if (ctx.Cache.StrategicPosture == AIDirectorStrategicPosture.Defense && card == CultureCardType.AdvancedMilitaryEnhance) score += 120f;
|
var heroData = ctx.Player?.PlayerHeroData;
|
||||||
if (ctx.Cache.SelfHeroes.Count >= 1 && card is CultureCardType.SecondHero or CultureCardType.ThirdHero or CultureCardType.AdvancedHeroEnhance) score += 100f;
|
var score = 860f;
|
||||||
if (ctx.Cache.StrategicPosture == AIDirectorStrategicPosture.Development && card == CultureCardType.AdvancedEconomyEnhance) score += 80f;
|
if (card == CultureCardType.SecondHero && heroData?.MaxHeroCount == 1) score += 80f;
|
||||||
|
if (card == CultureCardType.ThirdHero && heroData?.MaxHeroCount == 2) score += 70f;
|
||||||
|
if (ctx.Cache.SelfHeroes.Count <= 0) score -= 120f;
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user