From 5c84d239c91cff8e93df1ee3aba0138eedb13291 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 19:16:53 +0200 Subject: [PATCH 1/8] rename `ButtonPressUpdate` to `ButtonPressesCountUpdate` for clarity --- TPP.Core/Modes/Runmode.cs | 2 +- TPP.Core/Overlay/Events/RunInputEvents.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TPP.Core/Modes/Runmode.cs b/TPP.Core/Modes/Runmode.cs index 0144e210..62e10c86 100644 --- a/TPP.Core/Modes/Runmode.cs +++ b/TPP.Core/Modes/Runmode.cs @@ -179,7 +179,7 @@ private async Task CollectRunStatistics(User user, InputSequence input, string r if (runNumber != null && !user.ParticipationEmblems.Contains(runNumber.Value)) await _userRepo.GiveEmblem(user, runNumber.Value); long counter = await _runCounterRepo.Increment(runNumber, incrementBy: input.InputSets.Count); - await _overlayConnection.Send(new ButtonPressUpdate(counter), CancellationToken.None); + await _overlayConnection.Send(new ButtonPressesCountUpdate(counter), CancellationToken.None); } public async Task Run() diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index 356f7179..5a7a37c2 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -66,11 +66,11 @@ public sealed class AnarchyInputStop : IOverlayEvent } [DataContract] - public sealed class ButtonPressUpdate : IOverlayEvent + public sealed class ButtonPressesCountUpdate : IOverlayEvent { public string OverlayEventType => "button_press_update"; [DataMember(Name = "presses")] public long NumTotalButtonPresses { get; set; } - public ButtonPressUpdate(long numTotalButtonPresses) => NumTotalButtonPresses = numTotalButtonPresses; + public ButtonPressesCountUpdate(long numTotalButtonPresses) => NumTotalButtonPresses = numTotalButtonPresses; } } From 27fd7e6f23cefdc28de0fd4efa862ec95333a9fd Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 19:22:04 +0200 Subject: [PATCH 2/8] add `democracy_new_vote` overlay event --- TPP.Core/Overlay/Events/RunInputEvents.cs | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index 5a7a37c2..bb180b1c 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -73,4 +73,44 @@ public sealed class ButtonPressesCountUpdate : IOverlayEvent [DataMember(Name = "presses")] public long NumTotalButtonPresses { get; set; } public ButtonPressesCountUpdate(long numTotalButtonPresses) => NumTotalButtonPresses = numTotalButtonPresses; } + + [DataContract] + public readonly struct NewVote + { + [DataMember(Name = "command")] public string Command { get; init; } + [DataMember(Name = "user")] public User User { get; init; } + // [DataMember(Name = "button_sequence")] public InputSequence InputSequence { get; init; } // unused + // [DataMember(Name = "ts")] public Instant Timestamp { get; init; } // unused + } + + [DataContract] + public readonly struct Vote + { + [DataMember(Name = "command")] public string Command { get; init; } + [DataMember(Name = "count")] public int Count { get; init; } + // [DataMember(Name = "button_sequence")] public InputSequence InputSequence { get; init; } // unused + } + + [DataContract] + public sealed class DemocracyVotesUpdate : IOverlayEvent + { + public string OverlayEventType => "democracy_new_vote"; + + [DataMember(Name = "new_vote")] public NewVote NewVote { get; init; } + [DataMember(Name = "votes")] public List Votes { get; init; } + + public DemocracyVotesUpdate( + User newVoteUser, + InputSequence votedInput, + IReadOnlyDictionary votes) + { + string InputSetToString(InputSet set) => string.Join('+', set.Inputs.Select(input => input.ButtonName)); + string InputSeqToString(InputSequence seq) => string.Join("", seq.InputSets.Select(InputSetToString)); + NewVote = new NewVote { User = newVoteUser, Command = InputSeqToString(votedInput) }; + Votes = votes + .Select(kvp => new Vote { Command = InputSeqToString(kvp.Key), Count = kvp.Value }) + .OrderBy(vote => vote.Count) + .ToList(); + } + } } From 0cdfed971e554cf7aaee866a3551babed22a6dcb Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 19:38:24 +0200 Subject: [PATCH 3/8] add `democracy_reset` overlay event --- TPP.Core/Overlay/Events/RunInputEvents.cs | 10 ++++++++ .../Overlay/OverlayConnectionTest.cs | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index bb180b1c..c180c1b4 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -3,6 +3,7 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.Serialization; +using NodaTime; using TPP.Inputting; using TPP.Inputting.Inputs; using TPP.Model; @@ -113,4 +114,13 @@ public DemocracyVotesUpdate( .ToList(); } } + + [DataContract] + public sealed class DemocracyReset : IOverlayEvent + { + public string OverlayEventType => "democracy_reset"; + + [DataMember(Name = "vote_ends_at")] public Instant Timestamp { get; init; } + public DemocracyReset(Instant timestamp) => Timestamp = timestamp; + } } diff --git a/tests/TPP.Core.Tests/Overlay/OverlayConnectionTest.cs b/tests/TPP.Core.Tests/Overlay/OverlayConnectionTest.cs index 499aa975..499de1d7 100644 --- a/tests/TPP.Core.Tests/Overlay/OverlayConnectionTest.cs +++ b/tests/TPP.Core.Tests/Overlay/OverlayConnectionTest.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging.Abstractions; using Moq; +using NodaTime; using NUnit.Framework; using TPP.Core.Overlay; @@ -50,5 +51,28 @@ await _connection.Send( const string json = @"{""type"":""test"",""extra_parameters"":{""enum_value"":""foo_bar""}}"; _broadcastServerMock.Verify(s => s.Send(json, CancellationToken.None), Times.Once); } + + private struct EventWithInstant : IOverlayEvent + { + public string OverlayEventType => "iso_test"; + [DataMember(Name = "instant")] public Instant Instant { get; init; } + public EventWithInstant(Instant instant) => Instant = instant; + } + + [Test] + public async Task send_instant_as_iso8601() + { + await _connection.Send(new EventWithInstant(Instant.FromUnixTimeSeconds(0)), CancellationToken.None); + await _connection.Send(new EventWithInstant(Instant.FromUnixTimeSeconds(123)), CancellationToken.None); + await _connection.Send(new EventWithInstant(Instant.FromUnixTimeSeconds(123).PlusNanoseconds(1)), + CancellationToken.None); + const string json1 = @"{""type"":""iso_test"",""extra_parameters"":{""instant"":""1970-01-01T00:00:00Z""}}"; + const string json2 = @"{""type"":""iso_test"",""extra_parameters"":{""instant"":""1970-01-01T00:02:03Z""}}"; + const string json3 = + @"{""type"":""iso_test"",""extra_parameters"":{""instant"":""1970-01-01T00:02:03.000000001Z""}}"; + _broadcastServerMock.Verify(s => s.Send(json1, CancellationToken.None), Times.Once); + _broadcastServerMock.Verify(s => s.Send(json2, CancellationToken.None), Times.Once); + _broadcastServerMock.Verify(s => s.Send(json3, CancellationToken.None), Times.Once); + } } } From 0a9e8b2da6e7fc4ee55a236cdcb819b3988827a8 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 19:43:32 +0200 Subject: [PATCH 4/8] add `democracy_voting_over` overlay event, add `ToRepresentation()` to inputs --- TPP.Core/Overlay/Events/RunInputEvents.cs | 15 +++++++++++---- TPP.Inputting/InputSequence.cs | 3 +++ TPP.Inputting/InputSet.cs | 3 +++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index c180c1b4..43c69524 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -105,11 +105,9 @@ public DemocracyVotesUpdate( InputSequence votedInput, IReadOnlyDictionary votes) { - string InputSetToString(InputSet set) => string.Join('+', set.Inputs.Select(input => input.ButtonName)); - string InputSeqToString(InputSequence seq) => string.Join("", seq.InputSets.Select(InputSetToString)); - NewVote = new NewVote { User = newVoteUser, Command = InputSeqToString(votedInput) }; + NewVote = new NewVote { User = newVoteUser, Command = votedInput.ToRepresentation() }; Votes = votes - .Select(kvp => new Vote { Command = InputSeqToString(kvp.Key), Count = kvp.Value }) + .Select(kvp => new Vote { Command = kvp.Key.ToRepresentation(), Count = kvp.Value }) .OrderBy(vote => vote.Count) .ToList(); } @@ -123,4 +121,13 @@ public sealed class DemocracyReset : IOverlayEvent [DataMember(Name = "vote_ends_at")] public Instant Timestamp { get; init; } public DemocracyReset(Instant timestamp) => Timestamp = timestamp; } + + [DataContract] + public sealed class DemocracyVotingOver : IOverlayEvent + { + public string OverlayEventType => "democracy_voting_over"; + + [DataMember(Name = "winning_button_sequence")] public string WinningSequence { get; init; } + public DemocracyVotingOver(InputSequence input) => WinningSequence = input.ToRepresentation(); + } } diff --git a/TPP.Inputting/InputSequence.cs b/TPP.Inputting/InputSequence.cs index 1a9486b9..710ee960 100644 --- a/TPP.Inputting/InputSequence.cs +++ b/TPP.Inputting/InputSequence.cs @@ -33,5 +33,8 @@ public bool HasSameOutcomeAs(InputSequence other) } public override string ToString() => $"{nameof(InputSequence)}({string.Join(", ", InputSets)})"; + + /// Returns a string that when parsed would result in the same input sequence as this. + public string ToRepresentation() => string.Join("", InputSets.Select(set => set.ToRepresentation())); } } diff --git a/TPP.Inputting/InputSet.cs b/TPP.Inputting/InputSet.cs index f3edcbe0..baed3739 100644 --- a/TPP.Inputting/InputSet.cs +++ b/TPP.Inputting/InputSet.cs @@ -30,6 +30,9 @@ public bool HasSameOutcomeAs(InputSet other) } public override string ToString() => string.Join("+", Inputs); + + /// Returns a string that when parsed would result in the same input set as this. + public string ToRepresentation() => string.Join("+", Inputs.Select(input => input.OriginalText)); } /// From 093caee34d9bf86b5d2ca812fca5567c54b60ec4 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 20:51:19 +0200 Subject: [PATCH 5/8] InputSequence ToRepresentation: collapse repeats by default --- TPP.Inputting/InputSequence.cs | 11 ++++++- TPP.Inputting/LinqExtensions.cs | 51 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 TPP.Inputting/LinqExtensions.cs diff --git a/TPP.Inputting/InputSequence.cs b/TPP.Inputting/InputSequence.cs index 710ee960..4e7c3f1a 100644 --- a/TPP.Inputting/InputSequence.cs +++ b/TPP.Inputting/InputSequence.cs @@ -34,7 +34,16 @@ public bool HasSameOutcomeAs(InputSequence other) public override string ToString() => $"{nameof(InputSequence)}({string.Join(", ", InputSets)})"; + /// /// Returns a string that when parsed would result in the same input sequence as this. - public string ToRepresentation() => string.Join("", InputSets.Select(set => set.ToRepresentation())); + /// + /// Whether repeats like 'aaaab' should be collapsed to 'a4b'. + /// + public string ToRepresentation(bool collapseRepeats = true) => collapseRepeats + ? string.Join("", InputSets + .GroupAdjacent((set1, set2) => set1.HasSameOutcomeAs(set2)) + .Select(grp => grp.Key.ToRepresentation() + + grp.Count() switch { 1 => "", var more => more.ToString() })) + : string.Join("", InputSets.Select(set => set.ToRepresentation())); } } diff --git a/TPP.Inputting/LinqExtensions.cs b/TPP.Inputting/LinqExtensions.cs new file mode 100644 index 00000000..69c9ddaa --- /dev/null +++ b/TPP.Inputting/LinqExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace TPP.Inputting; + +public static class LinqExtensions +{ + private sealed class Group : IGrouping + { + public T Elem { get; } + public List Members { get; } + public Group(T elem, List members) + { + Elem = elem; + Members = members; + } + + public T Key => Elem; + public IEnumerator GetEnumerator() => Members.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)Members).GetEnumerator(); + } + + /// + /// Inspired by MoreLINQ's GroupAdjacent. + /// Might want to replace with proper dependency on `MoreLINQ` once more than this is needed. + /// + public static IEnumerable> GroupAdjacent( + this IEnumerable source, + Func equalityComparer) + { + Group? group = null; + foreach (T element in source) + { + if (group != null) + { + if (equalityComparer(group.Elem, element)) + { + group.Members.Add(element); + continue; + } + else + yield return group; + } + group = new Group(element, new List { element }); + } + if (group != null) + yield return group; + } +} From 1c7a206a95f89562911a6719d0206111f378f126 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 21:22:14 +0200 Subject: [PATCH 6/8] add a few tests regarding input representation rendering --- .../InputRepresentationTest.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tests/TPP.Inputting.Tests/InputRepresentationTest.cs diff --git a/tests/TPP.Inputting.Tests/InputRepresentationTest.cs b/tests/TPP.Inputting.Tests/InputRepresentationTest.cs new file mode 100644 index 00000000..19215d29 --- /dev/null +++ b/tests/TPP.Inputting.Tests/InputRepresentationTest.cs @@ -0,0 +1,81 @@ +using System.Collections.Immutable; +using System.Linq; +using NUnit.Framework; +using TPP.Inputting.Inputs; +using TPP.Inputting.Parsing; + +namespace TPP.Inputting.Tests; + +public class InputRepresentationTest +{ + private static InputSet Set(params string[] inputs) => + new(inputs.Select(s => new Input(s, s, s)).ToImmutableList()); + private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList()); + + [Test] + public void repeats_collapsing_buttons() + { + InputSequence seq = Seq(Set("x"), Set("a", "b"), Set("b", "a"), Set("a", "b"), Set("x")); + Assert.AreEqual("xa+b3x", seq.ToRepresentation(collapseRepeats: true)); + Assert.AreEqual("xa+bb+aa+bx", seq.ToRepresentation(collapseRepeats: false)); + } + + [Test] + public void repeats_collapsing_touchscreen() + { + InputSet TouchSet(uint x, uint y) => + new(ImmutableList.Create(new TouchscreenInput($"{x},{y}", "touchscreen", $"{x},{y}", x, y))); + InputSequence seq = Seq(Set("x"), TouchSet(123, 234), TouchSet(123, 234), TouchSet(321, 432), Set("x")); + // This is hilariously awful, but technically correct + Assert.AreEqual("x123,2342321,432x", seq.ToRepresentation(collapseRepeats: true)); + Assert.AreEqual("x123,234123,234321,432x", seq.ToRepresentation(collapseRepeats: false)); + } + + [Test] + public void repeats_collapsing_analog() + { + InputSet AnalogSet(float strength) => new(ImmutableList.Create( + new AnalogInput($"A.{strength * 10:F0}", "A", $"a.{strength * 10:F0}", strength))); + InputSequence seq = Seq(Set("x"), AnalogSet(.5f), AnalogSet(.5f), AnalogSet(.6f), Set("x")); + Assert.AreEqual("xa.52a.6x", seq.ToRepresentation(collapseRepeats: true)); + Assert.AreEqual("xa.5a.5a.6x", seq.ToRepresentation(collapseRepeats: false)); + } + + [Test] + public void represent_keeps_touch_coordinates() + { + IInputParser parser = InputParserBuilder.FromBare() + .Buttons("A", "B") + .LengthRestrictions(maxSetLength: 1, maxSequenceLength: 5) + .Touchscreen(400, 300, true, true) + .Build(); + InputSequence? seq = parser.Parse("a123,234b123,234>234,123a"); + Assert.That(seq?.InputSets.Count, Is.EqualTo(5)); + Assert.AreEqual("A", seq?.InputSets[0].Inputs[0].ButtonName); + Assert.That(seq?.InputSets[1].Inputs[0], Is.InstanceOf()); + Assert.AreEqual("B", seq?.InputSets[2].Inputs[0].ButtonName); + Assert.That(seq?.InputSets[3].Inputs[0], Is.InstanceOf()); + Assert.AreEqual("A", seq?.InputSets[4].Inputs[0].ButtonName); + Assert.NotNull(seq); + Assert.AreEqual("a123,234b123,234>234,123a", seq?.ToRepresentation()); + } + + [Test] + public void represent_keeps_analog_strength() + { + IInputParser parser = InputParserBuilder.FromBare() + .Buttons("A", "B") + .LengthRestrictions(maxSetLength: 1, maxSequenceLength: 5) + .AnalogInputs("X", "Y") + .Build(); + InputSequence? seq = parser.Parse("ax.1bya"); + Assert.That(seq?.InputSets.Count, Is.EqualTo(5)); + Assert.AreEqual("A", seq?.InputSets[0].Inputs[0].ButtonName); + Assert.That(seq?.InputSets[1].Inputs[0], Is.InstanceOf()); + Assert.AreEqual("B", seq?.InputSets[2].Inputs[0].ButtonName); + Assert.That(seq?.InputSets[3].Inputs[0], Is.InstanceOf()); + Assert.AreEqual("A", seq?.InputSets[4].Inputs[0].ButtonName); + Assert.NotNull(seq); + Assert.AreEqual("ax.1bya", seq?.ToRepresentation()); + } +} From 29cca7574f9e7a5cf7ccbf0a551823bf6c2acfa1 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 21:56:00 +0200 Subject: [PATCH 7/8] remove `ToRepresentation`, remember original texts instead Trying to turn the input sequence back into its original form was very error-prone, e.g. it didn't manage the `hold` special case right and would instead output `a+-`. I figured it'd be best to just remember the original texts instead. --- TPP.Core/Overlay/Events/RunInputEvents.cs | 6 +- TPP.Inputting/InputHoldTiming.cs | 2 +- TPP.Inputting/InputSequence.cs | 23 +-- TPP.Inputting/InputSet.cs | 13 +- TPP.Inputting/Parsing/BareInputParser.cs | 19 ++- TPP.Inputting/Parsing/SidedInputParser.cs | 3 +- .../TPP.Inputting.Tests/InputEqualityTest.cs | 13 +- .../InputHoldTimingTest.cs | 4 +- .../InputRepresentationTest.cs | 81 ----------- .../Parsing/BareInputParserTest.cs | 136 +++++++++--------- .../Parsing/ContextualInputParserTest.cs | 15 +- 11 files changed, 122 insertions(+), 193 deletions(-) delete mode 100644 tests/TPP.Inputting.Tests/InputRepresentationTest.cs diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index 43c69524..0df3d934 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -105,9 +105,9 @@ public DemocracyVotesUpdate( InputSequence votedInput, IReadOnlyDictionary votes) { - NewVote = new NewVote { User = newVoteUser, Command = votedInput.ToRepresentation() }; + NewVote = new NewVote { User = newVoteUser, Command = votedInput.OriginalText }; Votes = votes - .Select(kvp => new Vote { Command = kvp.Key.ToRepresentation(), Count = kvp.Value }) + .Select(kvp => new Vote { Command = kvp.Key.OriginalText, Count = kvp.Value }) .OrderBy(vote => vote.Count) .ToList(); } @@ -128,6 +128,6 @@ public sealed class DemocracyVotingOver : IOverlayEvent public string OverlayEventType => "democracy_voting_over"; [DataMember(Name = "winning_button_sequence")] public string WinningSequence { get; init; } - public DemocracyVotingOver(InputSequence input) => WinningSequence = input.ToRepresentation(); + public DemocracyVotingOver(InputSequence input) => WinningSequence = input.OriginalText; } } diff --git a/TPP.Inputting/InputHoldTiming.cs b/TPP.Inputting/InputHoldTiming.cs index 30d1cb29..1bf29aa1 100644 --- a/TPP.Inputting/InputHoldTiming.cs +++ b/TPP.Inputting/InputHoldTiming.cs @@ -51,7 +51,7 @@ public TimedInputSet TimeInput(InputSet inputSet, float duration) sleepDuration = _minSleepDuration; } - return new TimedInputSet(new InputSet(inputsWithoutHold), holdDuration, sleepDuration); + return new TimedInputSet(inputSet with { Inputs = inputsWithoutHold }, holdDuration, sleepDuration); } } } diff --git a/TPP.Inputting/InputSequence.cs b/TPP.Inputting/InputSequence.cs index 4e7c3f1a..0c9533f8 100644 --- a/TPP.Inputting/InputSequence.cs +++ b/TPP.Inputting/InputSequence.cs @@ -8,11 +8,13 @@ namespace TPP.Inputting /// An input sequence is a sequence of that are inputted in sequence. /// This is used e.g. in democracy mode. /// - public sealed record InputSequence(IImmutableList InputSets) + public sealed record InputSequence(IImmutableList InputSets, string OriginalText) { // Need to manually define these, because lists don't implement a proper Equals and GetHashCode themselves. - public bool Equals(InputSequence? other) => other != null && InputSets.SequenceEqual(other.InputSets); - public override int GetHashCode() => InputSets.Select(i => i.GetHashCode()).Aggregate(HashCode.Combine); + public bool Equals(InputSequence? other) => + other != null && InputSets.SequenceEqual(other.InputSets) && OriginalText == other.OriginalText; + public override int GetHashCode() => + InputSets.Select(i => i.GetHashCode()).Aggregate(HashCode.Combine) + OriginalText.GetHashCode(); /// /// Determines whether this input sequence is effectively equal to another input sequence, @@ -32,18 +34,7 @@ public bool HasSameOutcomeAs(InputSequence other) return true; } - public override string ToString() => $"{nameof(InputSequence)}({string.Join(", ", InputSets)})"; - - /// - /// Returns a string that when parsed would result in the same input sequence as this. - /// - /// Whether repeats like 'aaaab' should be collapsed to 'a4b'. - /// - public string ToRepresentation(bool collapseRepeats = true) => collapseRepeats - ? string.Join("", InputSets - .GroupAdjacent((set1, set2) => set1.HasSameOutcomeAs(set2)) - .Select(grp => grp.Key.ToRepresentation() + - grp.Count() switch { 1 => "", var more => more.ToString() })) - : string.Join("", InputSets.Select(set => set.ToRepresentation())); + public override string ToString() => + $"{nameof(InputSequence)}([{string.Join(", ", InputSets)}] '{OriginalText}')"; } } diff --git a/TPP.Inputting/InputSet.cs b/TPP.Inputting/InputSet.cs index baed3739..6144c1f6 100644 --- a/TPP.Inputting/InputSet.cs +++ b/TPP.Inputting/InputSet.cs @@ -10,11 +10,13 @@ namespace TPP.Inputting /// An input set is a set of inputs being inputted simultaneously. /// These include buttons, touch screen coordinates, waits etc. /// - public sealed record InputSet(ImmutableList Inputs) + public sealed record InputSet(ImmutableList Inputs, string OriginalText) { // Need to manually define these, because lists don't implement a proper Equals and GetHashCode themselves. - public bool Equals(InputSet? other) => other != null && Inputs.SequenceEqual(other.Inputs); - public override int GetHashCode() => Inputs.Select(i => i.GetHashCode()).Aggregate(HashCode.Combine); + public bool Equals(InputSet? other) => + other != null && Inputs.SequenceEqual(other.Inputs) && OriginalText == other.OriginalText; + public override int GetHashCode() => + Inputs.Select(i => i.GetHashCode()).Aggregate(HashCode.Combine) + OriginalText.GetHashCode(); /// /// Determines whether this input set is effectively equal to another input set, @@ -29,10 +31,7 @@ public bool HasSameOutcomeAs(InputSet other) return new HashSet(Inputs, SameOutcomeComparer.Instance).SetEquals(other.Inputs); } - public override string ToString() => string.Join("+", Inputs); - - /// Returns a string that when parsed would result in the same input set as this. - public string ToRepresentation() => string.Join("+", Inputs.Select(input => input.OriginalText)); + public override string ToString() => $"{nameof(InputSet)}({string.Join("+", Inputs)} '{OriginalText}')"; } /// diff --git a/TPP.Inputting/Parsing/BareInputParser.cs b/TPP.Inputting/Parsing/BareInputParser.cs index e4033e5f..14313014 100644 --- a/TPP.Inputting/Parsing/BareInputParser.cs +++ b/TPP.Inputting/Parsing/BareInputParser.cs @@ -62,9 +62,9 @@ public BareInputParser( } // Get the indexes that each input set ends at - IEnumerable inputSetEndIndexes = match.Groups["inputset"].Captures + IEnumerable<(int, int)> inputSetRanges = match.Groups["inputset"].Captures .OrderBy(c => c.Index) - .Select(c => c.Index + c.Length); + .Select(c => (c.Index, c.Index + c.Length)); // Get all captures as queues for easy consumption Dictionary> defsToCaptureQueues = _inputDefinitions .Select((def, i) => @@ -75,7 +75,7 @@ public BareInputParser( var capturesRepeat = new Queue(match.Groups["repeat"].Captures.OrderBy(c => c.Index)); var inputSets = new List(); - foreach (int endIndex in inputSetEndIndexes) + foreach ((int startIndex, int endIndex) in inputSetRanges) { var inputs = new List(); var inputWithIndexes = new List<(int, Input)>(); @@ -99,12 +99,19 @@ public BareInputParser( inputs.Add(HoldInput.Instance); capturesHold.Dequeue(); } + string originalText; int numRepeat = 1; - if (capturesRepeat.Any() && capturesRepeat.Peek().Index < endIndex) + int capturesRepeatIndex = capturesRepeat.Any() ? capturesRepeat.Peek().Index : endIndex; + if (capturesRepeatIndex < endIndex) { numRepeat = int.Parse(capturesRepeat.Dequeue().Value); + originalText = text[startIndex..capturesRepeatIndex]; } - var inputSet = new InputSet(inputs.ToImmutableList()); + else + { + originalText = text[startIndex..endIndex]; + } + var inputSet = new InputSet(inputs.ToImmutableList(), originalText); inputSets.AddRange(Enumerable.Repeat(inputSet, numRepeat)); // we need to check the length, because the regex cannot enforce the max length since the sequence may // have been lengthened with a specified number of repetitions for a button set. @@ -113,7 +120,7 @@ public BareInputParser( return null; } } - return new InputSequence(inputSets.ToImmutableList()); + return new InputSequence(inputSets.ToImmutableList(), text); } } } diff --git a/TPP.Inputting/Parsing/SidedInputParser.cs b/TPP.Inputting/Parsing/SidedInputParser.cs index 20295cdb..aedd56c6 100644 --- a/TPP.Inputting/Parsing/SidedInputParser.cs +++ b/TPP.Inputting/Parsing/SidedInputParser.cs @@ -46,7 +46,8 @@ public SidedInputParser(IInputParser delegateParser) bool direct = inputSide != null; var sideInput = new SideInput(inputSide, direct); return new InputSequence(inputSequence.InputSets - .Select(set => new InputSet(set.Inputs.Append(sideInput).ToImmutableList())).ToImmutableList()); + .Select(set => set with { Inputs = set.Inputs.Append(sideInput).ToImmutableList() }).ToImmutableList(), + text); } } } diff --git a/tests/TPP.Inputting.Tests/InputEqualityTest.cs b/tests/TPP.Inputting.Tests/InputEqualityTest.cs index 4c51dbb3..72fad6ab 100644 --- a/tests/TPP.Inputting.Tests/InputEqualityTest.cs +++ b/tests/TPP.Inputting.Tests/InputEqualityTest.cs @@ -8,9 +8,10 @@ namespace TPP.Inputting.Tests public class InputEqualityTest { private static InputSet Set(params string[] inputs) => - new(inputs.Select(s => new Input(s, s, s)).ToImmutableList()); + new(inputs.Select(s => new Input(s, s, s)).ToImmutableList(), string.Join('+', inputs)); - private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList()); + private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList(), + string.Join("", inputSets.Select(i => i.OriginalText))); [Test] public void TestSameOutcomeRegularInput() @@ -136,10 +137,10 @@ public void TestSameOutcomeInputSet() var input4A = new Input("Foo", "a", "foo"); var input4B = new Input("Bar", "a", "bar"); - var setRef = new InputSet(ImmutableList.Create(inputRefA, inputRefB)); - var setDifferentOrder = new InputSet(ImmutableList.Create(input1A, input1B)); - var setDifferentLength = new InputSet(ImmutableList.Create(input2)); - var setDifferentEffectiveInput = new InputSet(ImmutableList.Create(input4A, input4B)); + var setRef = new InputSet(ImmutableList.Create(inputRefA, inputRefB), "foo+bar"); + var setDifferentOrder = new InputSet(ImmutableList.Create(input1A, input1B), "baz+quz"); + var setDifferentLength = new InputSet(ImmutableList.Create(input2), "foo"); + var setDifferentEffectiveInput = new InputSet(ImmutableList.Create(input4A, input4B), "foo+bar"); Assert.AreNotEqual(setRef, setDifferentOrder); Assert.IsTrue(setRef.HasSameOutcomeAs(setDifferentOrder)); diff --git a/tests/TPP.Inputting.Tests/InputHoldTimingTest.cs b/tests/TPP.Inputting.Tests/InputHoldTimingTest.cs index 757120f5..bff72100 100644 --- a/tests/TPP.Inputting.Tests/InputHoldTimingTest.cs +++ b/tests/TPP.Inputting.Tests/InputHoldTimingTest.cs @@ -9,10 +9,10 @@ public class InputHoldTimingTest private const float Delta = 1 / 600f; private static readonly InputSet DummyInput = - new(ImmutableList.Create(new Input("A", "A", "A"))); + new(ImmutableList.Create(new Input("A", "A", "A")), "A"); private static readonly InputSet DummyInputHeld = - new(ImmutableList.Create(new Input("A", "A", "A"), HoldInput.Instance)); + new(ImmutableList.Create(new Input("A", "A", "A"), HoldInput.Instance), "A-"); [Test] public void regular_with_spare_time_divides_normally() diff --git a/tests/TPP.Inputting.Tests/InputRepresentationTest.cs b/tests/TPP.Inputting.Tests/InputRepresentationTest.cs deleted file mode 100644 index 19215d29..00000000 --- a/tests/TPP.Inputting.Tests/InputRepresentationTest.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Immutable; -using System.Linq; -using NUnit.Framework; -using TPP.Inputting.Inputs; -using TPP.Inputting.Parsing; - -namespace TPP.Inputting.Tests; - -public class InputRepresentationTest -{ - private static InputSet Set(params string[] inputs) => - new(inputs.Select(s => new Input(s, s, s)).ToImmutableList()); - private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList()); - - [Test] - public void repeats_collapsing_buttons() - { - InputSequence seq = Seq(Set("x"), Set("a", "b"), Set("b", "a"), Set("a", "b"), Set("x")); - Assert.AreEqual("xa+b3x", seq.ToRepresentation(collapseRepeats: true)); - Assert.AreEqual("xa+bb+aa+bx", seq.ToRepresentation(collapseRepeats: false)); - } - - [Test] - public void repeats_collapsing_touchscreen() - { - InputSet TouchSet(uint x, uint y) => - new(ImmutableList.Create(new TouchscreenInput($"{x},{y}", "touchscreen", $"{x},{y}", x, y))); - InputSequence seq = Seq(Set("x"), TouchSet(123, 234), TouchSet(123, 234), TouchSet(321, 432), Set("x")); - // This is hilariously awful, but technically correct - Assert.AreEqual("x123,2342321,432x", seq.ToRepresentation(collapseRepeats: true)); - Assert.AreEqual("x123,234123,234321,432x", seq.ToRepresentation(collapseRepeats: false)); - } - - [Test] - public void repeats_collapsing_analog() - { - InputSet AnalogSet(float strength) => new(ImmutableList.Create( - new AnalogInput($"A.{strength * 10:F0}", "A", $"a.{strength * 10:F0}", strength))); - InputSequence seq = Seq(Set("x"), AnalogSet(.5f), AnalogSet(.5f), AnalogSet(.6f), Set("x")); - Assert.AreEqual("xa.52a.6x", seq.ToRepresentation(collapseRepeats: true)); - Assert.AreEqual("xa.5a.5a.6x", seq.ToRepresentation(collapseRepeats: false)); - } - - [Test] - public void represent_keeps_touch_coordinates() - { - IInputParser parser = InputParserBuilder.FromBare() - .Buttons("A", "B") - .LengthRestrictions(maxSetLength: 1, maxSequenceLength: 5) - .Touchscreen(400, 300, true, true) - .Build(); - InputSequence? seq = parser.Parse("a123,234b123,234>234,123a"); - Assert.That(seq?.InputSets.Count, Is.EqualTo(5)); - Assert.AreEqual("A", seq?.InputSets[0].Inputs[0].ButtonName); - Assert.That(seq?.InputSets[1].Inputs[0], Is.InstanceOf()); - Assert.AreEqual("B", seq?.InputSets[2].Inputs[0].ButtonName); - Assert.That(seq?.InputSets[3].Inputs[0], Is.InstanceOf()); - Assert.AreEqual("A", seq?.InputSets[4].Inputs[0].ButtonName); - Assert.NotNull(seq); - Assert.AreEqual("a123,234b123,234>234,123a", seq?.ToRepresentation()); - } - - [Test] - public void represent_keeps_analog_strength() - { - IInputParser parser = InputParserBuilder.FromBare() - .Buttons("A", "B") - .LengthRestrictions(maxSetLength: 1, maxSequenceLength: 5) - .AnalogInputs("X", "Y") - .Build(); - InputSequence? seq = parser.Parse("ax.1bya"); - Assert.That(seq?.InputSets.Count, Is.EqualTo(5)); - Assert.AreEqual("A", seq?.InputSets[0].Inputs[0].ButtonName); - Assert.That(seq?.InputSets[1].Inputs[0], Is.InstanceOf()); - Assert.AreEqual("B", seq?.InputSets[2].Inputs[0].ButtonName); - Assert.That(seq?.InputSets[3].Inputs[0], Is.InstanceOf()); - Assert.AreEqual("A", seq?.InputSets[4].Inputs[0].ButtonName); - Assert.NotNull(seq); - Assert.AreEqual("ax.1bya", seq?.ToRepresentation()); - } -} diff --git a/tests/TPP.Inputting.Tests/Parsing/BareInputParserTest.cs b/tests/TPP.Inputting.Tests/Parsing/BareInputParserTest.cs index d39ed9f4..e1cb1d04 100644 --- a/tests/TPP.Inputting.Tests/Parsing/BareInputParserTest.cs +++ b/tests/TPP.Inputting.Tests/Parsing/BareInputParserTest.cs @@ -9,9 +9,10 @@ namespace TPP.Inputting.Tests.Parsing public class BareInputParserTest { private static InputSet Set(params string[] inputs) => - new(inputs.Select(s => new Input(s, s, s)).ToImmutableList()); + new(inputs.Select(s => new Input(s, s, s)).ToImmutableList(), string.Join('+', inputs)); - private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList()); + private static InputSequence Seq(string text, params InputSet[] inputSets) => + new(inputSets.ToImmutableList(), text); private IInputParser _inputParser = null!; @@ -29,12 +30,13 @@ public void TestBasicInputs() .Build(); // good cases - AssertInput("aa", Seq(Set("a"), Set("a"))); - AssertInput("a2a", Seq(Set("a"), Set("a"), Set("a"))); - AssertInput("a2a2", Seq(Set("a"), Set("a"), Set("a"), Set("a"))); - AssertInput("start4", Seq(Set("start"), Set("start"), Set("start"), Set("start"))); - AssertInput("a+b2startselect", Seq(Set("a", "b"), Set("a", "b"), Set("start"), Set("select"))); - AssertInput("b+a", Seq(Set("b", "a"))); // order is preserved + AssertInput("aa", Seq("aa", Set("a"), Set("a"))); + AssertInput("a2a", Seq("a2a", Set("a"), Set("a"), Set("a"))); + AssertInput("a2a2", Seq("a2a2", Set("a"), Set("a"), Set("a"), Set("a"))); + AssertInput("start4", Seq("start4", Set("start"), Set("start"), Set("start"), Set("start"))); + AssertInput("a+b2startselect", + Seq("a+b2startselect", Set("a", "b"), Set("a", "b"), Set("start"), Set("select"))); + AssertInput("b+a", Seq("b+a", Set("b", "a"))); // order is preserved // bad cases AssertInput("x", null); // not a button @@ -51,15 +53,15 @@ public void TestHold() // hold enabled _inputParser = builder.HoldEnabled(true).Build(); - AssertInput("a", Seq(Set("a"))); - Assert.AreEqual(Seq(new InputSet(ImmutableList.Create( - new Input("a", "a", "a"), - HoldInput.Instance)) + AssertInput("a", Seq("a", Set("a"))); + Assert.AreEqual(Seq("a-", new InputSet(ImmutableList.Create( + new Input("a", "a", "a"), + HoldInput.Instance), "a-") ), _inputParser.Parse("a-")); // hold disabled _inputParser = builder.HoldEnabled(false).Build(); - AssertInput("a", Seq(Set("a"))); + AssertInput("a", Seq("a", Set("a"))); AssertInput("a-", null); } @@ -73,9 +75,9 @@ public void TestAlias() .Build(); InputSequence? result = _inputParser.Parse("honky"); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("honk", "y", "honk"))), - new InputSet(ImmutableList.Create(new Input("y", "y", "y"))) + Assert.AreEqual(Seq("honky", + new InputSet(ImmutableList.Create(new Input("honk", "y", "honk")), "honk"), + new InputSet(ImmutableList.Create(new Input("y", "y", "y")), "y") ), result); } @@ -89,9 +91,9 @@ public void TestRemapping() .Build(); InputSequence? result = _inputParser.Parse("honky"); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("y", "y", "honk"))), - new InputSet(ImmutableList.Create(new Input("y", "y", "y"))) + Assert.AreEqual(Seq("honky", + new InputSet(ImmutableList.Create(new Input("y", "y", "honk")), "honk"), + new InputSet(ImmutableList.Create(new Input("y", "y", "y")), "y") ), result); } @@ -104,28 +106,28 @@ public void TestTouchscreen() .LengthRestrictions(maxSetLength: 2, maxSequenceLength: 4) .Build(); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))), + Assert.AreEqual(Seq("a234,123a", + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a"), new InputSet(ImmutableList.Create( - new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123))), - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))) + new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123)), "234,123"), + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a") ), _inputParser.Parse("a234,123a")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("239,159", new InputSet(ImmutableList.Create( - new TouchscreenInput("239,159", "touchscreen", "239,159", 239, 159))) + new TouchscreenInput("239,159", "touchscreen", "239,159", 239, 159)), "239,159") ), _inputParser.Parse("239,159")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("0,0", new InputSet(ImmutableList.Create( - new TouchscreenInput("0,0", "touchscreen", "0,0", 0, 0))) + new TouchscreenInput("0,0", "touchscreen", "0,0", 0, 0)), "0,0") ), _inputParser.Parse("0,0")); // allow leading zeroes - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("000,000", new InputSet(ImmutableList.Create( - new TouchscreenInput("000,000", "touchscreen", "000,000", 0, 0))) + new TouchscreenInput("000,000", "touchscreen", "000,000", 0, 0)), "000,000") ), _inputParser.Parse("000,000")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("012,023", new InputSet(ImmutableList.Create( - new TouchscreenInput("012,023", "touchscreen", "012,023", 12, 23))) + new TouchscreenInput("012,023", "touchscreen", "012,023", 12, 23)), "012,023") ), _inputParser.Parse("012,023")); // out of bounds Assert.IsNull(_inputParser.Parse("240,159")); @@ -144,24 +146,28 @@ public void TestTouchscreenDrag() .LengthRestrictions(maxSetLength: 2, maxSequenceLength: 4) .Build(); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))), + Assert.AreEqual(Seq("a234,123>222,111a", + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a"), new InputSet(ImmutableList.Create( - new TouchscreenDragInput("234,123>222,111", "touchscreen", "234,123>222,111", 234, 123, 222, 111))), - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))) + new TouchscreenDragInput("234,123>222,111", "touchscreen", "234,123>222,111", 234, 123, 222, + 111)), + "234,123>222,111"), + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a") ), _inputParser.Parse("a234,123>222,111a")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("239,159>0,0", new InputSet(ImmutableList.Create( - new TouchscreenDragInput("239,159>0,0", "touchscreen", "239,159>0,0", 239, 159, 0, 0))) + new TouchscreenDragInput("239,159>0,0", "touchscreen", "239,159>0,0", 239, 159, 0, 0)), + "239,159>0,0") ), _inputParser.Parse("239,159>0,0")); // allow leading zeroes - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("000,000>000,000", new InputSet(ImmutableList.Create( - new TouchscreenDragInput("000,000>000,000", "touchscreen", "000,000>000,000", 0, 0, 0, 0))) + new TouchscreenDragInput("000,000>000,000", "touchscreen", "000,000>000,000", 0, 0, 0, 0)), + "000,000>000,000") ), _inputParser.Parse("000,000>000,000")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("012,023>0,0", new InputSet(ImmutableList.Create( - new TouchscreenDragInput("012,023>0,0", "touchscreen", "012,023>0,0", 12, 23, 0, 0))) + new TouchscreenDragInput("012,023>0,0", "touchscreen", "012,023>0,0", 12, 23, 0, 0)), "012,023>0,0") ), _inputParser.Parse("012,023>0,0")); // out of bounds Assert.IsNull(_inputParser.Parse("240,159>0,0")); @@ -192,19 +198,19 @@ public void TestTouchscreenAlias() .LengthRestrictions(maxSetLength: 2, maxSequenceLength: 4) .Build(); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))), + Assert.AreEqual(Seq("amove12a", + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a"), new InputSet(ImmutableList.Create( - new TouchscreenInput("move1", "touchscreen", "move1", 42, 69))), + new TouchscreenInput("move1", "touchscreen", "move1", 42, 69)), "move1"), new InputSet(ImmutableList.Create( - new TouchscreenInput("move1", "touchscreen", "move1", 42, 69))), - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))) + new TouchscreenInput("move1", "touchscreen", "move1", 42, 69)), "move1"), + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a") ), _inputParser.Parse("amove12a")); - Assert.AreEqual(Seq( + Assert.AreEqual(Seq("234,123move1", new InputSet(ImmutableList.Create( - new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123))), + new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123)), "234,123"), new InputSet(ImmutableList.Create( - new TouchscreenInput("move1", "touchscreen", "move1", 42, 69))) + new TouchscreenInput("move1", "touchscreen", "move1", 42, 69)), "move1") ), _inputParser.Parse("234,123move1")); Assert.IsNull(_inputParser.Parse("234,123+move1")); } @@ -218,17 +224,17 @@ public void TestAnalog() .LengthRestrictions(maxSetLength: 2, maxSequenceLength: 4) .Build(); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))), - new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.2", 0.2f))), - new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.2", 0.2f))), - new InputSet(ImmutableList.Create(new Input("a", "a", "a"))) + Assert.AreEqual(Seq("aup.22a", + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a"), + new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.2", 0.2f)), "up.2"), + new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.2", 0.2f)), "up.2"), + new InputSet(ImmutableList.Create(new Input("a", "a", "a")), "a") ), _inputParser.Parse("aup.22a")); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "UP", 1.0f))) + Assert.AreEqual(Seq("UP", + new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "UP", 1.0f)), "UP") ), _inputParser.Parse("UP")); - Assert.AreEqual(Seq( - new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.9", 0.9f))) + Assert.AreEqual(Seq("up.9", + new InputSet(ImmutableList.Create(new AnalogInput("up", "up", "up.9", 0.9f)), "up.9") ), _inputParser.Parse("up.9")); Assert.IsNull(_inputParser.Parse("up.")); Assert.IsNull(_inputParser.Parse("up.0")); @@ -248,17 +254,19 @@ public void TestRetainCase() .Build(); Assert.AreEqual( - Seq(new InputSet(ImmutableList.Create(new Input("A", "A", "a")))), _inputParser.Parse("a")); + Seq("a", new InputSet(ImmutableList.Create(new Input("A", "A", "a")), "a")), _inputParser.Parse("a")); Assert.AreEqual( - Seq(new InputSet(ImmutableList.Create(new Input("B", "X", "b")))), _inputParser.Parse("b")); + Seq("b", new InputSet(ImmutableList.Create(new Input("B", "X", "b")), "b")), _inputParser.Parse("b")); Assert.AreEqual( - Seq(new InputSet(ImmutableList.Create(new Input("Y", "Y", "c")))), _inputParser.Parse("c")); + Seq("c", new InputSet(ImmutableList.Create(new Input("Y", "Y", "c")), "c")), _inputParser.Parse("c")); Assert.AreEqual( - Seq(new InputSet(ImmutableList.Create(new AnalogInput("UP", "UP", "up.2", 0.2f)))), + Seq("up.2", + new InputSet(ImmutableList.Create(new AnalogInput("UP", "UP", "up.2", 0.2f)), "up.2")), _inputParser.Parse("up.2")); Assert.AreEqual( - Seq(new InputSet( - ImmutableList.Create(new TouchscreenInput("MOVE1", "touchscreen", "move1", 10, 20)))), + Seq("move1", new InputSet( + ImmutableList.Create(new TouchscreenInput("MOVE1", "touchscreen", "move1", 10, 20)), + "move1")), _inputParser.Parse("move1")); } } diff --git a/tests/TPP.Inputting.Tests/Parsing/ContextualInputParserTest.cs b/tests/TPP.Inputting.Tests/Parsing/ContextualInputParserTest.cs index b4583b4b..d4e44420 100644 --- a/tests/TPP.Inputting.Tests/Parsing/ContextualInputParserTest.cs +++ b/tests/TPP.Inputting.Tests/Parsing/ContextualInputParserTest.cs @@ -11,9 +11,10 @@ namespace TPP.Inputting.Tests.Parsing public class ContextualInputParserTest { private static InputSet Set(params string[] inputs) => - new(inputs.Select(s => new Input(s, s, s)).ToImmutableList()); + new(inputs.Select(s => new Input(s, s, s)).ToImmutableList(), string.Join('+', inputs)); - private static InputSequence Seq(params InputSet[] inputSets) => new(inputSets.ToImmutableList()); + private static InputSequence Seq(params InputSet[] inputSets) => + new(inputSets.ToImmutableList(), string.Join("", inputSets.Select(set => set.OriginalText))); private IInputParser _inputParser = null!; @@ -48,12 +49,14 @@ public void TestMultitouch() .Build(); Assert.AreEqual(Seq(new InputSet(ImmutableList.Create( - new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123), - new TouchscreenInput("11,22", "touchscreen", "11,22", 11, 22))) + new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123), + new TouchscreenInput("11,22", "touchscreen", "11,22", 11, 22)), + "234,123+11,22") ), _inputParser.Parse("234,123+11,22")); Assert.AreEqual(Seq(new InputSet(ImmutableList.Create( - new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123), - new TouchscreenDragInput("11,22>33,44", "touchscreen", "11,22>33,44", 11, 22, 33, 44))) + new TouchscreenInput("234,123", "touchscreen", "234,123", 234, 123), + new TouchscreenDragInput("11,22>33,44", "touchscreen", "11,22>33,44", 11, 22, 33, 44)), + "234,123+11,22>33,44") ), _inputParser.Parse("234,123+11,22>33,44")); // multitouch disabled From 23cb3eba49fc873cf6312922a8906726eb702184 Mon Sep 17 00:00:00 2001 From: felk Date: Sun, 24 Apr 2022 22:28:18 +0200 Subject: [PATCH 8/8] add `democracy_sequence_start` overlay event --- TPP.Core/Overlay/Events/RunInputEvents.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/TPP.Core/Overlay/Events/RunInputEvents.cs b/TPP.Core/Overlay/Events/RunInputEvents.cs index 0df3d934..5cb1bdd5 100644 --- a/TPP.Core/Overlay/Events/RunInputEvents.cs +++ b/TPP.Core/Overlay/Events/RunInputEvents.cs @@ -130,4 +130,18 @@ public sealed class DemocracyVotingOver : IOverlayEvent [DataMember(Name = "winning_button_sequence")] public string WinningSequence { get; init; } public DemocracyVotingOver(InputSequence input) => WinningSequence = input.OriginalText; } + + [DataContract] + public sealed class DemocracySequenceStart : IOverlayEvent + { + public string OverlayEventType => "democracy_sequence_start"; + + [DataMember(Name = "button_sequence")] public List> Sequence { get; init; } + public DemocracySequenceStart(InputSequence input) + { + Sequence = input.InputSets + .Select(set => set.Inputs + .Select(i => i.DisplayedText).ToList()).ToList(); + } + } }