Составления алгоритма выпадения предметов
Нужно составить алгоритм выпадения предметов.
Входные данные:
open_cost - Стоимость открытия.
items - Массив предметов которые могут выпасть.
item['cost'] - Стоимость предмета по отдельности.
Также можно использовать:
items_count - Количество всех предметов.
items_cost - Стоимость всех предметов.
И т.п.
Алгоритм должен выдавать в среднем предмет со стоимостью равной стоимости открытия.
Мне нужен алгоритм на php. Вы можете писать на любом удобном языке, мне главное понять принцип.
Так же у меня есть свой алгоритм, но его проблема в том что для разных предметов требуется разный коэффициент, и его приходить находить с помощью подбора.
Вот данный алгоритм:
function getClosest($search, $array)
{
$num = null;
foreach ($array as $item) {
if ($item['chance'] < $search) {
continue;
}
if ($num === null || $item['chance'] < $num['chance']) {
$num = $item;
}
}
return $num;
}
function getRandomSkin($items, $open_cost, $coeff)
{
$accum = 0;
foreach ($items as &$item) {
if ($open_cost >= $item['cost']) {
$chance = $item['cost'] / $open_cost * $coeff;
} else {
$chance = $open_cost / $item['cost'];
}
$item['chance'] = round($chance, 3) * 1000;
$accum = $skin['chance'] += $accum;
}
$rand_num = rand(0, end($items)['chance']);
$rand_element = getClosest($rand_num, $items);
return $rand_element;
}
Пример:
$items = [
['id' => 1, 'cost' => 50],
['id' => 2, 'cost' => 100],
['id' => 3, 'cost' => 10],
['id' => 4, 'cost' => 200],
];
$open_cost = 50;
Исходя из этих данных шансы на выпадения предметов должны быть такими:
Формула - $item['cost'] / $open_cost
id_1 - 1
id_2 - 2
id_3 - 5
id_4 - 4
Чем меньше число тем больше шанс на выпадение.
Ответы (2 шт):
Можно добиться такого поведения через обычную сортировку с callback-ом.
<?php
$openCost = 50;
$items = [
['id' => 1, 'cost' => 50],
['id' => 1, 'cost' => 55],
['id' => 1, 'cost' => 35],
['id' => 1, 'cost' => 40],
['id' => 1, 'cost' => 60],
['id' => 2, 'cost' => 100],
['id' => 3, 'cost' => 10],
['id' => 4, 'cost' => 200],
];
usort(
$items,
static function ($a, $b) use ($openCost) {
$distA = ($previous - $a['cost']) < 0
? $a['cost'] - $openCost
: $openCost - $a['cost'];
$distB = ($previous - $b['cost']) < 0
? $b['cost'] - $openCost
: $openCost - $b['cost'];
return $distA === $distB
? 0
: (
$distA < $distB
? -1
: 1
);
}
);
foreach($items as $item) {
var_dump($item['cost']);
}
Ответ:
int(50)
int(55)
int(40)
int(60)
int(35)
int(10)
int(100)
int(200)
Playground: https://onlinephp.io/c/d9e8b , можете покрутить и увидеть результат.
Прошу учесть, что код рабочий, однако концептуальный, детали, вроде деления на 0 и прочего не обрабатывалось, возложим это на вас)
Код:
<?php
$items = [
['id' => 1, 'cost' => 50],
['id' => 1, 'cost' => 51],
['id' => 1, 'cost' => 53],
['id' => 1, 'cost' => 55],
['id' => 1, 'cost' => 35],
['id' => 1, 'cost' => 37],
['id' => 1, 'cost' => 40],
['id' => 1, 'cost' => 43],
['id' => 1, 'cost' => 60],
['id' => 1, 'cost' => 62],
['id' => 1, 'cost' => 67],
['id' => 2, 'cost' => 100],
['id' => 2, 'cost' => 127],
['id' => 3, 'cost' => 10],
['id' => 4, 'cost' => 200],
];
// стоимость открытия ящика
$openCost = 50;
// погрешность стоимости 50 + (50 * 0.2 = 10) = 60 это макс, мин будет на 10 меньше, за границы не уходим
$factor = 0.2; // env перменная
$factorValue = $openCost * $factor;
$minItemCost = $openCost - $factorValue; // 40
$maxItemCost = $openCost + $factorValue; // 60
// коэффициент выпадения элементов стоимостью больше, стоимости открытия
$coeff = 0.90; // env переменная, случайность выпадения будет примерно 45% больше стоимости к 55% меньше стоимости
// эти значения должны сохраняться в базу как счетчик, либо же ходить в нее и аггрегировать данные значения на каждое открытие
$moreCount = 5; // это счетчик, либо аггрегированное значение кол-ва выпадений больше стоимости
$lessCount = 5; // это счетчик, либо аггрегированное значение кол-ва выпадений меньше стоимости
// это переменная фарта, т.е. сколько итераций пользователю нужно прокрутиться, чтобы выиграть элемент больше стоимости открытия
$fart = 15;
$targetItemsSlice = array_values(
array_filter(
$items,
static function ($item) use ($minItemCost, $maxItemCost, &$moreCount, &$lessCount) {
return $item['cost'] >= $minItemCost && $item['cost'] <= $maxItemCost;
}
)
);
// чтобы не залипнуть на вечно в цикле, на всякий случай
$threshold = 1000; // env переменная
$curFart = 0; // тот самый счетчик удачи
for ($i = 0; $i <= $threshold; $i++) {
$targetItem = $targetItemsSlice[rand(0, count($targetItemsSlice) - 1)];
// var_dump($targetItem);
if ($targetItem['cost'] > $openCost) {
$moreCount = $moreCount + 1; // либо же пишем в базу и заного аггрегируем, чтобы узнать актуальные данные
} elseif ($targetItem['cost'] < $openCost) {
$lessCount = $lessCount + 1; // либо же пишем в базу и заного аггрегируем, чтобы узнать актуальные данные
}
// var_dump($moreCount,$lessCount); die;
// тут считаем коэффициент выпадений, больше стоимости открытия к меньше стоимости открытия
$curCoeff = $moreCount / $lessCount;
// если результат выполнения данного условия true, значит больше стоимости выпадает слишком часто
if ($curCoeff >= $coeff) {
if ($curFart >= $fart) {
// юзер выиграл айтем больше стоимости
var_dump(sprintf('Ваш выигрыш%s, поздравляем - ' . $targetItem['cost'], $targetItem['cost'] > $openCost ? ' больше стоимости открытия' : ''));
break;
}
$curFart++;
continue;
}
var_dump('Ваш выигрыш, поздравляем - ' . $targetItem['cost']);
break;
}
var_dump('The end.');