Jump to content

PHP Community Bingo


Shane_N

Recommended Posts

Hi All, im a beginner so please bear with me - as part of teh COVID-19 response we have been running a little free bingo night for our local village the numbers are called on facebook live on a saturday night and the village join in for a bit of fun, its taken off well and were finding that issuing the tickets is taking all day so were trying to simplify it - Ive got half way there with a peice of code i found online - but i cant quite get 'the whole way'! Wouldbe greatful if someone could helpo with a few issues! 

<?php


	DEFINE('BR', "<br />\n");
	
	$number_of_cards = 6; // the amount of unique cards to generate. don't make too many!

	$columns = array(
		range(1,15),
		range(16,30),
		range(31,45),
		range(46,60),
		range(61,90)
	);

	$bingo_cards = array();
	$card_hashes = array();
	$i = 0;
	
	/* GENERATE THE CARDS */
	while($i < $number_of_cards) {
		$bingo_card = array();

		for($j=0; $j<5; $j++) {
			$random_keys = array_rand($columns[$j], 5);
			$random_values = array_intersect_key($columns[$j], array_flip($random_keys)); // http://stackoverflow.com/a/18047331/3625228
			$bingo_card = array_merge($bingo_card, $random_values);
		}

		// generate a unique hash for this card and compare it to the ones we already have
		$card_hash = md5(json_encode($bingo_card)); // or whatever hashing algorithm is preferred

		if(!in_array($card_hash, $card_hashes)) {
			$bingo_cards[] = $bingo_card;
			$card_hashes[] = $card_hash;
			$i += 1;
		}

		if($i > 10000) break; // safety exit
	}

	/* OUTPUT, if needed (output with monospace font). could be made into an html table. */
	foreach($bingo_cards as $card) {
		for($k=0; $k<(sizeof($card)/5); $k++) {
			echo(str_pad($card[$k], 2, ' ', STR_PAD_LEFT).' | ');
			echo($card[$k+5].' | ');
			echo($card[$k+10].' | ');
			echo($card[$k+15].' | ');
			echo($card[$k+20].BR);
			if($k < 4) echo(str_repeat('-', 22).BR);
		}
		echo(BR.BR);
	}

?>

This code does what i need - sort of but creates a grid / table /array (?) thats 5 rows / 5 columns - what i would like to do is replciate (as close as possible) the tickets we have been sending out for the last few weeks 

these are 3 rows per ticket and 9 columns with the numbers 1-9 in C1, 10-19 in C2, 20-29 in c3 etc tec this makes it easy for older users to find the numbers while they are been called - see example below 

So far ive achieved this: http://culmstocklights.com/bingo.php each user visiting the page is presented with 3 Games (Green Pink and Brown) and 6 sets of numbers in each game (thats how we do it at the moment) 

Im 75% of the way there just need to get this to 3 rows and 9 columns per game and if possible randomly add the blanks. 

Any help would be massivly appreciated! Thanks in advance 

bingoticket.JPG

Link to comment
Share on other sites

Exactly what are you asking us to do? This is not a free programming forum. You are expected to do the work and if you have problems then we can help. You need to understand what the code you posted is doing then make the appropriate modifications. For example, there are 5 columns in your array. Change that to 9 and set the appropriate ranges for each.

Link to comment
Share on other sites

gw1500se, thank you for your repsonse - i was hoping that given the non commecial - comunity nature of this request that your answer may have been a little more charitable, im a complete beginner to PHP so really dont understand the code. That said i have tried what you suggested already I spotted the: 

$columns = array(
		range(1,15),
		range(16,30),
		range(31,45),
		range(46,60),
		range(61,90)
	);

part and expanded this to 9 'ranges' with the number outputs i was looking for i then changed 

$random_keys = array_rand($columns[$j], 5);

to 

$random_keys = array_rand($columns[$j], 9);

hoping that would solve my problem however the output didnt match my expecations i suspect thats becuase the output is driven from here: 

foreach($bingo_cards as $card) {
		for($k=0; $k<(sizeof($card)/5); $k++) {
			echo(str_pad($card[$k], 2, ' ', STR_PAD_LEFT).' | ');
			echo($card[$k+5].' | ');
			echo($card[$k+10].' | ');
			echo($card[$k+15].' | ');
			echo($card[$k+20].BR);
			if($k < 4) echo(str_repeat('-', 22).BR);
		}
		echo(BR.BR);

however thats so far away from my basic HTML knowledge that i cant even begin to see what i might change here to fix it! 

Edited by Shane_N
Link to comment
Share on other sites

PS the output from my changes i tried above can be viewed here: http://culmstocklights.com/bingo-dev.php as you can see it simply creates extra rows with sequesnced numbers 

 

this is generated byu the following code: 

<?php
	/*
		Random bingo Generator - by Shane Nelson
	*/

	DEFINE('BR', "<br />\n");
	
	$number_of_cards = 6; // the amount of unique cards to generate. don't make too many!

	$columns = array(
		range(1,9),
		range(10,19),
		range(20,29),
		range(30,39),
		range(40,49),
		range(50,59),
		range(60,69),
		range(70,79),
		range(80,89),
		range(90,99)
	);

	$bingo_cards = array();
	$card_hashes = array();
	$i = 0;
	
	/* GENERATE THE CARDS */
	while($i < $number_of_cards) {
		$bingo_card = array();

		for($j=0; $j<5; $j++) {
			$random_keys = array_rand($columns[$j], 9);
			$random_values = array_intersect_key($columns[$j], array_flip($random_keys)); // http://stackoverflow.com/a/18047331/3625228
			$bingo_card = array_merge($bingo_card, $random_values);
		}

		// generate a unique hash for this card and compare it to the ones we already have
		$card_hash = md5(json_encode($bingo_card)); // or whatever hashing algorithm is preferred

		if(!in_array($card_hash, $card_hashes)) {
			$bingo_cards[] = $bingo_card;
			$card_hashes[] = $card_hash;
			$i += 1;
		}

		if($i > 10000) break; // safety exit
	}

	/* OUTPUT, if needed (output with monospace font). could be made into an html table. */
	foreach($bingo_cards as $card) {
		for($k=0; $k<(sizeof($card)/5); $k++) {
			echo(str_pad($card[$k], 2, ' ', STR_PAD_LEFT).' | ');
			echo($card[$k+5].' | ');
			echo($card[$k+10].' | ');
			echo($card[$k+15].' | ');
			echo($card[$k+20].BR);
			if($k < 4) echo(str_repeat('-', 22).BR);
		}
		echo(BR.BR);
	}

?>

 

Edited by Shane_N
Link to comment
Share on other sites

Your example has missing cells, I assume they are supposed to be populated.  Maybe something like this is what you're looking for:

<?php
$columns = [
    range(1, 9),
    range(10, 19),
    range(20, 29),
    range(30, 39),
    range(40, 49),
    range(50, 59),
    range(60, 69),
    range(70, 79),
    range(80, 89),
    range(90, 99)
];

$number_of_cards = $_GET['cards'] ?? 6;
$bingo_cards = [];
while (count($bingo_cards) < $number_of_cards){
    $card = [];
    foreach ($columns as $columnRange){
        shuffle($columnRange);
        $card[] = array_slice($columnRange, 0, 3);
    }

    $hash = sha1(json_encode($card));
    if (!isset($bingo_cards[$hash])){
        $bingo_cards[$hash] = $card;
    }
}

foreach ($bingo_cards as $card){
    echo '<table>';
    for ($row = 0; $row < 3; $row++){
        echo '<tr>';
        for ($column = 0; $column < count($columns); $column++){
            echo '<td>'.$card[$column][$row].'</td>';
        }
        echo '</tr>';
    }
    echo '</table>';
}

For each card that is generated it goes through your $columns array and uses shuffle to randomize the order of the numbers.  It then uses array_slice to pick off the first three numbers, one for each row.

Finally it hashes the card and checks if that hash exists already to prevent duplicates.  Keep in mind this only prevents duplicates for that one generation cycle.  If you reload the page to generate more cards then there could be duplicates of the first batch in the second batch.  If you want to prevent duplicates across all generation cycles you'll need to track the cards in a file or database.

Finally it prints out a simple HTML table for each card.

 

Link to comment
Share on other sites

A friend of mine is also organising bingo sessions so I wrote a script for her, and I am happy to share it here.

There are additions I plan to make, such as

  • outputting each players cards in pdf for emailing out before the game
  • storing the cards and numbers called in a DB to facilitate checking when "Bingo!" is called

but here is current version which produces this...

image.thumb.png.b4e9ae2198cff3313a8e3eb5b456d526.png

Code

<?php            
$name = $_GET['name'] ?? '';
$date = $_GET['date'] ?? date('Y-m-d', strtotime("+1 day"));
$players = $_GET['players'] ?? '4';
$games = $_GET['games'] ?? '5';
$output = '';
$cards = [];           

if ($name) {
    $ranges  = [ 0 => range( 1,  9),
                 1 => range(10, 19),
                 2 => range(20, 29),
                 3 => range(30, 39),
                 4 => range(40, 49),
                 5 => range(50, 59),
                 6 => range(60, 69),
                 7 => range(70, 79),
                 8 => range(80, 90)
               ];

    if ($nCards = $players * $games)  {
        do {
            $card = makeCard($ranges);
            if (!in_array($card, $cards)) {
                $cards[] = $card;
            }
        } while ( count($cards) < $nCards);
        #echo '<pre>', print_r($cards, 1), '</pre>';
    }
    $output = '<div class="w3-container w3-purple w3-padding w3-margin"><h1>'.$name.'</h1></div>';
    $output .= cardOutput($cards, $players, $games);
}

/**
* build html output for the generated cards
* 
* @param array $cards
* @param int $players
* @param int  $games
*/
function cardOutput($cards, $players, $games)
{
    $cardColors = ['blue', 'green', 'yellow', 'orange', 'red'];
    $output = '';
    $pcards = array_chunk($cards, $games);
    foreach ($pcards as $pid =>$gcards) {
        $pno = $pid +1;
        $output .= "<div class=\"w3-container w3-indigo w3-margin w3-padding\"><h3>Player $pno</h3></div>";
        foreach ($gcards as $gid => $gnums) {
            $clr = $cardColors[$gid % 5];
            $gno = $gid + 1;
            $output .= "<div class='w3-content w3-$clr w3-margin-top'>
                        <table border='1'>
                           <tr><th colspan='9'>Game $gno</th></tr>";
            $rows = explode('|', $gnums);
            foreach ($rows as $rnums) {
                $nums = explode(',', $rnums);
                $output .= "<tr>";
                foreach ($nums as $n) {
                    $output .= $n!=' ' ? "<td>$n</td>" : "<td class='empty'></td>";
                }
                $output .= "</tr>";
            }               
            $output .= "</table>
                        </div>
                       ";
        }
    }
    return $output;
}

/**
* generate random bingo card - 3 rows x 9 columns  - 5 numbers per row
* 
* @param array $ranges
*/
function makeCard($ranges)
{
    $cols = array_keys($ranges);
    shuffle($cols);
    $colnums = [];
    foreach (array_slice($cols,0,6) as $c) {
        shuffle($ranges[$c]);
        $colnums[$c] = array_slice($ranges[$c],0,2);
        sort($colnums[$c]);
    }
    foreach (array_slice($cols,-3) as $c) {
        $colnums[$c][] = $ranges[$c][array_rand($ranges[$c])];
    }

    $krows = [0,0,0];
    $rownums = [];
    $first=1;
    foreach ($colnums as $c => $nums) {
        sort($nums); 
        switch (count($nums)) {
            case 1: 
                    if ($first) {
                        $r = rand(0,2);
                    } else {
                        $r = shortestRow($krows);
                    }
                    $first = 0;
                    $krows[$r]++;
                    $rownums[$r][$c] = $nums[0];
                    break;
                    
            default:
                    $check = [];
                    foreach ($nums as $n) {
                        $r = shortestRow($krows);
                        $krows[$r]++;
                        $rownums[$r][$c] = $n;
                        $check[] = [$r,$n];
                    }
                    // are col numbers in asc sequence?
                    if ( ($check[0][0] < $check[1][0] && $check[0][1] > $check[1][1]) || ($check[0][0] > $check[1][0] && $check[0][1] < $check[1][1]) )   {
                         // swap numbers
                         $rownums[$check[0][0]][$c] = $check[1][1];    
                         $rownums[$check[1][0]][$c] = $check[0][1];    
                    }
                    break;
        }
    }
    
    $rows = [];
    foreach ($rownums as $r => $carr) {
        $rstr = [];
        foreach (array_keys($ranges) as $i) {
            $rstr[] = isset($carr[$i]) ? $carr[$i] : ' ';
        }
        $rows[] = join(',',$rstr);
    }
    return join("|", $rows);
}

/**
* find row with fewest numbers
* 
* @param array $krows      row counts
*/
function shortestRow($krows)
{
    $n = min($krows);
    foreach ($krows as $r => $k) {
        if ($k==$n) return $r;
    }
}
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-language" content="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Bingo Setup</title>
<meta name="author" content="Barand">
<meta name="creation-date" content="04/26/2020">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<style type="text/css">
    table {
        width: 100%;
        border-collapse: collapse;
        border-color: #FFF;
    }
    td {
        background-color: #FFF;
        color: #000;
        margin: 5px;
        text-align: center;
        font-size: 12pt;
        font-weight: 600;
        width: 11%;
    }
    td.empty {
        background-color: #F8F8F8;
    }
</style>
</head>
<body>
<header class="w3-container w3-green">
    <h1>Bingo Game Setup</h1>
</header>
<div class="w3-content w3-round-large w3-border w3-margin-top w3-padding w3-sand">
    <form>
    <div class="w3-row">
        <label class="w3-third">Your name/group</label>
        <input type="text" class="w3-input w3-twothird" name="name" value="<?=$name?>">
    </div>
    <div class="w3-row">
        <label class="w3-third">Game date</label>
        <input type="date" class="w3-input w3-twothird" name="date" value='<?=$date?>'>
    </div>
    <div class="w3-row">
        <label class="w3-third">Number of players</label>
        <input type="number" class="w3-input w3-twothird" name="players" value='<?=$players?>'>
    </div>
    <div class="w3-row">
        <label class="w3-third">Number of games</label>
        <input type="number" class="w3-input w3-twothird" name="games" value='<?=$games?>'>
    </div>
    <button class="w3-button w3-blue">Generate game cards</button>
    </form>
</div>

      <?=$output?>
      
</body>
</html>

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.