How can I generate a round robin tournament in PHP and MySQL? - php

I need to generate sequences of games using the round robin algorithm. I have the php page where the user can input the tournament name which will be inserted into the database and it has got a drop down menu up to 32 teams (select number of teams).
So if I select 4 teams in the page, so it will be from team 1 to team 4 which would be 6 matches because every team plays the other team once. I know how the algorithm works but I am not quite sure how to write the query for that.
I created the table team:
Team_id 01 02 03 etc
Team_name Team1 Team2 Team3 etc.
What should I do from here?

I created a roundrobin function from scratch as i thought it might be easier to get the same results and also allowing me to use arrays filled with strings directly instead of numbers.
Because i pull a list of names from a database and add into an array i can now schedule this directly with below function. No extra step needed to link numbers to names etc.
Please feel free to try it and if it works then leave a comment.
I also have a version which allows for 2 way (home & return) schedule and or shuffle option. If somone is interested in that one then leave a coment as well.
<?php
/**
* #author D.D.M. van Zelst
* #copyright 2012
*/
function scheduler($teams){
if (count($teams)%2 != 0){
array_push($teams,"bye");
}
$away = array_splice($teams,(count($teams)/2));
$home = $teams;
for ($i=0; $i < count($home)+count($away)-1; $i++){
for ($j=0; $j<count($home); $j++){
$round[$i][$j]["Home"]=$home[$j];
$round[$i][$j]["Away"]=$away[$j];
}
if(count($home)+count($away)-1 > 2){
array_unshift($away,array_shift(array_splice($home,1,1)));
array_push($home,array_pop($away));
}
}
return $round;
}
?>
How to use, for example create an array like:
<?php $members = array(1,2,3,4); ?>
or
<?php $members = array("name1","name2","name3","name4"); ?>
then call the function to create your schedule based on above array:
<?php $schedule = scheduler($members); ?>
To display the resulted array schedule simply do like below or anyway you like:
This little code displays the schedule in a nice format but use it anyway you like.
<?php
foreach($schedule AS $round => $games){
echo "Round: ".($round+1)."<BR>";
foreach($games AS $play){
echo $play["Home"]." - ".$play["Away"]."<BR>";
}
echo "<BR>";
}
?>
Leave a note if it worked for you or if you are interested in the 2-way version with shuffle.

There is a fairly simple algorithm for doing round-robin matchups, my solution would be as follows (in pseudo-code):
fetch all the teams out of the database into an array, in any order
for (i = 1; i < number of teams; i++)
print matchups for Round #i:
the teams in the first half of the array are matched up in the same order with the teams in the last half of the array. That is, the team at any index [n] is matched up with the team at index [n + half the number of teams]. If you have 32 teams, [0] is matched with [16], [1] with [17], etc up to [15] and [31].
Now, "rotate" the teams through the array, but leave the first one in the array in place. That is, [1] becomes [2], [2] becomes [3], ..., up to [31] becomes [1], but do not move the team at index [0].
And that's it, that will produce all the matchups you need.
An example, with 4 teams:
First half of the array is on top, second half is on the bottom, match-ups are numbers above/below each other. Array indexes (to illustrate what I mean exactly):
[0] [1]
[2] [3]
Round 1:
1 2
3 4
Round 2:
1 4
2 3
Round 3:
1 3
4 2

Related

Algorithm that generates all versions of an array with possible weights assigned 0-100

I have a following array (php):
[
[id=>1,weight=]
[id=>2,weight=]
[id=>3,weight=]
[id=>4,weight=]
]
I need to create all possible versions of this array asigning 0-100 weight to each item['weight'] with a step of N.
I don't know how this type of problems are called. It is NOT permutation/combination.
Lets say N is 10, I am aiming to get:
[
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=70]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=20]
[id=>4,weight=60]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=30]
[id=>4,weight=50]
]
[
[id=>1,weight=10]
[id=>2,weight=10]
[id=>3,weight=40]
[id=>4,weight=40]
]
...all possible combination of weights for id=x.
[
[id=>1,weight=70]
[id=>2,weight=10]
[id=>3,weight=10]
[id=>4,weight=10]
]
]
Sum of 4 item['weights'] in array on same level is always 100 (or 0.1). And inside parent array I've all possible combinations of weights from 10-100 for id=x.
This problem is sometimes described as allocating identical balls into distinct bins. You didn't specify your problem exactly, so I'll take a guess here but the logic will be identical.
I'll assume you're distributing b = N/step balls into 4 bins.
Think of the balls all in a row, and then using 3 bars to separate the balls into 4 bins:
*|||*****.
If N=10 and you're distributing 100 points, the above example is the same is 30, 20, 0, 50. If zeroes aren't allowed, you can reduce the amount you're distributing by 4*b and assume each bin starts out with N/step in it (so you're distributing the leftover points).
The number of ways to do this is choose(balls + bins - 1, bins - 1).
Theres probably a better way, but heres my attempt:
$result=array(); // Empty array for your result
$array=range(1117,7777); // Make an array with every number between 1117 and 7777
foreach ($array as $k=>$v) { // Loop through numbers
if ((preg_match('/[890]/',$v) === 0) && (array_sum(str_split($v, 1)) === 10)) {
// If number does not contain 8,9 or 0 and sum of all 4 numbers is 10
// Apply function to multiply each number by 10 and add to result array
$result[] = array_map("magnitude", str_split($v, 1));
}
}
function magnitude($val) { // function to multiply by 10 for array map
return($val * 10);
}
print_r($result);
Working demo here
EDIT
Sorry I realised my code explanation isn't totally clear and I condensed it all a bit too much to make it easy to follow.
In your example the first array would contain (10,10,10,70). For the sake of simplicity I divided everything by 10 for the calculations and then just multiplied by 10 once I had a result, so your array of (10,10,10,70) becomes (1,1,1,7). Then your final array would be (70,10,10,10) which would become (7,1,1,1).
My approach was to first to create an array containing every combination of these four numbers, which I did in two steps.
This line $array=range(1117,7777); creates an array like this (1117, 1118, 1119 ... 7775, 7776, 7777) (My number range should really have been 1117 - 7111 instead of 1117-7777).
Applying str_split($v, 1) to each value in the loop splits each 4 digit number in the array into another array conatining 4 single digit numbers, so 1117 will become (1, 1, 1, 7) etc
As each of your items can't have a weight below 10 or above 70 we use (preg_match('/[890]/',$v) === 0) to skip any arrays which have 0,8 or 9 in them anywhere, then array_sum(str_split($v, 1)) === 10) adds up the four digits in the array and only returns arrays which total 10 (you wanted ones which total 100, but I divided by 10 earlier).
array_map applies a function to each element in an array. In my example the function multiplies each value by 10, to undo the fact I divided by 10 earlier.
When you say is it possible to alter steps, can you give me a couple of examples of other values and the output you want for them?
If you want a totally different approach and using mysql isn't a problem then this also works:
Create a new table with a single row. Insert all the values you need to check
INSERT INTO `numbers` (`number`) VALUES
(10),
(20),
(30),
(40),
(50),
(60),
(70);
Then your php looks like this
$result=array();
try {
$dbh = new PDO('mysql:host=aaaaa;dbname=bbb', 'ccc', 'dddd');
foreach($dbh->query('SELECT *
FROM numbers a
CROSS JOIN // A cross join returns the cartesian product of rows
numbers b // so every row with every combination of the other rows
CROSS JOIN
numbers c
CROSS JOIN
numbers d
ON
a.number = b.number OR a.number != b.number') as $row) {
if (($row[0] + $row[1] + $row[2] + $row[3]) === 100) {
$result[] = $row;
}
}
$dbh = null;
} catch (PDOException $e) {
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
print_r($result);

Undefined index error on key that has been declared

I am building a leaderboard (scorebored) for a poker tournament. My aim is to echo data to a table to display scores for the season and scores all time. I want the table to run in order with the highest season points at the top.
I am getting an error message that reads:
Notice: Undefined index: season in C:\wamp\www\UPT Site\leaders.php on line 11
When I print_r an array from the $allplayers array, it shows that all the players arrays are going in correctly, including the [season] key and value declared on line 6 in the bit below...
Can anyone please tell me how to fix my code? (note, the real code doesn't have line numbers in it, I just added them here to make discussion easier).
1 foreach($allplayers as $player){
2 $i = $player[1];
3 if (${"seasonplayerid" . $i}){
4 $sum = array_sum(${"seasonplayerid" . $i});}
5 //$sum = points this season.
6 ${"playerid" . $i}['season'] = $sum;
7 }
8 function val_sort($array,$key) {
9 //Loop through and get the values of our specified key
10 foreach($array as $k=>$v) {
11 $b[] = strtolower($v[$key]);
12 }
13 asort($b);
14 /* foreach($b as $k=>$v) {
15 $c[] = $array[$k];
16 }return $c;
17 */
18 }
19 $sorted = val_sort($allplayers, '[season]');
20 foreach($allplayers as $player){
21 $i = $player[1];
22 echo ("<tr><td>" . $player[0] . $t . ${"playerid" . $i}[3] . $t . ${"playerid" . $i}[4] . $t. ${"playerid" . $i}['season'] . $t. count(${"seasonplayerid" . $i}). "</td><tr>");
23 }
Here is the print_r output for array $playerid1:
Array ( [0] => Jonathan Thompson [1] => 1 [2] => 2015-S 3 [3] => 944 [4] => 7 [season] => 470 )
Here is a key of the information in the array:
/*
$allplayers is a multidimentional array, containing many arrays of players called $playerid1, $playerid2, $playerid3 etc
playerid1[0] = Player name
playerid1[1] = Player ID
playerid1[2] = Current season
playerid1[3] = total points earned
playerid1[4] = total games played games
playerid1[season] = points earned in current season
*/
Looks like at line 19 you are trying to pass key in second parameter but defined it incorrectly.So, to call function val_sort($array,$key) you have to do something like that.
Therefore at line 19 change
$sorted = val_sort($allplayers, '[season]');
to
$sorted = val_sort($allplayers, 'season');
Also i suggest you to use data table it is good and fast
Instead of below line
$sorted = val_sort($allplayers, '[season]');
you should pass the key as below
$sorted = val_sort($allplayers, 'season');
Hope this helps.
I did a dodgy quick fix in this instance.
I wrote a loop to array_pop each array inside the main array, and created a new variable for each piece of data as the array_pop loop was going.
I also array_pushed the data I wanted to sort by and an ID number to a new array.
I used rsort to sort the new array, then looped through it using the ID number to reconstruct a 3rd array in the order I wanted to output data.
Next time I will take the advice of #rocky and do a data table. I may even go back to redo this page as a data table. To be honest, I'd never heard of a data table before yesterday. Once I've given this project over to the boss I'll be investing my time in learning about data tables and AJAX.
Thank you all for your tips, but the quick fix will have to do this time as I am time poor (This site needs to be live tomorrow morning).

I'm creating a random array in PHP and my code doesnt seem to output a truly random answer

I want to construct an array of 3 offers that output in a random order. I have the following code and whilst it does output 3 random offers it doesn't appear to be random. The first value in the generated array always seems to be from the 1st 2 records in my offers table. The offers table only has 5 records in it (I dont know if this is affecting things).
$arrayOfferCount = $offerCount-1;
$displayThisManyOffers = 3;
$range = range(0, $arrayOfferCount);
$vals = array_rand($range, $displayThisManyOffers);`
Any help or advice would be appreciated.
Working fine here. Benchmark it over lots of runs instead of just gut feeling... here it is for 1,000 tries:
<?php
$offerCount = 5;
$arrayOfferCount = $offerCount-1;
$displayThisManyOffers = 3;
$range = range(0, $arrayOfferCount);
for($i = 0; $i < 1000; $i++) {
$vals = array_rand($range, $displayThisManyOffers);
foreach($vals as $val) {
$counts[$val]++;
}
}
sort($counts);
print_r($counts);
Generates:
Array
(
[0] => 583
[1] => 591
[2] => 591
[3] => 610
[4] => 625
)
I know that mt_rand() is much better PRNG.
However, in your case you need to let the database select them for you
SELECT * FROM ads ORDER BY RAND() LIMIT 0, 3
It is probably randomly picking which to display, but displaying them in the same order they appear in your array. If you do it enough times (~20) you should get the third one to show up once if this is the case (chances of choosing exactly the last 3 out of 5 would be 1 in 5*4, so around every 20th one you'll see the third option appear).
array_rand seems not to work properly sometimes (see PHP-Manual comments).
Workaround: Get the array size and pick a random index using the function mt_rand

Sorting data in PHP for a single elimination tournament display in HTML

I have a table that has the following columns and fields:
userid round faceoff username
------------------------------
1 1 2 Kevin
3 1 1 Steve
4 1 3 Jake
9 1 4 Sam
1 2 1 Kevin
9 2 2 Sam
1 3 1 Kevin
The round are the columns and faceoff are the rows. Thus round 1 has a max of faceoff 4, and round 2 has a faceoff max of 2, etc.
I want to display the data on users screens like so (basic HTML):
Round 1 Round 2 Final (round 3)
Steve
Kevin
Kevin
Kevin
Jake
Sam
Sam
Here is what I have so far:
$tor is the array from MySQL, I var_dump() it and everything is there. I'm just having issues with the for loops:
for ($u=0;$u<=$torindex;$u++)
{
for ($r=1;$r<=$torindex+1;$r++)
{
if ($tor[$u]['round'] == $r)
{
for ($f=1;$f<=$torindex+1;$f++)
{
if ($tor[$u]['faceoff'] == $f) $placement[$r][$f] = $tor[$u]['userid'].":".$tor[$u]['username'];
}
}
}
}
Where do I go from here?
For each row in your data table, you can find right row in HTML table to display it using this equation:
rowNum = pow(2, round - 1) + (faceoff - 1) * pow(2, round);
so this should work in MySQL:
SELECT * FROM tournament ORDER BY pow(2, round - 1) + (faceoff - 1) * pow(2, round);
Structure
I have used the Knockout tournament scheduler class by Nicholas Mossor Rathmann for this task in the past with great success. Given teams/players it will calculate the ties and eliminations based on the matches final score.
Output
For CSS/HTML output instead of the GD generated image the class gives you I used One Fork's jQuery & JSON to draw single-elimination tournament bracket with some modifications for IE compatibility.
Sort it by round like you have it and as you cycle through the rows keep track of the current round. If the row's round is not equal to the current round you know to move to the right. You can close and start a div or ul and float them to the left.
http://jsfiddle.net/RxDrd/

How do I pick a selection of rows with the minimum date difference

The question was difficult to phrase. Hopefully this will make sense.
I have a table of items in my INVENTORY.
Let's call the items Apple, Orange, Pear, Potato. I want to pick a basket of FRUIT (1 x Apple,1 x Orange, 1 x Pear).
Each item in the INVENTORY has a different date for availability. So that...
Apple JANUARY
Apple FEBRUARY
Apple MARCH
Orange APRIL
Apple APRIL
Pear MAY
I don't want to pick the items in the order they appear in the inventory. Instead I want to pick them according to the minimum date range in which all items can be picked. ie Orange & Apple in APRIL and the pear in MAY.
I'm not sure if this is a problem for MYSQL or for some PHP arrays. I'm stumped. Thanks in advance.
If array of fruits isn't already sorted by date, let's sort it.
Now, the simple O(n^2) solution would be to check all possible ranges. Pseudo-code in no particular language:
for (int i = 0; i < inventory.length; ++i)
hash basket = {}
for (int j = i; j < inventory.length; ++j) {
basket.add(inventory[j]);
if (basket.size == 3) { // or whatever's the number of fruits
// found all fruits
// compare range [i, j] with the best range
// update best range, if necessary
break;
}
}
end
You may find it's good enough.
Or you could write a bit more complicated O(n) solution. It's just a sliding window [first, last]. On each step, we move either left border (excluding one fruit from the basket) or right (adding one fruit to the basket).
int first = 0;
int last = 0;
hash count = {};
count[inventory[0]] = 1;
while (true) {
if (count[inventory[first]] > 0) {
--count[inventory[first]];
++first;
} else if (last < inventory.length) {
++last;
++count[inventory[last]];
} else {
break;
}
if (date[last] - date[first] < min_range
&& count.number_of_nonzero_elements == 3) {
// found new best answer
min_range = date[last] - date[first]
}
}
Given you table inventory is structured:
fruit, availability
apple, 3 // apples in march
//user picks the availability month maybe?
$this_month = 5 ;
//or generate it for today
$this_month = date('n') ;
// sql
"select distinct fruit from inventory where availability = $this_month";
Sound quite complicated. The way that I would approach the problem is to group each fruit into its availability month group and see how many are in each group.
JANUARY (1)
FEBRUARY (1)
MARCH (1)
APRIL (2)
MAY (1)
To see that the most fruits fall within APRIL. So APRIL is therefore our preferred month.
I would then remove the items from months with duplicates (Apples in your example), which would remove MARCH as an option. This step could either be done now, or after the next step depending on your data and the results you get.
I would then look at the next most popular month and calculate how far away that month is (eg. JAN is 3 away from APRIL, MARCH is 1 etc). If you then had a tie then it shouldn't matter which you choose. In this example though you would end up choosing the 2 fruits from APRIL and 1 fruit from MAY as you requested.
This approach may not work if the most popular month doesn't actually result in the "best" selection.

Categories