using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Crtr.Event; using System.Collections.Generic; namespace Cryville.Crtr { public class Judge { readonly PdtEvaluator _etor; readonly PdtRuleset _rs; readonly Dictionary ct = new Dictionary(); readonly Dictionary> evs = new Dictionary>(); readonly Dictionary> activeEvs = new Dictionary>(); struct JudgeEvent { public float StartTime { get; set; } public float EndTime { get; set; } public float StartClip { get; set; } public float EndClip { get; set; } public JudgeDefinition Definition { get; set; } public ContainerState State { get; set; } } static readonly IComparer _stcmp = new JudgeEventStartTimeComparer(); class JudgeEventStartTimeComparer : IComparer { public int Compare(JudgeEvent x, JudgeEvent y) { return x.StartClip.CompareTo(y.StartClip); } } public Judge(PdtRuleset rs) { _etor = ChartPlayer.etor; _rs = rs; foreach (var s in rs.scores) { var key = s.Key; scoreSrcs.Add(key.Key, new PropSrc.Float(() => scores[key.Key])); scoreOps.Add(key.Key, new PropOp.Float(v => scores[key.Key] = v)); scoreFmtKeys.Add(key.Key, IdentifierManager.SharedInstance.Request("_score_" + (string)key.Name)); scoreDefs.Add(key.Key, s.Value); scores.Add(key.Key, s.Value.init); } } public void Prepare(float st, float et, Identifier input, JudgeDefinition def, ContainerState container) { List list; if (!evs.TryGetValue(input, out list)) { ct.Add(input, 0); evs.Add(input, list = new List()); activeEvs.Add(input, new List()); } var ev = new JudgeEvent { StartTime = st, EndTime = et, StartClip = st + def.clip[0], EndClip = et + def.clip[1], Definition = def, State = container, }; var index = list.BinarySearch(ev, _stcmp); if (index < 0) index = ~index; list.Insert(index, ev); } static bool _flag; static readonly PropOp.Boolean _flagop = new PropOp.Boolean(v => _flag = v); static readonly int _var_fn = IdentifierManager.SharedInstance.Request("fn"); static readonly int _var_tn = IdentifierManager.SharedInstance.Request("tn"); static readonly int _var_ft = IdentifierManager.SharedInstance.Request("ft"); static readonly int _var_tt = IdentifierManager.SharedInstance.Request("tt"); readonly byte[] _numbuf1 = new byte[sizeof(float)]; readonly byte[] _numbuf2 = new byte[sizeof(float)]; readonly byte[] _numbuf3 = new byte[sizeof(float)]; readonly byte[] _numbuf4 = new byte[sizeof(float)]; unsafe void LoadNum(byte[] buffer, float value) { fixed (byte* ptr = buffer) *(float*)ptr = value; } // Adopted from System.Collections.Generic.ArraySortHelper.InternalBinarySearch(T[] array, int index, int length, T value, IComparer comparer) int BinarySearch(List list, float time, int stack) { int num = 0; int num2 = list.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); int num4 = -list[num3].Definition.stack.CompareTo(stack); if (num4 == 0) num4 = list[num3].StartClip.CompareTo(time); if (num4 == 0) return num3; else if (num4 < 0) num = num3 + 1; else num2 = num3 - 1; } return ~num; } int BinarySearchFirst(List list, float time, int stack) { if (list[0].Definition.stack == stack && list[0].StartClip == time) return 0; int num = 0; int num2 = list.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); int num4 = -list[num3].Definition.stack.CompareTo(stack); if (num4 == 0) num4 = list[num3].StartClip.CompareTo(time); if (num4 >= 0) num2 = num3 - 1; else num = num3 + 1; } return num + 1; } public void Feed(Identifier target, float ft, float tt) { Forward(target, tt); var actlist = activeEvs[target]; if (actlist.Count > 0) { LoadNum(_numbuf3, ft); _etor.ContextCascadeUpdate(_var_ft, new PropSrc.Arbitrary(PdtInternalType.Number, _numbuf3)); LoadNum(_numbuf4, tt); _etor.ContextCascadeUpdate(_var_tt, new PropSrc.Arbitrary(PdtInternalType.Number, _numbuf4)); var index = 0; while (index >= 0 && index < actlist.Count) { var ev = actlist[index]; LoadNum(_numbuf1, ev.StartTime); _etor.ContextCascadeUpdate(_var_fn, new PropSrc.Arbitrary(PdtInternalType.Number, _numbuf1)); LoadNum(_numbuf2, ev.EndTime); _etor.ContextCascadeUpdate(_var_tn, new PropSrc.Arbitrary(PdtInternalType.Number, _numbuf2)); var def = ev.Definition; if (def.hit != null) _etor.Evaluate(_flagop, def.hit); else _flag = true; if (_flag) { if (def.scores != null) UpdateScore(def.scores); if (def.pass != null) Pass(def.pass); actlist.RemoveAt(index); if (def.prop != 0 && actlist.Count > 0) { index = BinarySearchFirst(actlist, ev.StartClip, def.stack - def.prop); if (index < 0) index = ~index; } else index++; } else index++; } } } bool Pass(Identifier[] ids) { foreach (var i in ids) { var def = _rs.judges[i]; if (def.hit != null) _etor.Evaluate(_flagop, def.hit); else _flag = true; if (_flag) { if (def.scores != null) UpdateScore(def.scores); if (def.pass != null) Pass(def.pass); return true; } } return false; } public void Cleanup(Identifier target, float ft, float tt) { Forward(target, tt); var actlist = activeEvs[target]; for (int i = actlist.Count - 1; i >= 0; i--) { JudgeEvent ev = actlist[i]; if (tt > ev.EndClip) { actlist.RemoveAt(i); if (ev.Definition.miss != null) Pass(ev.Definition.miss); } } } void Forward(Identifier target, float tt) { var list = evs[target]; var actlist = activeEvs[target]; JudgeEvent ev; while (list.Count > 0 && (ev = list[0]).StartClip <= tt) { list.RemoveAt(0); var index = BinarySearch(actlist, ev.StartClip, ev.Definition.stack); if (index < 0) index = ~index; actlist.Insert(index, ev); } } void UpdateScore(Dictionary scoreops) { foreach (var scoreop in scoreops) { var key = scoreop.Key; _etor.ContextSelfValue = scoreSrcs[key.name.Key]; _etor.Evaluate(scoreOps[key.name.Key], scoreop.Value); scoreSrcs[key.name.Key].Invalidate(); foreach (var s in _rs.scores) { if (s.Value.value != null) { _etor.ContextSelfValue = scoreSrcs[s.Key.Key]; _etor.Evaluate(scoreOps[s.Key.Key], s.Value.value); scoreSrcs[s.Key.Key].Invalidate(); } } } ScoreCache.Clear(); } readonly Dictionary scoreFmtKeys = new Dictionary(); readonly Dictionary scoreSrcs = new Dictionary(); readonly Dictionary scoreOps = new Dictionary(); readonly Dictionary scoreDefs = new Dictionary(); public readonly Dictionary scores = new Dictionary(); readonly Dictionary ScoreCache = new Dictionary(); readonly object _lock = new object(); public Dictionary GetFormattedScoreStrings() { lock (_lock) { if (ScoreCache.Count == 0) { foreach (var s in scores) ScoreCache.Add(scoreFmtKeys[s.Key], s.Value.ToString(scoreDefs[s.Key].format)); } return ScoreCache; } } public string GetFullFormattedScoreString() { bool flag = false; string result = ""; foreach (var s in GetFormattedScoreStrings()) { result += string.Format(flag ? "\n{0}: {1}" : "{0}: {1}", IdentifierManager.SharedInstance.Retrieve(s.Key), s.Value); flag = true; } return result; } } public class InputDefinition { public int dim; public bool notnull; public Dictionary pass; } public class JudgeDefinition { public float[] clip; public PdtExpression input; public PdtExpression hit; public Identifier[] pass; public Identifier[] miss; public Dictionary scores; public int stack; public int prop = 1; } public class ScoreOperation { public Identifier name; public Identifier op; public override string ToString() { if (op == default(Identifier)) return name.ToString(); else return string.Format("{0} {1}", name, op); } } public class ScoreDefinition { public PdtExpression value; public float init = 0; public string format = ""; } }