FoldScorable.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
35 using System;
36 using System.Collections.Generic;
37 using System.Linq;
38 using System.Text;
39 using System.Threading;
40 using System.Threading.Tasks;
41 
42 namespace Microsoft.Bot.Builder.Scorables.Internals
43 {
47  public enum FoldStage
48  {
52  AfterFold,
53 
57  StartPost,
58 
62  AfterPost
63  }
64 
72  public abstract class FoldScorable<Item, Score> : ScorableBase<Item, IReadOnlyList<FoldScorable<Item, Score>.State>, Score>
73  {
77  public delegate bool OnStageDelegate(FoldStage stage, IScorable<Item, Score> scorable, Item item, object state, Score score);
78 
79  protected readonly IComparer<Score> comparer;
80  protected readonly IEnumerable<IScorable<Item, Score>> scorables;
81 
82  public FoldScorable(IComparer<Score> comparer, IEnumerable<IScorable<Item, Score>> scorables)
83  {
84  SetField.NotNull(out this.comparer, nameof(comparer), comparer);
85  SetField.NotNull(out this.scorables, nameof(scorables), scorables);
86  }
87 
94  protected abstract OnStageDelegate OnStage { get; }
95 
99  public struct State
100  {
101  public readonly int ordinal;
103  public readonly object state;
104  public State(int ordinal, IScorable<Item, Score> scorable, object state)
105  {
106  this.ordinal = ordinal;
107  this.scorable = scorable;
108  this.state = state;
109  }
110  }
111 
112  protected override async Task<IReadOnlyList<State>> PrepareAsync(Item item, CancellationToken token)
113  {
114  var states = new List<State>();
115 
116  foreach (var scorable in this.scorables)
117  {
118  var state = await scorable.PrepareAsync(item, token);
119  int ordinal = states.Count;
120  states.Add(new State(ordinal, scorable, state));
121  if (scorable.HasScore(item, state))
122  {
123  var score = scorable.GetScore(item, state);
124  if (!this.OnStage(FoldStage.AfterFold, scorable, item, state, score))
125  {
126  break;
127  }
128  }
129  }
130 
131  states.Sort((one, two) =>
132  {
133  var oneHasScore = one.scorable.HasScore(item, one.state);
134  var twoHasScore = two.scorable.HasScore(item, two.state);
135  if (oneHasScore && twoHasScore)
136  {
137  var oneScore = one.scorable.GetScore(item, one.state);
138  var twoScore = two.scorable.GetScore(item, two.state);
139 
140  // sort largest scores first
141  var compare = this.comparer.Compare(twoScore, oneScore);
142  if (compare != 0)
143  {
144  return compare;
145  }
146  }
147  else if (oneHasScore)
148  {
149  return -1;
150  }
151  else if (twoHasScore)
152  {
153  return +1;
154  }
155 
156  // stable sort otherwise
157  return one.ordinal.CompareTo(two.ordinal);
158  });
159 
160  return states;
161  }
162 
163  protected override bool HasScore(Item item, IReadOnlyList<State> states)
164  {
165  if (states.Count > 0)
166  {
167  var state = states[0];
168  return state.scorable.HasScore(item, state.state);
169  }
170 
171  return false;
172  }
173 
174  protected override Score GetScore(Item item, IReadOnlyList<State> states)
175  {
176  var state = states[0];
177  return state.scorable.GetScore(item, state.state);
178  }
179 
180  protected override async Task PostAsync(Item item, IReadOnlyList<State> states, CancellationToken token)
181  {
182  foreach (var state in states)
183  {
184  if (!state.scorable.HasScore(item, state.state))
185  {
186  break;
187  }
188 
189  var score = state.scorable.GetScore(item, state.state);
190 
191  if (!this.OnStage(FoldStage.StartPost, state.scorable, item, state.state, score))
192  {
193  break;
194  }
195 
196  await state.scorable.PostAsync(item, state.state, token);
197 
198  if (!this.OnStage(FoldStage.AfterPost, state.scorable, item, state.state, score))
199  {
200  break;
201  }
202  }
203  }
204 
205  protected override async Task DoneAsync(Item item, IReadOnlyList<State> states, CancellationToken token)
206  {
207  foreach (var state in states)
208  {
209  await state.scorable.DoneAsync(item, state.state, token);
210  }
211  }
212  }
213 
217  public class DelegatingFoldScorable<Item, Score> : FoldScorable<Item, Score>
218  {
219  private readonly OnStageDelegate onStage;
220  public DelegatingFoldScorable(OnStageDelegate onStage, IComparer<Score> comparer, IEnumerable<IScorable<Item, Score>> scorables)
221  : base(comparer, scorables)
222  {
223  this.onStage = onStage ?? this.OnStageHandler;
224  }
225 
226  protected override OnStageDelegate OnStage => this.onStage;
227 
228  public virtual bool OnStageHandler(FoldStage stage, IScorable<Item, Score> scorable, Item item, object state, Score score)
229  {
230  switch (stage)
231  {
232  case FoldStage.AfterFold: return true;
233  case FoldStage.StartPost: return true;
234  case FoldStage.AfterPost: return false;
235  default: throw new NotImplementedException();
236  }
237  }
238  }
239 
243  public sealed class NullComparer<T> : IComparer<T>
244  {
245  public static readonly IComparer<T> Instance = new NullComparer<T>();
246 
247  private NullComparer()
248  {
249  }
250 
251  int IComparer<T>.Compare(T x, T y)
252  {
253  return 0;
254  }
255  }
256 }
After IScorable.PrepareAsync has been called and the state and score will be folded into the aggregat...
DelegatingFoldScorable(OnStageDelegate onStage, IComparer< Score > comparer, IEnumerable< IScorable< Item, Score >> scorables)
A null comparer that pretends every item is equal. This is particularly useful with stable sorts...
FoldStage
The stage of the FoldScorable events.
Definition: FoldScorable.cs:47
FoldScorable(IComparer< Score > comparer, IEnumerable< IScorable< Item, Score >> scorables)
Definition: FoldScorable.cs:82
Per-scorable opaque state used during scoring process.
Definition: FoldScorable.cs:99
override bool HasScore(Item item, IReadOnlyList< State > states)
Fold an aggregation of scorables to produce a winning scorable.
Definition: FoldScorable.cs:72
Allow for static type checking of opaque state for convenience of scorable implementations.
Definition: ScorableBase.cs:55
override Score GetScore(Item item, IReadOnlyList< State > states)
Namespace for internal machinery that is not useful for most developers and may change in the future...
override async Task< IReadOnlyList< State > > PrepareAsync(Item item, CancellationToken token)
Before IScorable.PostAsync has been called to initiate the next best scorable&#39;s action.
override async Task PostAsync(Item item, IReadOnlyList< State > states, CancellationToken token)
virtual bool OnStageHandler(FoldStage stage, IScorable< Item, Score > scorable, Item item, object state, Score score)
State(int ordinal, IScorable< Item, Score > scorable, object state)
readonly IEnumerable< IScorable< Item, Score > > scorables
Definition: FoldScorable.cs:80
override async Task DoneAsync(Item item, IReadOnlyList< State > states, CancellationToken token)
This scorable delegates the stage event handler to an external delegate or an overridable virtual met...
After IScorable.PostAsync has been called to complete the next best scorable&#39;s action.
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Root namespace for the Microsoft Bot Builder SDK.