Example #1
0
        /// <summary>
        /// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
        /// the <see cref="ScoreProcessor"/> of the <see cref="JudgementResult"/>.
        /// </summary>
        /// <param name="application">The callback that applies changes to the <see cref="JudgementResult"/>.</param>
        protected void ApplyResult(Action <JudgementResult> application)
        {
            if (Result.HasResult)
            {
                throw new InvalidOperationException("Cannot apply result on a hitobject that already has a result.");
            }

            application?.Invoke(Result);

            if (!Result.HasResult)
            {
                throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
            }

            if (!Result.Type.IsValidHitResult(Result.Judgement.MinResult, Result.Judgement.MaxResult))
            {
                throw new InvalidOperationException(
                          $"{GetType().ReadableName()} applied an invalid hit result (was: {Result.Type}, expected: [{Result.Judgement.MinResult} ... {Result.Judgement.MaxResult}]).");
            }

            Result.TimeOffset = Math.Min(MaximumJudgementOffset, Time.Current - HitObject.GetEndTime());

            if (Result.HasResult)
            {
                updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
            }

            OnNewResult?.Invoke(this, Result);
        }
Example #2
0
        /// <summary>
        /// Applies the <see cref="Result"/> of this <see cref="DrawableHitObject"/>, notifying responders such as
        /// the <see cref="ScoreProcessor"/> of the <see cref="JudgementResult"/>.
        /// </summary>
        /// <param name="application">The callback that applies changes to the <see cref="JudgementResult"/>.</param>
        protected void ApplyResult(Action <JudgementResult> application)
        {
            application?.Invoke(Result);

            if (!Result.HasResult)
            {
                throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
            }

            judgementOccurred = true;

            // Ensure that the judgement is given a valid time offset, because this may not get set by the caller
            var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;

            Result.TimeOffset = Time.Current - endTime;

            switch (Result.Type)
            {
            case HitResult.None:
                break;

            case HitResult.Miss:
                State.Value = ArmedState.Miss;
                break;

            default:
                State.Value = ArmedState.Hit;
                break;
            }

            OnNewResult?.Invoke(this, Result);
        }
Example #3
0
        protected virtual void AddNested(DrawableHitObject h)
        {
            h.OnNewResult            += (d, r) => OnNewResult?.Invoke(d, r);
            h.OnRevertResult         += (d, r) => OnRevertResult?.Invoke(d, r);
            h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);

            nestedHitObjects.Value.Add(h);
        }
Example #4
0
        private void addNested(DrawableHitObject hitObject)
        {
            // Todo: Exists for legacy purposes, can be removed 20200417

            hitObject.OnNewResult            += (d, r) => OnNewResult?.Invoke(d, r);
            hitObject.OnRevertResult         += (d, r) => OnRevertResult?.Invoke(d, r);
            hitObject.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);

            nestedHitObjects.Value.Add(hitObject);
        }
Example #5
0
        /// <summary>
        /// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
        /// </summary>
        /// <param name="hitObject">The <see cref="TObject"/> to add the visual representation for.</param>
        private void addRepresentation(TObject hitObject)
        {
            var drawableObject = GetVisualRepresentation(hitObject);

            if (drawableObject == null)
            {
                return;
            }

            drawableObject.OnNewResult    += (_, r) => OnNewResult?.Invoke(r);
            drawableObject.OnRevertResult += (_, r) => OnRevertResult?.Invoke(r);

            Playfield.Add(drawableObject);
        }
Example #6
0
        private void apply(HitObject hitObject)
        {
            if (nestedHitObjects.IsValueCreated)
            {
                nestedHitObjects.Value.Clear();
                ClearNestedHitObjects();
            }

            foreach (var h in hitObject.NestedHitObjects)
            {
                var drawableNested = CreateNestedHitObject(h) ?? throw new InvalidOperationException($"{nameof(CreateNestedHitObject)} returned null for {h.GetType().ReadableName()}.");

                drawableNested.OnNewResult            += (d, r) => OnNewResult?.Invoke(d, r);
                drawableNested.OnRevertResult         += (d, r) => OnRevertResult?.Invoke(d, r);
                drawableNested.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);

                nestedHitObjects.Value.Add(drawableNested);
                AddNestedHitObject(drawableNested);
            }
        }
Example #7
0
 private void onNewResult(DrawableHitObject drawableHitObject, JudgementResult result) => OnNewResult?.Invoke(drawableHitObject, result);
Example #8
0
        private SearchResult AlphaBetaSearch(Position position, int alpha, int beta, int depth, int reportdepth, int depthUp, ref List <Move> orderedMoves)
        {
            ulong hash = 0L;

            if (useCache)
            {
                // If we've already processed this position, use the saved evaluation
                hash = position.GetHash();
                if (hashtable.ContainsKey(hash))
                {
                    hashLookups++;
                    return(hashtable[hash]);
                }
            }

            // Evaluate the position
            int eval = EvaluatePosition(position, depthUp);

            // See if someone won
            if (Math.Abs(eval) > Constants.VictoryScore - 100)
            {
                return(new SearchResult()
                {
                    Score = eval,
                    PrimaryVariation = eval > 0 ? "BLUE-WINS" : "RED-WINS"
                });
            }

            // See if we need to immediately stop searching
            if (timer.ElapsedMilliseconds >= timeLimitMilliseconds)
            {
                cutoff = true;
                return(new SearchResult());
            }

            // We've reached the depth of our search, so return the heuristic evaluation of the position
            // Make sure we're evaluating after our opponent's last move (meaning it's our turn to move again) so that we calculate full move pairs
            if (depth <= 0 && position.PlayerToMove == initiatingPlayer)
            {
                return(new SearchResult()
                {
                    Score = eval,
                    PrimaryVariation = ""
                });
            }

            bool         maximizingPlayer = position.PlayerToMove == Player.Blue;
            SearchResult best             = new SearchResult()
            {
                Score = maximizingPlayer ? int.MinValue : int.MaxValue
            };

            List <Move> nullMoves = null;
            List <Move> moves;

            if (orderedMoves != null)
            {
                // Use an ordered list of moves based on the evaluations from a previous search depth
                moves = orderedMoves;
            }
            else
            {
                moves = position.GetValidMoves();
            }

            // If we have no moves, return the evaluation of the position
            if (moves.Count == 0)
            {
                return(new SearchResult()
                {
                    Score = eval,
                    PrimaryVariation = ""
                });
            }

            int movenum = 1;

            foreach (Move move in moves)
            {
                // Copy the board and make a move
                Position copy = position.Clone();
                copy.MakeMove(move);

                // Don't repeat positions
                if (copy.LastMoveWasRepetition())
                {
                    continue;
                }

                // Store the current node for search reporting
                if (reportdepth > 0 && depthUp == 1)
                {
                    levelOneNode = move.ToString();
                }

                // Find opponents best counter move
                SearchResult child = AlphaBetaSearch(copy, alpha, beta, depth - 1, reportdepth - 1, depthUp + 1, ref nullMoves);

                // Store the evaluation for iterative deepening move ordering
                move.Evaluation = child.Score;

                if (maximizingPlayer)
                {
                    if (child.Score > best.Score)
                    {
                        best.Score            = child.Score;
                        best.BestMove         = move;
                        best.PrimaryVariation = move.ToString() + " " + child.PrimaryVariation;
                    }

                    alpha = Math.Max(alpha, best.Score);

                    if (beta <= alpha)
                    {
                        // Beta cutoff
                        break;
                    }
                }
                else
                {
                    if (child.Score < best.Score)
                    {
                        best.Score            = child.Score;
                        best.BestMove         = move;
                        best.PrimaryVariation = move.ToString() + " " + child.PrimaryVariation;
                    }

                    beta = Math.Min(beta, best.Score);

                    if (beta <= alpha)
                    {
                        // Alpha cutoff
                        break;
                    }
                }

                if (reportdepth > 0)
                {
                    if (best == null)
                    {
                        best = new SearchResult();
                    }
                    if (best.BestMove == null)
                    {
                        best.BestMove = new Move();
                    }

                    // Report on the progress of our search
                    OnNewResult?.Invoke(null, new SearchStatus()
                    {
                        BestMoveSoFar       = best,
                        SearchedNodes       = evaluations,
                        ElapsedMilliseconds = timer.ElapsedMilliseconds,
                        CurrentMove         = movenum++,
                        TotalMoves          = moves.Count,
                        Depth            = currentDepth,
                        HashLookups      = hashLookups,
                        CurrentVariation = (depthUp != 1 ? levelOneNode + " " : "") + move.ToString() + " " + child.PrimaryVariation
                    });
                }
            }

            if (useCache)
            {
                if (!hashtable.ContainsKey(hash))
                {
                    hashtable.Add(hash, best);
                }
            }

            // Store the moves for the next depth (if using iterative deepening
            if (depthUp == 1)
            {
                orderedMoves = moves;
            }

            return(best);
        }