Tuesday, 30 January 2018

Football Match - Using football simulation engine

I recently created a football simulation engine for npm which I talked about here.

I also posted a youtube video of me demonstrating the code, and after some interest I thought I would upload the helper code I used in order to visualise the match!

You can access the github page here: https://github.com/GallagherAiden/footballsimulationexample



Here's the read me:
# Football Simulation Example

## Install
1. Download the project onto your system ({dir})
2. cd {dir}
3. run ``npm install``
---
## Run the Game
1. load your team into the {dir}/teams directory (you can use an existing team as an example)
2. in '{dir}/server.js' change either
```
readFile("teams/team1.json").then(function (team1) {
```
to your newly created .json file i.e.
```
readFile("teams/myTeam.json").then(function (team1) {
```
3. Set the pitch size by changing the ``pitchWidth`` and ``pitchHeight`` in '{dir}/server.js'
*Note: Any changes will need changes made to the start positions of the players*
4. run ``npm start``
5. Go To ``http://localhost:1442/match.html``
---
## Playing A Match
1. Press ``start match``
- This starts a new match where the pitch size is displayed on the screen and the players are in their correct positions.
2. Press ``play match``
- This runs '10' iterations, moving the players and give iteration logs for each iteration.
i.e. `Iteration 50: Closest Player to ball: Louise Johnson,Closest Player to ball: Aiden Smith`
3. Press ``play match`` until the iterations reach 5,000
- We can make this quicker by changing the number of iterations per "play" in {dir}/public/js/match.js
```
function getMatch() {
var iterations = 10;
for (i = 0; i < iterations; i++) {
movePlayers("/movePlayers");
}
}
```
to
```
function getMatch() {
var iterations = X;
for (i = 0; i < iterations; i++) {
movePlayers("/movePlayers");
}
}
```
where X is the number of iterations to do at a time.
4. Press ``start second half``
- This flips the players to opposite sides
5. Press ``play match`` until the iterations reach 10,000
6. Record the results

Game Example Results:
Match 1 - Team1: 5 - 3 :Team2  (same json)
Match 2 - Team1: 3 - 4 :Team2  (same json)
Match 3 - Team1: 9 - 2 :Team2  (same json)
Match 4 - Team1: 2 - 5  :Team2  (team2 higher skills)
Match 5 - Team1: 0 - 7  :Team2  (team2 higher skills)
Match 6 - Team1: 8 - 1 :Team2  (team1 higher skills)
Match 7 - Team1: 3 - 0 :Team2  (team1 higher skills)

Have fun! Let me know if you need any help: aiden.g@live.co.uk


Thursday, 25 January 2018

Node JS Football / Soccer Simulation Engine Module

Football Simulation Engine - Node Module

As previously mentioned in my blog HERE, I had been looking to design and build a football manager game, with the main purpose being to improve my JavaScript (JS) and node (Node JS) skills. I soon found there were no existing node package modules (npm) that could provide a simulated match result.

I posted on a few sites to try and see if anyone had done this before - there’s no point repeating work is there? After finding nothing specific that matched my purpose I went on ahead and built my prototypes which consisted of two random numbers and a little use of player skill to create results (see previous blog), but it didn’t really fit the bill for a football manager games back end as it didn’t take other factors into consideration.

So, I went ahead and built my own, and as of today it is now available on the npm website HERE or run 
          npm install footballsimulationengine

How does it work?
Great question, glad you asked. Basically, you provide it with two JSON strings, each its own team and requiring a specific set of JSON objects. This includes team name, players, player positions, skills and each players starting position.
Example:
{
  "name": "Team1",
  "players": [{
      "name": "Player",
      "position": "GK",
      "rating": "99",
      "skill": {
        "passing": "99",
        "shooting": "99",
        "tackling": "99",
        "saving": "99",
        "agility": "99",
        "strength": "99",
        "penalty_taking": "99",
        "jumping": "300"
      },
      "startPOS": [60,0],
      "injured": false
    }...],
  "manager": "Aiden"
}

Key things to remember:
·      Each team MUST have 11 players
·      The code currently does not check these parameters, so the skills SHOULD be between 1 and 100.
·      Except for “jumping” which is intended to be the maximum jump height of the player in centimetres.
·      Start Position should between you pitch Sizes Width and half of the pitch length. The code deals with switching their start position side. E.g if my pitch size is [120, 600], “startPOS” SHOULD BE [0-120, 0 - 300].

The other thing we will need to start a game/match is pitch size, which is shown in [width, height] where the pitch is intended to be taller than it is wide, like below.

PIC

Example Pitch JSON:
{
            "pitchWidth": 120,
            "pitchHeight": 600
}

We feed these three JSONs into the initiate function like so:

Example:
initiateGame(team1, team2, pitch).then(function(matchDetails){
  console.log(matchDetails);
}).catch(function(error){
  console.error("Error: ", error);
})

what we get back is a super larger JSON with both teams, their originPOS (so we can get them back to their original position), relativePOS (so we know where they’re heading towards), and a whole bunch of match details such as the score, number of shots and importantly, where the ball is, ball direction.

The main idea is that the game runs in iterations this allows the client using the simulation engine to change players throughout the game, set up the game in certain positions.

Playing iterations is then as simple as running that big old outputted JSON back into a play iteration function which in turn, gives an updated big old output JSON.

Example:
playIteration(matchDetails).then(function (matchDetails) {
  console.log(matchDetails);
}).catch(function(error){
  console.error("Error: ", error);
}

Example Output:
{ kickOffTeam:
   { name: 'Team1',
     players:
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ],
     manager: 'Aiden'
     intent: 'defend' },
  secondTeam:
   { name: 'Team2',
     players:
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ],
     manager: 'Joe',
     intent: 'attack' },
  pitchSize: [ 120, 600 ],
  ball:
   { position: [ 76, 314 ],
     withPlayer: true,
     Player: 'Joe Bloggs',
     withTeam: 'Team2',
     direction: 'south' },
  half: 1,
  kickOffTeamStatistics:
   { goals: 0,
     shots: 0,
     corners: 0,
     freekicks: 0,
     penalties: 0,
     fouls: 0 },
  secondTeamStatistics:
   { goals: 0,
     shots: 0,
     corners: 0,
     freekicks: 0,
     penalties: 0,
     fouls: 0 },
  iterationLog:
   [ 'Closest Player to ball: Aiden Gallagher',
     'Closest Player to ball: Joe Bloggs' ] }

An important feature here is the “iterationLog” which gives an overview of what has happened during that iteration. i.e. someone has passed the ball, someone has taken a shot, scored, freekick awarded, penalty taken, a throwin given and taken.

Finally, the last function that can be played is switch sides of the teams that are playing, i.e. after half time or if running into extra time.

Example:
startSecondHalf(matchDetails).then(function (matchDetails) {
  console.log(matchDetails);
}).catch(function(error){
  console.error("Error: ", error);
}

TL;DR / Overview:
·      Three function; initiateGame - needs two team JSONs and a Pitch Details JSON, playIteration - takes the output from initiate game and updates one movement of each player, startSecondHalf - switches the sides the players are on.
·      All three functions are return promises
·      JSON isn’t checked so make sure you’ve got it right
·      Recommended match length is 10,000 iterations.

If you’d like help setting up, have some general questions please feel free to comment below and I’ll get back to you ASAP.

Improvements:
This code is setup on a public Github which you can find HERE and I’m hoping people who use the module (if anyone) will help me improve it! You can raise issues HERE but otherwise please get in touch by email: aiden.g@live.co.uk

Visual Example:
For those who want to see, here is a youtube video of the game being used in an example I have set up. Feel free to get in touch and I can send you the "manager" setup and some instructions. 


Wednesday, 29 November 2017

Motion Detector using a PhotoResistor and Arduino

I recently brought two "WeMos D1 WiFi Uno ESP8266 ESP-12E Development Board" hoping to get my first connectivity of two arduino systems via wifi.

Getting the WeMos D1's working

The first task is to set up the arduinos. I could write out how in full, but there's no point in reinventing the wheel, especially when the instructables website does such a good job here:

Once we have this setup its important to understand how we get that connectivity to the internet, what code we need and what the errors and status codes we might see in order to handle them in the code. The best link I have found, or the simplest for Connecting Arduino to Wifi (including status codes) is the one below.

However, the easiest way to get something working straight away is to use: http://www.esp8266learning.com/wemos-webserver-example.php all that is needed is to set your wifi name and password, upload to your Arduino and your done. Follow the IP address that is printed on the serial monitor and you'll have a webpage where you can turn on and off the light of the arduino. 

Try it with any webpage viewable device connected to the same network and voila!

Do this with both Arduino's. One of the Arduino's will be used as a web server and the other will be used to send the sensor information to switch the light on, on the web server in order to "Alert" someone watching the server Arduino that someone has passed the sensor that could be in another room.

Setting up a sensor to output analog (Alert Arduino)

For this we will need;
 - 1 PhotoResistor
 - 3 Male to Male Jumper Cable
 - 1 Resistor
 - 1 Breadboard

We plug the PhotoResistor into the breadboard on one of the centre areas, one leg will be connected to a positive 5V charge from the Arduino. The other will have a resistor across it into a new line and also a male to male jumper cable into one of the analog pins. The final piece is to connect the other end of the resistor to GND (ground).
The sensor will be pointing off the table towards where you want to monitor
We need to add the variable for the photoResistor. 

Before the Setup Code we add:
int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor
Within the loop:
sensorValue = analogRead(sensorPin);
Serial.println(sensorValue);

The information for this was taken from here: https://www.arduino.cc/en/Tutorial/AnalogInput
Once this runs we will start to see the unhindered sensor reading. In my case, this was around 355 - 380 and when covered by hand close and far the readings varied from 150 - 350. 

We can then add some code to the loop to turn the light on/off depending on the value returned by the sensor.
   if(sensorValue > 350){
      //turn light off
   }else{
      //turn light on
   }

Waiting for the Alert (Server Arduino)

We need to add the listener to turn on the light when the sensor is triggered.

In the "match request" section underneath the /LED we need to setup actions when the sensors alert.
  if (request.indexOf("/sensor=ON") != -1){
    digitalWrite(ledPin, HIGH);
    value = LOW;
  }
  if (request.indexOf("/sensor=OFF") != -1){
    digitalWrite(ledPin, LOW);
    value = LOW;
  }
Upload.

To test we can go to 
IPAddress:/sensor=ON
and then
 IPAddress:/sensor=OFF
on a network connected device. We should see the server Arduino's light turn on and then off. 

Posting an Alert when the sensor is triggered (Alert Arduino)

After a little google, the best Arduino POST example I could find using the examples we have was the answer by "stiff" on the following stack overflow question: https://stackoverflow.com/questions/3677400/making-a-http-post-request-using-arduino

Where we need to add;
   #include <ESP8266HTTPClient.h> 
   //inside the loop
   HTTPClient http;    //Declare object of class HTTPClient
   http.begin("http://IPAddress/sensor=OFF");      //Specify request destination
   http.addHeader("Content-Type", "text/plain");  //Specify content-type header
   int httpCode = http.POST("Message from ESP8266");   //Send the request
   String payload = http.getString();                  //Get the response payload

   Serial.println(httpCode);   //Print HTTP return code
   Serial.println(payload);    //Print request response payload
   http.end();  //Close connection
We can then use the:
 http.begin("http://IPAddress/sensor=ON");
with the sensorValue IF statement we made earlier.
if(sensorValue > 350){
   http.begin("http://IPAddress/sensor=OFF");      //Specify request destination
 }else{
   http.begin("http://IPAddress/sensor=ON");
  }
We have now completed the minimum viable product. I will share the full source code below as well as a video of the result. If you have any questions or would like the source code directly feel free to comment!
*UPDATE: below the original source code is an adapted piece of code which is much quicker and more responsive! enjoy!

Full Source Code: 

Alert Arduino: (With Sensor)

#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>

const char* ssid = "XXX";
const char* password = "YYY";

int ledPin = D5;
int sensorPin = A0;
int sensorValue = 0;

void setup() {

  Serial.begin(115200);                 //Serial connection
 
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);   //WiFi connection

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

}

void loop() {
   sensorValue = analogRead(sensorPin);
   Serial.println(sensorValue);
   if(WiFi.status()== WL_CONNECTED){   //Check WiFi connection status

   HTTPClient http;    //Declare object of class HTTPClient
   if(sensorValue > 350){
   http.begin("http://
IPAddress/sensor=OFF");      //Specify request destination
   }else{
   http.begin("http://IPAddress/sensor=ON");
   }
   http.addHeader("Content-Type", "text/plain");  //Specify content-type header
   int httpCode = http.POST("Message from ESP8266");   //Send the request
   String payload = http.getString();                  //Get the response payload

   Serial.println(httpCode);   //Print HTTP return code
   Serial.println(payload);    //Print request response payload
   http.end();  //Close connection

 }else{

    Serial.println("Error in WiFi connection");

 }

  delay(10);

Server Arduino:

#include <ESP8266WiFi.h>

const char* ssid = "XXX";
const char* password = "YYY";

int ledPin = D5;
WiFiServer server(80);

void setup() {
  Serial.begin(115200);
  delay(10);


  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  // Start the server
  server.begin();
  Serial.println("Server started");

  // Print the IP address
  Serial.print("Use this URL : ");
  Serial.print("http://");
  Serial.print(WiFi.localIP());
  Serial.println("/");

}

void loop() {
  // Check if a client has connected
  WiFiClient client = server.available();
  if (!client) {
    return;
  }

  // Wait until the client sends some data
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }

  // Read the first line of the request
  String request = client.readStringUntil('\r');
  Serial.println(request);
  client.flush();

  // Match the request

  int value = LOW;
  if (request.indexOf("/LED=ON") != -1) {
    digitalWrite(ledPin, HIGH);
    value = HIGH;
  }
  if (request.indexOf("/LED=OFF") != -1){
    digitalWrite(ledPin, LOW);
    value = LOW;
  }
  if (request.indexOf("/sensor=ON") != -1){
    digitalWrite(ledPin, HIGH);
    value = LOW;
  }
  if (request.indexOf("/sensor=OFF") != -1){
    digitalWrite(ledPin, LOW);
    value = LOW;
  }



  // Return the response
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");

  client.print("Led pin is now: ");

  if(value == HIGH) {
    client.print("On");
  } else {
    client.print("Off");
  }
  client.println("<br><br>");
  client.println("Click <a href=\"/LED=ON\">here</a> turn the LED on pin 5 ON<br>");
  client.println("Click <a href=\"/LED=OFF\">here</a> turn the LED on pin 5 OFF<br>");
  client.println("</html>");

  delay(1);
  Serial.println("Client disconnected");
  Serial.println("");

}
Results:


UPDATE: From the above video we can see that the response is pretty slow with the code we have. To fix this I have edited the "Alert Arduino" code. Below is the results of changes made (below that will be the code).


Video Links: https://www.youtube.com/watch?v=Pfqs-WN9OiQ
                      https://www.youtube.com/watch?v=iTQJmba_wcw

Updated Code:

Alert Arduino: (With Sensor)
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>

const char* ssid = "XXX";
const char* password = "YYY";

int ledPin = D5;
int sensorPin = A0;
int sensorValue = 0;
int sensorOn = 0;
int currentSensor = 0;

void setup() {

  Serial.begin(115200);                 //Serial connection
 
  // Connect to WiFi network
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);   //WiFi connection

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

}

void loop() {
   sensorValue = analogRead(sensorPin);
   Serial.println(sensorValue);
   if(sensorValue > 350){
    sensorOn = 0;
   }else{
    sensorOn = 1;
   }
 
   if(WiFi.status()== WL_CONNECTED){   //Check WiFi connection status

   HTTPClient http;    //Declare object of class HTTPClient
 
   if(currentSensor == 1 && sensorOn == 0){
 
    currentSensor = 0;
    http.begin("http://IPAddress/sensor=OFF");      //Specify request destination
    http.addHeader("Content-Type", "text/plain");  //Specify content-type header
    int httpCode = http.POST("Message from ESP8266");   //Send the request
    http.end();  //Close connection
 
   }else if(currentSensor == 0 && sensorOn == 1){
 
    currentSensor = 1;
    http.begin("http://IPAddress/sensor=ON");
    http.addHeader("Content-Type", "text/plain");  //Specify content-type header
    int httpCode = http.POST("Message from ESP8266");   //Send the request
    http.end();  //Close connection
 
   }else{
 
    //do nothing
 
   }
   //String payload = http.getString();                  //Get the response payload
   //Serial.println(httpCode);   //Print HTTP return code
   //Serial.println(payload);    //Print request response payload

 }else{

    Serial.println("Error in WiFi connection");

 }

  delay(500);

}

You don't need a PHD to open a .phd file!

Today I was looking at some Java Core dumps on a system and wanted to start debugging to see what was causing an apparent memory leak. All was going well until I was led to:
  /log/javacore-filename.phd
I hadn't stumbled across a Portable Heap Dump (phd) before, so I started to have a little google as to how I could open it but couldn't find a command line answer to my problem. Apparently no conversion will get me what I want and I'll have to use a set of tools.

One of the first things that showed up was "IBM Memory Analyzer*" which gives analysis on heap/core dumps. To install you can have a stand alone application but it more conveniently comes as a plugin to be installed directly into an Eclipse runtime.
    http://www.eclipse.org/downloads/eclipse-packages/ 

*Being an IBMer myself I was inclined to use this since its one of our own products.*

To install you simply open eclipse and go to: help -> eclipse marketplace -> search for: "memory analyser" and select go -> install -> follow instructions and restart eclipse

I thought, brilliant I should be able to load the .phd and get the data I need. However, when I tried to load my file I got:
Error opening heap dump 'javaheapdump-user-cmc.phd'. Check the error log for further details.
Error opening heap dump 'javaheapdump-user-cmc.phd'. Check the error log for further details.
Not a HPROF heap dump (java.io.IOException)
Not a HPROF heap dump
After a little more google investigation I found I needed a little extra component called "IBM Monitoring and Diagnostic Tools for Java" or "IBM DTFJ" for short. This works on top of memory analyser and will allow us to open the phd file.

To download it go to: help -> Install new software -> Select the add button next to the "work with" text bar. -> In both boxes type: http://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/runtimes/tools/dtfj/ -> toggle "IBM Monitoring and Diagnostic Tools" -> install -> follow instructions and reset eclipse.

Now when you go to open the .phd file, memory analyser will be able to find it and will use DTFJ to open it. From here we can get component reports and leak suspects.

Wednesday, 22 November 2017

Simulating Football / Soccer Results

I've recently started to look for a football simulation engine (soccer over in the USA) that can be used  to begin a football manager game. I realised quickly this is something lots of people try but don't necessarily finish or keep on top of:
- https://github.com/d-nation/soccer-sim-engine
https://github.com/atas76/SimpleFootie
and there's plenty of online forums asking how you might make a simple match simulator, so I thought why not just give it a go and see what I come up with.

My code language of choice is Javascript and I'll be using NodeJS throughout.

Attempt 1 - Use Team Rating to generate prediction

This uses a function that generates a random number
function getRandomNumber(min, max) {
    var random = Math.floor(Math.random() * (max - min + 1)) + min;
    return random;
}
we can then feed minimum and maximum numbers to get a random number between the two.
This attempt uses the team rating to determine different maximums, so that better rated teams can get a higher score but it isn't impossible for the lower teams to win.
function getHighestLikely(teamRating) {
    if (teamRating < 100 && teamRating > 80) {
        return 20;
    } else if (teamRating < 79 && teamRating > 60) {
        return 8;
    } else if (teamRating < 59 && teamRating > 30) {
        return 2;
    } else if (teamRating < 29 && teamRating > 0) {
        return 1;
    }
}
we can then run the following where the team ratings can be passed in by the user:
var team1rating = 73;
var team2rating = 45;
var homeTeamScore = getRandomNumber(0, getHighestLikely(team1rating)); 
var awayTeamScore = getRandomNumber(0, getHighestLikely(team2rating));
console.log("Home: " + homeTeamScore + " - " + awayTeamScore + " : Away");

Attempt 2 - Random Event, Random Team Member and Player Rating Comparison

I then looked at how we can utilise some random shooting events alongside a random team member and a comparison of two players ratings.

For this we will need:
- an events variable for goal scoring events
- readFile function (will show below)
- two JSON files with players and player ratings
function readFile(filePath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, data) {
            if (err) {
                reject(err);
            } else {
                data = JSON.parse(data);
                resolve(data);
            }
        })
    });
}
We use the above function to read in two json files with player ratings, in this example I've put a lot of information that I may or may not use in the future:
{
  "name": "Brewers",
  "rating": "89",
  "players": [{
      "name": "Bill Gallagher",
      "position": "GK",
      "rating": "75",
      "skill": {
        "passing": "78",
        "shooting": "12",
        "saving": "75",
        "penalty_taking": "43"
      }
    },
    {
      "name": "Fred Gallagher",
      "position": "LB",
      "rating": "90",
      "skill": {
        "passing": "83",
        "shooting": "40",
        "tackling": "32",
        "penalty_taking": "53"
      }
    },
    {
      "name": "George Gallagher",
      "position": "CB",
      "rating": "84",
      "skill": {
        "passing": "78",
        "shooting": "37",
        "tackling": "21",
        "penalty_taking": "66"
      }
    },
    {
      "name": "Jim Gallagher",
      "position": "CB",
      "rating": "75",
      "skill": {
        "passing": "33",
        "shooting": "76",
        "tackling": "76",
        "penalty_taking": "21"
      }
    },
    {
      "name": "Sid Gallagher",
      "position": "RB",
      "rating": "82",
      "skill": {
        "passing": "66",
        "shooting": "65",
        "tackling": "81",
        "penalty_taking": "88"
      }
    },
    {
      "name": "Gregory Gallagher",
      "position": "LM",
      "rating": "87",
      "skill": {
        "passing": "51",
        "shooting": "88",
        "tackling": "81",
        "penalty_taking": "94"
      }
    },
    {
      "name": "Arthur Gallagher",
      "position": "CM",
      "rating": "41",
      "skill": {
        "passing": "33",
        "shooting": "66",
        "tackling": "55",
        "penalty_taking": "1"
      }
    },
    {
      "name": "Cameron Gallagher",
      "position": "CM",
      "rating": "99",
      "skill": {
        "passing": "88",
        "shooting": "95",
        "tackling": "91",
        "penalty_taking": "62"
      }
    },
    {
      "name": "Tanisha Gallagher",
      "position": "RM",
      "rating": "79",
      "skill": {
        "passing": "56",
        "shooting": "79",
        "tackling": "74",
        "penalty_taking": "32"
      }
    },
    {
      "name": "Aiden Gallagher",
      "position": "ST",
      "rating": "75",
      "skill": {
        "passing": "83",
        "shooting": "88",
        "tackling": "59",
        "penalty_taking": "45"
      }
    },
    {
      "name": "Louise Peverley",
      "position": "ST",
      "rating": "88",
      "skill": {
        "passing": "73",
        "shooting": "61",
        "tackling": "44",
        "penalty_taking": "66"
      }
    }
  ],
  "manager": "Aiden",
  "formation": [4,4,2]
}
the two json files differed only slightly.
I will also require a variable called "events" : var events = ["shot", "penalty"];
function playMatch(team1Config, team2Config) {
    var matchEventsNo = getRandomNumber(0, 10);
    console.log("Total Events in the Match: " + matchEventsNo);
    readFile(team1Config).then(function (team1) {
        readFile(team2Config).then(function (team2) {
            while (matchEventsNo != 0) {
                console.log("Event: " + matchEventsNo);
                var eventTeam = getRandomNumber(1, 2);
                var eventPlayerHome = team1.players[getRandomNumber(0, 10)];
                var eventPlayerAway = team2.players[getRandomNumber(0, 10)];
                if (eventTeam === 1) {
                    console.log("Event Team: " + team1.name);
                } else if (eventTeam === 2) {
                    console.log("Event Team: " + team2.name);
                }
                var thisEvent = events[getRandomNumber(0, 1)];
                console.log("Event Type: " + thisEvent);
                if (thisEvent === "penalty") {
                    if (eventTeam === 1) {
                        console.log("Player: " + eventPlayerHome.name + "(" + eventPlayerHome.position + ") rating: " + eventPlayerHome.rating);
                        console.log("Player: " + team2.players[0].name + "(" + team2.players[0].position + ") rating: " + team2.players[0].rating);
                        if (eventPlayerHome.rating > team2.players[0].rating) {
                            homeScore++;
                        }
                    } else if (eventTeam === 2) {
                        console.log("Player: " + eventPlayerAway.name + "(" + eventPlayerAway.position + ") rating: " + eventPlayerAway.rating);
                        console.log("Player: " + team1.players[0].name + "(" + team1.players[0].position + ") rating: " + team1.players[0].rating);
                        if (eventPlayerAway.rating > team1.players[0].rating) {
                            awayScore++;
                        }
                    }
                } else if (thisEvent === "shot") {
                    if (eventTeam === 1) {
                        console.log("Player: " + eventPlayerHome.name + "(" + eventPlayerHome.position + ") rating: " + eventPlayerHome.rating);
                        console.log("Player: " + team2.players[0].name + "(" + team2.players[0].position + ") rating: " + team2.players[0].rating);
                        if (eventPlayerHome.rating > team2.players[0].rating) {
                            homeScore++;
                        }
                    } else if (eventTeam === 2) {
                        console.log("Player: " + eventPlayerAway.name + "(" + eventPlayerAway.position + ") rating: " + eventPlayerAway.rating);
                        console.log("Player: " + team1.players[0].name + "(" + team1.players[0].position + ") rating: " + team1.players[0].rating);
                        if (eventPlayerAway.rating > team1.players[0].rating) {
                            awayScore++;
                        }
                    }
                }
                matchEventsNo--;
                console.log(team1.name + " " + homeScore + " - " + awayScore + " " + team2.name);
            }
        });
    });
}

function getRandomNumber(min, max) {
    var random = Math.floor(Math.random() * (max - min + 1)) + min;
    return random;
}
This created up to ten random events per "Match". For each match it would then decide which of two events had occurred. Either a penalty or a shot. It would then decide which of the two teams (randomly again) has incurred the event. We then channel a series of if statements to determine if there was a goal by comparing the player who took the shot/penalty (randomly chosen from the team) against the opposition goalkeepers ability.

We can run this using:
playMatch("teams/team1.json", "teams/team2.json").then(function(){ 
});

This gives us a limited number of goals (10 max) but can be increased by saying a minimum of X events and a maximum of (infinite) events to increase goals that can be scored.

Random "Plays" made of up to 10 random events.

Whilst this was good and worked, I thought the best way to boost this method of generating football events and therefore goals was to create "plays" which would consist of 10 random events. These would begin by an event starter such as a throw in, corner, goal kick, free kick or a penalty.

All of these are events that "start" play from a stoppage. After this has been determined a continue of the event is decided as either pass, cross or shoot.  A play is only finished being generate once a "shoot" token has been given.

Once the 10 plays have been decided with their potentially infinite events, there is a formula run to see if a goal is scored. First we decide which team is doing each of the 10 events (an array of 10, each number in the array either being 0 or 1 to determine if the home or away team are performing the "play").

We then compare the players skill rating for the given event against a random number between 0 and 100. Thus higher rated players are more likely to be successful in whatever event they are doing i.e. passing. At any stage a pass, cross, shot or penalty can miss thus ending the play and meaning no goal will be scored.

var fs = require("fs");
var async = require("async");
var teamName;
var userName;
var eventStart = ["throwin", "corner", "goalkick", "freekick", "penalty"];
var events = ["pass", "cross", "shoot"];
var homeScore = 0;
var awayScore = 0;
var play = [];
var eventTeams = [];

playMatch("teams/team1.json", "teams/team2.json");
resetVars();

function playMatch(team1Config, team2Config) {
    eventTeam().then(function () {
        readFile(team1Config).then(function (team1) {
            readFile(team2Config).then(function (team2) {
                async.eachSeries(eventTeams, function eachTeam(thisTeam, thisTeamCallback) {
                    console.log("--------------------");
                    console.log("Starting Play");
                    if (thisTeam === 0) {
                        generatePlay().then(function () {
                            goalScored(team1, team2, team1).then(function (score) {
                                if (score === 1) {
                                    console.log("Team 1 scored");
                                    homeScore++;
                                    play = [];
                                    thisTeamCallback();
                                } else {
                                    console.log("Team 1 missed");
                                    play = [];
                                    thisTeamCallback();
                                }
                            });
                        });
                    } else {
                        generatePlay().then(function () {
                            goalScored(team1, team2, team2).then(function (score) {
                                if (score === 1) {
                                    console.log("Team 2 scored");
                                    awayScore++;
                                    play = [];
                                    thisTeamCallback();
                                } else {
                                    console.log("Team 2 missed");
                                    play = [];
                                    thisTeamCallback();
                                }
                            });
                        });
                    }
                }, function afterAllTppUserOrgs() {
                    console.log(team1.name + " " + homeScore + " - " + awayScore + " " + team2.name);
                });
            });
        });
    });
}

function eventTeam() {
    return new Promise(function (resolve, reject) {
        var playTotal = getRandomNumber(1, 10);
        console.log("Total plays in the Match: " + playTotal);
        while (playTotal !== 0) {
            eventTeams.push(getRandomNumber(0, 1));
            playTotal--;
            if (playTotal === 0) {
                resolve();
            }
        }
    });
}

function goalScored(team1, team2, whichTeam) {
    return new Promise(function (resolve, reject) {
        var score = 0;
        async.eachSeries(play, function eachEvent(playEvent, playEventCallback) {
            console.log(playEvent);
            if (score === -1) {
                playEventCallback();
            } else {
                if (playEvent === "throwin" || playEvent === "corner" || playEvent === "goalkick" || playEvent === "freekick" || playEvent === "pass" || playEvent === "cross") {
                    if (whichTeam.players[getRandomNumber(1, 10)].skill.passing > getRandomNumber(0, 100)) {
                        playEventCallback();
                    } else {
                        score = -1;
                        resolve(score);
                    }
                } else if (playEvent === "penalty" || playEvent === "shoot") {
                    if (whichTeam.players[getRandomNumber(1, 10)].skill.shooting > getRandomNumber(0, 100)) {
                        console.log("goal scored");
                        score++;
                        resolve(score);
                    } else {
                        console.log("shot missed");
                        resolve(score);
                    }
                }
            }
        }, function afterAllEvents() {
            resolve(score);
        });
    });
}

function getRandomNumber(min, max) {
    var random = Math.floor(Math.random() * (max - min + 1)) + min;
    return random;
}

function generatePlay() {
    return new Promise(function (resolve, reject) {
        var startEvent = eventStart[getRandomNumber(0, 4)];
        play.push(startEvent);
        if (startEvent === "penalty") {
            resolve(play);
        } else {
            newEventInPlay(function () {
                resolve(play);
            });
        }
    });
}

function newEventInPlay(callback) {
    newEvent = events[getRandomNumber(0, 2)];
    play.push(newEvent);
    if (newEvent === "shoot") {
        callback();
    } else {
        newEventInPlay(callback);
    }
}

function resetVars() {
    homeScore = 0;
    awayScore = 0;
}

function readFile(filePath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, data) {
            if (err) {
                reject(err);
            } else {
                data = JSON.parse(data);
                resolve(data);
            }
        })
    });

Added tackling and saving to the formula

My final thought was how I could incorporate the defensive teams statistics to make it more of a two team game. To do this, when a player misses a pass or cross etc. We see if they were tackled or intercepted after the failure.
If yes, the particular play ends with no further actions. If not, the play continues. When a player shoots successfully (the rating is higher than the random number generated between 0 and 100) we see if the keeper was able to save it. To alleviate the bias this gives against scoring, we play many more events. In this example, 30.
var fs = require("fs");
var async = require("async");
var eventStart = ["throwin", "corner", "goalkick", "freekick", "penalty"];
var events = ["pass", "cross", "shoot"];
var homeScore = 0;
var awayScore = 0;
var play = [];
var eventTeams = [];

playMatch("teams/team1.json", "teams/team2.json");

function playMatch(team1Config, team2Config) {
        eventTeam().then(function () {
            readFile(team1Config).then(function (team1) {
                readFile(team2Config).then(function (team2) {
                    console.log("Todays Match is: " + team1.name + " vs " + team2.name);
                    async.eachSeries(eventTeams, function eachTeam(thisTeam, thisTeamCallback) {
                        //console.log("--------------------");
                        //console.log("Starting Play");
                        if (thisTeam === 0) {
                            generatePlay().then(function () {
                                goalScored(team1, team2).then(function (score) {
                                    if (score === 1) {
                                        //console.log(team1.name + " scored");
                                        homeScore++;
                                        play = [];
                                        thisTeamCallback();
                                    } else {
                                        //console.log(team1.name + " missed");
                                        play = [];
                                        thisTeamCallback();
                                    }
                                });
                            });
                        } else {
                            generatePlay().then(function () {
                                goalScored(team2, team1).then(function (score) {
                                    if (score === 1) {
                                        //console.log(team2.name + " scored");
                                        awayScore++;
                                        play = [];
                                        thisTeamCallback();
                                    } else {
                                        //console.log(team2.name + " missed");
                                        play = [];
                                        thisTeamCallback();
                                    }
                                });
                            });
                        }
                    }, function afterAllTppUserOrgs() {
                        console.log(team1.name + " " + homeScore + " - " + awayScore + " " + team2.name);
                        resetVars();
                        resolve();
                    });
                });
            });
        });
}

function eventTeam() {
    return new Promise(function (resolve, reject) {
        var playTotal = getRandomNumber(1, 30);
        //console.log("Total plays in the Match: " + playTotal);
        while (playTotal !== 0) {
            eventTeams.push(getRandomNumber(0, 1));
            playTotal--;
            if (playTotal === 0) {
                resolve();
            }
        }
    });
}

function goalScored(whichTeam, oppTeam) {
    return new Promise(function (resolve, reject) {
        var score = 0;
        async.eachSeries(play, function eachEvent(playEvent, playEventCallback) {
            var thisPlayer = whichTeam.players[getRandomNumber(1, 10)];
            var oppPlayer = oppTeam.players[getRandomNumber(0, 10)];
            //console.log(thisPlayer.name + " (" + thisPlayer.position + ")");
            //console.log(playEvent);
            if (score === -1) {
                playEventCallback();
            } else {
                if (playEvent === "throwin" || playEvent === "corner" || playEvent === "goalkick" || playEvent === "freekick" || playEvent === "pass" || playEvent === "cross") {
                    if (thisPlayer.skill.passing > getRandomNumber(0, 100)) {
                        playEventCallback();
                    } else {
                        //console.log("Retain play?");
                        //console.log("Challenge made by: " + oppPlayer.name + " (" + oppPlayer.position + ")");
                        if (oppPlayer.skill.tackling > getRandomNumber(0, 100)) {
                            //console.log("tackled. posession lost");
                            score = -1;
                            resolve(score);
                        } else {
                            //console.log("retained possession. Continue play");
                            playEventCallback();
                        }
                    }
                } else if (playEvent === "penalty" || playEvent === "shoot") {
                    if (thisPlayer.skill.shooting > getRandomNumber(0, 100)) {
                        if (oppTeam.players[0].skill.saving > getRandomNumber(0, 100)) {
                            //console.log("shot saved by " + oppTeam.players[0].name + " (" + oppTeam.players[0].position + ")");
                            resolve(score);
                        } else {
                            //console.log("goal conceeded by " + oppTeam.players[0].name + " (" + oppTeam.players[0].position + ")");
                            //console.log("goal scored by " + thisPlayer.name + " (" + thisPlayer.position + ")");
                            score++;
                            resolve(score);
                        }
                    } else {
                        //console.log("shot missed");
                        resolve(score);
                    }
                }
            }
        }, function afterAllEvents() {
            resolve(score);
        });
    });
}

function getRandomNumber(min, max) {
    var random = Math.floor(Math.random() * (max - min + 1)) + min;
    return random;
}

function generatePlay() {
    return new Promise(function (resolve, reject) {
        var startEvent = eventStart[getRandomNumber(0, 4)];
        play.push(startEvent);
        if (startEvent === "penalty") {
            resolve(play);
        } else {
            newEventInPlay(function () {
                resolve(play);
            });
        }
    });
}

function newEventInPlay(callback) {
    newEvent = events[getRandomNumber(0, 2)];
    play.push(newEvent);
    if (newEvent === "shoot") {
        callback();
    } else {
        newEventInPlay(callback);
    }
}

function resetVars() {
    homeScore = 0;
    awayScore = 0;
    play = [];
    eventTeams = [];
}

function readFile(filePath) {
    return new Promise(function (resolve, reject) {
        fs.readFile(filePath, 'utf8', function (err, data) {
            if (err) {
                reject(err);
            } else {
                data = JSON.parse(data);
                resolve(data);
            }
        })
    });
}

What next?

The next step for this project would require much more intelligent movement, ratings, and event play for the user. I'm planning on developing this over the next few months and with any luck will have something to open source by the middle of next year.
However, this gives quite a good amount of data and events already. At the bottom I will show the output we see using this bit of code. However, if you have used, or adapted this code. Feel free to get in touch and let me know how it goes by email.
Good Luck!
Output:
Total plays in the Match: 22
Todays Match is: Brewers vs Arsenal
--------------------
Starting Play
Jim Boden (CB)
penalty
penalty on target by Jim Boden (CB)
goal conceeded by Bill Gallagher (GK)
Arsenal scored
--------------------
Starting Play
Gregory Gallagher (LM)
throwin
Retain play?
Challenge made by: Sid Boden (RB)
tackled. posession lost
Brewers missed
--------------------
Starting Play
Louise Boden (ST)
throwin
Retain play?
Challenge made by: Bill Gallagher (GK)
retained possession. Continue play
Arthur Boden (CM)
cross
Retain play?
Challenge made by: Cameron Gallagher (CM)
tackled. posession lost
Arsenal missed
--------------------
Starting Play
Gregory Boden (LM)
penalty
penalty on target by Gregory Boden (LM)
penalty saved by Bill Gallagher (GK)
Arsenal missed
--------------------
Starting Play
Aiden Boden (ST)
penalty
penalty on target by Aiden Boden (ST)
goal conceeded by Bill Gallagher (GK)
Arsenal scored
--------------------
Starting Play
George Boden (CB)
goalkick
Retain play?
Challenge made by: Louise Peverley (ST)
retained possession. Continue play
Sid Boden (RB)
shoot
goal conceeded by Bill Gallagher (GK)
goal scored by Sid Boden (RB)
Arsenal scored
--------------------
Starting Play
Louise Boden (ST)
corner
Retain play?
Challenge made by: Aiden Gallagher (ST)
retained possession. Continue play
Tanisha Boden (RM)
shoot
shot saved by Bill Gallagher (GK)
Arsenal missed
--------------------
Starting Play
Gregory Boden (LM)
freekick
Gregory Boden (LM)
cross
Cameron Boden (CM)
cross
Gregory Boden (LM)
cross
Sid Boden (RB)
pass
Arthur Boden (CM)
shoot
goal conceeded by Bill Gallagher (GK)
goal scored by Arthur Boden (CM)
Arsenal scored
--------------------
Starting Play
Jim Boden (CB)
goalkick
Retain play?
Challenge made by: Arthur Gallagher (CM)
retained possession. Continue play
George Boden (CB)
pass
Tanisha Boden (RM)
cross
Retain play?
Challenge made by: Cameron Gallagher (CM)
retained possession. Continue play
Aiden Boden (ST)
pass
Gregory Boden (LM)
pass
Fred Boden (LB)
pass
Retain play?
Challenge made by: Cameron Gallagher (CM)
retained possession. Continue play
Sid Boden (RB)
pass
Arthur Boden (CM)
cross
Retain play?
Challenge made by: Bill Gallagher (GK)
retained possession. Continue play
Jim Boden (CB)
cross
Retain play?
Challenge made by: Jim Gallagher (CB)
retained possession. Continue play
Gregory Boden (LM)
shoot
goal conceeded by Bill Gallagher (GK)
goal scored by Gregory Boden (LM)
Arsenal scored
--------------------
Starting Play
Cameron Gallagher (CM)
throwin
Gregory Gallagher (LM)
cross
Retain play?
Challenge made by: Tanisha Boden (RM)
tackled. posession lost
Brewers missed
--------------------
Starting Play
Louise Boden (ST)
goalkick
Cameron Boden (CM)
pass
George Boden (CB)
pass
Retain play?
Challenge made by: Louise Peverley (ST)
retained possession. Continue play
Fred Boden (LB)
shoot
shot missed
Arsenal missed
--------------------
Starting Play
Jim Boden (CB)
freekick
Jim Boden (CB)
cross
Retain play?
Challenge made by: Aiden Gallagher (ST)
retained possession. Continue play
Tanisha Boden (RM)
pass
Retain play?
Challenge made by: Tanisha Gallagher (RM)
tackled. posession lost
Arsenal missed
--------------------
Starting Play
Aiden Boden (ST)
corner
George Boden (CB)
cross
Louise Boden (ST)
cross
Retain play?
Challenge made by: George Gallagher (CB)
retained possession. Continue play
Arthur Boden (CM)
shoot
shot saved by Bill Gallagher (GK)
Arsenal missed
--------------------
Starting Play
Sid Gallagher (RB)
corner
Sid Gallagher (RB)
shoot
goal conceeded by Bill Boden (GK)
goal scored by Sid Gallagher (RB)
Brewers scored
--------------------
Starting Play
George Gallagher (CB)
freekick
Gregory Gallagher (LM)
cross
Retain play?
Challenge made by: Louise Boden (ST)
tackled. posession lost
Brewers missed
--------------------
Starting Play
Sid Gallagher (RB)
penalty
penalty on target by Sid Gallagher (RB)
goal conceeded by Bill Boden (GK)
Brewers scored
--------------------
Starting Play
Tanisha Boden (RM)
corner
Sid Boden (RB)
cross
Arthur Boden (CM)
cross
Retain play?
Challenge made by: Jim Gallagher (CB)
tackled. posession lost
Arsenal missed
--------------------
Starting Play
Arthur Boden (CM)
goalkick
Retain play?
Challenge made by: Tanisha Gallagher (RM)
retained possession. Continue play
Aiden Boden (ST)
shoot
shot saved by Bill Gallagher (GK)
Arsenal missed
--------------------
Starting Play
Arthur Boden (CM)
corner
Retain play?
Challenge made by: Louise Peverley (ST)
tackled. posession lost
Arsenal missed
--------------------
Starting Play
Tanisha Gallagher (RM)
corner
Sid Gallagher (RB)
shoot
shot saved by Bill Boden (GK)
Brewers missed
--------------------
Starting Play
Arthur Boden (CM)
corner
Tanisha Boden (RM)
pass
George Boden (CB)
shoot
shot missed
Arsenal missed
--------------------
Starting Play
Sid Boden (RB)
throwin
Tanisha Boden (RM)
shoot
shot missed
Arsenal missed
Brewers 2 - 5 Arsenal