<commit_before>/**
 * Random Battles chat-plugin
 * Written by Kris with inspiration from sirDonovan and The Immortal
 *
 * Set probability code written by Annika
 */

import {FS, Utils} from '../../lib';
import {SSBSet, ssbSets} from '../../data/mods/ssb/random-teams';


interface SetCriteria {
	moves: {mustHave: Move[], mustNotHave: Move[]};
	ability: {mustHave?: Ability, mustNotHave: Ability[]};
	item: {mustHave?: Item, mustNotHave: Item[]};
	nature: {mustHave?: Nature, mustNotHave: Nature[]};
}


function getHTMLCriteriaDescription(criteria: SetCriteria) {
	const format = (list: {name: string}[]) => list.map(m => Utils.html`<strong>${m.name}</strong>`);
	const parts = [];

	const {moves, ability, item, nature} = criteria;

	if (moves.mustHave.length) {
		parts.push(`had the move${Chat.plural(moves.mustHave.length)} ${Chat.toListString(format(moves.mustHave))}`);
	}
	if (moves.mustNotHave.length) {
		parts.push(`did not have the move${Chat.plural(moves.mustNotHave.length)} ${Chat.toListString(format(moves.mustNotHave), 'or')}`);
	}

	if (ability.mustHave) {
		parts.push(Utils.html`had the ability <strong>${ability.mustHave.name}</strong>`);
	}
	if (ability.mustNotHave.length) {
		parts.push(`did not have the ${Chat.plural(ability.mustNotHave.length, 'abilities', 'ability')} ${Chat.toListString(format(ability.mustNotHave), 'or')}`);
	}

	if (item.mustHave) {
		parts.push(Utils.html`had the item <strong>${item.mustHave.name}</strong>`);
	}
	if (item.mustNotHave.length) {
		parts.push(`did not have the item${Chat.plural(item.mustNotHave.length)} ${Chat.toListString(format(item.mustNotHave), 'or')}`);
	}

	if (nature.mustHave) {
		parts.push(Utils.html`had the nature <strong>${nature.mustHave.name}</strong>`);
	}
	if (nature.mustNotHave.length) {
		parts.push(`did not have the nature${Chat.plural(nature.mustNotHave.length)} ${Chat.toListString(format(nature.mustNotHave), 'or')}`);
	}

	return Chat.toListString(parts, 'and');
}

function setProbability(
	species: Species,
	format: Format,
	criteria: SetCriteria,
	rounds = 700
): {rounds: number, matches: number} {
	const results = {rounds, matches: 0};
	const generator = Teams.getGenerator(format);

	for (let i = 0; i < rounds; i++) {
		const set = generator.randomSet(species);

		if (criteria.item.mustHave && set.item !== criteria.item.mustHave.name) continue;
		if (criteria.item.mustNotHave.some(item => item.name === set.item)) continue;

		if (criteria.ability.mustHave && set.ability !== criteria.ability.mustHave.name) continue;
		if (criteria.ability.mustNotHave.some(ability => ability.name === set.ability)) continue;

		if (criteria.nature.mustHave && set.nature !== criteria.nature.mustHave.name) continue;
		if (criteria.nature.mustNotHave.some(nature => nature.name === set.nature)) continue;

		const setHasMove = (move: Move) => {
			const id = move.id === 'hiddenpower' ? `${move.id}${toID(move.type)}` : move.id;
			return set.moves.includes(id);
		};
		if (!criteria.moves.mustHave.every(setHasMove)) continue;
		if (criteria.moves.mustNotHave.some(setHasMove)) continue;

		results.matches++;
	}
<commit_msg>/setodds: Properly handle doubles and no dynamax<commit_after>/**
 * Random Battles chat-plugin
 * Written by Kris with inspiration from sirDonovan and The Immortal
 *
 * Set probability code written by Annika
 */

import {FS, Utils} from '../../lib';
import {SSBSet, ssbSets} from '../../data/mods/ssb/random-teams';


interface SetCriteria {
	moves: {mustHave: Move[], mustNotHave: Move[]};
	ability: {mustHave?: Ability, mustNotHave: Ability[]};
	item: {mustHave?: Item, mustNotHave: Item[]};
	nature: {mustHave?: Nature, mustNotHave: Nature[]};
}


function getHTMLCriteriaDescription(criteria: SetCriteria) {
	const format = (list: {name: string}[]) => list.map(m => Utils.html`<strong>${m.name}</strong>`);
	const parts = [];

	const {moves, ability, item, nature} = criteria;

	if (moves.mustHave.length) {
		parts.push(`had the move${Chat.plural(moves.mustHave.length)} ${Chat.toListString(format(moves.mustHave))}`);
	}
	if (moves.mustNotHave.length) {
		parts.push(`did not have the move${Chat.plural(moves.mustNotHave.length)} ${Chat.toListString(format(moves.mustNotHave), 'or')}`);
	}

	if (ability.mustHave) {
		parts.push(Utils.html`had the ability <strong>${ability.mustHave.name}</strong>`);
	}
	if (ability.mustNotHave.length) {
		parts.push(`did not have the ${Chat.plural(ability.mustNotHave.length, 'abilities', 'ability')} ${Chat.toListString(format(ability.mustNotHave), 'or')}`);
	}

	if (item.mustHave) {
		parts.push(Utils.html`had the item <strong>${item.mustHave.name}</strong>`);
	}
	if (item.mustNotHave.length) {
		parts.push(`did not have the item${Chat.plural(item.mustNotHave.length)} ${Chat.toListString(format(item.mustNotHave), 'or')}`);
	}

	if (nature.mustHave) {
		parts.push(Utils.html`had the nature <strong>${nature.mustHave.name}</strong>`);
	}
	if (nature.mustNotHave.length) {
		parts.push(`did not have the nature${Chat.plural(nature.mustNotHave.length)} ${Chat.toListString(format(nature.mustNotHave), 'or')}`);
	}

	return Chat.toListString(parts, 'and');
}

function setProbability(
	species: Species,
	format: Format,
	criteria: SetCriteria,
	rounds = 700
): {rounds: number, matches: number} {
	const results = {rounds, matches: 0};
	const generator = Teams.getGenerator(format);

	for (let i = 0; i < rounds; i++) {
		const set = generator.randomSet(species, {}, false, format.gameType !== 'singles', format.ruleTable?.has('dynamaxclause'));

		if (criteria.item.mustHave && set.item !== criteria.item.mustHave.name) continue;
		if (criteria.item.mustNotHave.some(item => item.name === set.item)) continue;

		if (criteria.ability.mustHave && set.ability !== criteria.ability.mustHave.name) continue;
		if (criteria.ability.mustNotHave.some(ability => ability.name === set.ability)) continue;

		if (criteria.nature.mustHave && set.nature !== criteria.nature.mustHave.name) continue;
		if (criteria.nature.mustNotHave.some(nature => nature.name === set.nature)) continue;

		const setHasMove = (move: Move) => {
			const id = move.id === 'hiddenpower' ? `${move.id}${toID(move.type)}` : move.id;
			return set.moves.includes(id);
		};
		if (!criteria.moves.mustHave.every(setHasMove)) continue;
		if (criteria.moves.mustNotHave.some(setHasMove)) continue;

		results.matches++;
	}

{'fraction_non_alphanumeric': 0.17713745866792632, 'fraction_numerical': 0.001574555188159345, 'mean_word_length': 5.149080348499516, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 31, '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': '5092287', 'n_tokens_mistral': 2281, 'n_tokens_neox': 2164, 'n_words': 442}