Generating LiveCode using ChatGPT

by Geoff Canyon on February 20, 2023 6 comments

Geoff Canyon is a product manager highly interested in machine learning and available to help make your product awesome! 

LiveCode

LiveCode is a combination IDE/UI Toolkit/programming language with a history stretching back to the 1990s, and with predecessors from the 1980s (HyperCard). I’m fond of LiveCode: I’ve used it since the ‘90s, I spoke about it at Apple’s WWDC and other conferences, and I’ve helped to demo it at trade shows by building a working app for Windows, Mac, and Linux, all while holding my breath. Despite being a product manager and not a developer, I still maintain a well-liked plug-in for the dev environment, and I use LiveCode whenever I need a quick UI+code environment in which to work.

ChatGPT  

ChatGPT is a language machine learning model recently released by OpenAI, based on their GPT 3.5 model, fine-tuned with both supervised and reinforcement learning techniques to function as a chatbot. It is a remarkable advancement over previous language generation models, capable of producing a convincingly-human conversational experience.

But it’s not limited to chat. It was trained on an enormous set of data, and it can perform many seemingly human tasks, including coding in a variety of programming languages. Many examples exist online already using languages such as Python (ex1, ex2), SQL (ex1), and others. 

How much training data is needed?

Python and SQL are wildly popular languages, with a ton of examples on the internet for ChatGPT to study, and millions of people to make corrections when ChatGPT gets something wrong, which helps it get better at those languages. 

But what about languages with (far) fewer examples in ChatGPT’s training data? However much I like LiveCode, it’s inarguably a less-used programming language compared with Python, SQL, Java, or many others. On the PYPL language popularity list, LiveCode is less common than COBOL.

That isn’t to critique LiveCode, but to make clear that there are far fewer examples of LiveCode code available on the internet for ChatGPT to train on, and fewer people to point out if/when ChatGPT makes a mistake generating LiveCode. 

I’m going to show three examples of using ChatGPT to generate code for LiveCode, with explanations of how I guided it to working solutions, and the pitfalls and remarkable successes I had along the way. Even though I’ve written probably over a hundred thousand lines of code in LiveCode, I learned a few things.

Disclaimer: I understand that ChatGPT is a language model. When I say things like “ChatGPT understands…” don’t read into that any claim that ChatGPT is more than a language model. It’s just my own syntactic shorthand for “the language model is able to usefully respond to…”

TL;DR

Despite the relatively limited training data available on the internet for the LiveCode programming language, ChatGPT is often able to generate usable code. It sometimes gets syntax wrong and has to be corrected, but it takes direction well. Someone with knowledge of LiveCode could possibly be productive with it, and even learn some things from it. With no knowledge of LiveCode, or coding in general, it’s unlikely that ChatGPT would be able to operate completely independently.

Example 1: Fizz Buzz

Fizz Buzz is a relatively simple programming problem requiring a loop and conditional statements. It’s fairly common, and sometimes used for programming interviews – we used it at Answers.com when I worked there, and screened out roughly 20% of candidates who couldn’t whiteboard it. Here’s how it played out when I asked ChatGPT to code it for me (ignore the fact that I misremembered the name!).

Me:“fizz bin” is a program that counts from 1 to 100, replacing every number divisible by 5 with “fizz” and every number divisible by 7 with “bin”; and numbers divisible by both 5 and 7 with “fizz bin”. Write code in the programming language livecode to execute “fizz bin”

ChatGPT

repeat with x = 1 to 100
    if x mod 5 = 0 and x mod 7 = 0 then
        put "fizz bin"
    else if x mod 5 = 0 then
        put "fizz"
    else if x mod 7 = 0 then
        put "bin"
    else
        put x
    end if
end repeat

Hopefully LiveCode is self-explanatory enough that programmers in other languages can follow that, but in case it’s not clear: that works perfectly as-is

But it follows the instructions perhaps too literally: the results fly by in the message box, which is the default output in LiveCode. My first instinct was to fix the code myself, so I put a field and a button on a card, and edited the code to this:

on mouseUp
   repeat with x = 1 to 100
      if x mod 5 = 0 and x mod 7 = 0 then
         put "fizz bin" & cr after fld 1
      else if x mod 5 = 0 then
         put "fizz" & cr after fld 1
      else if x mod 7 = 0 then
         put "bin" & cr after fld 1
      else
         put x & cr after fld 1
      end if
   end repeat
end mouseUp

Things to note in that code: it is now wrapped in an “on mouseUp” handler, so it can be used as the code in the button. It adds “& cr” to each concatenation, and puts the output into the field. This is all standard stuff that any LiveCode developer would do without a second thought.

It was at this point I realized I was potentially selling ChatGPT short. So I tried:

Modify that code so it works as an “on mouseUp” handler in a button, and concatenates the results on individual lines, and puts the final result into the field “display”

on mouseUp
   put "" into fizzbin
   repeat with x = 1 to 100
      if x mod 5 = 0 and x mod 7 = 0 then
          put "fizz bin" & return after fizzbin
      else if x mod 5 = 0 then
          put "fizz" & return after fizzbin
      else if x mod 7 = 0 then
          put "bin" & return after fizzbin
      else
          put x & return after fizzbin
      end if
   end repeat
   put fizzbin into field "display"
end mouseUp

This code uses the “on mouseUp” handler to execute the script when the button is clicked. The results are concatenated and separated by a new line using the “& return” operator after each output. The final result is placed into a field with the name “display” using the “put” command.

That code executes perfectly right out of the box, with no errors, and of course even though I didn’t ask it to, ChatGPT uses a variable to store the intermediate values, saving the field update for the end. This is much faster than the way I did it with 100 field updates. It wouldn’t be an issue iterating from 1 to 100 – both options execute in a fraction of a second – but for larger values it would be a critical optimization.

Earlier, I posted this to a LiveCode email list. I cheekily added to the email:

If I were writing it, I’d probably do something like this to make it a little faster, if not as clear.

on mouseUp
   put "" into fizzbin
   repeat with x = 1 to 100
      if x mod 5 = 0 then put "fizz " after fizzbin
      if x mod 7 = 0 then put "bin " after fizzbin
      if char -1 of fizzbin is space 
          then put return into char -1 of fizzbin
          else put x & return after fizzbin
   end repeat
   put fizzbin into field "display"
end mouseUp

In fact, I tested later and found out I was wrong: not only is it clearer than my version, ChatGPT’s code is also something like 30% faster than mine. Score one for the machines!

I decided to have ChatGPT turn the handler into a function:

Now rewrite the code to be a function named “fizzbin” that takes an argument “n”, repeats with 1 to n, and returns the results.

function fizzbin n
   put "" into fizzbin
   repeat with x = 1 to n
      if x mod 5 = 0 and x mod 7 = 0 then
          put "fizz bin" & return after fizzbin
      else if x mod 5 = 0 then
          put "fizz" & return after fizzbin
      else if x mod 7 = 0 then
          put "bin" & return after fizzbin
      else
          put x & return after fizzbin
      end if
   end repeat
   return fizzbin
end fizzbin

This code defines a function named “fizzbin” which takes an argument “n” that determines the range of the repeat loop. The function builds the result string in a variable “fizzbin” and return the final result. The calling script can then use the returned value for further processing or for displaying in a field.

Again, this works perfectly, and minor grammatical issue with the commentary (should be “returns”) notwithstanding, that’s good documentation.

10/10 for ChatGPT on Fizz Buzz. It produced code that was correct, and both clearer and faster  than the code I wrote by hand. And in case it’s not clear, the above is the exact transcript from ChatGPT. I was careful with how I phrased the requests, but everything went exactly as shown on the first try.

Example 2: Reverse Lines

When I posted the above to the LiveCode mailing list, someone pointed out that Fizz Buzz is common on the internet. Even though I called it by a different name, it makes sense that ChatGPT might already have a good idea what to do. So I tried a less common task: taking a variable with a number of lines of text, and reversing the order of those lines. 

Initial Solution

Where I was careful with how I phrased the description for “fizz bin”, here I was purposefully more general in my description to give ChatGPT room to succeed or fail on its own.

Write code in the programming language livecode to reverse the order of the lines in the input.

on reverseLines pInput
    put pInput into tInput
    put line 1 to -1 of tInput into tLines
    put tLines into tReversedLines
    return tReversedLines
end reverseLines

This code defines a function called reverseLines that takes in a parameter called pInput. The function first assigns the input to a variable tInput. It then gets all the lines from the input, assigns it to tLines and then reverses the order of the lines and assigns it to tReversedLines. The function then returns the reversed lines.

There are several problems with that code. The most important flaw is that it doesn’t actually attempt to reverse the lines. I tried pulling a fast one by adding the “Also, functions start with ‘function’” after telling ChatGPT to “Please try again.” But as usual, ChatGPT is more than up to the task of understanding multiple intents and correctly parsing my input:

That wouldn’t work at all. It doesn’t reverse the lines of the input. Please try again. Also, functions start with “function”

I apologize for the confusion. Here’s a corrected version of the code:

function reverseLines pInput
    put the lines of pInput into tLines
    put tLines into tReversedLines
    reverse tReversedLines
    return tReversedLines
end reverseLines

This code defines a function called reverseLines that takes in a parameter called pInput. The function first assigns the lines of the input to a variable tLines. It then assigns it to tReversedLines and then reverse the order of the lines. The function then returns the reversed lines.

But this code still won’t work. ChatGPT made up the “reverse” command; it doesn’t exist in LiveCode. Cheeky AI!

There is no “reverse” command in livecode. You will have to write the code to get the lines in reverse order and append them to a new variable. Please try again.

I apologize for the mistake. Here is an implementation of the reverseLines function that manually reverses the order of the lines in the input without using a builtin reverse command:

function reverseLines pInput
    put the lines of pInput into tLines
    put "" into tReversedLines
    repeat with i = the number of lines of tLines down to 1
        add line i of tLines & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

This code defines a function called reverseLines that takes in a parameter called pInput. The function first assigns the lines of the input to a variable tLines. Then it creates an empty variable called tReversedLines and uses a repeat loop that runs from the last line of the input to the first, adding each line to tReversedLines. Finally, it returns the reversed lines in tReversedLines.

Close, but not quite. I gave corrections:

You don’t need to duplicate the argument; you can work on it directly. In livecode you use “put” to concatenate, not “add”

You’re right, my apologies for the confusion. Here is the corrected version of the reverseLines function:

function reverseLines pInput
    put "" into tReversedLines
    repeat with i = the number of lines of pInput down to 1
        put line i of pInput & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

This code defines a function called reverseLines that takes in a parameter called pInput. The function uses a repeat loop that runs from the last line of the input to the first, using the put command to add each line to tReversedLines. Finally, it returns the reversed lines in tReversedLines

And there it is, code that works.

So 6/10 for getting to the initial solution with multiple corrections; someone who knows LiveCode at least a little bit would likely be able to get it there. I’m very impressed that ChatGPT can correctly react to a statement as abstract as “You don’t need to duplicate the argument; you can work on it directly.”

Optimizing: repeat for each

There’s a common learning-curve moment among LiveCode developers when they first learn to avoid code like the above. “put line i of pInput” will get slower and slower as the input string grows. LiveCode has a code structure “repeat for each” that scales performance linearly with the size of the input. It does go beginning-to-end, so it requires prepending to the result, rather than appending. That’s slower, but not enough to overcome the gain from “repeat for each”. 

I wrote my own version of the code above, using “repeat for each line L in pInput” and found that for a 1000-line input mine was roughly 6x as fast as ChatGPT’s. At 10,000 lines it’s 25x as fast. I initially stopped there, but while writing this I realized that I was shortchanging ChatGPT: my original instructions specified how to do the task, not what to do: “You will have to write the code to get the lines in reverse order and append them to a new variable.” So I gave ChatGPT a chance to be smart. Continuing the original session:

Getting each line in reverse is slow. Instead, parse the lines sequentially and add them to the beginning of the result.

That’s pretty much spelling it out for ChatGPT, but I couldn’t think of a less direct way short of just saying “Can you make this faster?”

function reverseLines pInput
    put "" into tReversedLines
    repeat for each line tLine in pInput
        put tLine & cr before tReversedLines
    end repeat
    return tReversedLines
end reverseLines

This code defines a function called reverseLines that takes in a parameter called pInput. The function uses a repeat loop that runs through each line of the input using the repeat for each line construct, using the put command to add the line at the beginning of tReversedLines variable. Finally, it returns the reversed lines in tReversedLines.

That’s almost exactly what I wrote when I hand-optimized the function. No notes. 8/10 for coming up with the exact correct “repeat for each” solution when prompted with a description of it.

Actually being smarter

Then I realized that maybe I was still selling ChatGPT short. The above prompt still told ChatGPT how to do the task, rather than what to do. I wanted to give ChatGPT a chance to actually be smart. So I started a new session and entered:

Here is a function in the programming language LiveCode. Is there any way to optimize its performance?

function reverseLines pInput
    put "" into tReversedLines
    repeat with i = the number of lines of pInput down to 1
        put line i of pInput & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

Yes, there are several ways to optimize the performance of this function:

1. Instead of using the “repeat with” loop to iterate through the lines of the input, you could use the “for each” loop. This is generally faster as it doesn’t require an explicit index, and it doesn’t have to count the number of lines every time.
2. You could also use the “reverse” function to reverse the order of the lines in the input, rather than manually iterating through them in reverse order.
3. Instead of concatenating each line of the input to a variable (tReversedLines), you could use an array to store the lines, and then use the “join” function to concatenate them into a single string at the end. This is more efficient, as it doesn’t require constant memory allocation for the growing string.
4. You could also use the “chunk” function to break the input into smaller chunks, and reverse the lines in each chunk in parallel, this way you can take advantage of multi-core processors, and speed up the process.

My comments, in order:

  1. This is exactly the “repeat for each” solution from before. So yes, ChatGPT can come up with the idea if given a suggestion to “make it faster.”
  2. There is no “reverse” function in LiveCode. This is a mistake.
  3. This is faster, but the “combine” command – the LiveCode equivalent of “join” – doesn’t respect the order of the keys in this way, and the results don’t come out in the correct order. 
  4. LiveCode doesn’t support parallel processing, so this is a non-starter.

This means option 1 is the only viable way forward:

Try option 1

Sure, here is an optimized version of the function that uses the “for each” loop:

function reverseLines pInput
    put "" into tReversedLines
    for each line tLine in pInput
        put tLine & cr before tReversedLines
    end for each
    return tReversedLines
end reverseLines

This function works in the same way as the original function, but it uses the “for each” loop to iterate through the lines of the input. This eliminates the need to count the number of lines and use an explicit index, which should result in faster performance.

This is close enough to correct that it fooled me for some time. But note the “for each” instead of “repeat for each”. I tried to correct ChatGPT without just telling it what to do:

That’s not the correct syntax for a repeat loop.

I apologize for the mistake, you are correct. The syntax for the “for each” loop is different from the “repeat with” loop. Here is the correct version of the function that uses “for each” loop:

function reverseLines pInput
    put "" into tReversedLines
    for each tLine in lines of pInput
        put tLine & cr before tReversedLines
    end for each
    return tReversedLines
end reverseLines

This function works in the same way as the original function, but it uses the “for each” loop to iterate through the lines of the input. This eliminates the need to count the number of lines, which should result in faster performance.

“lines of” is superfluous here, and that’s not a correct usage anyway. So I tried again:

No, it was closer before, but it needs to be “repeat for each”

You are correct again, my apologies.
Here is the correct version of the function that uses “repeat for each” loop :

function reverseLines pInput
    put "" into tReversedLines
    repeat for each line tLine in pInput
        put tLine & cr before tReversedLines
    end repeat
    return tReversedLines
end reverseLines

This function works in the same way as the original function, but it uses the “repeat for each” loop to iterate through the lines of the input. This eliminates the need to count the number of lines and use an explicit index, which should result in faster performance.

Success! It took several tries, and ChatGPT gave one correct suggestion out of four to the, “Can it be optimized?” question. But setting that aside, ChatGPT came up with the same method I would have used, without me having to prompt it other than asking it to make the code faster.

5/10 for at least coming up with “repeat for each” as one possible optimization out of four, the other three being incorrect. A point off for trying to fool me with the “for each” instead of “repeat for each”.

Even faster?

Albeit that it wouldn’t work, ChatGPT’s third suggestion stuck with me. Prepending is slow, even if it’s not as slow as the original solution of repeatedly parsing the lines. Although “combine” doesn’t respect the order of the keys in the source array, there is another way to use arrays for this task: the “split” command does respect the order of the lines in the source variable. I wrote this code to take advantage of that:

function reverseLines pInput
    put "" into tReversedLines
    split pInput using cr
    repeat with i = the number of elements of pInput down to 1
        put SS[i] & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

For 10,000 lines, this method is about 3.5x faster than the “repeat for each with prepending” solution. To be clear, I never would have thought of this method without ChatGPT’s prompting. I’ve used the “repeat for each” method in similar circumstances many times over the years, and this method never occurred to me. 

Convincing ChatGPT to write something similar to the above code proved very difficult. For example, explicitly telling it to use “split” failed miserably:

What if you split the array and then appended the array elements in reverse order?

Yes, splitting the input into an array and then appending the elements in reverse order is another way to optimize the performance of the function. Here’s an example of how it could be done:

function reverseLines pInput
    put split(pInput,cr) into tLines
    put "" into tReversedLines
    repeat for each element tLine in tLines
        put tLine & cr before tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “split” function is used to divide the input into an array of lines, stored in the variable “tLines”. Then, the “repeat for each” loop is used to iterate through the elements of the array in reverse order, and append each line to the “tReversedLines” variable.

This method can be more efficient than the previous ones, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

“split” is a command, not a function. The “repeat for each element” will work, but I’m not sure it will maintain the order, and the code is still prepending.

Take that, and use a reverse index to access the array elements and append them to the result variable.

You’re right, using the “repeat for each” loop in combination with reverse indexing to access the array elements is a good approach to optimize the function. Here’s an example of how it could be done in LiveCode:

function reverseLines pInput
    put split(pInput,cr) into tLines
    put "" into tReversedLines
    repeat for i = (the number of lines of tLines) to 1 step -1
        put tLines[i] & cr before tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “split” function is used to divide the input into an array of lines, stored in the variable “tLines”. Then, the “repeat” loop is used to iterate through the elements of the array in reverse order, using the “i” variable as the index, which starts at the number of lines of tLines and decrements by one in each iteration. In each iteration the line is being appended to the tReversedLines variable.

This method can be more efficient than the previous ones, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index, and it doesn’t require additional operations to reverse the array.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

This is even worse. If the “split” function worked, tLines would be an array, and “the number of lines” of it would return 0.

“split” is a command, not a function. And you can use the extents of the array to get the count instead of counting lines

I apologize for the mistake, you are correct. In LiveCode, “split” is a command, not a function, and it doesn’t return a value. Also, you can use the extents of the array to get the count instead of counting lines. Here’s an example of how it could be done in LiveCode:

function reverseLines pInput
    put line 1 to -1 of pInput into tLines
    put "" into tReversedLines
    repeat for i = (the number of items of tLines) to 1 step -1
        put tLines[i] & cr before tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “line 1 to -1 of pInput” is used to divide the input into an array of lines, stored in the variable “tLines”. Then, the “repeat” loop is used to iterate through the elements of the array in reverse order, using the “i” variable as the index, which starts at the number of items of tLines and decrements by one in each iteration. In each iteration the line is being appended to the tReversedLines variable.

This method can be more efficient than the previous ones, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index, and it doesn’t require additional operations to reverse the array.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

Now I’m just flailing. This is nowhere near the desired solution.

Okay, that’s not right. Let’s start over. Write a function in the LiveCode programming language to reverse the lines in a string by: 1. Using the LiveCode split command to convert the string to an array based on the lines in it. 2. Counting down from the number of elements of the array to 1, placing each element after the result.

Sure, here’s an example of a function in LiveCode that reverses the lines in a string:

function reverseLines pInput
    put split(pInput,cr) into tLines
    put "" into tReversedLines
    repeat with i = the number of items of tLines down to 1
        put tLines[i] & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “split” command is used to divide the input into an array of lines, stored in the variable “tLines”, using the line break as the separator. Then, the “repeat” loop is used to iterate through the elements of the array in reverse order, using the “i” variable as the index, which starts at the number of items of tLines and decrements by one in each iteration. In each iteration the line is being appended to the tReversedLines variable, using the “after” command.

This method can be efficient, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

ChatGPT still insists that “split” is a function rather than a command. And that arrays have items.

In LiveCode, “split” is a command. It looks like this: split variable {by | using | with} primaryDelimiter [and secondaryDelimiter]. Arrays have elements, not items

I apologize for the mistake, you are correct. In LiveCode, “split” is a command, not a function. and it should be used as follows:

split variable by primaryDelimiter [and secondaryDelimiter]

Here’s an example of a function in LiveCode that reverses the lines in a string using the correct syntax and terminology:

function reverseLines pInput
    split pInput by cr
    put "" into tReversedLines
    repeat with i = the number of elements of it down to 1
        put it[i] & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “split” command is used to divide the input into an array of lines, stored in the default variable “it”, using the line break as the separator. Then, the “repeat” loop is used to iterate through the elements of the array in reverse order, using the “i” variable as the index, which starts at the number of elements of it and decrements by one in each iteration. In each iteration the line is being appended to the tReversedLines variable, using the “after” command.

This method can be efficient, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

It got the split syntax, and the elements of arrays, but where did “it” come from?

Use “pInput”, not “it”

I apologize for the mistake, you are correct. Here’s an example of a function in LiveCode that reverses the lines in a string using the correct syntax and terminology, and also the correct variable name:

function reverseLines pInput
    split pInput by cr
    put "" into tReversedLines
    repeat with i = the number of elements of pInput down to 1
        put pInput[i] & cr after tReversedLines
    end repeat
    return tReversedLines
end reverseLines

In this version, the “split” command is used to divide the input into an array of lines, stored in the “pInput” variable, using the line break as the separator. Then, the “repeat” loop is used to iterate through the elements of the array in reverse order, using the “i” variable as the index, which starts at the number of elements of pInput and decrements by one in each iteration. In each iteration the line is being appended to the tReversedLines variable, using the “after” command.

This method can be efficient, because it allows you to iterate through the lines in reverse order without counting the number of lines or using an explicit index.

Keep in mind that the best optimization will depend on the specific use case and the size of the input. It’s recommended to test the performance of different options and choose the one that works best for your specific scenario.

Victory! Eventually! 2/10 – I basically had to hand-hold ChatGPT through much of this solution.

Example 3: Closest Points

Reversing the lines in a variable still seems pretty mundane, so I went with a requirement that I was pretty confident would be less common in the material available on the internet: finding the nearest among a set of points. Again, I was purposefully general in this description to give ChatGPT room to interpret the goal.

“closestPoints” is a function that takes a list of points in a 2D coordinate system, compares the distances between each pair of points, and returns the two points that are closest together from the original set in a list. If there are more than two points that have the same distance and are closest, it returns all the closest points. Write code in the programming language livecode to execute “closestPoints”

from math import dist

def closestPoints(points):
    closest_points = []
    min_distance = float('inf')

    for i in range(len(points)):
        for j in range(i+1, len(points)):
            distance = dist(points[i], points[j])
            if distance < min_distance:
                min_distance = distance
                closest_points = [(points[i], points[j])]
            elif distance == min_distance:
                closest_points.append((points[i], points[j]))

    return closest_points

This function takes in a list of points, which are assumed to be in the form of (x, y) tuples. It uses a nested for loop to compare the distance between each pair of points using the dist function. It keeps track of the minimum distance and the closest points using two variables, min_distance and closest_points. It returns a list of tuples containing the closest points.

Hopefully by this point it’s obvious that ChatGPT did not return code compatible with LiveCode. Otherwise it looks reasonable-ish?

That looks like Python, which is not what I asked for. Rewrite the function in the livecode programming language.

on closestPoints pPoints
    put 0 into minDist
    put empty into closestPoints
    repeat with i = 1 to the number of items in pPoints
        repeat with j = i + 1 to the number of items in pPoints
            put distance(pPoints[i],pPoints[j]) into dist
            if dist < minDist then
                put dist into minDist
                put pPoints[i] & pPoints[j] into closestPoints
            else if dist = minDist then
                put pPoints[i] & pPoints[j] after closestPoints
            end if
        end repeat
    end repeat
    return closestPoints
end closestPoints

This function takes a list of points pPoints as input and uses nested repeat loops to compare the distance between each pair of points using the distance function. It keeps track of the minimum distance and the closest points using two variables, minDist and closestPoints. It returns a list containing the closest points.

Note that the language ‘livecode’ is not a widely used language, and i am not able to test the above code.

ChatGPT throws in a criticism of LiveCode’s level of usage for free. 
There are a number of issues with this code, so I list them all to see how ChatGPT takes multiple directions in one message. Calling out one interesting point, ChatGPT goes from min_distance = float(‘inf’) in the (pseudo?) Python to put 0 into minDist in the new LiveCode version. Zero and infinity don’t mean the same thing at all…

arrays have elements, not items. Change the code to reference elements of the array instead of items. if minDist starts at 0 no distance will be less than it. Change the code to start minDist at the distance between the first two points. Using & to concatenate two points will look bad. Concatenate them using a space, and concatenate additional pairs of points using a return.

on closestPoints pPoints
    put distance(pPoints[1],pPoints[2]) into minDist
    put pPoints[1] & return & pPoints[2] into closestPoints
    repeat with i = 1 to the number of elements in pPoints
        repeat with j = i + 1 to the number of elements in pPoints
            put distance(pPoints[i],pPoints[j]) into dist
            if dist < minDist then
                put dist into minDist
                put pPoints[i] & return & pPoints[j] into closestPoints
            else if dist = minDist then
                put return & pPoints[i] & return & pPoints[j] after closestPoints
            end if
        end repeat
    end repeat
    return closestPoints
end closestPoints

This function takes a list of points pPoints as input and uses nested repeat loops to compare the distance between each pair of points using the distance function. The variable minDist is initialized with the distance between the first two points. It keeps track of the closest points using the variable closestPoints. It returns a list containing the closest points.

It might not be obvious (it wasn’t to me) but if the first two points are the closest, this code will return those points twice. This isn’t ChatGPT’s fault, it’s a consequence of what I told it to do when setting the initial minimum value. ChatGPT did make a mistake with how it assembles the list of closest points, using “return”s everywhere instead of just between two pairs. I’m happy to concede that it’s difficult to phrase the instructions for that correctly and clearly, and I could have done better. I think the below is clearer, and ChatGPT gets it right after this.

I made a mistake. Change the code to start minDist at one plus the distance between the first two points. Also separate the points in each pair with space, and continue to separate the pairs of points with return.

on closestPoints pPoints
    put 1 + distance(pPoints[1],pPoints[2]) into minDist
    put pPoints[1] & " " & pPoints[2] into closestPoints
    repeat with i = 1 to the number of elements in pPoints
        repeat with j = i + 1 to the number of elements in pPoints
            put distance(pPoints[i],pPoints[j]) into dist
            if dist < minDist then
                put dist into minDist
                put pPoints[i] & " " & pPoints[j] into closestPoints
            else if dist = minDist then
                put return & pPoints[i] & " " & pPoints[j] after closestPoints
            end if
        end repeat
    end repeat
    return closestPoints
end closestPoints

This function takes a list of points pPoints as input and uses nested repeat loops to compare the distance between each pair of points using the distance function. The variable minDist is initialized with one plus the distance between the first two points. It keeps track of the closest points using the variable closestPoints. The points in each pair are separated by space and the pairs of points are separated by return. It returns a list containing the closest points.

I can’t believe I didn’t notice this until now, but:

Functions in livecode start with “function” not “on” — please rewrite accordingly.

function closestPoints pPoints
    put 1 + distance(pPoints[1],pPoints[2]) into minDist
    put pPoints[1] & " " & pPoints[2] into closestPoints
    repeat with i = 1 to the number of elements in pPoints
        repeat with j = i + 1 to the number of elements in pPoints
            put distance(pPoints[i],pPoints[j]) into dist
            if dist < minDist then
                put dist into minDist
                put pPoints[i] & " " & pPoints[j] into closestPoints
            else if dist = minDist then
                put return & pPoints[i] & " " & pPoints[j] after closestPoints
            end if
        end repeat
    end repeat
    return closestPoints
end closestPoints

This function takes a list of points pPoints as input and uses nested repeat loops to compare the distance between each pair of points using the distance function. The variable minDist is initialized with one plus the distance between the first two points. It keeps track of the closest points using the variable closestPoints. The points in each pair are separated by space and the pairs of points are separated by return. It returns a list containing the closest points.

While a friend was reviewing this, they pointed out that LiveCode recently added the “infinity” constant. Of course ChatGPT is easily able to update to use it:

Please rewrite the “closestPoints” function to initiate minDist to infinity, which I just found out is a recent addition to LiveCode.

function closestPoints pPoints
    put infinity into minDist
    put pPoints[1] & " " & pPoints[2] into closestPoints
    repeat with i = 1 to the number of elements in pPoints
        repeat with j = i + 1 to the number of elements in pPoints
            put distance(pPoints[i],pPoints[j]) into dist
            if dist < minDist then
                put dist into minDist
                put pPoints[i] & " " & pPoints[j] into closestPoints
            else if dist = minDist then
                put return & pPoints[i] & " " & pPoints[j] after closestPoints
            end if
        end repeat
    end repeat
    return closestPoints
end closestPoints

This function takes a list of points pPoints as input and uses nested repeat loops to compare the distance between each pair of points using the distance function. The variable minDist is initialized with infinity. It keeps track of the closest points using the variable closestPoints. The points in each pair are separated by space and the pairs of points are separated by return. It returns a list containing the closest points.

7/10 for eventually getting there. Requiring the input in an array is understandable, since I didn’t specify one way or the other. The formatting issues are understandable: it was a complex problem, and once I worded it correctly ChatGPT had no problem with it. A point off for the side quest into Python.

Getting the Distance Function

ChatGPT seems to be assuming LiveCode has a built-in “distance” function – it doesn’t. So it needs to write a distance function.

Great! Now I need the function “distance”. It should take two points as arguments, and return the distance between them.

function distance p1, p2
    put sqrt((p2[1]-p1[1])^2 + (p2[2]-p1[2])^2) into d
    return d
end distance

This function takes two points p1 and p2 as input, assumed to be in the form of (x, y) tuples. It calculates the distance between them using the distance formula, which is the square root of the sum of the squares of the differences of the x and y coordinates. The result is stored in the variable d and returned.

I accepted the point list as an array, but LiveCode doesn’t have tuples (in the sense that other languages special-case them) and usually points are just a list of two items, like so: 123,19

Rewrite the function so instead of arrays the points are lists of two items.

function distance p1, p2
    put sqrt((p2[1]-p1[1])^2 + (p2[2]-p1[2])^2) into d
    return d
end distance

This function takes two points p1 and p2 as input, assumed to be in the form of lists of two items (x, y). It calculates the distance between them using the distance formula, which is the square root of the sum of the squares of the differences of the x and y coordinates. The result is stored in the variable d and returned.

Yep, no change. Trying again:

Lists in livecode are not arrays. They are comma-delimited. Rewrite the function to use items in a list instead of elements in an array.

function distance p1, p2
    put sqrt((item 2 of p2 - item 2 of p1)^2 + (item 1 of p2 - item 1 of p1)^2) into d
    return d
end distance

This function takes two points p1 and p2 as input, assumed to be in the form of lists of two items (x, y) comma delimited. It calculates the distance between them using the distance formula, which is the square root of the sum of the squares of the differences of the x and y coordinates. The result is stored in the variable d and returned.

9/10 for “distance”. ChatGPT had the right idea from the beginning, I just had to get it to use the standard LiveCode form for points. A point off for having to tell it twice.

I’m going to specify the whole interface, so:

Now write a “mouseUp” handler for a button that takes the contents of field “input”, splits the lines into an array, and uses closestPoints to find the closest points and put the answer into field “output”

on mouseUp
   put the text of field "input" into inputText
   put line 1 to -1 of inputText into inputList
   put closestPoints(inputList) into result
   put result into field "output"
end mouseUp

This script handle the mouseUp event of a button. It gets the text of the field “input” and stores it in the variable inputText. It takes the lines of the text and stores it in the variable inputList which is passed to the function closestPoints and the result is stored in the variable result. The variable result then is put into the field “output” so the user can see the closest points.

The “put line 1 to -1 of inputText into inputList” is either unnecessary validation, or not enough. Also a bit disappointing that, since ChatGPT wrote “closestPoints” to take an array, it didn’t convert the field contents into an array without me having to tell it to.

change the handler to convert the list to an array before handing it to closestPoints

on mouseUp
   put the text of field "input" into inputText
   put line 1 to -1 of inputText into inputList
   put inputList into anArray
   put closestPoints(anArray) into result
   put result into field "output"
end mouseUp

This script handle the mouseUp event of a button. It gets the text of the field “input” and stores it in the variable inputText. It takes the lines of the text and stores it in the variable inputList then it converts the list to an array and stores it in anArray which is passed to the function closestPoints and the result is stored in the variable result. The variable result then is put into the field “output” so the user can see the closest points.

Putting the field contents into a variable called “anArray” doesn’t automatically make it an array 😂 But it gives me the opportunity to try something, and for ChatGPT to be awesome. This is the full template for “split” from the LiveCode documentation:

The “split” command in livecode changes a list into an array. It looks like this: split variable {by | using | with} primaryDelimiter [and secondaryDelimiter]. Change the handler to use split to convert the list to an array before handing it to closestPoints.

on mouseUp
   put the text of field "input" into inputText
   put line 1 to -1 of inputText into inputList
   split inputList by return
   put it into anArray
   put closestPoints(anArray) into result
   put result into field "output"
end mouseUp

This script handle the mouseUp event of a button. It gets the text of the field “input” and stores it in the variable inputText. It takes the lines of the text and stores it in the variable inputList then it converts the list to an array using the split command, splitting the inputList by return and stores it in anArray which is passed to the function closestPoints and the result is stored in the variable result. The variable result then is put into the field “output” so the user can see the closest points.

I honestly didn’t expect that to work. ChatGPT took a template that included multiple choice entries and optional entries and from that perfectly used the “split” command. There’s still a flaw to correct though.

“result” is a reserved word in livecode. use a different variable name.

on mouseUp
   put the text of field "input" into inputText
   put line 1 to -1 of inputText into inputList
   split inputList by return
   put it into anArray
   put closestPoints(anArray) into closestPointsList
   put closestPointsList into field "output"
end mouseUp

This script handle the mouseUp event of a button. It gets the text of the field “input” and stores it in the variable inputText. It takes the lines of the text and stores it in the variable inputList then it converts the list to an array using the split command, splitting the inputList by return and stores it in anArray which is passed to the function closestPoints and the result is stored in the variable closestPointsList. The variable closestPointsList then is put into the field “output” so the user can see the closest points.

So close, but “split” doesn’t populate the “it” generic variable.

remove the references to anArray. Use inputList in the closestPoints function instead

on mouseUp
   put the text of field "input" into inputText
   put line 1 to -1 of inputText into inputList
   split inputList by return
   put closestPoints(inputList) into closestPointsList
   put closestPointsList into field "output"
end mouseUp

This script handle the mouseUp event of a button. It gets the text of the field “input” and stores it in the variable inputText. It takes the lines of the text and stores it in the variable inputList and then it converts the list to an array using the split command, splitting the inputList by return. The inputList is then passed to the function closestPoints and the result is stored in the variable closestPointsList. The variable closestPointsList then is put into the field “output” so the user can see the closest points.

And that works!

7/10 This shouldn’t have been this difficult to generate. It’s disappointing that ChatGPT didn’t remember that it built the “closestPoints” function to take an array and built the “mouseUp” handler to match. “it” is a thing in LiveCode (populated by statements like “get 2+3”) but the reference to it here was a mistake. A point off for having to tell ChatGPT how split works, but a bonus point for ChatGPT correctly interpreting a copy-paste template from the docs.

Conclusions

  1. Yes, ChatGPT can absolutely write code in LiveCode, a somewhat unusual language that is definitely under-represented in the training data.
  2. ChatGPT is somewhat hit-or-miss with syntax, and when it makes mistakes they can be hard to spot even when you know the language. 
  3. That said, actually attempting to run the code makes the syntax errors obvious.
  4. Logic errors can be harder to find.
  5. Just telling ChatGPT what needs to happen, without going into complete detail about how, will often result in (close to) working code.
  6. ChatGPT knows more than one way to get a task done, and often knows the trade-offs, e.g. in performance.
  7. ChatGPT understands the syntax of code, and can easily and correctly interpret modification directions based on references to elements of code.
  8. ChatGPT can correctly interpret multiple instructions in one expression.
  9. ChatGPT’s obsequiousness can be annoying. I don’t need to be told every time I make a correction that I’m right.

In short, it appears ChatGPT can be used to create significant code to perform real tasks in LiveCode. I’ll definitely try even more complex/real world scenarios as they come up.

Geoff CanyonGenerating LiveCode using ChatGPT

Related Posts

Take a look at these posts

6 comments

Join the conversation
  • Jim Lambert - February 20, 2023 reply

    jiml@netrin.com “Note that the language ‘livecode’ is not a widely used language, and i am not able to test the above code.”
    This implies that with some languages ChatGPT may actually test its suggested code before responding. Perhaps even refines it before answering. Wonder if that is so?

  • Bryan - February 21, 2023 reply

    Favorably, not doubt its also easy to plug in modules of script written in other languages to your liveCode projects as well, such as written in JavaScript which is probably well supported by ChatGPT. That is definitely a very powerful feature of liveCode development especially for web projects.

  • Torsten - May 15, 2023 reply

    As can be seen, chatGPT mixes the “knowledge” about different languages and gives a result that is most probable for all languages. Very often it “guesses” about commands and functions and took the names of other languages. So it is sometimes astonishing, how good the results are and sometimes the result is pure wrong or suboptimal.
    Up to now it is a very good help and faster than hours of stackoverflow, but you still need to know something in order to make the code correct. The vision of “just tell the system what you want and you will get it” is not yet there 😉

    Heather Laine - May 15, 2023 reply

    Right. But this is using generic, untrained chatGPT. We will be connecting using an API, and training chatGPT specifically on LiveCode. Better results can be expected.

  • Olaf - November 2, 2023 reply

    Heather, when will you have trained ChatGPT on LC?

    Heather Laine - November 2, 2023 reply

    🙂 its an ongoing process. We’re getting some pretty nice results internally now with LiveCode Create.

Join the conversation

*