| | | 1 | | using Intentum.Core.Behavior; |
| | | 2 | | |
| | | 3 | | namespace Intentum.Simulation; |
| | | 4 | | |
| | | 5 | | /// <summary> |
| | | 6 | | /// Default implementation of behavior space simulation. |
| | | 7 | | /// </summary> |
| | | 8 | | public sealed class BehaviorSpaceSimulator : IBehaviorSpaceSimulator |
| | | 9 | | { |
| | | 10 | | /// <inheritdoc /> |
| | | 11 | | public BehaviorSpace FromSequence( |
| | | 12 | | IReadOnlyList<(string Actor, string Action)> sequence, |
| | | 13 | | DateTimeOffset? baseTime = null) |
| | 3 | 14 | | { |
| | 3 | 15 | | var space = new BehaviorSpace(); |
| | 3 | 16 | | var time = baseTime ?? DateTimeOffset.UtcNow; |
| | 17 | 17 | | foreach (var (actor, action) in sequence) |
| | 4 | 18 | | { |
| | 4 | 19 | | space.Observe(new BehaviorEvent(actor, action, time)); |
| | 4 | 20 | | time = time.AddSeconds(1); |
| | 4 | 21 | | } |
| | 3 | 22 | | return space; |
| | 3 | 23 | | } |
| | | 24 | | |
| | | 25 | | /// <inheritdoc /> |
| | | 26 | | public BehaviorSpace GenerateRandom( |
| | | 27 | | IReadOnlyList<string> actors, |
| | | 28 | | IReadOnlyList<string> actions, |
| | | 29 | | int eventCount, |
| | | 30 | | int? randomSeed = null) |
| | 4 | 31 | | { |
| | 4 | 32 | | if (actors is null || actors.Count == 0) |
| | 0 | 33 | | throw new ArgumentException("At least one actor required.", nameof(actors)); |
| | 4 | 34 | | if (actions is null || actions.Count == 0) |
| | 0 | 35 | | throw new ArgumentException("At least one action required.", nameof(actions)); |
| | 4 | 36 | | if (eventCount < 0) |
| | 0 | 37 | | throw new ArgumentOutOfRangeException(nameof(eventCount), "Event count must be non-negative."); |
| | | 38 | | |
| | 4 | 39 | | var seed = randomSeed ?? Environment.TickCount; |
| | 4 | 40 | | var space = new BehaviorSpace(); |
| | 4 | 41 | | var time = DateTimeOffset.UtcNow; |
| | 4 | 42 | | var actorCount = actors.Count; |
| | 4 | 43 | | var actionCount = actions.Count; |
| | | 44 | | |
| | 34 | 45 | | for (var i = 0; i < eventCount; i++) |
| | 13 | 46 | | { |
| | 13 | 47 | | var actorIndex = Math.Abs(Hash(seed, i, 0)) % actorCount; |
| | 13 | 48 | | var actionIndex = Math.Abs(Hash(seed, i, 1)) % actionCount; |
| | 13 | 49 | | var timeDelta = 1 + (Math.Abs(Hash(seed, i, 2)) % 59); |
| | 13 | 50 | | space.Observe(new BehaviorEvent(actors[actorIndex], actions[actionIndex], time)); |
| | 13 | 51 | | time = time.AddSeconds(timeDelta); |
| | 13 | 52 | | } |
| | | 53 | | |
| | 4 | 54 | | return space; |
| | 4 | 55 | | } |
| | | 56 | | |
| | | 57 | | private static int Hash(int seed, int index, int salt) |
| | 39 | 58 | | { |
| | | 59 | | unchecked |
| | 39 | 60 | | { |
| | 39 | 61 | | var h = (seed * 31 + index) * 31 + salt; |
| | 39 | 62 | | return h == int.MinValue ? 0 : h; |
| | | 63 | | } |
| | 39 | 64 | | } |
| | | 65 | | } |