Make ConditionExpression use counts.
This commit is contained in:
@@ -222,21 +222,21 @@ namespace OpenRA.Support
|
|||||||
return new VariableToken(start, expression.Substring(start));
|
return new VariableToken(start, expression.Substring(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ParseSymbol(VariableToken t, IReadOnlyDictionary<string, bool> symbols)
|
static int ParseSymbol(VariableToken t, IReadOnlyDictionary<string, int> symbols)
|
||||||
{
|
{
|
||||||
bool value;
|
int value;
|
||||||
symbols.TryGetValue(t.Symbol, out value);
|
symbols.TryGetValue(t.Symbol, out value);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyBinaryOperation(Stack<bool> s, Func<bool, bool, bool> f)
|
static void ApplyBinaryOperation(Stack<int> s, Func<int, int, int> f)
|
||||||
{
|
{
|
||||||
var x = s.Pop();
|
var x = s.Pop();
|
||||||
var y = s.Pop();
|
var y = s.Pop();
|
||||||
s.Push(f(x, y));
|
s.Push(f(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ApplyUnaryOperation(Stack<bool> s, Func<bool, bool> f)
|
static void ApplyUnaryOperation(Stack<int> s, Func<int, int> f)
|
||||||
{
|
{
|
||||||
var x = s.Pop();
|
var x = s.Pop();
|
||||||
s.Push(f(x));
|
s.Push(f(x));
|
||||||
@@ -271,21 +271,21 @@ namespace OpenRA.Support
|
|||||||
yield return s.Pop();
|
yield return s.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Evaluate(IReadOnlyDictionary<string, bool> symbols)
|
public int Evaluate(IReadOnlyDictionary<string, int> symbols)
|
||||||
{
|
{
|
||||||
var s = new Stack<bool>();
|
var s = new Stack<int>();
|
||||||
foreach (var t in postfix)
|
foreach (var t in postfix)
|
||||||
{
|
{
|
||||||
if (t is AndToken)
|
if (t is AndToken)
|
||||||
ApplyBinaryOperation(s, (x, y) => y & x);
|
ApplyBinaryOperation(s, (x, y) => y > 0 ? x : y);
|
||||||
else if (t is NotEqualsToken)
|
else if (t is NotEqualsToken)
|
||||||
ApplyBinaryOperation(s, (x, y) => y ^ x);
|
ApplyBinaryOperation(s, (x, y) => (y != x) ? 1 : 0);
|
||||||
else if (t is OrToken)
|
else if (t is OrToken)
|
||||||
ApplyBinaryOperation(s, (x, y) => y | x);
|
ApplyBinaryOperation(s, (x, y) => y > 0 ? y : x);
|
||||||
else if (t is EqualsToken)
|
else if (t is EqualsToken)
|
||||||
ApplyBinaryOperation(s, (x, y) => y == x);
|
ApplyBinaryOperation(s, (x, y) => (y == x) ? 1 : 0);
|
||||||
else if (t is NotToken)
|
else if (t is NotToken)
|
||||||
ApplyUnaryOperation(s, x => !x);
|
ApplyUnaryOperation(s, x => (x > 0) ? 0 : 1);
|
||||||
else if (t is VariableToken)
|
else if (t is VariableToken)
|
||||||
s.Push(ParseSymbol((VariableToken)t, symbols));
|
s.Push(ParseSymbol((VariableToken)t, symbols));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
int nextToken = 1;
|
int nextToken = 1;
|
||||||
|
|
||||||
/// <summary>Cache of condition -> enabled state for quick evaluation of boolean conditions.</summary>
|
/// <summary>Cache of condition -> enabled state for quick evaluation of boolean conditions.</summary>
|
||||||
readonly Dictionary<string, bool> conditionCache = new Dictionary<string, bool>();
|
readonly Dictionary<string, int> conditionCache = new Dictionary<string, int>();
|
||||||
|
|
||||||
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
|
/// <summary>Read-only version of conditionCache that is passed to IConditionConsumers.</summary>
|
||||||
IReadOnlyDictionary<string, bool> readOnlyConditionCache;
|
IReadOnlyDictionary<string, int> readOnlyConditionCache;
|
||||||
|
|
||||||
void INotifyCreated.Created(Actor self)
|
void INotifyCreated.Created(Actor self)
|
||||||
{
|
{
|
||||||
state = new Dictionary<string, ConditionState>();
|
state = new Dictionary<string, ConditionState>();
|
||||||
readOnlyConditionCache = new ReadOnlyDictionary<string, bool>(conditionCache);
|
readOnlyConditionCache = new ReadOnlyDictionary<string, int>(conditionCache);
|
||||||
|
|
||||||
var allConsumers = new HashSet<IConditionConsumer>();
|
var allConsumers = new HashSet<IConditionConsumer>();
|
||||||
var allWatchers = self.TraitsImplementing<IConditionTimerWatcher>().ToList();
|
var allWatchers = self.TraitsImplementing<IConditionTimerWatcher>().ToList();
|
||||||
@@ -96,7 +96,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (w.Condition == condition)
|
if (w.Condition == condition)
|
||||||
cs.Watchers.Add(w);
|
cs.Watchers.Add(w);
|
||||||
|
|
||||||
conditionCache[condition] = false;
|
conditionCache[condition] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
conditionState.Tokens.Add(kv.Key);
|
conditionState.Tokens.Add(kv.Key);
|
||||||
conditionCache[kv.Value] = conditionState.Tokens.Count > 0;
|
conditionCache[kv.Value] = conditionState.Tokens.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var sc in self.Info.TraitInfos<StackedConditionInfo>())
|
foreach (var sc in self.Info.TraitInfos<StackedConditionInfo>())
|
||||||
@@ -133,7 +133,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
else
|
else
|
||||||
conditionState.Tokens.Add(token);
|
conditionState.Tokens.Add(token);
|
||||||
|
|
||||||
conditionCache[condition] = conditionState.Tokens.Count > 0;
|
conditionCache[condition] = conditionState.Tokens.Count;
|
||||||
|
|
||||||
foreach (var t in conditionState.Consumers)
|
foreach (var t in conditionState.Consumers)
|
||||||
t.ConditionsChanged(self, readOnlyConditionCache);
|
t.ConditionsChanged(self, readOnlyConditionCache);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
/// <summary>Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.)</summary>
|
/// <summary>Use as base class for *Info to subclass of UpgradableTrait. (See UpgradableTrait.)</summary>
|
||||||
public abstract class ConditionalTraitInfo : IConditionConsumerInfo, IRulesetLoaded
|
public abstract class ConditionalTraitInfo : IConditionConsumerInfo, IRulesetLoaded
|
||||||
{
|
{
|
||||||
static readonly IReadOnlyDictionary<string, bool> NoConditions = new ReadOnlyDictionary<string, bool>(new Dictionary<string, bool>());
|
static readonly IReadOnlyDictionary<string, int> NoConditions = new ReadOnlyDictionary<string, int>(new Dictionary<string, int>());
|
||||||
|
|
||||||
[ConsumedConditionReference]
|
[ConsumedConditionReference]
|
||||||
[Desc("Boolean expression defining the condition to enable this trait.")]
|
[Desc("Boolean expression defining the condition to enable this trait.")]
|
||||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||||
{
|
{
|
||||||
EnabledByDefault = RequiresCondition != null ? RequiresCondition.Evaluate(NoConditions) : true;
|
EnabledByDefault = RequiresCondition != null ? RequiresCondition.Evaluate(NoConditions) > 0 : true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,13 +77,13 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
void INotifyCreated.Created(Actor self) { Created(self); }
|
void INotifyCreated.Created(Actor self) { Created(self); }
|
||||||
|
|
||||||
void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary<string, bool> conditions)
|
void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary<string, int> conditions)
|
||||||
{
|
{
|
||||||
if (Info.RequiresCondition == null)
|
if (Info.RequiresCondition == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var wasDisabled = IsTraitDisabled;
|
var wasDisabled = IsTraitDisabled;
|
||||||
IsTraitDisabled = !Info.RequiresCondition.Evaluate(conditions);
|
IsTraitDisabled = Info.RequiresCondition.Evaluate(conditions) <= 0;
|
||||||
|
|
||||||
if (IsTraitDisabled != wasDisabled)
|
if (IsTraitDisabled != wasDisabled)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
public interface IConditionConsumer
|
public interface IConditionConsumer
|
||||||
{
|
{
|
||||||
IEnumerable<string> Conditions { get; }
|
IEnumerable<string> Conditions { get; }
|
||||||
void ConditionsChanged(Actor self, IReadOnlyDictionary<string, bool> conditions);
|
void ConditionsChanged(Actor self, IReadOnlyDictionary<string, int> conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface INotifyHarvesterAction
|
public interface INotifyHarvesterAction
|
||||||
|
|||||||
@@ -21,20 +21,20 @@ namespace OpenRA.Test
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class ConditionExpressionTest
|
public class ConditionExpressionTest
|
||||||
{
|
{
|
||||||
IReadOnlyDictionary<string, bool> testValues = new ReadOnlyDictionary<string, bool>(new Dictionary<string, bool>()
|
IReadOnlyDictionary<string, int> testValues = new ReadOnlyDictionary<string, int>(new Dictionary<string, int>()
|
||||||
{
|
{
|
||||||
{ "true", true },
|
{ "true", 1 },
|
||||||
{ "false", false }
|
{ "false", 0 }
|
||||||
});
|
});
|
||||||
|
|
||||||
void AssertFalse(string expression)
|
void AssertFalse(string expression)
|
||||||
{
|
{
|
||||||
Assert.False(new ConditionExpression(expression).Evaluate(testValues), expression);
|
Assert.False(new ConditionExpression(expression).Evaluate(testValues) > 0, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertTrue(string expression)
|
void AssertTrue(string expression)
|
||||||
{
|
{
|
||||||
Assert.True(new ConditionExpression(expression).Evaluate(testValues), expression);
|
Assert.True(new ConditionExpression(expression).Evaluate(testValues) > 0, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertParseFailure(string expression)
|
void AssertParseFailure(string expression)
|
||||||
@@ -69,7 +69,7 @@ namespace OpenRA.Test
|
|||||||
AssertFalse("false == true");
|
AssertFalse("false == true");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(TestName = "Not-equals (XOR) operation")]
|
[TestCase(TestName = "Not-equals operation")]
|
||||||
public void TestNotEquals()
|
public void TestNotEquals()
|
||||||
{
|
{
|
||||||
AssertFalse("true != true");
|
AssertFalse("true != true");
|
||||||
@@ -141,7 +141,7 @@ namespace OpenRA.Test
|
|||||||
AssertParseFailure("false ||");
|
AssertParseFailure("false ||");
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestCase(TestName = "Undefined symbols are treated as `false` values")]
|
[TestCase(TestName = "Undefined symbols are treated as `false` (0) values")]
|
||||||
public void TestUndefinedSymbols()
|
public void TestUndefinedSymbols()
|
||||||
{
|
{
|
||||||
AssertFalse("undef1 || undef2");
|
AssertFalse("undef1 || undef2");
|
||||||
|
|||||||
Reference in New Issue
Block a user