
Contents
Adoption – Who Uses Lua?
While Lua is not popular in the mainstream programming world, it can be found in many applications. Adobe Photoshop Lightroom is one of them.
Lua is also applied extensively in video games, with Angry Birds, being one. Popular flight simulator X-Plane uses Lua for its aircraft systems and plugins.
The mobile payment app Venmo, for example, was built using Lua. The programming language was also used in the development of CISCO Systems alongside other networking applications.
Chapter 1: Setting up your environment
In order to write and compile Lua, we need to set up an environment to do so.
While there are a host of development tools available for use, we will choose the option of installing an IDE to get us up and running in no time.
1.1 Installing an IDE for Lua.
The platform-independent ZeroBrane Studio is an ideal choice for today’s exercise. The lightweight IDE is available for free and easy to install.
Head over to the official website to download a suitable version for your workstation.
After installation, we shall need an API token to authenticate all our requests to the endpoint of our Football API.
1.2 Obtaining an API Token
To use the API, sign up for an account. Your account automatically comes with the Danish Super Liga and Scottish Premier League, these are free plans with no costs.
Head over to the create an API Token section.
Once you have your API Token, which is required for authentication and accessing the data endpoints, you must keep it private.
Chapter 2 Making Your First API Call
For today’s exercise, we will use the Match Day 1 clash between Juventus and recently promoted Como which ended in a 3-0 win for the Old Lady. That game left Juventus as the club to have won the most games on Match Day 1 in Serie A [60] history.
Head over to the ID Finder where you will obtain the ID value of 19155072 which we shall need for our url of interest.
2.1 Getting Fixture Details.
We will retrieve the details for this fixture using Lua and the complete URI below which you can test out on your browser.
https://api.sportmonks.com/v3/football/fixtures/{ID}?api_token=YOUR_TOKEN
local http = require("socket.http") local ltn12 = require("ltn12") -- Needed for response storage function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/fixtures" local apiToken = "API Token" local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) -- Prepare a response table local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) -- Capture the response body } -- Check the status code and print the result if statusCode == 200 then print("Response: " .. table.concat(response)) else print("Failed with status: " .. tostring(statusCode)) end end -- Call the function with a specific fixture ID fetchFixtureData("19155150")
Response: {"data":{"id":19155150,"sport_id":1,"league_id":384,"season_id":23746,"stage_id":77471748,"group_id":null,"aggregate_id":null,"round_id":341521,"state_id":5,"venue_id":1721,"name":"Inter vs Juventus","starting_at":"2024-10-27 17:00:00","result_info":"Game ended in draw.","leg":"1\/1","details":null,"length":90,"placeholder":false,"has_odds":true,"has_premium_odds":true,"starting_at_timestamp":1730048400},"subscription":[{"meta":{"trial_ends_at":"2024-10-16 14:50:45","ends_at":null,"current_timestamp":1736507811},"plans":[{"plan":"Enterprise plan (loyal)","sport":"Football","category":"Advanced"},{"plan":"Enterprise Plan","sport":"Cricket","category":"Standard"},{"plan":"Formula One","sport":"Formula One","category":"Standard"}],"add_ons":[{"add_on":"All-in News API","sport":"Football","category":"News"},{"add_on":"pressure index add-on","sport":"Football","category":"Default"},{"add_on":"Enterprise Plan Predictions","sport":"Football","category":"Predictions"},{"add_on":"xG Advanced","sport":"Football","category":"Expected"}],"widgets":[{"widget":"Sportmonks Widgets","sport":"Football"}]}],"rate_limit":{"resets_in_seconds":3573,"remaining":2998,"requested_entity":"Fixture"},"timezone":"UTC"}
Explanation
The socket.http is a library for making HTTP requests in Lua, while the statement ltn12: Provides utilities for handling streams and storing response data in Lua tables.
We then created a function fetchFixtureData that accepts one parameter: fixtureId, representing the unique ID of a football fixture.
A statement to construct the url using the Base URL which is the main API endpoint and a token for authentication is then made to dynamically construct our url of interest.
We then check the response, and if the response is code 200 [OK] then it indicates that the request was successful.
The statement table.concat(response) combines all parts of the response table into a single string for easy printing.
If the request fails, the script prints the HTTP status code which you can cross reference with error codes from our documentation. However, there is a drawback. The response is difficult to read. We will fix this in the next section.
2.2 Pretty Print Fixture Details.
You must have heard of the term pretty print while working with an API that returns a JSON response. We shall modify the earlier piece of code to print the JSON response in a much more readable format without the use of an external library.
local http = require("socket.http") local ltn12 = require("ltn12") -- Pretty-print function for JSON strings local function prettyPrintJSON(jsonString, indentSize) indentSize = indentSize or 2 local indent = string.rep(" ", indentSize) -- Create indentation local level = 0 local prettyJSON = "" for i = 1, #jsonString do local char = jsonString:sub(i, i) if char == "{" or char == "[" then prettyJSON = prettyJSON .. char .. "\n" .. string.rep(indent, level + 1) level = level + 1 elseif char == "}" or char == "]" then level = level - 1 prettyJSON = prettyJSON .. "\n" .. string.rep(indent, level) .. char elseif char == "," then prettyJSON = prettyJSON .. char .. "\n" .. string.rep(indent, level) else prettyJSON = prettyJSON .. char end end return prettyJSON end -- Function to fetch and pretty-print fixture data function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/fixtures" local apiToken = "API_Token" local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) print("Pretty-Printed JSON Response:") print(prettyPrintJSON(rawJson)) else print("Failed with status: " .. tostring(statusCode)) end end -- Call the function with a specific fixture ID fetchFixtureData("19155072")
Pretty-Printed JSON Response: { "data":{ "id":19155072, "sport_id":1, "league_id":384, "season_id":23746, "stage_id":77471748, "group_id":null, "aggregate_id":null, "round_id":341513, "state_id":5, "venue_id":118500, "name":"Juventus vs Como", "starting_at":"2024-08-19 18:45:00", "result_info":"Juventus won after full-time.", "leg":"1\/1", "details":null, "length":90, "placeholder":false, "has_odds":true, "has_premium_odds":true, "starting_at_timestamp":1724093100 }, "subscription":[ { "meta":{ "trial_ends_at":"2024-10-16 14:50:45", "ends_at":null, "current_timestamp":1734917581 }, "plans":[ { "plan":"Enterprise plan (loyal)", "sport":"Football", "category":"Advanced" }, { "plan":"Enterprise Plan", "sport":"Cricket", "category":"Standard" }, { "plan":"Formula One", "sport":"Formula One", "category":"Standard" } ], "add_ons":[ { "add_on":"All-in News API", "sport":"Football", "category":"News" }, { "add_on":"pressure index add-on", "sport":"Football", "category":"Default" }, { "add_on":"Enterprise Plan Predictions", "sport":"Football", "category":"Predictions" }, { "add_on":"xG Advanced", "sport":"Football", "category":"Expected" } ], "widgets":[ { "widget":"Sportmonks Widgets", "sport":"Football" } ] } ], "rate_limit":{ "resets_in_seconds":3600, "remaining":2999, "requested_entity":"Fixture" }, "timezone":"UTC" }
As you can see, we have a more readable format; we shall move on to the next section, where we will retrieve predictions from our fixture of interest, Juventus vs. Como.
2.3. Get Predictions by Fixture ID
While not all fixtures contain sufficient data for reliable predictions, you can enrich your applications with our prediction API, which employs machine learning techniques and models for detailed predictions that include scores, over/under, and both teams to score (BTTS).
We shall focus on predictions for scores in this blog. You may visit the documentation page to learn more about our prediction API.
Here is our URL of interest, which you can try out on your browser.
local http = require("socket.http") local ltn12 = require("ltn12") -- Pretty-print function for JSON strings local function prettyPrintJSON(jsonString, indentSize) indentSize = indentSize or 2 local indent = string.rep(" ", indentSize) -- Create indentation local level = 0 local prettyJSON = "" for i = 1, #jsonString do local char = jsonString:sub(i, i) if char == "{" or char == "[" then prettyJSON = prettyJSON .. char .. "\n" .. string.rep(indent, level + 1) level = level + 1 elseif char == "}" or char == "]" then level = level - 1 prettyJSON = prettyJSON .. "\n" .. string.rep(indent, level) .. char elseif char == "," then prettyJSON = prettyJSON .. char .. "\n" .. string.rep(indent, level) else prettyJSON = prettyJSON .. char end end return prettyJSON end -- Function to fetch and pretty-print fixture data function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/predictions/probabilities/fixtures" local apiToken = "API_Token" local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) print("Pretty-Printed JSON Response:") print(prettyPrintJSON(rawJson)) else print("Failed with status: " .. tostring(statusCode)) end end -- Call the function with a specific fixture ID fetchFixtureData("19155072")
Pretty-Printed JSON Response: { "data":[ { "id":14610512, "fixture_id":19155072, "predictions":{ "yes":70.69, "no":18.78, "equal":10.54 }, "type_id":1686 }, { "id":14610513, "fixture_id":19155072, "predictions":{ "yes":10.24, "no":89.76 }, "type_id":1679 }, { "id":14610514, "fixture_id":19155072, "predictions":{ "yes":94.83, "no":2.07, "equal":3.1 }, "type_id":1690 }, { "id":14610516, "fixture_id":19155072, "predictions":{ "yes":46.2, "no":41.38, "equal":12.42 }, "type_id":1687 }, { "id":14610517, "fixture_id":19155072, "predictions":{ "yes":89.36, "no":5.17, "equal":5.46 }, "type_id":1683 }, { "id":14610518, "fixture_id":19155072, "predictions":{ "yes":58.62, "no":29.31, "equal":12.07 }, "type_id":1689 }, { "id":14610519, "fixture_id":19155072, "predictions":{ "yes":81.22, "no":10.64, "equal":8.14 }, "type_id":1685 }, { "id":14610520, "fixture_id":19155072, "predictions":{ "yes":34.57, "no":53.8, "equal":11.63 }, "type_id":1688 }, { "id":14610521, "fixture_id":19155072, "predictions":{ "yes":24.58, "no":65.43, "equal":9.99 }, "type_id":1684 }, { "id":14610522, "fixture_id":19155072, "predictions":{ "yes":21.54, "no":78.46 }, "type_id":332 }, { "id":14610523, "fixture_id":19155072, "predictions":{ "yes":34.57, "no":65.43, "equal":null }, "type_id":1585 }, { "id":14610524, "fixture_id":19155072, "predictions":{ "yes":39.06, "no":60.94 }, "type_id":331 }, { "id":14610525, "fixture_id":19155072, "predictions":{ "yes":56.75, "no":43.25 }, "type_id":333 }, { "id":14610526, "fixture_id":19155072, "predictions":{ "yes":15.52, "no":84.48 }, "type_id":330 }, { "id":14610527, "fixture_id":19155072, "predictions":{ "yes":5.89, "no":94.11 }, "type_id":328 }, { "id":14610528, "fixture_id":19155072, "predictions":{ "yes":73.39, "no":26.61 }, "type_id":334 }, { "id":14610529, "fixture_id":19155072, "predictions":{ "yes":1.48, "no":98.52 }, "type_id":327 }, { "id":14610530, "fixture_id":19155072, "predictions":{ "yes":19.73, "no":80.27 }, "type_id":236 }, { "id":14610531, "fixture_id":19155072, "predictions":{ "scores":{ "0-0":10.75, "0-1":9.08, "0-2":4.24, "0-3":1.08, "1-0":14.12, "1-1":13.11, "1-2":5.48, "1-3":1.62, "2-0":9.33, "2-1":9.07, "2-2":3.94, "2-3":1.2, "3-0":4, "3-1":3.95, "3-2":1.99, "3-3":0.51, "Other_1":5.06, "Other_2":1.47, "Other_X":0.01 } }, "type_id":240 }, { "id":14610532, "fixture_id":19155072, "predictions":{ "yes":5.07, "no":94.93 }, "type_id":326 }, { "id":14610533, "fixture_id":19155072, "predictions":{ "yes":39.37, "no":60.63 }, "type_id":235 }, { "id":14610534, "fixture_id":19155072, "predictions":{ "yes":66.06, "no":33.94 }, "type_id":234 }, { "id":14610535, "fixture_id":19155072, "predictions":{ "home":32.22, "away":22.82, "draw":44.97 }, "type_id":233 }, { "id":14610536, "fixture_id":19155072, "predictions":{ "home_home":25.67, "home_away":1.86, "home_draw":5.43, "away_home":2.5, "away_away":14.57, "away_draw":5.28, "draw_draw":18.72, "draw_home":17.19, "draw_away":8.78 }, "type_id":232 }, { "id":14610537, "fixture_id":19155072, "predictions":{ "draw_home":75.82000000000001, "draw_away":52.480000000000004, "home_away":71.7 }, "type_id":239 } ], "pagination":{ "count":25, "per_page":25, "current_page":1, "next_page":"https:\/\/api.sportmonks.com\/v3\/football\/predictions\/probabilities\/fixtures\/19155072?page=2", "has_more":true }, "subscription":[ { "meta":{ "trial_ends_at":"2024-10-16 14:50:45", "ends_at":null, "current_timestamp":1734983903 }, "plans":[ { "plan":"Enterprise plan (loyal)", "sport":"Football", "category":"Advanced" }, { "plan":"Enterprise Plan", "sport":"Cricket", "category":"Standard" }, { "plan":"Formula One", "sport":"Formula One", "category":"Standard" } ], "add_ons":[ { "add_on":"All-in News API", "sport":"Football", "category":"News" }, { "add_on":"pressure index add-on", "sport":"Football", "category":"Default" }, { "add_on":"Enterprise Plan Predictions", "sport":"Football", "category":"Predictions" }, { "add_on":"xG Advanced", "sport":"Football", "category":"Expected" } ], "widgets":[ { "widget":"Sportmonks Widgets", "sport":"Football" } ] } ], "rate_limit":{ "resets_in_seconds":2803, "remaining":2997, "requested_entity":"Prediction" }, "timezone":"UTC" }
A brief explanation of the response can be found on our documentation page.
Chapter 3. Create a Horizontal Bar Chart Using Predictions.
Now that we have our predictions pretty printed, we can now create a bar chart using the scores and probabilities while ignoring the other details from the JSON response. Without the use of any external library, we will create a text-based bar chart.
-- Function to calculate proportions and create a text-based bar chart function drawTextBarChart(predictions) local total = 0 for _, prediction in ipairs(predictions) do total = total + prediction.value end -- Generate bar chart segments print("Text-Based Bar Chart for Predictions") for _, prediction in ipairs(predictions) do local proportion = math.floor((prediction.value / total) * 50) -- Scale to 50 blocks print(string.format("%-10s: %s (%.2f%%)", prediction.name, string.rep(("\226\150\136"), proportion), (prediction.value / total) * 100)) end end -- Function to parse JSON string manually (very basic parsing, only works for simple flat structures) function simpleJSONParse(jsonString) local parsed = {} -- Remove curly braces and spaces around key-value pairs jsonString = jsonString:match("^%s*(.*)%s*$") jsonString = jsonString:sub(2, -2) -- Remove curly braces -- Split the string into key-value pairs for key, value in jsonString:gmatch('"([^"]+)":%s*([^,}]+)') do -- Try to convert number values value = tonumber(value) or value parsed[key] = value end return parsed end -- Function to fetch data from API and extract scores for the bar chart function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/predictions/probabilities/fixtures" local apiToken = "API_Token" -- Replace with your actual API token local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local http = require("socket.http") local ltn12 = require("ltn12") local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) -- Check if "scores" is present in the JSON response local scoresStart, scoresEnd = rawJson:find('"scores":%{') if scoresStart then -- Extract the "scores" part of the JSON response local scoresJson = rawJson:sub(scoresStart + 9, rawJson:find("}", scoresStart) - 1) -- Parse the scores into a table local predictionScores = simpleJSONParse("{" .. scoresJson .. "}") local predictions = {} -- Convert the scores into a table for the bar chart for score, value in pairs(predictionScores) do table.insert(predictions, {name = score, value = value}) end -- Call the function to draw the text-based bar chart drawTextBarChart(predictions) else print("Error: 'scores' data not found in the response.") end else print("Failed to fetch data with status: " .. tostring(statusCode)) end end -- Call the function with a specific fixture ID fetchFixtureData("19155072")
Text-Based Bar Chart for Predictions: 3-3 : (0.51%) 2-3 : (1.20%) Other_2 : (1.47%) 0-1 : ████ (9.08%) 0-0 : █████ (10.75%) 3-0 : █ (4.00%) 2-2 : █ (3.94%) 1-2 : ██ (5.48%) 3-2 : (1.99%) 1-0 : ███████ (14.12%) Other_X : (0.01%) Other_1 : ██ (5.06%) 3-1 : █ (3.95%) 1-3 : (1.62%) 0-2 : ██ (4.24%) 2-0 : ████ (9.33%) 2-1 : ████ (9.07%) 1-1 : ██████ (13.11%) 0-3 : (1.08%)
As we can see, the results include undefined predictions for scores that have been represented as Others. It is also noticeable that values below 2% are not being properly represented on our bar chart.
We shall fix this in the next section.
Chapter 3.1 Refine the Bar Chart
By increasing the scale to 100 blocks for finer granularity, we shall ensure a minimum of one block is displayed for very small percentages. This will ensure all predictions are graphically represented by text.
We shall also make our results more readable by including an empty line between each bar chart for better readability. In addition, we will remove all values that begin with ‘Others.’
-- Function to calculate proportions and create a text-based bar chart function drawTextBarChart(predictions) local total = 0 for _, prediction in ipairs(predictions) do total = total + prediction.value end -- Adjust the scale to accommodate small percentages (e.g., scale to 100 blocks) local scale = 100 -- Increase the scale to allow finer granularity -- Generate bar chart segments print("Text-Based Bar Chart for Predictions:") for _, prediction in ipairs(predictions) do local proportion = math.floor((prediction.value / total) * scale) -- Scale to more blocks proportion = math.max(proportion, 1) -- Ensure at least one block is displayed print(string.format("%-10s: %s (%.2f%%)", prediction.name, string.rep(("\226\150\136"), proportion), (prediction.value / total) * 100)) print() -- Add a blank line after each bar for spacing end end -- Function to parse JSON string manually (very basic parsing, only works for simple flat structures) function simpleJSONParse(jsonString) local parsed = {} -- Remove curly braces and spaces around key-value pairs jsonString = jsonString:match("^%s*(.*)%s*$") jsonString = jsonString:sub(2, -2) -- Remove curly braces -- Split the string into key-value pairs for key, value in jsonString:gmatch('"([^"]+)":%s*([^,}]+)') do -- Try to convert number values value = tonumber(value) or value parsed[key] = value end return parsed end -- Function to fetch data from API and extract scores for the bar chart function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/predictions/probabilities/fixtures" local apiToken = "API_Token" -- Replace with your actual API token local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local http = require("socket.http") local ltn12 = require("ltn12") local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) -- Check if "scores" is present in the JSON response local scoresStart, scoresEnd = rawJson:find('"scores":%{') if scoresStart then -- Extract the "scores" part of the JSON response local scoresJson = rawJson:sub(scoresStart + 9, rawJson:find("}", scoresStart) - 1) -- Parse the scores into a table local predictionScores = simpleJSONParse("{" .. scoresJson .. "}") local predictions = {} -- Convert the scores into a table for the bar chart, filtering out "Other" scores for score, value in pairs(predictionScores) do -- Skip scores that contain "Other" if not string.match(score, "^Other") then table.insert(predictions, {name = score, value = value}) end end -- Call the function to draw the text-based bar chart drawTextBarChart(predictions) else print("Error: 'scores' data not found in the response.") end else print("Failed to fetch data with status: " .. tostring(statusCode)) end end -- Call the function with a specific fixture ID fetchFixtureData("19155072")
Text-Based Bar Chart for Predictions: 3-3 : █ (0.55%) 2-3 : █ (1.28%) 0-1 : █████████ (9.71%) 0-0 : ███████████ (11.50%) 3-0 : ████ (4.28%) 2-2 : ████ (4.22%) 1-2 : █████ (5.86%) 3-2 : ██ (2.13%) 1-0 : ███████████████ (15.11%) 3-1 : ████ (4.23%) 1-3 : █ (1.73%) 0-2 : ████ (4.54%) 2-0 : █████████ (9.98%) 2-1 : █████████ (9.70%) 1-1 : ██████████████ (14.03%) 0-3 : █ (1.16%)
Chapter 4.0: Making Our Code Dynamic.
In this chapter, which will be the final, we shall make our code dynamic by accepting the fixture ID from the console, rather than having it hard-coded.
This way we can quickly create a text-based bar chart for any matchup of interest. We will also include a new function to capture the name of the fixture from another API endpoint and have it printed in our first line, just before the bar chart.
-- Define the API token once local apiToken = "API_Token" -- Replace with your actual API token -- Function to parse JSON string manually (basic parsing, only for simple flat structures) function simpleJSONParse(jsonString) local parsed = {} -- Remove curly braces and spaces around key-value pairs jsonString = jsonString:match("^%s*(.*)%s*$") jsonString = jsonString:sub(2, -2) -- Remove curly braces -- Split the string into key-value pairs for key, value in jsonString:gmatch('"([^"]+)":%s*([^,}]+)') do -- Try to convert number values value = tonumber(value) or value:match('^"(.*)"$') or value parsed[key] = value end return parsed end -- Function to fetch the fixture name from the API function fetchFixtureName(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/fixtures" local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local http = require("socket.http") local ltn12 = require("ltn12") local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) -- Extract the fixture name local nameStart, nameEnd = rawJson:find('"data"%s*:%s*%{.-"name"%s*:%s*"([^"]+)"') local fixtureName = nameStart and rawJson:match('"name"%s*:%s*"([^"]+)"') or "Unknown Fixture" return fixtureName else print("Failed to fetch fixture name with status: " .. tostring(statusCode)) return "Unknown Fixture" end end -- Function to calculate proportions and create a text-based bar chart function drawTextBarChart(predictions, fixtureName) -- Print the fixture name as the first statement print("\nPredictions for " .. fixtureName) print("\n") local total = 0 for _, prediction in ipairs(predictions) do total = total + prediction.value end -- Adjust the scale to accommodate small percentages (e.g., scale to 100 blocks) local scale = 100 -- Increase the scale to allow finer granularity -- Generate bar chart segments for _, prediction in ipairs(predictions) do local proportion = math.floor((prediction.value / total) * scale) -- Scale to more blocks proportion = math.max(proportion, 1) -- Ensure at least one block is displayed print(string.format("%-10s %s (%.2f%%)", prediction.name, string.rep("█", proportion), (prediction.value / total) * 100)) print() -- Add a blank line after each bar for spacing end end -- Function to fetch data from API and extract scores for the bar chart function fetchFixtureData(fixtureId) local baseUrl = "https://api.sportmonks.com/v3/football/predictions/probabilities/fixtures" local apiUrl = string.format("%s/%s?api_token=%s", baseUrl, fixtureId, apiToken) local http = require("socket.http") local ltn12 = require("ltn12") local response = {} local _, statusCode = http.request{ url = apiUrl, sink = ltn12.sink.table(response) } if statusCode == 200 then local rawJson = table.concat(response) -- Extract scores (if available) local scoresStart, scoresEnd = rawJson:find('"scores"%s*:%s*%{') if scoresStart then local scoresJson = rawJson:sub(scoresStart + 9, rawJson:find("}", scoresStart) - 1) local predictionScores = simpleJSONParse("{" .. scoresJson .. "}") local predictions = {} -- Convert the scores into a table for the bar chart for score, value in pairs(predictionScores) do if not string.match(score, "^Other") then -- Skip scores with "Other" table.insert(predictions, {name = score, value = value}) end end -- Fetch the fixture name using the new function local fixtureName = fetchFixtureName(fixtureId) -- Call the function to draw the text-based bar chart drawTextBarChart(predictions, fixtureName) else print("Error: 'scores' data not found in the response.") end else print("Failed to fetch data with status: " .. tostring(statusCode)) end end -- Ask the user to input the fixture ID print("Please enter the fixture ID:") local fixtureId = io.read() -- Call the function with the user-provided fixture ID fetchFixtureData(fixtureId)
Predictions for Juventus vs Como 3-3 █ (0.55%) 2-3 █ (1.28%) 0-1 █████████ (9.71%) 0-0 ███████████ (11.50%) 3-0 ████ (4.28%) 2-2 ████ (4.22%) 1-2 █████ (5.86%) 3-2 ██ (2.13%) 1-0 ███████████████ (15.11%) 3-1 ████ (4.23%) 1-3 █ (1.73%) 0-2 ████ (4.54%) 2-0 █████████ (9.98%) 2-1 █████████ (9.70%) 1-1 ██████████████ (14.03%) 0-3 █ (1.16%)
Conclusion.
With your brand new piece of code, you can be the first to put out a text-based prediction bar chart for the final scoreline right before kickoff.
While not a popular programming language, Lua, in combination with Sportmonks’ Football API, which offers coverage of more than 2300 leagues, has no limit to what can be achieved.
In the next Deverlopers’ guide, we will explore how you can convert the JSON response, as seen here, into a spreadsheet format. Meanwhile, why not explore our list of endpoints?
FAQ
- Create an account on My Sportmonks and get immediate access to our free plan.
- Subscribe to one of our paid plans and receive a one-time-only 14-day free trial.