Difference between revisions of "Programming by exploration guide"

From ChatMUD Wiki
Jump to: navigation, search
(Created page with "There are a number of moo programming tutorials out there. This one aims to be a bit different from them. For one thing, it is ChatMUD-tailored, which is important to note bec...")
 
Line 12: Line 12:
  
 
Output from the moo is proceeded by two less than signs. <<
 
Output from the moo is proceeded by two less than signs. <<
 +
 +
Execution tick counts are sometimes included as part of output. If you want to enable or disable them, use the command prog-o 3.
  
 
==It's Literally this simple: single-line evaluation of literal variables==
 
==It's Literally this simple: single-line evaluation of literal variables==
  
First let's evaluate some literal variables. The semicolon ; command begins a single-line  evaluation. This lets you run simple snipets of code without programming a whole verb and having to delete it. It's also really nice for testing what verbs do, as you'll get the return value right there.
+
First let's evaluate some literal variables. The semicolon ; command begins a single-line  evaluation. This lets you run simple snipets of code without programming a whole verb and having to delete it. It's also really nice for testing what verbs do, as you'll get the return value right there. The return value is the value immediately after the => in the output.
 
  <nowiki>
 
  <nowiki>
 
> ;5
 
> ;5
Line 90: Line 92:
 
> ;$string_utils:lowercase(35);
 
> ;$string_utils:lowercase(35);
 
</nowiki>
 
</nowiki>
 +
 +
==When 1 + 1 doesn't equal 2: type conversions and inheritance hierarchies==
 +
 +
If you worked through some of the stretch your thinking exercises, you may have realized that you can add strings together in order to make a much longer string. In programming jargon, this is called string concatenation. You will be using this technique often when you want to display messages to players. First of all, let's learn how to do a multiline evaluation, because this will let you do more complex exercises that involve more than single line of code to get the result. You have two options for multiline evaluation:
 +
* Send a line containing a single ; character. This will open a prompt where you can enter your entire multiline evaluation line by line. When you're finished, send a single . on its own line to get the result.
 +
* Do an  evaluation like normal, but with two semicolons at the beginning, and send all the text on a single line. This method is a bit more convenient, but a little harder to read because of how cramped it is.
 +
 +
In this guide we'll be using the former method, with a ; as the level 4 heading so you can just paste in the code to your own client and follow along without hassle. Let's do a multiline evaluation now.
 +
 +
====;====
 +
<nowiki>
 +
str1="I like ";
 +
str2="ChatMUD!";
 +
return str1+str2;
 +
.
 +
 +
<< => "I like ChatMUD"
 +
<< [used 1 tick, 0 seconds. Finished in 0 seconds.]
 +
</nowiki>
 +
 +
This is a pretty simple program on the surface level. There are still some important syntactical points to notice. Each line of instructions has a semicolon after it. Not including it is incorrect, and ChatMUD will not be happy with you if you neglect semicolons, so be sure to include them. We have created two temporary variables, str1 and str2, to store our strings. These temporary variables are destroyed after the function finishes its work. In this function, this is done from the statement beginning with the word return, and in this case, we want to return the value of str1 followed by the value of str2. You can also run a program without a return statement, in which case it will run to the end and then return 0.
 +
 +
Imagine you want to code a verb where food is being eaten. Let's evaluate some code to show a bite being taken out of the food, and return a string showing how many bites are left.
 +
 +
====;====
 +
<nowiki>
 +
bites=5;
 +
bites=bites-1;
 +
message="You take a bite out of the food, which now has "+bites+" bites left.";
 +
return message;
 +
.
 +
<< Evaluated input( on line 3): Type mismatch (expected string; got integer)
 +
<< Via eval()
 +
</nowiki>
 +
 +
First let's discuss what the programmer was intending. He first sets the value of the variable called bites to 5. If he were actually coding a verb for a food object, he would probably include the bites as a property on the object, but for the sake of example, he just writes the value 5 in. Then, he sets the value of bites to the value of bites minus 1, which in this case would be 4, indicating that there are only 4 bites left. In both cases, the = operator sets the variable or property on the left to whatever is returned from evaluating the right side. So since bites-1 is on the right, and bites was equal to 5, 5-1 = 4 and 4 is returned. 4 is then assigned as the value of bites. This right to left evaluation of the assignment operator is extremely important, as exercises increase in complexity. So remember it now.
 +
The next line is also an assignment line. On the right side of the equals sign, we see the programmer attempting to concatenate a string with a number. MOO does not like this, and it stops the entire program with an error. The variable message, on the left side of the assignment operator, doesn't get any data at all, even though the first part of the message, the string part, looks fine. Later we will cover how to let the program keep running instead of just stopping when an error like this occurs. The final line would return the message if the programmer manages to diagnose and fix the error, which is what we will do now.
 +
 +
Your error line number may be slightly different, but the important part is the type mismatch error. You get this because you tried to add a number to a string, and MOO isn't smart enough to know that you just want to insert the number there. You can convert the number into its string representation with the built-in function tostr(). Make it a habit to convert any numeric values into strings when you display them as part of strings. It'll save you a lot of headaches later.
 +
 +
====;====
 +
<nowiki>
 +
bites=5;
 +
bites=bites-1;
 +
message="You take a bite out of the food, which now has "+tostr(bites)+" bites left.";
 +
return message;
 +
.
 +
<< => "you take a bite out of the food, which has 4 bites left."
 +
<< [used 2 ticks, 0 seconds. Finished in 0 seconds.]
 +
</nowiki>
 +
 +
You will come to know what operators can be used with which types as you continue to program more. For example, many of the functions in $math_utils require the float type, which is simply a number with a decimal point. Yes, 5 and 5.0 are very different in moo. You can't even add those two numbers together without getting a type mismatch error!
 +
 +
If you want to convert something to an int, use the toint() function. To convert to a float, use the tofloat() function. You will be using these a lot, so remember them! There is another universal conversion function called toliteral(), which converts whatever you send in to a string that you could send back to the evaluator in order to get the original value back. For example, the toliteral function puts quotes around strings whereas the tostr function does not.
 +
 +
There are a few other types that you will learn about later, including lists, maps, and errors. For now, just know that they exist, and that you can convert them all to literals with the toliteral() function.
 +
 +
Let's practice some type conversions, and notice some curious things as we do it!
 +
 +
<nowiki>
 +
> tostr(25)
 +
<< => "25"
 +
> ;"The sum of 5 and 8 is "+tostr(5+8)
 +
<< => "The sum of 5 and 8 is 13"
 +
> ;5.0+5.0+5.0
 +
<< =>15.0;
 +
> ; 32.0/4.0
 +
<< => 8.0
 +
> ;31.0/4.0
 +
<< => 7.75
 +
> ;tostr(#2)
 +
<< => "#2"
 +
; > toint(#5);
 +
<< => 5
 +
</nowiki>
 +
 +
Nothing should be overly surprising with these demos. You will notice that when you divide two floats, you get the full decimal value of the answer. Another thing to note: when you convert an object to a string, the # sign before the object number is preserved. But when you convert it to an integer or floating point, it goes away.
 +
 +
Now, let's discuss some more about objects. You may have heard about the player tell verb. To see it in action, let's send ourself a message.
 +
 +
<nowiki>
 +
> ;me:tell("I am legion");
 +
<< I am legion
 +
<< => 0
 +
<< [used 676 ticks, 0 seconds. Finished in 0 seconds.]
 +
</nowiki>
 +
 +
The word "me" in the evaluation is what is known as an automatic variable. It references the player who is using that particular connection, and only works in evaluations. If you were to simply evaluate the word me, it would return your player object. Thus, me:tell calls the tell verb on your player object, which then sends you the message. The tell verb always returns 0. There are several other automatic variables that you'll learn about later.
 +
 +
Your player has verbs? Indeed it does, even though you haven't programmed them. They are inherited from your parent objects, and this is what is known as an inheritance hierarchy. To get the inheritance hierarchy of an object, use the @parents command. For example, @parents me will give you the inheritance hierarchy of yourself. You will see about a half dozen different objects, that all contribute verbs to your player. The :tell verb is part of the $root_class object, which you could find out by typing @verbs $root_class and perusing that dauntingly long list of verbs. So any object that is a child of $root_class has a tell verb inherited. Feel free to experiment with some other verbs in this inheritance hierarchy.
 +
 +
===Caution===
 +
 +
If you extrapolate a little, you will probably realize that other players have a :tell verb just like you. You can use this verb to tell them anything you want at any time. Don't abuse this! If your victim can't figure out who's sending the messages, they will probably ask a wizard to investigate, and this sort of anonymous harassment is against the rules.
 +
 +
===Stretch your Thinking===
 +
 +
# Write a single-line evaluation that uses the player:tell verb to tell yourself the result of 17 to the power of 10. Do not use the multiplication operator. The evaluation should return 0. Hint: You may want to peruse the functions in $math_utils to help you.
 +
# Write a single-line evaluation that would let you find the name of the object that was the sum of the string utilities and math utilities objects, which are #20 and #26 respectively. Your evaluation should return the reference to the object.
 +
# Critical thinking: Integer division does not seem all that useful because it just drops the entire decimal value of the division operation. When might this property have actual utility?

Revision as of 01:25, 5 September 2020

There are a number of moo programming tutorials out there. This one aims to be a bit different from them. For one thing, it is ChatMUD-tailored, which is important to note because ChatMUD's codebase is a fair bit expanded from the one on lamda MOO. For another thing, this tutorial isn't supposed to be easy, and it won't teach you how to make an interesting object by the end. Instead, it will hopefully furnish you with knowledge of many basic concepts, a toolbox to help you remember common coding patterns, and exercises to help satiate your curiosity.

If you have questions, feel free to ask on the dev channel. There is usually at least one or two people awake who can help you puzzle something out. Also see the end of this document for links to some of the other moo programming tutorials previously mentioned, which apply many of the same concepts discussed.

Some Basic Assumptions

We assume that you have created your ChatMUD character and gotten a programmer bit. This can be done by connecting to the progbits channel and sending the message "progbit" to it. We also assume that you know how to create and destroy objects, and that you know how to examine objects to see the possible verb commands you can use with them that way. Also, hopefully you are working on a computer, because programming on a mobile interface is usually quite laborious.

Conventions

If there is a single-line command for you type into the mud, it is proceeded by a > sign. Verb programs start with a level 4 heading which contains the @program line that begins the programming of the verb, and ends with a single . You can copy and paste the text, including the beginning and ending line, to send all of this code to the mud, and it should work as a valid program.

Output from the moo is proceeded by two less than signs. <<

Execution tick counts are sometimes included as part of output. If you want to enable or disable them, use the command prog-o 3.

It's Literally this simple: single-line evaluation of literal variables

First let's evaluate some literal variables. The semicolon ; command begins a single-line evaluation. This lets you run simple snipets of code without programming a whole verb and having to delete it. It's also really nice for testing what verbs do, as you'll get the return value right there. The return value is the value immediately after the => in the output.

> ;5
<< => 5
<< [used -2 ticks, 0 seconds. Finished in 0 seconds.]

> 5+7
<< => 12
<< [used -2 ticks, 0 seconds. Finished in 0 seconds.]

> ;"hello world"
<< => "hello world"
<< [used -2 ticks, 0 seconds. Finished in 0 seconds.]


Nothing too weird here yet. Notice chatMUD adds 5+7 and instnatly returns 12. Also, notice the double quotes around hello world. Double quotes indicate a string. You must have double quotes around a string, because otherwise ChatMUD doesn't know it's a string. Try evaluating hello world without double quotes around it, to see what error you get. Be very mindful of where your quotation marks are! This is one of the most common pitfalls for any coder, even experienced ones.

>#2
<< => #2  (Sinistral)
<< [used -2 ticks, 0 seconds. Finished in 0 seconds.]

A number sign # followed by an object number will return that object, and ChatMUD is nice enough to tell you what its name is as well. Try evaluating random object numbers to see what you can find. Every time someone uses the @create command to make something, they make a new object, and it gets an object number. Every room, exit, and new player created also takes up an object number. To see the highest object number currently in use, run this command:

> ;max_object();
<< => #21183  ()
<< [used -1 ticks, 0 seconds. Finished in 0 seconds.]

The number will probably be different at any given time as objects are created and destroyed. The evaluation you just saw is a built-in function. ChatMUD has a number of built-in functions that make up the very lowest level and usually have easier ways of doing the same functionality built on top of them. These easier ways have the jargon word of wrappers. You will not be using many of them. For a full list, along with thousands of words of programming information that you probably don't care about, check out the full lamda moo + stunt programmers' manual.

Now let's evaluate some verbs. First, let's make the moo shout at us, by capitalizing any text we send over.

> ;$string_utils:uppercase("I like ChatMUD!");
<< => "I LIKE CHATMUD!"
<< [used 168 ticks, 0 seconds. Finished in 0 seconds.]

Remember, the string you send in must have quotes around it! Now, let's make ChatMUD calculate the greatest common divisor between 24 and 30, which is a problem you may remember from math class at school.

> ;$math_utils:gcd(30,24);
<< => 6
<< [used 21 ticks, 0 seconds. Finished in 0 seconds.]

Pretty neat, huh? These examples might seem contrived. How would anyone know what math_utils was, or that the gcd function was available? The first thing to know is that when you see a colon : symbol, and it's not part of a string, the thing on the left of the colon is an object, and the thing on the right is a verb. The verb is part of the object. Think of the object as a box, with verbs that work like levers to make things happen.

Try evaluating $math_utils now, and you will see that this evaluation is perfectly valid and will return object #20. $math_utils is just a convenient mnemonic to reference object #20, and this mnemonic is what is known as being corrified. Similarly, you can evaluate $string_utils. What object number do you get?

Note that these objects aren't supposed to be tangible things that you can pick up and manipulate, like the objects you may have been filling your area with. They are more of an organizational scheme to group similar sets of functions together. The math_utils object has a number of math algorithms, and the $string_utils object has many verbs to help you manipulate strings. Meanwhile the $object_utils object has a number of useful verbs for probing at the functionality of other objects. To get a full list of all of the utilities packages, type @kids $generic_utils. This command will show you all of the descendants of the generic utilities package. You can even make your own utilities package if you hvae ideas for useful functions for others to try; just make an object that is based off the $generic_utils package and go from there.

Let's explore what is available on the math and the string utilities objects. To do so, type the following:

>help $math_utils

You will get a long list of verbs that are available. Most of them provide adequate help information about how to use them. Type help $math_utils:verbname to see help for that particular verb. Now you can do the same for string utilities, and most of the other utilities packages.

Unfortunately, these help files are not automatically updated, so if a wizard adds a new verb to a utilities package, the help information is generally not updated to reflect the change. You can get an exhaustive list of verbs on a utility package by typing: @verbs <object>. Do this now to the string utilities object, and you will see several new verbs that were not listed in the help file. Try getting help on these new verbs. The results are mixed, but you will often be rewarded with information. If you are not, complain on the dev channel.

Stretch your thinking

The stretch your thinking sections contain some exploration questions to help you solidify what you learned, and stretch your thinking. There are no right or wrong answers here. But feel free to try things out while struggling with them. ChatMUD is built with security in mind, meaning it's very difficult to do permanent damage.

  1. Every object might have verbs on it, not just the utils packages. Use the @verbs command to probe some of the items in the player creations database to see what verbs they have.
  2. Can you add or subtract other kinds of literal values besides numbers? Try adding and subtracting strings. Try adding or subtracting objects.
  3. The multiplication, division, and modulus operators are *, /, and %, respectivly. These operators only work on numbers. They don't work on strings or objects. However, you might notice something weird with the division operator. What happens if you evaluate 32/4? What about 33/4? 31/4? Make a conjecture about why your results come up this way, and try some other division evaluations to see if it checks out.
  4. What happens if you send the wrong type of value to a verb? Well let's find out! Evaluate:
> ;$string_utils:lowercase(35);

When 1 + 1 doesn't equal 2: type conversions and inheritance hierarchies

If you worked through some of the stretch your thinking exercises, you may have realized that you can add strings together in order to make a much longer string. In programming jargon, this is called string concatenation. You will be using this technique often when you want to display messages to players. First of all, let's learn how to do a multiline evaluation, because this will let you do more complex exercises that involve more than single line of code to get the result. You have two options for multiline evaluation:

  • Send a line containing a single ; character. This will open a prompt where you can enter your entire multiline evaluation line by line. When you're finished, send a single . on its own line to get the result.
  • Do an evaluation like normal, but with two semicolons at the beginning, and send all the text on a single line. This method is a bit more convenient, but a little harder to read because of how cramped it is.

In this guide we'll be using the former method, with a ; as the level 4 heading so you can just paste in the code to your own client and follow along without hassle. Let's do a multiline evaluation now.

;

str1="I like ";
str2="ChatMUD!";
return str1+str2;
.

<< => "I like ChatMUD"
<< [used 1 tick, 0 seconds. Finished in 0 seconds.]

This is a pretty simple program on the surface level. There are still some important syntactical points to notice. Each line of instructions has a semicolon after it. Not including it is incorrect, and ChatMUD will not be happy with you if you neglect semicolons, so be sure to include them. We have created two temporary variables, str1 and str2, to store our strings. These temporary variables are destroyed after the function finishes its work. In this function, this is done from the statement beginning with the word return, and in this case, we want to return the value of str1 followed by the value of str2. You can also run a program without a return statement, in which case it will run to the end and then return 0.

Imagine you want to code a verb where food is being eaten. Let's evaluate some code to show a bite being taken out of the food, and return a string showing how many bites are left.

;

bites=5;
bites=bites-1;
message="You take a bite out of the food, which now has "+bites+" bites left.";
return message;
.
<< Evaluated input( on line 3): Type mismatch (expected string; got integer)
<< Via eval()

First let's discuss what the programmer was intending. He first sets the value of the variable called bites to 5. If he were actually coding a verb for a food object, he would probably include the bites as a property on the object, but for the sake of example, he just writes the value 5 in. Then, he sets the value of bites to the value of bites minus 1, which in this case would be 4, indicating that there are only 4 bites left. In both cases, the = operator sets the variable or property on the left to whatever is returned from evaluating the right side. So since bites-1 is on the right, and bites was equal to 5, 5-1 = 4 and 4 is returned. 4 is then assigned as the value of bites. This right to left evaluation of the assignment operator is extremely important, as exercises increase in complexity. So remember it now. The next line is also an assignment line. On the right side of the equals sign, we see the programmer attempting to concatenate a string with a number. MOO does not like this, and it stops the entire program with an error. The variable message, on the left side of the assignment operator, doesn't get any data at all, even though the first part of the message, the string part, looks fine. Later we will cover how to let the program keep running instead of just stopping when an error like this occurs. The final line would return the message if the programmer manages to diagnose and fix the error, which is what we will do now.

Your error line number may be slightly different, but the important part is the type mismatch error. You get this because you tried to add a number to a string, and MOO isn't smart enough to know that you just want to insert the number there. You can convert the number into its string representation with the built-in function tostr(). Make it a habit to convert any numeric values into strings when you display them as part of strings. It'll save you a lot of headaches later.

;

bites=5;
bites=bites-1;
message="You take a bite out of the food, which now has "+tostr(bites)+" bites left.";
return message;
.
<< => "you take a bite out of the food, which has 4 bites left."
<< [used 2 ticks, 0 seconds. Finished in 0 seconds.]

You will come to know what operators can be used with which types as you continue to program more. For example, many of the functions in $math_utils require the float type, which is simply a number with a decimal point. Yes, 5 and 5.0 are very different in moo. You can't even add those two numbers together without getting a type mismatch error!

If you want to convert something to an int, use the toint() function. To convert to a float, use the tofloat() function. You will be using these a lot, so remember them! There is another universal conversion function called toliteral(), which converts whatever you send in to a string that you could send back to the evaluator in order to get the original value back. For example, the toliteral function puts quotes around strings whereas the tostr function does not.

There are a few other types that you will learn about later, including lists, maps, and errors. For now, just know that they exist, and that you can convert them all to literals with the toliteral() function.

Let's practice some type conversions, and notice some curious things as we do it!

> tostr(25)
<< => "25"
> ;"The sum of 5 and 8 is "+tostr(5+8)
<< => "The sum of 5 and 8 is 13"
> ;5.0+5.0+5.0
<< =>15.0;
> ; 32.0/4.0
<< => 8.0
> ;31.0/4.0
<< => 7.75
> ;tostr(#2)
<< => "#2"
; > toint(#5);
<< => 5

Nothing should be overly surprising with these demos. You will notice that when you divide two floats, you get the full decimal value of the answer. Another thing to note: when you convert an object to a string, the # sign before the object number is preserved. But when you convert it to an integer or floating point, it goes away.

Now, let's discuss some more about objects. You may have heard about the player tell verb. To see it in action, let's send ourself a message.

> ;me:tell("I am legion");
<< I am legion
<< => 0
<< [used 676 ticks, 0 seconds. Finished in 0 seconds.]

The word "me" in the evaluation is what is known as an automatic variable. It references the player who is using that particular connection, and only works in evaluations. If you were to simply evaluate the word me, it would return your player object. Thus, me:tell calls the tell verb on your player object, which then sends you the message. The tell verb always returns 0. There are several other automatic variables that you'll learn about later.

Your player has verbs? Indeed it does, even though you haven't programmed them. They are inherited from your parent objects, and this is what is known as an inheritance hierarchy. To get the inheritance hierarchy of an object, use the @parents command. For example, @parents me will give you the inheritance hierarchy of yourself. You will see about a half dozen different objects, that all contribute verbs to your player. The :tell verb is part of the $root_class object, which you could find out by typing @verbs $root_class and perusing that dauntingly long list of verbs. So any object that is a child of $root_class has a tell verb inherited. Feel free to experiment with some other verbs in this inheritance hierarchy.

Caution

If you extrapolate a little, you will probably realize that other players have a :tell verb just like you. You can use this verb to tell them anything you want at any time. Don't abuse this! If your victim can't figure out who's sending the messages, they will probably ask a wizard to investigate, and this sort of anonymous harassment is against the rules.

Stretch your Thinking

  1. Write a single-line evaluation that uses the player:tell verb to tell yourself the result of 17 to the power of 10. Do not use the multiplication operator. The evaluation should return 0. Hint: You may want to peruse the functions in $math_utils to help you.
  2. Write a single-line evaluation that would let you find the name of the object that was the sum of the string utilities and math utilities objects, which are #20 and #26 respectively. Your evaluation should return the reference to the object.
  3. Critical thinking: Integer division does not seem all that useful because it just drops the entire decimal value of the division operation. When might this property have actual utility?