AI preset curves and expandable optimisation (#1346)

* AI preset curves and expandable optimisation

Added preset curves for considerations to use just to avoid repeating the same variables all over the shop.

Moved common considerations for expanded actions onto the expandable action e.g. you need a free hand to be able to PickUpGloves so we'll just check it the once rather than for each action.

* FIX PRAGMA

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
metalgearsloth
2020-08-12 06:01:55 +10:00
committed by GitHub
parent c00a08f504
commit b34bd7c188
43 changed files with 409 additions and 127 deletions

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States.Utility;
using JetBrains.Annotations;
namespace Content.Server.AI.Utility.Considerations
{
@@ -18,50 +19,114 @@ namespace Content.Server.AI.Utility.Considerations
return Math.Clamp(adjustedScore, 0.0f, 1.0f);
}
[Pure]
private static float BoolCurve(float x)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return x == 1.0f ? 1.0f : 0.0f;
}
public Func<float> BoolCurve(Blackboard context)
{
float Result()
{
var adjustedScore = GetAdjustedScore(context);
// ReSharper disable once CompareOfFloatsByEqualityOperator
return adjustedScore == 1.0f ? 1.0f : 0.0f;
return BoolCurve(adjustedScore);
}
return Result;
}
[Pure]
private static float InverseBoolCurve(float x)
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
return x == 1.0f ? 0.0f : 1.0f;
}
public Func<float> InverseBoolCurve(Blackboard context)
{
float Result()
{
var adjustedScore = GetAdjustedScore(context);
// ReSharper disable once CompareOfFloatsByEqualityOperator
return adjustedScore == 1.0f ? 0.0f : 1.0f;
return InverseBoolCurve(adjustedScore);
}
return Result;
}
[Pure]
private static float LogisticCurve(float x, float slope, float exponent, float yOffset, float xOffset)
{
return Math.Clamp(
exponent * (1 / (1 + (float) Math.Pow(Math.Log(1000) * slope, -1 * x + xOffset))) + yOffset, 0.0f, 1.0f);
}
public Func<float> LogisticCurve(Blackboard context, float slope, float exponent, float yOffset, float xOffset)
{
float Result()
{
var adjustedScore = GetAdjustedScore(context);
return Math.Clamp(exponent * (1 / (1 + (float) Math.Pow(Math.Log(1000) * slope, -1 * adjustedScore + xOffset))) + yOffset, 0.0f, 1.0f);
return LogisticCurve(adjustedScore, slope, exponent, yOffset, xOffset);
}
return Result;
}
[Pure]
private static float QuadraticCurve(float x, float slope, float exponent, float yOffset, float xOffset)
{
return Math.Clamp(slope * (float) Math.Pow(x - xOffset, exponent) + yOffset, 0.0f, 1.0f);
}
public Func<float> QuadraticCurve(Blackboard context, float slope, float exponent, float yOffset, float xOffset)
{
float Result()
{
var adjustedScore = GetAdjustedScore(context);
return Math.Clamp(slope * (float) Math.Pow(adjustedScore - xOffset, exponent) + yOffset, 0.0f, 1.0f);
return QuadraticCurve(adjustedScore, slope, exponent, yOffset, xOffset);
}
return Result;
}
/// <summary>
/// For any curves that are re-used across actions so you only need to update it once.
/// </summary>
/// <param name="context"></param>
/// <param name="preset"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public Func<float> PresetCurve(Blackboard context, PresetCurve preset)
{
float Result()
{
var adjustedScore = GetAdjustedScore(context);
switch (preset)
{
case Considerations.PresetCurve.Distance:
return QuadraticCurve(adjustedScore, -1.0f, 1.0f, 1.0f, 0.02f);
case Considerations.PresetCurve.Nutrition:
return QuadraticCurve(adjustedScore, 2.0f, 1.0f, -1.0f, -0.2f);
case Considerations.PresetCurve.TargetHealth:
return QuadraticCurve(adjustedScore, 1.0f, 0.4f, 0.0f, -0.02f);
default:
throw new ArgumentOutOfRangeException(nameof(preset), preset, null);
}
}
return Result;
}
}
/// <summary>
/// Preset response curves for considerations
/// </summary>
public enum PresetCurve
{
Distance,
Nutrition,
TargetHealth,
}
}

View File

@@ -1,28 +0,0 @@
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.GameObjects.Components;
using Content.Server.GameObjects.Components.GUI;
namespace Content.Server.AI.Utility.Considerations.Hands
{
/// <summary>
/// Returns 1 if in our hands else 0
/// </summary>
public sealed class TargetInOurHandsCon : Consideration
{
protected override float GetScore(Blackboard context)
{
var owner = context.GetState<SelfState>().GetValue();
var target = context.GetState<TargetEntityState>().GetValue();
if (target == null ||
!target.HasComponent<ItemComponent>() ||
!owner.TryGetComponent(out HandsComponent handsComponent))
{
return 0.0f;
}
return handsComponent.IsHolding(target) ? 1.0f : 0.0f;
}
}
}

View File

@@ -6,7 +6,7 @@ using Content.Server.GameObjects.Components;
namespace Content.Server.AI.Utility.Considerations.Inventory
{
public class CanPutTargetInHandsCon : Consideration
public class CanPutTargetInInventoryCon : Consideration
{
protected override float GetScore(Blackboard context)
{

View File

@@ -1,6 +1,7 @@
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Server.AI.WorldState.States.Inventory;
using Content.Server.GameObjects;
using Content.Server.GameObjects.Components;
namespace Content.Server.AI.Utility.Considerations.Inventory

View File

@@ -3,7 +3,7 @@ using Content.Server.AI.WorldState.States;
namespace Content.Server.AI.Utility.Considerations.Movement
{
public sealed class DistanceCon : Consideration
public sealed class TargetDistanceCon : Consideration
{
protected override float GetScore(Blackboard context)
{
@@ -14,8 +14,8 @@ namespace Content.Server.AI.Utility.Considerations.Movement
return 0.0f;
}
// Kind of just pulled a max distance out of nowhere. Add 0.01 just in case it's reaally far and we have no choice so it'll still be considered at least.
return (target.Transform.GridPosition.Position - self.Transform.GridPosition.Position).Length / 100 + 0.01f;
// Anything further than 100 tiles gets clamped
return (target.Transform.GridPosition.Position - self.Transform.GridPosition.Position).Length / 100;
}
}
}