BINGO! Or BitNGo

A few weeks ago, I attended a family bingo game. Not so much happened, we won a couple of prizes, but nothing else.
The thing is, I took notes and registered every number that came through before marking it, so I have a log of around 15 games. I thought about using that data to analyze it and do something with statistics. But things have taken a turn.
I decided to just simulate a game of bingo in a small program that I decided to call "BitNGo", because uhhhh bits and computers and bingo.
So yeah that's what I'm going to talk about in this post.

Warning: Madness Ahead

By the time I'm writing this, I have already written some lines of code, so I'll just try to explain how it works.
Spoiler: I am going to absolutely abuse the use of arrays.

And no, the "concept" that I first wrote on my whiteboard, doesn't necessarily have to make sense.

Creating the boards

First, I have to know how many boards I am going to have. As of right now, this needs to be defined through the console.
Then, we execute the create_boards() function. What this does, basically, is that it generates a random number for each BINGO letter, without repeating them; it repeats this process for each board that is going to be created.
But in the first position of the array, we assign the board id, which will help us identify said board later in case of a bingo.
The function looks like this: 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function create_boards() //creates a number of boards depending on number_of_boards
{
    for(i = 0; i<number_of_boards; i++)
    {
        var used_numbers = []; //contains all the already used numbers in the board
        var id = i.toString(); //assigns an id to each board, starting from 0
        var board = [[],[],[],[],[]]; //a subarray for each bingo letter
        board.unshift(id); //puts the id before the letters on the board
        console.table(board);
        for(l = 1; l<6; l++)
        {
            for(n = 0; n<5; n++)
            {
                var newnum = random(((15 * l - 15) + 1), ((15 * l) + 1)); //creates a new number based on the letter
                while(used_numbers.includes(newnum))
                {
                    newnum = random(((15 * l - 15) + 1), (15 * l)); //if the  number already exists in the table, another one is created
                }
                used_numbers.push(newnum); //pushes the new number inside the used_numbers array
                board[l].push(newnum); //pushes the new number inside its letter array
            }
        }
        boards.push(board); //pushes the new board inside the boards array
    }
    for(i=0; i<boards.length; i++) //function that already puts a chip in the center of the board
    {
        boards[i][3][2] = "X";
    }
}

Playing

Then, we start playing.
We do this by simply playing a certain chip, this is random.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function play_chip() //normal function to pick a chip randomly
{
    var letter = random(1, 5); //gets the letter between BINGO
    var num = random((15 * letter - 15) + 1, 15 * letter); //gets a number from that letter range
    while(played_nums.includes(num))
    {
        num = random((15 * letter - 15) + 1, 15 * letter); //if the number already exists, picks another one. HERE THERE WILL BE A BUG
    }
    played_nums.push(num); //puts the new number in the played numbers array
    console.log(bingo_letters[letter-1] + num); 
    check_boards(letter, num); //marks "X" through the boards globally
    win_conditions[curr_wc](); //looks for bingos depending on the current win condition
}

Although it works fine for playing, sometimes, for testing purposes, I need a specific chip to be played. For this, I made a function to help me do exactly that:

1
2
3
4
5
6
7
function hack_chip(hackl, hackn) //get whatever chip i want, like hacking. It's just a devtool to test things out. haclk is for intended letter, hackn is for intended number
{
    played_nums.push(hackl);
    console.log(bingo_letters[hackl-1] + hackn);
    check_boards(hackl, hackn);
    win_conditions[curr_wc]();
}

Checking the boards

When a chip is played, we need to look for that number in each and every board that is currently playing.
So we do that by using a series of for loops, and by knowing the letter and the numbers that we need to look for:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function check_boards(ltr, nm) //puts chips in played numbers
{
    for(i=0; i<boards.length; i++) //checks each board
    {
        if(boards[i][ltr].includes(nm)) //if it includes the number...
        {
            boards[i][ltr][boards[i][ltr].indexOf(nm)] = "X"; //it marks it as "X"
        }
    }
    console.table(boards[0]);
}

Win Conditions

As you may already know, there are a lot of game modes in a game of bingo.
  1. Normal Bingo: Horizontal, vertical, or diagonal lines.
  2. Full table: The whole 25 boxes checked.
  3. Four corners: Well, obviously, the four corners checked.
  4. Linked four corners: The outer ring of the board checked (16 boxes).
Each of these win conditions require a specific function, but the fundamentals are the same, we just loop through the boxes to find a whole line, or whatever condition we need.

The Normal Bingo function:

Although it is the first and most common win condition, I think it also is the most complex one to loop through. The thing is that with this one, you can have around 12 different bingo combinations, whereas with the other ones, you are very specific 
Full table, four corners, and linked four corners have all just one combination; and you can even optimize it in such a way where you only start looking for bingos once the minimum amount of chips to win has been played, so in full table for example, you would only start checking for bingos after the 24th chip, since it's the minimum amount of chips required to win in that specific game mode.

Now, I have to be clear about something, I don't think that the way this function works is very fast, or very resource optimizing, so even though I'll live it like that for now, I will definitely work on optimizing it in the future.

The function is this:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function wc_bingo() //normal bingo win condition
{
    for(i=0; i<boards.length; i++)
    {
        for(l=1; l<=5; l++)
        {
            if(boards[i][l].every(element => element === "X")) //bingo on one letter
            {
                console.log("Bingo on board " + i);
            }
            if(boards[i][1][l] === "X" && boards[i][2][l] === "X" && boards[i][3][l] === "X" && boards[i][4][l] === "X" && boards[i][5][l] === "X") //sideways bingo
            {
                console.log("sideways bingo on board " + i);
            }
        }
        //diagonal bingo. We don't check boards[i][3][2] because it is the center and already has an "X"
        if(boards[i][1][0] === "X" && boards[i][2][1] === "X" && boards[i][4][3] === "X" && boards[i][5][4] === "X")
        {
            console.log("Diagonal bingo on board " + i);
        }
        if(boards[i][5][4] === "X" && boards[i][4][3] === "X" && boards[i][2][1] === "X" && boards[i][1][0] === "X")
        {
            console.log("Diagonal bingo on board " + i);
        }
    }
}

As you can see, it's not very optimized, but it works for now.
I think that a good way of optimizing it would be by indexing all the boards that have a specific number somewhere, so we don't waste time looking for empty boards. But we will leave that for the future.

I don't want to make these posts too long, so I'll leave it here. I'm going to code and explain the remaining win conditions in later posts. I am also going to enjoy some free time by playing Stardew Valley, best game of all time.

Comments

Popular posts from this blog

An Audioguide about the Geology of Ecuador

Active Projects as of Today

Explaining Color-Driven Summation