Допустим, у меня есть массив с массивами, например:
const array = [
['a', 'b', 'c'],
['d', 'e'],
['f', 'g', 'h', 'i', 'j'],
['k'],
['l'],
['m'],
['n', 'o', 'p'],
['q', 'r', 's'],
['t', 'u', 'v'],
['x']
];
Я хочу выбрать произвольную комбинацию, которая соответствует следующим правилам:
- Общая длинаиз всех выбранных комбинаций всегда должно быть 10. Возможным результатом могут быть первые 3 элемента
array
- Выбранные комбинации должны быть в состоянии разбить на две группы по 5. И снова первые 3предметы будут соответствовать этому условию:
length
из ['a', 'b, 'c']
+ length
из ['d', 'e']
равно 5
, и в то же время длина ['f', 'g', 'h', 'i', 'j']
равна 5
. Это две группы 5
. С другой стороны, последние 4 элемента array
не смогут выполнить это условие, даже если они соответствуют первому (общая длина = 10).
Это может помочь понять цель этого: у меня есть небольшая многопользовательская игра. Для игр нужны 2 команды по 5 игроков. И игроки могут войти в игру с другом, чтобы играть в одной команде (или даже с 5 друзьями, мгновенно заполнив всю команду).
Идея: игроки нажимают «Старт». Тогда моя функция поместит их в массив, подобный приведенному выше. Каждый раз, когда происходил толчок, запускалась функция игрока / команды (которую я прошу о вашей помощи). Если совпадение будет найдено, игра начнется.
У меня такое ощущение, что лучше всего это сделать с помощью какой-нибудь рекурсивной функции, но у меня в голове проблемы с ее выяснением.
Через пару часов вот решение, которое я нашел. Пройдены все мои тесты.
//const { shuffle, flatten } = require('lodash');
const pool = [
['a', 'b', 'c'],
['d', 'e'],
['f', 'g', 'h', 'i', 'j'],
['k'],
['l'],
['m'],
['n', 'o', 'p'],
['q', 'r', 's'],
['t', 'u', 'v'],
['x']
];
function getMaxPickSize ( draw ) {
let x = 5;
let y = 5;
draw.forEach( pick => {
if ( x - pick.length >= 0 ) {
x -= pick.length;
} else if ( y - pick.length >= 0 ) {
y -= pick.length;
}
});
return Math.max(x,y);
}
function doDraw( pool ) {
//no need to move further if there arent even 10 players
if ( _.flatten(pool).length < 10 ) {
return false;
}
// keep register of all draws and pools, and items. if we
// figure out an attempt doesnt work, we can go back anytime
// and skip picks that dont work
let prevs = [
// array of objects that will look like this.
// {
// pool: [],
// draw: [],
// skip: []
// }
// ...
];
//let's try. First step, shuffle the pool;
pool = _.shuffle(pool);
function doIt( curr_pool, curr_draw = [], skip_items_w_length ) {
let new_pool = [...curr_pool];
let new_draw = [...curr_draw];
let pick;
if ( skip_items_w_length == undefined ) {
//in first loop it starts here
//if we happen to have luck and fill the draw in
//one go, the else statement below will never execute
pick = new_pool.shift();
} else {
let to_skip = prevs[prevs.length - 1].skip;
to_skip.push(skip_items_w_length);
pick = _.find(new_pool, item => !to_skip.includes(item.length) );
if ( pick ) {
new_pool.splice(new_pool.indexOf(pick), 1);
} else {
if ( !prevs.length ) {
return false;
}
let prev = prevs.pop();
let prev_pool = prev.pool;
let prev_draw = prev.draw;
let last_item_in_prev_draw = prev_draw.pop();
return doIt(prev_pool, prev_draw, last_item_in_prev_draw.length );
}
}
new_draw = [...curr_draw, pick];
//if draw is complete, return it
if ( _.flatten(new_draw).length === 10 ) {
return new_draw;
}
//else draw process continues
//find items in pool that can still fit into draw
const max_pick_size = getMaxPickSize(new_draw);
new_pool = new_pool.filter(item => item.length <= max_pick_size);
//if items dont contain enough players to fill remaining spots,
//repeat this exact step, ignoring items without pick's length
//as none of the remaining picks can follow. if we discover in
// later repeats that no pick allows other picks to follow
// we'll go back 1 step, using previous pool and draw, and
// ignoring all picks with the associated picks length
if ( _.flatten(new_pool).length < 10 - _.flatten(new_draw).length ) {
return doIt(curr_pool, curr_draw, pick.length);
}
prevs.push({
pool: curr_pool,
draw: curr_draw,
skip: []
});
return doIt(new_pool, new_draw);
}
return doIt(pool);
}
const draw = doDraw( pool );
Спасибо, ребята!