Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Read by thought-leaders and decision-makers around the world. Phone Number: +1-650-246-9381 Email: pub@towardsai.net
228 Park Avenue South New York, NY 10003 United States
Website: Publisher: https://towardsai.net/#publisher Diversity Policy: https://towardsai.net/about Ethics Policy: https://towardsai.net/about Masthead: https://towardsai.net/about
Name: Towards AI Legal Name: Towards AI, Inc. Description: Towards AI is the world's leading artificial intelligence (AI) and technology publication. Founders: Roberto Iriondo, , Job Title: Co-founder and Advisor Works for: Towards AI, Inc. Follow Roberto: X, LinkedIn, GitHub, Google Scholar, Towards AI Profile, Medium, ML@CMU, FreeCodeCamp, Crunchbase, Bloomberg, Roberto Iriondo, Generative AI Lab, Generative AI Lab Denis Piffaretti, Job Title: Co-founder Works for: Towards AI, Inc. Louie Peters, Job Title: Co-founder Works for: Towards AI, Inc. Louis-François Bouchard, Job Title: Co-founder Works for: Towards AI, Inc. Cover:
Towards AI Cover
Logo:
Towards AI Logo
Areas Served: Worldwide Alternate Name: Towards AI, Inc. Alternate Name: Towards AI Co. Alternate Name: towards ai Alternate Name: towardsai Alternate Name: towards.ai Alternate Name: tai Alternate Name: toward ai Alternate Name: toward.ai Alternate Name: Towards AI, Inc. Alternate Name: towardsai.net Alternate Name: pub.towardsai.net
5 stars – based on 497 reviews

Frequently Used, Contextual References

TODO: Remember to copy unique IDs whenever it needs used. i.e., URL: 304b2e42315e

Resources

Take our 85+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!

Publication

Jumbled Word Game
Programming

Jumbled Word Game

Last Updated on December 2, 2020 by Editorial Team

Author(s): Sumeet Lalla

Programming

Jumbled Word — A Game Using Python And Jupyter IPyWidget

Jumbled Word Puzzle
Jumbled Word Game | Source: Image by the author.

Introduction

The game can be played by a single user, and it involves a random word from the dictionary, which is jumbled up. The user has to guess and enter the same in a timer controlled gameplay environment.

Rules

The player can select one of the levels, i.e., Beginner, Intermediate, and Advanced, from the dropdown menu. When the player clicks the start button, the following outcomes happen:

  • Based on the level selected, a random word pops up of length 4,5,6 according to the level selected.
  • A timer starts at the bottom with a start time of 80,100 and 120 seconds according to the level selected.
  • The resume and pause buttons are enabled.
  • The hint button is enabled based on the level selected.
  • If the level selected is Beginner, then the hint Button is disabled.
  • If the level selected is Intermediate or Advanced, then the hint button is enabled with the default hint text.
  • The dropdown menu gets disabled after the start button is clicked.
  • When the player clicks the pause button, the following outcomes happen:
  • The hint button gets disabled.
  • The pause button is disabled to prevent multiple clicks on it.
  • The text box where the user enters his answer is disabled.
  • The score gets decreased by 1 irrespective of the levels. However, it is not decreased when it is 0.
  • The timer at the bottom gets paused.

4. When the player clicks the resume button, the following outcomes happen:

  • The text box where the user enters his answer is enabled.
  • The pause button is enabled.
  • The hint button gets enabled based on the following constraint.
  • If the level selected is Beginner, it is disabled.
  • If the level selected is Intermediate or Advanced, if all the hints are used by the player, then it is disabled.
  • Else it is enabled.
  • The timer in the bottom gets resumed from where it was paused.

5. When the player clicks the exit button, the following outcomes happen:

  • The start, pause, resume, and exit buttons get disabled.
  • The dropdown menu, text box, and hint button get disabled.
  • Basically, it invalidates all the controls associated, and the user has to rerun the game.

6. The scoring is decided based on the level selected. It is as follows:

  • If the level selected is Beginner, the score gets incremented by 1.
  • If the level selected is Intermediate, the score gets incremented by 2.
  • If the level selected is Advanced, the score gets incremented by 4.

7. The number of hints is decided based on the level selected. It is as follows:

  • If the level selected is Beginner, it is 0.
  • If the level selected is Intermediate, it is 1.
  • If the level selected is Advanced, it is 2.

8. The length of the random word to be guessed is decided based on the level selected. It is as follows:

  • If the level selected is Beginner, it is 4.
  • If the level selected is Intermediate, it is 5.
  • If the level selected is Advanced, it is 6.

9. The timer value is decided based on the level selected. It is as follows:

  • If the level selected is Beginner, it is 80 seconds.
  • If the level selected is Intermediate, it is 100 seconds.
  • If the level selected is Advanced, it is 120 seconds.

Gameplay

The jumbled up word is in lower case, and the player has to enter the text in lower case. Based on the text entered, if it matches the random word, then the timer stops, and in the bottom “Right” message is generated, and the score gets updated. Else the gameplay continues till the timer countdown finishes. If an attempt is made by the player and is incorrect, then in the bottom, a “Wrong” message is generated. In the case of no attempt, only the timer countdown value is shown when finished.

Code

!pip install ipywidgets
!pip install requests
import copy
import random
import threading
import time
import ipywidgets
import requests
from IPython.display import display

random_word = ''
score = 0
random_word_length = 0
no_of_hints = 0
data = []
hint_letter = ''

jumbled_text = ipywidgets.Label(value='Jumbled Text : ')
text_entered = ipywidgets.Label(value='Text Entered : ')
text = ipywidgets.Text(description="Text", disabled=True)
score_label = ipywidgets.Label(value='Score : ')
hint_text = ipywidgets.Label(value='Hint Text : ')
hint_remaining = ipywidgets.Label(value='Hints Remaining : ')
btn_hint = ipywidgets.Button(description='Hint', disabled=True)
high_score_label = ipywidgets.Label(value='High Score : ')
time_remaining = 0


def text_change(change):
"""
Changing text entered label value when the user enters text through the text box.
When the text entered is equal to the random word, the textbox is rendered empty.
:param change: Handling change event from the text box.
"""
if change["new"] == "Expected":
text.value = ""
text_entered.value = "Text Entered : " + change["new"]


text.observe(text_change, names='value')

w = ipywidgets.Dropdown(
options=['Beginner', 'Intermediate', 'Advanced'],
value='Beginner',
description='Choose Level:',
)


def set_hint_text():
"""
Set the default hint text for the intermediate and advanced level.
The default hint text is '__'*{word_length} based on the level selected.
"""
initial_hint_text = ''
if random_word_length > 4:
for i in range(random_word_length):
initial_hint_text += '__'
if i < random_word_length - 1:
initial_hint_text += " "

hint_text.value = 'Hint Text : ' + initial_hint_text


def jumble_random_word():
"""
Jumbling the random word of a given length depending on the level selected.
The random.shuffle() method is sometimes giving same string thus a while loop to re-iterate the same.
Also the random word generated by the above might be a word in data obtained from json so it is also re-iterated.
:return: jumbled_word.
"""
set_word_length_on_level()
set_random_word()
temp_word = random_word
random_word_list = list(random_word)
temp = copy.deepcopy(random_word_list)
while temp == random_word_list or temp_word in data:
random.shuffle(random_word_list)
temp_word = ''.join(random_word_list)
jumbled_word = ''.join(random_word_list)
return jumbled_word


def set_random_word():
"""
Setting the random word based on the level selected by user.
"""
global data
global random_word
words_with_length_k = []
response = requests.get("http://raw.githubusercontent.com/sindresorhus/mnemonic-words/master/words.json")
data = response.json()
for _, value in enumerate(data):
if len(value) == random_word_length:
words_with_length_k.append(value)
random_word = random.choice(words_with_length_k)


def set_word_length_on_level():
"""
Setting random word length based on the level selected.
"""
global random_word_length
if w.value == 'Beginner':
random_word_length = 4
elif w.value == 'Intermediate':
random_word_length = 5
elif w.value == 'Advanced':
random_word_length = 6


display(w)
display(score_label)
display(jumbled_text)
b = ipywidgets.HBox([hint_text, btn_hint, hint_remaining])
display(b)
display(text_entered, text)

btn_start = ipywidgets.Button(description='Start')
btn_pause = ipywidgets.Button(description='Pause',disabled=True)
btn_resume = ipywidgets.Button(description='Resume',disabled=True)
btn_exit = ipywidgets.Button(description='Exit')
left_box = ipywidgets.VBox([btn_start, btn_pause])
right_box = ipywidgets.VBox([btn_resume, btn_exit])
a = ipywidgets.HBox([left_box, right_box])
display(a)


def on_pause_button_clicked(b):
"""
On Click Event for the Pause Button.
The timer thread is notified to stop through threading.
The text box and hint button is disabled.
The pause button is disabled.
The score is decremented based on the constraint.
:param b: pause button.
"""
global score
e.clear()
b.disabled = True
text.disabled = True
btn_hint.disabled = True
if score > 0:
score -= 1
score_label.value = 'Score : ' + str(score)


btn_pause.on_click(on_pause_button_clicked)


def on_exit_button_clicked(b):
"""
On Click event for the exit button.
The timer thread is notified to stop.
The controls are invalidated.
:param b: exit button.
"""
e.clear()
text.disabled = True
btn_start.disabled = True
btn_resume.disabled = True
btn_pause.disabled = True
b.disabled = True
w.disabled = True
btn_hint.disabled = True


btn_exit.on_click(on_exit_button_clicked)


def on_hint_button_clicked(b):
"""
On Click event for the hint button.
The hint text is updated based on the level selected.
The hints remaining label value is updated.
The hint button is disabled based on the remaining hints.
:param b: hint button.
"""
global no_of_hints
global hint_letter
s_upd = ''

if no_of_hints == 0:
s_upd += random_word[0]
hint_letter = random_word[0]
s_upd += " "
for i in range(random_word_length - 1):
s_upd += '__'
if i < random_word_length - 2:
s_upd += " "
elif no_of_hints == 1:
for i in range(random_word_length):
if i > 0:
if i == 3:
s_upd += random_word[3]
else:
s_upd += '__'
else:
s_upd += hint_letter
if i < random_word_length - 1:
s_upd += " "

hint_text.value = 'Hint Text : ' + s_upd
no_of_hints += 1
set_hint_remaining()
btn_hint.disabled = disable_hint_button_based_on_hints()


btn_hint.on_click(on_hint_button_clicked)


def on_resume_button_clicked(b):
"""
On Click Event for the resume button.
The timer thread is notified to resume again.
The text box is enabled.
The pause button is enabled if it is disabled.
The hint button is enabled/disabled based on the number of hints remaining.
:param b: resume button.
"""
e.set()
text.disabled = False
if(btn_pause.disabled):
btn_pause.disabled = False
btn_hint.disabled = disable_hint_button_based_on_hints()


def disable_hint_button_based_on_hints():
"""
Disabling the hint button based on constraints for the game levels.
:return: True indicating the button can be disabled else False.
"""
return (random_word_length == 4) or (random_word_length == 5 and no_of_hints == 1) or (random_word_length == 6 and no_of_hints == 2)


btn_resume.on_click(on_resume_button_clicked)


def set_timer_based_on_level():
"""
Setting the timer based on the game level.
"""
global time_remaining
if w.value == 'Beginner':
time_remaining = 80
elif w.value == 'Intermediate':
time_remaining = 100
elif w.value == 'Advanced':
time_remaining = 120


def set_hint_remaining():
"""
Setting the hint remaining label value based on the number of hints for each game level.
"""
value = 0
if random_word_length == 5:
value = 1 - no_of_hints
elif random_word_length == 6:
value = 2 - no_of_hints

hint_remaining.value = 'Hints Remaining : ' + str(value)


def on_start_button_clicked(b):
"""
On Click Event for the start button.
Setting the timer function based on level is called.
The jumbled random word function is called.
The set hint text and hints remaining function is called.
The threading mechanism is called to start the countdown timer.
The start button is disabled while pause and resume buttons are enabled.
The textbox is enabled and the dropdown menu is disabled.
The hint button is disabled based on the random word length.

:param b: start button.
"""
set_timer_based_on_level()
jumbled_word = jumble_random_word()
jumbled_text.value = "Jumbled Text : " + jumbled_word
set_hint_text()
set_hint_remaining()
thread = threading.Thread(target=countdown, args=(e,))
thread.start()
e.set()
btn_start.disabled = True
text.disabled = False
btn_pause.disabled = False
btn_resume.disabled = False
w.disabled = True
if random_word_length > 4:
btn_hint.disabled = False


btn_start.on_click(on_start_button_clicked)


def update_score_on_level():
"""
Updating the game score based on the correct answer for the selected game level.
"""
global score
if w.value == 'Beginner':
score += 1
elif w.value == 'Intermediate':
score += 2
else:
score += 4


def print_result_on_console(result):
"""
Printing the result after game finishes.
:param result: The message to be printed on the console after the game finishes which is Right/Wrong.
"""
print(result, end='\r')
text.value = ""


def countdown(event):
"""
Running timer based on game level and handling thread to notify the timer to start/stop based on user action.
The user action here denotes the click on resume/pause button.
Printing the result message on the console function is called.
The update score function is called.
The timer countdown is printed on the bottom of the screen.
The dropdown menu is enabled post game completion while other controls are invalidated.
The start button is enabled.
:param event: Denoting it as a target function for threading.
"""
global time_remaining
global no_of_hints
global data
event_is_set = True

while time_remaining >= 0 and event_is_set:
event_is_set = e.wait()
if text.value == random_word:
print_result_on_console("Right")
time_remaining = 0
update_score_on_level()
break

minutes, secs = divmod(time_remaining, 60)
time_format = '{:02d}:{:02d}'.format(minutes, secs)
print(time_format, end='\r')
time.sleep(1)
time_remaining -= 1

if text.value != "" and text.value != random_word:
print_result_on_console("Wrong")

score_label.value = 'Score : ' + str(score)
hint_remaining.value = 'Hints Remaining : '
jumbled_text.value = "Jumbled Text : "
btn_start.disabled = False
w.disabled = False
text.disabled = True
hint_text.value = 'Hint Text : '
btn_hint.disabled = True
btn_pause.disabled = True
btn_resume.disabled = True
no_of_hints = 0
data = []


e = threading.Event()


Jumbled Word Game was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.

Published via Towards AI

Feedback ↓

Sign Up for the Course
`; } else { console.error('Element with id="subscribe" not found within the page with class "home".'); } } }); // Remove duplicate text from articles /* Backup: 09/11/24 function removeDuplicateText() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, strong'); // Select the desired elements const seenTexts = new Set(); // A set to keep track of seen texts const tagCounters = {}; // Object to track instances of each tag elements.forEach(el => { const tagName = el.tagName.toLowerCase(); // Get the tag name (e.g., 'h1', 'h2', etc.) // Initialize a counter for each tag if not already done if (!tagCounters[tagName]) { tagCounters[tagName] = 0; } // Only process the first 10 elements of each tag type if (tagCounters[tagName] >= 2) { return; // Skip if the number of elements exceeds 10 } const text = el.textContent.trim(); // Get the text content const words = text.split(/\s+/); // Split the text into words if (words.length >= 4) { // Ensure at least 4 words const significantPart = words.slice(0, 5).join(' '); // Get first 5 words for matching // Check if the text (not the tag) has been seen before if (seenTexts.has(significantPart)) { // console.log('Duplicate found, removing:', el); // Log duplicate el.remove(); // Remove duplicate element } else { seenTexts.add(significantPart); // Add the text to the set } } tagCounters[tagName]++; // Increment the counter for this tag }); } removeDuplicateText(); */ // Remove duplicate text from articles function removeDuplicateText() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, strong'); // Select the desired elements const seenTexts = new Set(); // A set to keep track of seen texts const tagCounters = {}; // Object to track instances of each tag // List of classes to be excluded const excludedClasses = ['medium-author', 'post-widget-title']; elements.forEach(el => { // Skip elements with any of the excluded classes if (excludedClasses.some(cls => el.classList.contains(cls))) { return; // Skip this element if it has any of the excluded classes } const tagName = el.tagName.toLowerCase(); // Get the tag name (e.g., 'h1', 'h2', etc.) // Initialize a counter for each tag if not already done if (!tagCounters[tagName]) { tagCounters[tagName] = 0; } // Only process the first 10 elements of each tag type if (tagCounters[tagName] >= 10) { return; // Skip if the number of elements exceeds 10 } const text = el.textContent.trim(); // Get the text content const words = text.split(/\s+/); // Split the text into words if (words.length >= 4) { // Ensure at least 4 words const significantPart = words.slice(0, 5).join(' '); // Get first 5 words for matching // Check if the text (not the tag) has been seen before if (seenTexts.has(significantPart)) { // console.log('Duplicate found, removing:', el); // Log duplicate el.remove(); // Remove duplicate element } else { seenTexts.add(significantPart); // Add the text to the set } } tagCounters[tagName]++; // Increment the counter for this tag }); } removeDuplicateText(); //Remove unnecessary text in blog excerpts document.querySelectorAll('.blog p').forEach(function(paragraph) { // Replace the unwanted text pattern for each paragraph paragraph.innerHTML = paragraph.innerHTML .replace(/Author\(s\): [\w\s]+ Originally published on Towards AI\.?/g, '') // Removes 'Author(s): XYZ Originally published on Towards AI' .replace(/This member-only story is on us\. Upgrade to access all of Medium\./g, ''); // Removes 'This member-only story...' }); //Load ionic icons and cache them if ('localStorage' in window && window['localStorage'] !== null) { const cssLink = 'https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css'; const storedCss = localStorage.getItem('ionicons'); if (storedCss) { loadCSS(storedCss); } else { fetch(cssLink).then(response => response.text()).then(css => { localStorage.setItem('ionicons', css); loadCSS(css); }); } } function loadCSS(css) { const style = document.createElement('style'); style.innerHTML = css; document.head.appendChild(style); } //Remove elements from imported content automatically function removeStrongFromHeadings() { const elements = document.querySelectorAll('h1, h2, h3, h4, h5, h6, span'); elements.forEach(el => { const strongTags = el.querySelectorAll('strong'); strongTags.forEach(strongTag => { while (strongTag.firstChild) { strongTag.parentNode.insertBefore(strongTag.firstChild, strongTag); } strongTag.remove(); }); }); } removeStrongFromHeadings(); "use strict"; window.onload = () => { /* //This is an object for each category of subjects and in that there are kewords and link to the keywods let keywordsAndLinks = { //you can add more categories and define their keywords and add a link ds: { keywords: [ //you can add more keywords here they are detected and replaced with achor tag automatically 'data science', 'Data science', 'Data Science', 'data Science', 'DATA SCIENCE', ], //we will replace the linktext with the keyword later on in the code //you can easily change links for each category here //(include class="ml-link" and linktext) link: 'linktext', }, ml: { keywords: [ //Add more keywords 'machine learning', 'Machine learning', 'Machine Learning', 'machine Learning', 'MACHINE LEARNING', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, ai: { keywords: [ 'artificial intelligence', 'Artificial intelligence', 'Artificial Intelligence', 'artificial Intelligence', 'ARTIFICIAL INTELLIGENCE', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, nl: { keywords: [ 'NLP', 'nlp', 'natural language processing', 'Natural Language Processing', 'NATURAL LANGUAGE PROCESSING', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, des: { keywords: [ 'data engineering services', 'Data Engineering Services', 'DATA ENGINEERING SERVICES', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, td: { keywords: [ 'training data', 'Training Data', 'training Data', 'TRAINING DATA', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, ias: { keywords: [ 'image annotation services', 'Image annotation services', 'image Annotation services', 'image annotation Services', 'Image Annotation Services', 'IMAGE ANNOTATION SERVICES', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, l: { keywords: [ 'labeling', 'labelling', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, pbp: { keywords: [ 'previous blog posts', 'previous blog post', 'latest', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, mlc: { keywords: [ 'machine learning course', 'machine learning class', ], //Change your article link (include class="ml-link" and linktext) link: 'linktext', }, }; //Articles to skip let articleIdsToSkip = ['post-2651', 'post-3414', 'post-3540']; //keyword with its related achortag is recieved here along with article id function searchAndReplace(keyword, anchorTag, articleId) { //selects the h3 h4 and p tags that are inside of the article let content = document.querySelector(`#${articleId} .entry-content`); //replaces the "linktext" in achor tag with the keyword that will be searched and replaced let newLink = anchorTag.replace('linktext', keyword); //regular expression to search keyword var re = new RegExp('(' + keyword + ')', 'g'); //this replaces the keywords in h3 h4 and p tags content with achor tag content.innerHTML = content.innerHTML.replace(re, newLink); } function articleFilter(keyword, anchorTag) { //gets all the articles var articles = document.querySelectorAll('article'); //if its zero or less then there are no articles if (articles.length > 0) { for (let x = 0; x < articles.length; x++) { //articles to skip is an array in which there are ids of articles which should not get effected //if the current article's id is also in that array then do not call search and replace with its data if (!articleIdsToSkip.includes(articles[x].id)) { //search and replace is called on articles which should get effected searchAndReplace(keyword, anchorTag, articles[x].id, key); } else { console.log( `Cannot replace the keywords in article with id ${articles[x].id}` ); } } } else { console.log('No articles found.'); } } let key; //not part of script, added for (key in keywordsAndLinks) { //key is the object in keywords and links object i.e ds, ml, ai for (let i = 0; i < keywordsAndLinks[key].keywords.length; i++) { //keywordsAndLinks[key].keywords is the array of keywords for key (ds, ml, ai) //keywordsAndLinks[key].keywords[i] is the keyword and keywordsAndLinks[key].link is the link //keyword and link is sent to searchreplace where it is then replaced using regular expression and replace function articleFilter( keywordsAndLinks[key].keywords[i], keywordsAndLinks[key].link ); } } function cleanLinks() { // (making smal functions is for DRY) this function gets the links and only keeps the first 2 and from the rest removes the anchor tag and replaces it with its text function removeLinks(links) { if (links.length > 1) { for (let i = 2; i < links.length; i++) { links[i].outerHTML = links[i].textContent; } } } //arrays which will contain all the achor tags found with the class (ds-link, ml-link, ailink) in each article inserted using search and replace let dslinks; let mllinks; let ailinks; let nllinks; let deslinks; let tdlinks; let iaslinks; let llinks; let pbplinks; let mlclinks; const content = document.querySelectorAll('article'); //all articles content.forEach((c) => { //to skip the articles with specific ids if (!articleIdsToSkip.includes(c.id)) { //getting all the anchor tags in each article one by one dslinks = document.querySelectorAll(`#${c.id} .entry-content a.ds-link`); mllinks = document.querySelectorAll(`#${c.id} .entry-content a.ml-link`); ailinks = document.querySelectorAll(`#${c.id} .entry-content a.ai-link`); nllinks = document.querySelectorAll(`#${c.id} .entry-content a.ntrl-link`); deslinks = document.querySelectorAll(`#${c.id} .entry-content a.des-link`); tdlinks = document.querySelectorAll(`#${c.id} .entry-content a.td-link`); iaslinks = document.querySelectorAll(`#${c.id} .entry-content a.ias-link`); mlclinks = document.querySelectorAll(`#${c.id} .entry-content a.mlc-link`); llinks = document.querySelectorAll(`#${c.id} .entry-content a.l-link`); pbplinks = document.querySelectorAll(`#${c.id} .entry-content a.pbp-link`); //sending the anchor tags list of each article one by one to remove extra anchor tags removeLinks(dslinks); removeLinks(mllinks); removeLinks(ailinks); removeLinks(nllinks); removeLinks(deslinks); removeLinks(tdlinks); removeLinks(iaslinks); removeLinks(mlclinks); removeLinks(llinks); removeLinks(pbplinks); } }); } //To remove extra achor tags of each category (ds, ml, ai) and only have 2 of each category per article cleanLinks(); */ //Recommended Articles var ctaLinks = [ /* ' ' + '

Subscribe to our AI newsletter!

' + */ '

Take our 85+ lesson From Beginner to Advanced LLM Developer Certification: From choosing a project to deploying a working product this is the most comprehensive and practical LLM course out there!

'+ '

Towards AI has published Building LLMs for Production—our 470+ page guide to mastering LLMs with practical projects and expert insights!

' + '
' + '' + '' + '

Note: Content contains the views of the contributing authors and not Towards AI.
Disclosure: This website may contain sponsored content and affiliate links.

' + 'Discover Your Dream AI Career at Towards AI Jobs' + '

Towards AI has built a jobs board tailored specifically to Machine Learning and Data Science Jobs and Skills. Our software searches for live AI jobs each hour, labels and categorises them and makes them easily searchable. Explore over 10,000 live jobs today with Towards AI Jobs!

' + '
' + '

🔥 Recommended Articles 🔥

' + 'Why Become an LLM Developer? Launching Towards AI’s New One-Stop Conversion Course'+ 'Testing Launchpad.sh: A Container-based GPU Cloud for Inference and Fine-tuning'+ 'The Top 13 AI-Powered CRM Platforms
' + 'Top 11 AI Call Center Software for 2024
' + 'Learn Prompting 101—Prompt Engineering Course
' + 'Explore Leading Cloud Providers for GPU-Powered LLM Training
' + 'Best AI Communities for Artificial Intelligence Enthusiasts
' + 'Best Workstations for Deep Learning
' + 'Best Laptops for Deep Learning
' + 'Best Machine Learning Books
' + 'Machine Learning Algorithms
' + 'Neural Networks Tutorial
' + 'Best Public Datasets for Machine Learning
' + 'Neural Network Types
' + 'NLP Tutorial
' + 'Best Data Science Books
' + 'Monte Carlo Simulation Tutorial
' + 'Recommender System Tutorial
' + 'Linear Algebra for Deep Learning Tutorial
' + 'Google Colab Introduction
' + 'Decision Trees in Machine Learning
' + 'Principal Component Analysis (PCA) Tutorial
' + 'Linear Regression from Zero to Hero
'+ '

', /* + '

Join thousands of data leaders on the AI newsletter. It’s free, we don’t spam, and we never share your email address. Keep up to date with the latest work in AI. From research to projects and ideas. If you are building an AI startup, an AI-related product, or a service, we invite you to consider becoming a sponsor.

',*/ ]; var replaceText = { '': '', '': '', '
': '
' + ctaLinks + '
', }; Object.keys(replaceText).forEach((txtorig) => { //txtorig is the key in replacetext object const txtnew = replaceText[txtorig]; //txtnew is the value of the key in replacetext object let entryFooter = document.querySelector('article .entry-footer'); if (document.querySelectorAll('.single-post').length > 0) { //console.log('Article found.'); const text = entryFooter.innerHTML; entryFooter.innerHTML = text.replace(txtorig, txtnew); } else { // console.log('Article not found.'); //removing comment 09/04/24 } }); var css = document.createElement('style'); css.type = 'text/css'; css.innerHTML = '.post-tags { display:none !important } .article-cta a { font-size: 18px; }'; document.body.appendChild(css); //Extra //This function adds some accessibility needs to the site. function addAlly() { // In this function JQuery is replaced with vanilla javascript functions const imgCont = document.querySelector('.uw-imgcont'); imgCont.setAttribute('aria-label', 'AI news, latest developments'); imgCont.title = 'AI news, latest developments'; imgCont.rel = 'noopener'; document.querySelector('.page-mobile-menu-logo a').title = 'Towards AI Home'; document.querySelector('a.social-link').rel = 'noopener'; document.querySelector('a.uw-text').rel = 'noopener'; document.querySelector('a.uw-w-branding').rel = 'noopener'; document.querySelector('.blog h2.heading').innerHTML = 'Publication'; const popupSearch = document.querySelector$('a.btn-open-popup-search'); popupSearch.setAttribute('role', 'button'); popupSearch.title = 'Search'; const searchClose = document.querySelector('a.popup-search-close'); searchClose.setAttribute('role', 'button'); searchClose.title = 'Close search page'; // document // .querySelector('a.btn-open-popup-search') // .setAttribute( // 'href', // 'https://medium.com/towards-artificial-intelligence/search' // ); } // Add external attributes to 302 sticky and editorial links function extLink() { // Sticky 302 links, this fuction opens the link we send to Medium on a new tab and adds a "noopener" rel to them var stickyLinks = document.querySelectorAll('.grid-item.sticky a'); for (var i = 0; i < stickyLinks.length; i++) { /* stickyLinks[i].setAttribute('target', '_blank'); stickyLinks[i].setAttribute('rel', 'noopener'); */ } // Editorial 302 links, same here var editLinks = document.querySelectorAll( '.grid-item.category-editorial a' ); for (var i = 0; i < editLinks.length; i++) { editLinks[i].setAttribute('target', '_blank'); editLinks[i].setAttribute('rel', 'noopener'); } } // Add current year to copyright notices document.getElementById( 'js-current-year' ).textContent = new Date().getFullYear(); // Call functions after page load extLink(); //addAlly(); setTimeout(function() { //addAlly(); //ideally we should only need to run it once ↑ }, 5000); }; function closeCookieDialog (){ document.getElementById("cookie-consent").style.display = "none"; return false; } setTimeout ( function () { closeCookieDialog(); }, 15000); console.log(`%c 🚀🚀🚀 ███ █████ ███████ █████████ ███████████ █████████████ ███████████████ ███████ ███████ ███████ ┌───────────────────────────────────────────────────────────────────┐ │ │ │ Towards AI is looking for contributors! │ │ Join us in creating awesome AI content. │ │ Let's build the future of AI together → │ │ https://towardsai.net/contribute │ │ │ └───────────────────────────────────────────────────────────────────┘ `, `background: ; color: #00adff; font-size: large`); //Remove latest category across site document.querySelectorAll('a[rel="category tag"]').forEach(function(el) { if (el.textContent.trim() === 'Latest') { // Remove the two consecutive spaces (  ) if (el.nextSibling && el.nextSibling.nodeValue.includes('\u00A0\u00A0')) { el.nextSibling.nodeValue = ''; // Remove the spaces } el.style.display = 'none'; // Hide the element } }); // Add cross-domain measurement, anonymize IPs 'use strict'; //var ga = gtag; ga('config', 'G-9D3HKKFV1Q', 'auto', { /*'allowLinker': true,*/ 'anonymize_ip': true/*, 'linker': { 'domains': [ 'medium.com/towards-artificial-intelligence', 'datasets.towardsai.net', 'rss.towardsai.net', 'feed.towardsai.net', 'contribute.towardsai.net', 'members.towardsai.net', 'pub.towardsai.net', 'news.towardsai.net' ] } */ }); ga('send', 'pageview'); -->