Commit f3ebd0af authored by Korko's avatar Korko
Browse files

Don't compute all the possibilities just to get a random one, just fetch the first that's ok

parent 47010f87
......@@ -4,7 +4,7 @@ namespace Korko\SecretSanta\Http\Controllers;
use Illuminate\Http\Request;
use Korko\SecretSanta\Http\Requests\RandomFormRequest;
use Korko\SecretSanta\Libs\Randomizer;
use Korko\SecretSanta\Libs\Resolver;
use Mail;
use Sms;
use Statsd;
......@@ -20,7 +20,7 @@ class RandomFormController extends Controller
{
$participants = $this->getParticipants($request);
$hat = Randomizer::randomize($participants);
$hat = Resolver::resolve($participants);
$this->sendMessages($request, $participants, $hat);
......
......@@ -12,6 +12,8 @@ class Combinator
$combinations = [];
foreach (self::getGenerator($elements) as $combination) {
$combination = array_combine($elements, $combination);
if (isset($validator) && !self::isCombinationValid($combination, $validator)) {
continue;
}
......@@ -22,17 +24,33 @@ class Combinator
return $combinations;
}
public static function getGenerator(array $elements) : Traversable
// With php7.1, return should be "?array" to return null in case of error
public static function one(array $elements, Closure $validator = null) : array
{
if ($elements === []) {
yield;
}
$rndElements = $elements;
shuffle($rndElements);
foreach (self::getGenerator_internal($rndElements) as $combination) {
yield array_combine($elements, $combination);
foreach (self::getGenerator($rndElements) as $combination) {
$combination = array_combine($elements, $combination);
if (isset($validator) && !self::isCombinationValid($combination, $validator)) {
continue;
}
return $combination;
}
return [];
}
protected static function getGenerator(array $elements) : Traversable
{
if ($elements === []) {
return;
}
foreach (self::getGenerator_internal($elements) as $combination) {
yield $combination;
}
}
......
......@@ -4,21 +4,18 @@ namespace Korko\SecretSanta\Libs;
use Exception;
class Randomizer
class Resolver
{
public static function randomize(array $participants) : array
public static function resolve(array $participants) : array
{
$combinations = Combinator::all(array_keys($participants), function ($santa, $target) use ($participants) {
$combination = Combinator::one(array_keys($participants), function ($santa, $target) use ($participants) {
return $santa !== $target && !in_array($target, $participants[$santa]['exclusions']);
});
if ($combinations === []) {
if ($combination === []) {
throw new Exception('Cannot resolve '.json_encode($participants));
}
$rnd = array_rand($combinations);
$combination = $combinations[$rnd];
foreach ($combination as $idx => $idx2) {
$combination[$idx] = $participants[$idx2]['name'];
}
......
<?php
use Korko\SecretSanta\Libs\Randomizer;
use Korko\SecretSanta\Libs\Resolver;
class RandomizerTest extends TestCase
class ResolverTest extends TestCase
{
public function testDuoRandomize()
public function testDuoResolve()
{
$this->assertEquals([0 => 'B', 1 => 'A'], Randomizer::randomize([
$this->assertEquals([0 => 'B', 1 => 'A'], Resolver::resolve([
0 => ['name' => 'A', 'exclusions' => []],
1 => ['name' => 'B', 'exclusions' => []],
]));
$this->assertEquals([0 => 'A', 1 => 'B'], Randomizer::randomize([
$this->assertEquals([0 => 'A', 1 => 'B'], Resolver::resolve([
0 => ['name' => 'B', 'exclusions' => []],
1 => ['name' => 'A', 'exclusions' => []],
]));
}
public function testTrioRandomize()
public function testTrioResolve()
{
$this->assertEquals([0 => 'B', 1 => 'C', 2 => 'A'], Randomizer::randomize([
$this->assertEquals([0 => 'B', 1 => 'C', 2 => 'A'], Resolver::resolve([
0 => ['name' => 'A', 'exclusions' => []],
1 => ['name' => 'B', 'exclusions' => ['0']],
2 => ['name' => 'C', 'exclusions' => ['1']],
]));
$this->assertEquals([0 => 'C', 1 => 'A', 2 => 'B'], Randomizer::randomize([
$this->assertEquals([0 => 'C', 1 => 'A', 2 => 'B'], Resolver::resolve([
0 => ['name' => 'A', 'exclusions' => ['1']],
1 => ['name' => 'B', 'exclusions' => ['2']],
2 => ['name' => 'C', 'exclusions' => []],
......@@ -36,9 +36,9 @@ class RandomizerTest extends TestCase
* @expectedException Exception
* @expectedExceptionMessageRegExp #^Cannot resolve#
*/
public function testFailRandomize()
public function testFailResolve()
{
Randomizer::randomize([
Resolver::resolve([
0 => ['name' => 'A', 'exclusions' => ['1', '2']],
1 => ['name' => 'B', 'exclusions' => []],
2 => ['name' => 'C', 'exclusions' => []],
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment