<commit_before><?php

// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

namespace App\Libraries\Multiplayer;

use App\Exceptions\InvariantException;

class Mod
{
    // common
    const DIFFICULTY_ADJUST = 'DA';
    const DAYCORE = 'DC';
    const DOUBLETIME = 'DT';
    const EASY = 'EZ';
    const FLASHLIGHT = 'FL';
    const HIDDEN = 'HD';
    const HARDROCK = 'HR';
    const HALFTIME = 'HT';
    const NIGHTCORE = 'NC';
    const NOFAIL = 'NF';
    const PERFECT = 'PF';
    const RELAX = 'RX';
    const SUDDENDEATH = 'SD';
    const WIND_UP = 'WU';
    const WIND_DOWN = 'WD';

    // osu-specific
    const OSU_AUTOPILOT = 'AP';
    const OSU_BLIND = 'BL';
    const OSU_SPUNOUT = 'SO';
    const OSU_TARGET = 'TP';
    const OSU_TRANSFORM = 'TR';
    const OSU_WIGGLE = 'WG';
    const OSU_GROW = 'GR';
    const OSU_DEFLATE = 'DF';
    const OSU_SPININ = 'SI';
    const OSU_TRACEABLE = 'TC';
    const OSU_CLASSIC = 'CL';

    // mania-specific
    const MANIA_KEY1 = '1K';
    const MANIA_KEY2 = '2K';
    const MANIA_KEY3 = '3K';
    const MANIA_KEY4 = '4K';
    const MANIA_KEY5 = '5K';
    const MANIA_KEY6 = '6K';
    const MANIA_KEY7 = '7K';
    const MANIA_KEY8 = '8K';
    const MANIA_KEY9 = '9K';
    const MANIA_KEY10 = '10K';
    const MANIA_DUALSTAGES = 'DS';
    const MANIA_FADEIN = 'FI';
    const MANIA_MIRROR = 'MR';
    const MANIA_RANDOM = 'RD';
    const MANIA_INVERT = 'IN';
    const MANIA_CONSTANTSPEED = 'CS';

    // taiko-specific
    const TAIKO_RANDOM = 'RD';

    // non-scorable
    const AUTOPLAY = 'AT';
    const CINEMA = 'CN';
    const NOMOD = 'NM';

    const SCORABLE_COMMON = [
        // common
        self::DAYCORE,
        self::DIFFICULTY_ADJUST,
        self::DOUBLETIME,
        self::EASY,
        self::FLASHLIGHT,
        self::HIDDEN,
        self::HARDROCK,
        self::HALFTIME,
        self::NIGHTCORE,
        self::NOFAIL,
        self::PERFECT,
        self::RELAX,
        self::SUDDENDEATH,
        self::WIND_DOWN,
        self::WIND_UP,
    ];

    // Defines mutual-exclusivity for groups of mods, i.e. only one mod within each group can be active at a time
    const EXCLUSIVITY_COMMON = [
        [
            self::RELAX,
            self::SUDDENDEATH,
            self::NOFAIL,
            self::PERFECT,
            self::OSU_AUTOPILOT,
        ],
        [
            self::OSU_AUTOPILOT,
            self::OSU_SPUNOUT,
        ],
        [
            self::HARDROCK,
            self::EASY,
            self::DIFFICULTY_ADJUST,
        ],
        [
            self::DOUBLETIME,
            self::HALFTIME,
            self::DAYCORE,
            self::NIGHTCORE,
        ],
        [
            self::WIND_DOWN,
            self::WIND_UP,
        ],
        [
            self::MANIA_KEY1,
            self::MANIA_KEY2,
            self::MANIA_KEY3,
            self::MANIA_KEY4,
            self::MANIA_KEY5,
            self::MANIA_KEY6,
            self::MANIA_KEY7,
            self::MANIA_KEY8,
            self::MANIA_KEY9,
            self::MANIA_KEY10,
        ],
        [
            self::OSU_TRANSFORM,
            self::OSU_WIGGLE,
        ],
        [
            self::OSU_GROW,
            self::OSU_DEFLATE,
            self::OSU_SPININ,
            self::OSU_TRACEABLE,
        ],
        [
            self::HIDDEN,
            self::OSU_SPININ,
            self::OSU_TRACEABLE,
        ],
    ];

    const SETTINGS = [
        self::DIFFICULTY_ADJUST => [
            'drain_rate' => 'float',
            'overall_difficulty' => 'float',
            'circle_size' => 'float',
            'approach_rate' => 'float',
            'extended_limits' => 'bool',
            'scroll_speed' => 'float',
        ],
        self::DOUBLETIME => [
            'speed_change' => 'float',
        ],
        self::NIGHTCORE => [
            'speed_change' => 'float',
        ],
        self::EASY => [
            'retries' => 'int',
        ],
        self::HALFTIME => [
            'speed_change' => 'float',
        ],
        self::DAYCORE => [
            'speed_change' => 'float',
        ],
        self::WIND_UP => [
            'initial_rate' => 'float',
            'final_rate' => 'float',
            'adjust_pitch' => 'bool',
        ],
        self::WIND_DOWN => [
            'initial_rate' => 'float',
            'final_rate' => 'float',
            'adjust_pitch' => 'bool',
        ],
        self::OSU_GROW => [
            'start_scale' => 'float',
        ],
        self::OSU_DEFLATE => [
            'start_scale' => 'float',
        ],
        self::OSU_CLASSIC => [
            'classic_note_lock' => 'bool',
            'fixed_follow_circle_hit_area' => 'bool',
            'no_slider_head_accuracy' => 'bool',
            'no_slider_head_movement' => 'bool',
            'always_play_tail_sample' => 'bool',
        ],
    ];

    public static function assertValidExclusivity($requiredIds, $allowedIds, $ruleset)
    {
        $exclusiveRequiredIds = [];

        foreach (static::exclusivityByRuleset()[$ruleset] as $group) {
            $intersection = array_intersect($requiredIds, $group);

            if (count($intersection) > 1) {
                throw new InvariantException('incompatible mods: '.implode(', ', $intersection));
            }

            if (count($intersection) === 1) {
                $exclusiveRequiredIds = array_merge($exclusiveRequiredIds, $group);
            }<commit_msg>Add follow delay flashlight mod setting<commit_after><?php

// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

namespace App\Libraries\Multiplayer;

use App\Exceptions\InvariantException;

class Mod
{
    // common
    const DIFFICULTY_ADJUST = 'DA';
    const DAYCORE = 'DC';
    const DOUBLETIME = 'DT';
    const EASY = 'EZ';
    const FLASHLIGHT = 'FL';
    const HIDDEN = 'HD';
    const HARDROCK = 'HR';
    const HALFTIME = 'HT';
    const NIGHTCORE = 'NC';
    const NOFAIL = 'NF';
    const PERFECT = 'PF';
    const RELAX = 'RX';
    const SUDDENDEATH = 'SD';
    const WIND_UP = 'WU';
    const WIND_DOWN = 'WD';

    // osu-specific
    const OSU_AUTOPILOT = 'AP';
    const OSU_BLIND = 'BL';
    const OSU_SPUNOUT = 'SO';
    const OSU_TARGET = 'TP';
    const OSU_TRANSFORM = 'TR';
    const OSU_WIGGLE = 'WG';
    const OSU_GROW = 'GR';
    const OSU_DEFLATE = 'DF';
    const OSU_SPININ = 'SI';
    const OSU_TRACEABLE = 'TC';
    const OSU_CLASSIC = 'CL';

    // mania-specific
    const MANIA_KEY1 = '1K';
    const MANIA_KEY2 = '2K';
    const MANIA_KEY3 = '3K';
    const MANIA_KEY4 = '4K';
    const MANIA_KEY5 = '5K';
    const MANIA_KEY6 = '6K';
    const MANIA_KEY7 = '7K';
    const MANIA_KEY8 = '8K';
    const MANIA_KEY9 = '9K';
    const MANIA_KEY10 = '10K';
    const MANIA_DUALSTAGES = 'DS';
    const MANIA_FADEIN = 'FI';
    const MANIA_MIRROR = 'MR';
    const MANIA_RANDOM = 'RD';
    const MANIA_INVERT = 'IN';
    const MANIA_CONSTANTSPEED = 'CS';

    // taiko-specific
    const TAIKO_RANDOM = 'RD';

    // non-scorable
    const AUTOPLAY = 'AT';
    const CINEMA = 'CN';
    const NOMOD = 'NM';

    const SCORABLE_COMMON = [
        // common
        self::DAYCORE,
        self::DIFFICULTY_ADJUST,
        self::DOUBLETIME,
        self::EASY,
        self::FLASHLIGHT,
        self::HIDDEN,
        self::HARDROCK,
        self::HALFTIME,
        self::NIGHTCORE,
        self::NOFAIL,
        self::PERFECT,
        self::RELAX,
        self::SUDDENDEATH,
        self::WIND_DOWN,
        self::WIND_UP,
    ];

    // Defines mutual-exclusivity for groups of mods, i.e. only one mod within each group can be active at a time
    const EXCLUSIVITY_COMMON = [
        [
            self::RELAX,
            self::SUDDENDEATH,
            self::NOFAIL,
            self::PERFECT,
            self::OSU_AUTOPILOT,
        ],
        [
            self::OSU_AUTOPILOT,
            self::OSU_SPUNOUT,
        ],
        [
            self::HARDROCK,
            self::EASY,
            self::DIFFICULTY_ADJUST,
        ],
        [
            self::DOUBLETIME,
            self::HALFTIME,
            self::DAYCORE,
            self::NIGHTCORE,
        ],
        [
            self::WIND_DOWN,
            self::WIND_UP,
        ],
        [
            self::MANIA_KEY1,
            self::MANIA_KEY2,
            self::MANIA_KEY3,
            self::MANIA_KEY4,
            self::MANIA_KEY5,
            self::MANIA_KEY6,
            self::MANIA_KEY7,
            self::MANIA_KEY8,
            self::MANIA_KEY9,
            self::MANIA_KEY10,
        ],
        [
            self::OSU_TRANSFORM,
            self::OSU_WIGGLE,
        ],
        [
            self::OSU_GROW,
            self::OSU_DEFLATE,
            self::OSU_SPININ,
            self::OSU_TRACEABLE,
        ],
        [
            self::HIDDEN,
            self::OSU_SPININ,
            self::OSU_TRACEABLE,
        ],
    ];

    const SETTINGS = [
        self::DIFFICULTY_ADJUST => [
            'drain_rate' => 'float',
            'overall_difficulty' => 'float',
            'circle_size' => 'float',
            'approach_rate' => 'float',
            'extended_limits' => 'bool',
            'scroll_speed' => 'float',
        ],
        self::DOUBLETIME => [
            'speed_change' => 'float',
        ],
        self::NIGHTCORE => [
            'speed_change' => 'float',
        ],
        self::EASY => [
            'retries' => 'int',
        ],
        self::HALFTIME => [
            'speed_change' => 'float',
        ],
        self::DAYCORE => [
            'speed_change' => 'float',
        ],
        self::WIND_UP => [
            'initial_rate' => 'float',
            'final_rate' => 'float',
            'adjust_pitch' => 'bool',
        ],
        self::WIND_DOWN => [
            'initial_rate' => 'float',
            'final_rate' => 'float',
            'adjust_pitch' => 'bool',
        ],
        self::FLASHLIGHT => [
            'follow_delay' => 'float',
        ],
        self::OSU_GROW => [
            'start_scale' => 'float',
        ],
        self::OSU_DEFLATE => [
            'start_scale' => 'float',
        ],
        self::OSU_CLASSIC => [
            'classic_note_lock' => 'bool',
            'fixed_follow_circle_hit_area' => 'bool',
            'no_slider_head_accuracy' => 'bool',
            'no_slider_head_movement' => 'bool',
            'always_play_tail_sample' => 'bool',
        ],
    ];

    public static function assertValidExclusivity($requiredIds, $allowedIds, $ruleset)
    {
        $exclusiveRequiredIds = [];

        foreach (static::exclusivityByRuleset()[$ruleset] as $group) {
            $intersection = array_intersect($requiredIds, $group);

            if (count($intersection) > 1) {
                throw new InvariantException('incompatible mods: '.implode(', ', $intersection));
            }

            if (count($intersection) === 1) {
                $exclusiveRequiredIds = array_merge($exclusiveRequiredIds, $group);
            }
{'fraction_non_alphanumeric': 0.13291703835860838, 'fraction_numerical': 0.006601248884924175, 'mean_word_length': 1.7125574643116381, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 81, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4419195', 'n_tokens_mistral': 3837, 'n_tokens_neox': 3735, 'n_words': 690}