Understanding Simulator

A simulator is developed to create an environment where intuitive decisions can be made. This section covers all the rules and basics of how the stimulator works.

The How

At the very beginning of this research, I knew that we'll need some sort of a simulator where we can test our AI's intuitive decisions (if it ever gets developed) so it was not a discovery or realization in between and that is why from start I was looking at other simulations that have been used in the study of AI.

In search of simulator

Upon researching the AI learning how to play different games like chess, go, etc, there was not a single game that could be used for this study to its full extent. I went through AlphaGo, MuZero, and a bunch of others. Though mastering the game of go which has many possibilities was amazing but at the same time, it does not satisfy the principles of intuition.

Intuition has more to do with the psychology of mind so I knew that I had to define certain principles of psychology to the simulator like what makes a human takes an intuitive decision? It's the past experience and the present moment and the thought of what will happen in the future. It should be a simulation where every action is inspired by a psychological principle of the real world that is known to humankind. Basically, the idea was to reduce certainty as much as possible.

Some of the psychological principles or real-life scenarios that were considered are:

  • Humans have free will. Intuition is heavily dependent on free will, as these decisions are on spot and it is up to the person and if the person chooses to go with it or not is also on to the person.

  • The randomness of life scenarios. We all live on the same planet yet for every one of us there is a different world that we live in and we make decisions based on that view of the world but certain studies also tell us that we take intuitive decisions mostly in a situation that we’re not familiar with.

  • Everyone sees more failures in life than success even though if the success might be bigger than all the failures combined but we're talking about numbers here.

  • We all have at certain points of our lives people who are guiding us or providing feedback about something we have asked them but the decision at end of every discussion is again in our own hands whether we decide to go with the suggestion or the other way around.

  • Sometimes in our life, we have secondary goals. If we decide to pursue them they are rewarding but if we skip them there is no harm. With intuition, this factor is pretty heavy as we have to choose among different thoughts and we go with one of it and not selecting others usually doesn't have any harm but maybe another option that we skipped will be more rewarding than the one we have selected but we are happy because we have achieved the goal we were after.

After looking at certain examples of games and simulations used in AI, I decided to go with Wumpus World. Wumpus World, which was a simple world example to illustrate the worth of a knowledge-based agent to represent knowledge representation in the early ages of artificial intelligence. But the concept of the Wumpus world is not suitable for an intuitive-based environment so in order to develop an algorithm we have changed some rules and added some of our own rules to satisfy some real-life scenarios.

The Simulator

As the Intuitive AI stimulator is inspired by Wumpus World we wanted to generate an environment where it doesn't matter who is playing (Human or AI) both are forced to make some decisions based on intuition only. So, we changed some rules in the Wumpus Wold stimulator to fulfill our requirements.

Intuitive AI stimulator is a 4x4 grid (16 blocks). Every game has

  • 4 pits (🕳️ ),

  • 1 gold (💰 ), and

  • 1 monster (👹 )

Every time a new game is started the grid is randomized and one box is revealed with a hint.

  • Your goal is to find 👹 (Monster), doing so will give you 1 point and the game will end in a win.

  • If you find 💰 (Gold) before finding the monster you'll get 1 more point, hence in a game you can get a maximum of 2 points. Finding 💰 (Gold) will not end the game, think of gold as an optional encouraged behavior.

  • If you fall in 🕳️ (Pit) your score will -1 from the total score you have at the moment and the game will end in loss.

  • If a box doesn't reveal any of the three i.e. ( 👹 , 💰 , 🕳️ ) above then the box will reveal a hint that will tell you what is in the adjacent boxes.

Hints are of the following types:

  • Breeze 💨 Indicate that there is Pit(s) in the adjacent boxes.

  • Smell 👃 Indicate that there is a Monster in one of the adjacent boxes.

  • Glitter ✨ Indicate that there is Gold in one of the adjacent boxes.

  • Breeze & Smell 💨 👃 Indicate that there is Monster & Pit(s) in the adjacent boxes.

  • Breeze & Glitter 💨 ✨ Indicate that there is Gold & Pit(s) in the adjacent boxes.

  • Smell & Glitter 👃 ✨ Indicate that there is Monster & Gold in the adjacent boxes.

  • Breeze & Glitter & Smell 💨 ✨ 👃 Indicate that there is Monster, Pit(s) & Gold in the adjacent boxes.

  • Nothing ❎ This indicates that there is nothing in the adjacent boxes.

Adjacent boxes are only up, down, left, right, and not diagonal. For example, the adjacent boxes to the hint below are marked with a red dot on the grid in the picture below:

You can open any box at any given time there is no pattern to follow just a goal (find monster) in place and hints to guide you to achieve that goal. These hints are always helpful and sometimes they'll help you reach the goal easily but sometimes they'll put you in a situation where you have to take risk.

You can follow your intuition to open any random box and ignore following the hint too, for example, the above hint which is wind 💨 is telling us that there are one or more pits in the adjacent boxes. It can be in all four or it can be in one and so on. So will you take the risk to open one of the adjacent boxes or choose to open any random box that your intuition tells you to open? All these choices are up to you!

Here are all the possible outcomes of a game:

  1. The User falls into the pit. (Game Lost)

  2. The User finds gold and falls into the pit. (Game Lost)

  3. The User finds wumpus. (Game Won)

  4. The User finds gold and wumpus. (Game Won)

Out of the 16 boxes, 10 boxes will have hits, 4 will have pits, one will be monster and one will be gold.

This basic summary of the stimulation is encouraging three types of behaviors:

  1. Goal

  2. Risk

  3. Encouraged Optional Behavior (EOB)

The reason for placing this high number (4:1 for every four boxes there will be one pit) of risk, is on purpose and by design. Because in intuition the risk is involved and although intuitive decisions are mostly right there is always high risk and from one of the real-life scenarios this fulfills the one where everyone sees more failures than success.

EOB was something that came a bit late in the design but it was a perfect move. As humans, we strive for more with less risk. This option adds greediness to the world, where you’re striving towards a goal and you have full authority to take risks and win more points, but the risk is there equally, you can fall into the pit.

One game of IntuitiveAI simulator reflects one scenario of life in which a person is taking intuition-based decisions.

When a new stimulation loads it randomly shuffles the 4x4 grid so every time you’ll be getting a new environment with the same rules.

The new stimulation will open a box at a random spot which will give you a starting hint.

Now you can either take that hint and start planning where to move from there or you can open a totally random box out of the 15 remainings.

If we talk about the probability of risk of opening a box other than the adjacent boxes let’s say it’s 0.5, it’ll be the same for every box that is not an adjacent box to the opened hint box as shown in the image below. Hints tell us what is in the adjacent boxes.

The psychological principles or real-life scenarios how they fit into this simulator is as follows:

  • Even though user gets a hint at the start they have a free will to open any box at any time. The hint is the knowledge we already have and the sense it gives us about the adjacent boxes is the knowledge we derive about the future from the knowledge we already have of which we have a certain certainty but there is still a risk that which one or all of them.

  • Every single time use will be faced with a random grid with a random box revealed. The rules will always remain the same just like in our real world and we have to make intuitive decisions when we’re in an uncertain situation.

  • There are 4 pits, which is a risk and the chance of failing is higher than the chase of finding the goal.

  • Hints are the helping guides. You can either take them or make your own path. But in our lives, we have such helping hands that influence our decisions. In this case, all the hints will be telling the truth in the current version.

  • If you find gold you get 1 extra point but if you don’t there is no negative penalty. So, it’s completely optional.

In our life, we face small situations almost every day. That one situation is one game of IntuitiveAI. We have free will, we can deal with that situation in any way we want but within certain rules. We all have a goal in life, that changes as well but we have goals. We all see failures more than success. There were times where we had a choice where we knew if we do this it’ll add an extra cherry on the top but if not it’ll still be fine. When a new game is started the grid is randomized so the previous knowledge you had is useless because it's a totally new world with the same rules. Using these principles the IntuitiveAI simulator is designed. To fit any situation in IntuitiveAI we have to define what is the goal, what are the risks and it needs to have an optional encouraged behavior. The scoring in the game is also built on psychological principles. You can only get -1, 0, 1, or 2 in the game. Most people will strive for 1, this portrays the 80% of the world population that is middle class. Many people if they see a good calculated chance that they'll be able to get 2 only then they'll take it where some people will go out of the way to take the risk of getting the 2. The boxes you open are either intuition-based or you know exactly what will happen when you open a box just like in a real-life decision-making process. The simulator lets you take the decision not only using your calculated side of the brain but also the creative side of the brain where intuition is.

The Intuitive AI simulator is a pioneering project aimed at exploring and enhancing intuitive decision-making in AI. By replicating real-world scenarios within a 4x4 grid game, this simulator allows users and AI to navigate through environments filled with uncertainty, where choices must be made with limited information. The game's design, incorporating elements like Monsters, Pits, and Gold, along with helpful hints, mirrors real-life decision-making processes that blend logic, intuition, and experience. This innovative approach seeks to understand and develop AI systems capable of making decisions in unpredictable situations, reflecting the nuanced way humans interact with the world around them.

Basic Logic of Simulator

The IntuitiveAI simulator for humans to play is built on JavaScript and can be accessed: https://intuitive-ai.firebaseapp.com/

Every game has an array indexed from 0...15, which is randomized on a 4x4 grid that has 4 pits, 1 gold, and 1 monster.

Example:

Consider this EXAMPLE of a new game. The array is randomized and the game array now is:

strArr = ["Pit", "Glitter & Breeze", "Gold", "Glitter", "Breeze", "Pit", "Monster", "Smell Breeze", "Breeze", "Breeze", "Smell Breeze", "Pit", "Pit", "Breeze", "X", "Breeze"]

User will see the strings (symbols) as the strArr shows but behind the scenes the above array is:

indexArr = [0, 12, 7, 6, 10, 9, 11, 14, 2, 3, 1, 5, 15, 4, 13, 8]

indexArr was a sequence from 0 to 15 values and is then randomized to generate this particular game pattern and wherever in indexArr there is:

  • 0 and 9 and 5 and 15 there will always be a Pit

  • 7 there will always be Gold

  • 11 there will always be the Monster

The adjacent values are then calculated for each of the above elements and hence the hints are generated and finally a random index other than the one assigned to the elements is picked and is revealed.

Source Code

The source code is available in JavaScript and Python. Here we'll discuss the logic behind using the JavaScript version. A link to python will be available at the end.

We start by writing a new game function:

function newGame() {
//Code comig soon
}

Inside the newGame function we start by initializing some variables:

var numbers = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

var numberStrs = ['❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎', '❎'];
var tempArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

var didFMonster = false;
var didFGold = false;
var didFInPit = false;

var userGamePlayPattern = [];
var userGamePlayPatternIndex = [];

finalScore = 0;

Our main array numbers is a size 16 array which has values from 0 to 15 in sequence. Then we have numberStrs and tempArray which will be used to work side by side one will provide a GUI based approach the other will hold numeric data against each GUI element (i.e. emoji).

You'll understand more about what data we're storing for analysis purposes in the sub-page Understanding DataSet.

The next three i.e. didFMonster, didFGold, and didFInPit are all checking the outcome of the game that either user has won or lost the game and if the user has found gold in a single game.

Finally, userGamePlayPattern stores the GUI values of how use has played the game and userGamePlayPatternIndex stores from which index user moved to which index basically maps out how user played the game. In the end, we have finalScore which will store the score of a game.

Next, we need to write a method that'll shuffle arrays.

function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

The above function is written outside the newGame function and it is plain and simple shuffling each element to a random position within the grid.

Now we go back to our newGame function and we overwrite numbers array

numbers = shuffle(numbers);

Next, Using the updated grid we assign fixed indexes to their correspondent values:

    var indexOfMonster = numbers.indexOf(11);
    var indexOfGold = numbers.indexOf(7);
    var indexOfPit1 = numbers.indexOf(0);
    var indexOfPit2 = numbers.indexOf(5);
    var indexOfPit3 = numbers.indexOf(9);
    var indexOfPit4 = numbers.indexOf(15);

The above code will assign the index of wherever the value 11 is shuffled to indexOfMonster and just like that, we have reserved numbers that will always be the same, meaning doesn't matter where 11 is shuffled to in the list, where ever it'll be the game will place monster there and the same goes for gold and our four pits.

Now we need to find adjacent of each of these values so that we can assign hints accordingly. This is an important step as we need to calculate these values carefully so that we can place the right hint at the right place. Here is the code:

    //Findinng Adjacents
    
    var adjToMonster = []
    if (indexOfMonster+4 < 16) { //number below
        adjToMonster.push(numbers[indexOfMonster+4]);
    }
    if (indexOfMonster+1 < 16 && indexOfMonster%4 != 3) { //number right
        adjToMonster.push(numbers[indexOfMonster+1]);
    }
    if (indexOfMonster-4 >= 0) { //number Above
        adjToMonster.push(numbers[indexOfMonster-4]);
    }
    if (indexOfMonster-1 >= 0 && indexOfMonster%4 != 0) { //number left
        adjToMonster.push(numbers[indexOfMonster-1]);
    }
    
    var adjToGold = []
    if (indexOfGold+4 < 16) { //number below
        adjToGold.push(numbers[indexOfGold+4]);
    }
    if (indexOfGold+1 < 16 && indexOfGold%4 != 3) { //number right
        adjToGold.push(numbers[indexOfGold+1]);
    }
    if (indexOfGold-4 >= 0) { //number Above
        adjToGold.push(numbers[indexOfGold-4]);
    }
    if (indexOfGold-1 >= 0 && indexOfGold%4 != 0) { //number left
        adjToGold.push(numbers[indexOfGold-1]);
    }
    
    var adjToPit1 = []
    if (indexOfPit1+4 < 16) { //number below
        adjToPit1.push(numbers[indexOfPit1+4]);
    }
    if (indexOfPit1+1 < 16 && indexOfPit1%4 != 3) { //number right
        adjToPit1.push(numbers[indexOfPit1+1]);
    }
    if (indexOfPit1-4 >= 0) { //number Above
        adjToPit1.push(numbers[indexOfPit1-4]);
    }
    if (indexOfPit1-1 >= 0 && indexOfPit1%4 != 0) { //number left
        adjToPit1.push(numbers[indexOfPit1-1]);
    }
    
    var adjToPit2 = []
    if (indexOfPit2+4 < 16) { //number below
        adjToPit2.push(numbers[indexOfPit2+4]);
    }
    if (indexOfPit2+1 < 16 && indexOfPit2%4 != 3) { //number right
        adjToPit2.push(numbers[indexOfPit2+1]);
    }
    if (indexOfPit2-4 >= 0) { //number Above
        adjToPit2.push(numbers[indexOfPit2-4]);
    }
    if (indexOfPit2-1 >= 0 && indexOfPit2%4 != 0) { //number left
        adjToPit2.push(numbers[indexOfPit2-1]);
    }
    
    var adjToPit3 = []
    if (indexOfPit3+4 < 16) { //number below
        adjToPit3.push(numbers[indexOfPit3+4]);
    }
    if (indexOfPit3+1 < 16 && indexOfPit3%4 != 3) { //number right
        adjToPit3.push(numbers[indexOfPit3+1]);
    }
    if (indexOfPit3-4 >= 0) { //number Above
        adjToPit3.push(numbers[indexOfPit3-4]);
    }
    if (indexOfPit3-1 >= 0 && indexOfPit3%4 != 0) { //number left
        adjToPit3.push(numbers[indexOfPit3-1]);
    }
    
    var adjToPit4 = []
    if (indexOfPit4+4 < 16) { //number below
        adjToPit4.push(numbers[indexOfPit4+4]);
    }
    if (indexOfPit4+1 < 16 && indexOfPit4%4 != 3) { //number right
        adjToPit4.push(numbers[indexOfPit4+1]);
    }
    if (indexOfPit4-4 >= 0) { //number Above
        adjToPit4.push(numbers[indexOfPit4-4]);
    }
    if (indexOfPit4-1 >= 0 && indexOfPit4%4 != 0) { //number left
        adjToPit4.push(numbers[indexOfPit4-1]);
    }

The above code is calculating adjacents for the monster, gold, and our 4 pits.

Let's say that pit 4 is at the index 12 in the grid and the grid starts from 0 and Left-Top this makes the 12th index first block of the 4th row as shown in the image below.

The adjacent boxes to 12 are 8 (Above) and 13 (Right). So in the code, I've added the comment for each if-condition that explains the code so when the code will reach if (indexOfPit4+4 < 16) { //number below it'll add 12+4 which is equal to 16 and the condition will only run if 16 is less than 16 which is false so the number below will have nothing which is fine as with this example this is the expected outcome. The next if-condition is if (indexOfPit4+1 < 16 && indexOfPit4%4 != 3) { //number right will do 12+1 less than 16 AND 12%4 is not equal to 3. Both these conditions are true as 13 is less than 16 and 0 is not equal to 3 so index 13 will be added to the adjToPit4. Similarly, we check for left and above.

Now that we know the adjacents we add some weight to each adjacent cell. Let's look at the code first:

    for(var i = 0; i < adjToMonster.length; i++) {
        let temp = numbers.indexOf(adjToMonster[i])
        addValueToArray(temp, 10);
        
    }
    
    for(var i = 0; i < adjToGold.length; i++) {
        let temp = numbers.indexOf(adjToGold[i])
        addValueToArray(temp, 5);
    }
    
    for(var i = 0; i < adjToPit1.length; i++) {
        let temp = numbers.indexOf(adjToPit1[i])
        addValueToArray(temp, 2);
    }
    
    for(var i = 0; i < adjToPit2.length; i++) {
        let temp = numbers.indexOf(adjToPit2[i])
        addValueToArray(temp, 2);
    }
    
    for(var i = 0; i < adjToPit3.length; i++) {
        let temp = numbers.indexOf(adjToPit3[i])
        addValueToArray(temp, 2);
    }
    
    for(var i = 0; i < adjToPit4.length; i++) {
        let temp = numbers.indexOf(adjToPit4[i])
        addValueToArray(temp, 2);
    }

Let's again take pit 4 to understand the code. We call a function that we haven't created yet, named addValueToArray let's add that first:

function addValueToArray(index, value) {
    tempArray[index] = tempArray[index] + value;
}

So now in the adjToPit4 array we have index 8 and 13. So the for-loop will run two times. As at the start, we randomized the numbers array so we find at which index of the numbers array 8 and 13 have shuffled to. We store it in a temp variable and then addValueToArray adds it at the tempArray exactly at the same index as numbers array. As tempArray was initialized with 0 so we add 2 in the case of pits and 5 in the case of gold and 10 in the case of monster. These values will help us determine which hint will show at which index in the grid. Next, we'll do that.

for(var i = 0; i<tempArray.length; i++) {
        switch(tempArray[i]) {
            case 10:
                numberStrs[i] = "👃";
                break;
            case 5:
                numberStrs[i] = "✨";
                break;
            case 2:
            case 4:
            case 6:
            case 8:
                numberStrs[i] = "💨";
                break;
            case 23:
            case 21:
            case 19:
            case 17:
                numberStrs[i] = "💨✨👃";
                break;
           case 15:
                numberStrs[i] = "👃✨";
                break;
            case 7:
            case 9:
            case 11:
            case 13:
                numberStrs[i] = "✨💨";
                break;
            case 18:
            case 16:
            case 14:
            case 12:
                numberStrs[i] = "👃💨";
                break;
           case 0:
                numberStrs[i] = "❎";
                break;
            default:
                console.log("Error# 874B3: Temp Value array value is not found. Value = " + tempArray[i]);
        } //EO Switch
    }//EO For-loop

Once the tempArray is loaded with the values we made we run a for-loop on it. The case numbers are very easy to determine, as we know the weight for each pit is 2, gold is 5 and monster carries a weight of 10. So let's say a box has all three a pit, gold ad monster its value will be 2+5+10 = 17 and in the code above you can see case 17 is adding 💨✨👃 to the array. Let's say there were two pits, gold and monster around a box its value will be 2+2+5+10 = 19, and as you can see how the case is handled.

As we're replacing numbers with emojis so we still need to replace values for our monster, gold, and pits. So after the for-loop ends we add:

    numberStrs[indexOfMonster] = "👹";
    numberStrs[indexOfGold] = "💰";
    numberStrs[indexOfPit1] = "🕳️";
    numberStrs[indexOfPit2] = "🕳️";
    numberStrs[indexOfPit3] = "🕳️";
    numberStrs[indexOfPit4] = "🕳️";

Until this point, the code will create the grid exactly required by the simulator. Now to create a gameplay we need to open one box randomly, but it has to be a hint box and not the gold, monster, or a pit. So, we add this code:

    //Radom opening of one box when game starts
    var tempRandNum;
    
    do {
        tempRandNum = Math.floor(Math.random() * 16);
    } while (tempRandNum == indexOfMonster || tempRandNum == indexOfGold || tempRandNum == indexOfPit1 || tempRandNum == indexOfPit2 || tempRandNum == indexOfPit3 || tempRandNum == indexOfPit4);
    
    console.log(numberStrs[tempRandNum]);
    
    userGamePlayPattern.push(numberStrs[tempRandNum]);
    userGamePlayPatternIndex.push(tempRandNum);

We generate a random number from 0-15 and keep doing that until we find a value that is not equal to the index of our monster, gold, and, pits. In this code, I've just printed that value to the console, but ow we have the index that'll be opened when the game start.

Using this exact logic, with HTML and buttons press code we've developed a simulator running here: https://intuitive-ai.firebaseapp.com/.

Expanding The Grid

In this section, we'll set some ground rules of if this grid of 4x4 is to be expanded then how do we go about it.

A 4x4 grid can make up to 16! = 20,922,789,888,000 (twenty trillion, nine hundred twenty-two billion, seven hundred eighty-nine million, eight hundred eighty-eight thousand) unique games.

When expanding the 4x4 grid it always has to be both the row and column. For example, expanding 4x4 gid to 5x5 is valid but 4x5 or 5x4 is invalid.

If you have added one row and one column then there should be one additional pit. For example, if the grid is now 5x5 there must be 5 pits in it whereas the gold ad monster remains the same.

The index you'll give to the new pit can be an odd number from any of the 5th-row or 5th column.

The above three rules are all there is if you want to expand the grid.

Last updated