using Cryville.Common; using Cryville.Common.Buffers; using Cryville.Common.Pdt; using System; using System.Collections.Generic; using System.Globalization; using System.Text.Formatting; namespace Cryville.Crtr { public class Judge { #region Data readonly ChartPlayer _sys; readonly PdtEvaluator _etor; readonly PdtRuleset _rs; readonly Dictionary> evs = new Dictionary>(); readonly Dictionary> activeEvs = new Dictionary>(); static readonly int _var_pause = IdentifierManager.SharedInstance.Request("pause"); readonly JudgeDefinition _judgePause; public struct JudgeEvent { public double StartTime { get; set; } public double EndTime { get; set; } public double StartClip { get; set; } public double EndClip { get; set; } public Chart.Judge BaseEvent { get; set; } public JudgeDefinition Definition { get; set; } public NoteHandler Handler { 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(ChartPlayer sys, PdtRuleset rs) { _sys = sys; _etor = ChartPlayer.etor; _rs = rs; _numsrc1 = new PropSrc.Float(() => _numbuf1); _numsrc2 = new PropSrc.Float(() => _numbuf2); _numsrc3 = new PropSrc.Float(() => _numbuf3); _numsrc4 = new PropSrc.Float(() => _numbuf4); _rs.judges.TryGetValue(new Identifier(_var_pause), out _judgePause); foreach (var i in rs.inputs.Keys) { var l = new List(); evs.Add(i, l); activeEvs.Add(i, new List()); if (_judgePause != null && i.Key == _var_pause) { l.Add(new JudgeEvent { StartTime = double.NegativeInfinity, EndTime = double.PositiveInfinity, StartClip = double.NegativeInfinity, EndClip = double.PositiveInfinity, Definition = _judgePause, }); } } InitJudges(); InitScores(); } public void Prepare(StampedEvent sev, NoteHandler handler) { var tev = (Chart.Judge)sev.Unstamped; if (tev.Id.Key == _var_pause) throw new InvalidOperationException("Cannot assign the special judge \"pause\" to notes"); Identifier input = default(Identifier); ChartPlayer.etor.Evaluate(new PropOp.Identifier(v => input = new Identifier(v)), _rs.judges[tev.Id].input); double st = sev.Time, et = st + sev.Duration; var list = evs[input]; var def = _rs.judges[tev.Id]; var ev = new JudgeEvent { StartTime = st, EndTime = et, StartClip = st + def.clip.Behind, EndClip = et + def.clip.Ahead, BaseEvent = tev, Definition = def, Handler = handler, }; var index = list.BinarySearch(ev, _stcmp); if (index < 0) index = ~index; list.Insert(index, ev); } #endregion #region Judge internal readonly Dictionary judgeMap = new Dictionary(); void InitJudges() { foreach (var i in _rs.judges.Keys) { judgeMap.Add(i.Key, IdentifierManager.SharedInstance.Request("judge_" + i.Name)); } } 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"); float _numbuf1, _numbuf2, _numbuf3, _numbuf4; readonly PropSrc _numsrc1, _numsrc2, _numsrc3, _numsrc4; 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 if (num4 < 0) num = num3 + 1; else if (num != num3) num2 = num3; else return num; } return ~num; } public void Feed(Identifier target, float ft, float tt) { Forward(target, tt); var actlist = activeEvs[target]; if (actlist.Count > 0) { _numbuf3 = ft; _numsrc3.Invalidate(); _etor.ContextCascadeUpdate(_var_ft, _numsrc3); _numbuf4 = tt; _numsrc4.Invalidate(); _etor.ContextCascadeUpdate(_var_tt, _numsrc4); var index = 0; while (index >= 0 && index < actlist.Count) { var ev = actlist[index]; _numbuf1 = (float)ev.StartTime; _numsrc1.Invalidate(); _etor.ContextCascadeUpdate(_var_fn, _numsrc1); _numbuf2 = (float)ev.EndTime; _numsrc2.Invalidate(); _etor.ContextCascadeUpdate(_var_tn, _numsrc2); var def = ev.Definition; if (def.hit != null) _etor.Evaluate(_flagop, def.hit); else _flag = true; if (_flag) { if (ev.Definition == _judgePause) _sys.TogglePause(); if (def.scores != null) UpdateScore(def.scores); if (def.pass != null) Pass(ev, (ft + tt) / 2, def.pass); if (def.persist != null) _etor.Evaluate(_flagop, def.persist); else _flag = false; if (!_flag) actlist.RemoveAt(index); if (def.prop != 0 && actlist.Count > 0) { index = BinarySearchFirst(actlist, (float)ev.StartClip, def.stack - def.prop); if (index < 0) index = ~index; } else index++; } else index++; } } } bool Pass(JudgeEvent ev, float time, 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(ev, time, def.pass); ev.Handler.ReportJudge(ev, time, i); return true; } } return false; } public void Cleanup(Identifier target, 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, tt, 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, (float)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); InvalidateScore(key.name.Key); 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); InvalidateScore(s.Key.Key); } } } } #endregion #region Score readonly Dictionary scoreStringKeys = new Dictionary(); readonly Dictionary scoreStringKeysRev = new Dictionary(); readonly Dictionary scoreSrcs = new Dictionary(); readonly Dictionary scoreOps = new Dictionary(); readonly Dictionary scoreDefs = new Dictionary(); readonly Dictionary scores = new Dictionary(); readonly Dictionary scoreStringCache = new Dictionary(); readonly ArrayPool scoreStringPool = new ArrayPool(); readonly Dictionary scoreFormatCache = new Dictionary(); readonly TargetString scoreFullStr = new TargetString(); readonly StringBuffer scoreFullBuf = new StringBuffer(); void InitScores() { foreach (var s in _rs.scores) { var key = s.Key.Key; var strkey = IdentifierManager.SharedInstance.Request("_score_" + (string)s.Key.Name); scoreStringKeys.Add(key, strkey); scoreStringKeysRev.Add(strkey, key); scoreSrcs.Add(key, new PropSrc.Float(() => scores[key])); scoreOps.Add(key, new PropOp.Float(v => scores[key] = v)); scoreDefs.Add(key, s.Value); scores.Add(key, s.Value.init); scoreStringCache.Add(scoreStringKeys[key], null); scoreSrcs.Add(scoreStringKeys[key], new ScoreStringSrc(scoreStringPool, () => scores[key], scoreDefs[key].format)); scoreFormatCache[key] = string.Format("{{0:{0}}}", s.Value.format); } } void InvalidateScore(int key) { scoreSrcs[key].Invalidate(); scoreStringCache[scoreStringKeys[key]] = null; scoreSrcs[scoreStringKeys[key]].Invalidate(); } public bool TryGetScoreSrc(int key, out PropSrc value) { return scoreSrcs.TryGetValue(key, out value); } public TargetString GetFullFormattedScoreString() { bool flag = false; scoreFullBuf.Clear(); foreach (var s in scores.Keys) { scoreFullBuf.AppendFormat(flag ? "\n{0}: " : "{0}: ", (string)IdentifierManager.SharedInstance.Retrieve(s)); scoreFullBuf.AppendFormat(scoreFormatCache[s], scores[s]); flag = true; } scoreFullStr.Length = scoreFullBuf.Count; var arr = scoreFullStr.TrustedAsArray(); scoreFullBuf.CopyTo(0, arr, 0, scoreFullBuf.Count); return scoreFullStr; } class ScoreStringSrc : PropSrc { readonly Func _cb; readonly string _format; readonly ArrayPool _pool; readonly StringBuffer _buf = new StringBuffer() { Culture = CultureInfo.InvariantCulture }; public ScoreStringSrc(ArrayPool pool, Func cb, string format) : base(PdtInternalType.String) { _pool = pool; _cb = cb; _format = string.Format("{{0:{0}}}", format); } public override void Invalidate() { if (buf != null) { _pool.Return(buf); base.Invalidate(); } } protected override unsafe void InternalGet() { var src = _cb(); _buf.Clear(); _buf.AppendFormat(_format, src); int strlen = _buf.Count; buf = _pool.Rent(sizeof(int) + strlen * sizeof(char)); fixed (byte* _ptr = buf) { *(int*)_ptr = strlen; char* ptr = (char*)(_ptr + sizeof(int)); _buf.CopyTo(ptr, 0, strlen); } } } #endregion } #region Definitions public class InputDefinition { public int dim; public bool notnull; public Dictionary pass; } public class JudgeDefinition { public Clip clip; public PdtExpression input; public PdtExpression hit; public PdtExpression persist; 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 = ""; } #endregion }