Programming by exploration guide

From ChatMUD Wiki
Revision as of 15:26, 5 September 2020 by Athlon (talk | contribs)
Jump to: navigation, search

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. Multi-line programs are block-quoted. 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 expected.

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 #26. $math_utils is just a convenient mnemonic to reference object #26, 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-related 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 have 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 the beginning of the program 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 now 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! To convert a value into an object, use the toobj() function. For anything besides integer and floating point numbers, you get the system object, otherwise you get the object equivalent of the integer value. 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
> toobj(20)
<< => #20  (string utilities)

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 can be used in evaluation code, not standard verb code. 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, so you will most likely be reprimanded.

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 wizard utilities and math utilities objects, which are #24 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?
  4. In many other programming languages, adding 3 and 5.0 would not give an error, but would quietly convert the value 3 to 3.0 before running the calculation. MOO is not designed to do these implicit type conversions. Imagine you would like to add this functionality to moo, and develop a list of rules denoting how the moo should implicitly convert values.

What really are verbs, properties, and abstraction? Beginning Object Design

When talking about objects, you will hear the terms "verbs" and "properties" very often. Academically, you know that verbs are actual programs, and properties are simply values stored on the object. Conceptually this might not really be clicking so let's think about how we would make something really simple, a virtual flower. We will do this by discussing some examples of how other objecst are made. You will begin the flower's design during this discussion and think through it more deeply in the exercises.

But before we start coding, it's important to think about what we actually want out of this virtual flower. For the sake of the example, we just want to be able to look at the flower. The flower color should be able to be changed with a property, because the only other way to change it is by modifying the code, and that's a lot more trouble and less reliable. In real life, a flower is not simply some object with a color that one looks at. Actual flowers are infinitely more complex, with various pollination methods, petals that can move depending on ambient sunlight, and photosynthesis abilities. This is why our virtual flower is an abstraction from the real flower: these other concepts are not important to us at this time.

The movement from the actual object to its abstraction presents a question: how much detail do you want your object to have? To see the very differing answers to this question, you can peruse the verbs and properties on various objects in the player creations database, with the @verbs <object> and @props <object> commands.

For example, let's start with the %guitar object. There is a single property, playing, and a single verb, play. To explore the play verb of the guitar to see how it works, use the command

> @list %guitar:play without numbers

This lists all the lines of code in the play verb of the guitar object. The "without numbers", which can be shortened to "wo n", suppresses the showing of line numbers for this listing. The code for this particular object is pretty self explanatory. There is a check to the .playing property so that you must wait for the guitar to finish playing before playing it a second time. If the guitar was not already being played, the program continues, with a couple unchanging messages before it's finally finished. The suspend() built-in function pauses execution of the entire program for the specified number of seconds. This description of how the play verb does leave out some details, but as you work through the guide you will probably understand it better.

Now let's look at another object, the %firework. Before you list its code, you may want to read the help file to see how the programmer intends you to use the firework. To do so, type help %firework. On the surface this seems pretty simple to use. Just drop and light it and boom! If we look at the code, we will see that the programmer put a good deal of thought in to the functionality of the object.

First let's start with the properties. There are quite a few of them. The properties ending in _msg are messages, which you may remember from working through the building guide. The @messages <object> command simply grabs all the properties ending in _msg that the object and all its parents have, and displays them after stripping off the _msg from each name. Try evaluating some of these properties. For example,

> ;%firework.types
<< => {"brocade", "cake", "chrysanthemum", "Crossette", "Dahlia", "falling leaves", "Farfalle", "Roman Candle", "palm tree", "pistil", "Salute", "spider", "strobe", "waterfall", "Tourbillion", "flower", "badger", "panda", "dog", "cat", "dragon", "hedge hog"}
<< [used -1 ticks, 0 seconds. Finished in 0 seconds.]

Notice how we evaluate properties: the object, followed by a . followed by the property name. Also notice the result of this particular evaluation. It looks like a number of strings separated by commas and surrounded by braces. The two braces represent a list. A list is another type of value in moo, similar to how integers, floats, strings, and objects are types. A list can contain 0 or more items, and the items are separated by commas. Lists can contain any type of item, including other lists. Evaluate some more of the firework's properties to familiarize yourself with doing it. Check out the verbs on the firework at your leisure, and you should be able to follow the code pretty well. Start with the light verb. You will see that the light verb calls the this:explode() verb.

The word this is another automatic variable referring to the object that is running the verb code, which in this case is the generic firework. Whenever someone makes their own firework, the value of this changes to be the firework they created, not the generic one. Always remember that functionality! This:moveto(#-1) simply moves the object to #-1, which simply means that the object is in the moo's concept of the void.

Since the code in %firework:light calls this:explode, you will probably want to take a look at the %firework:explode verb to see what happens. You will see several calls to this:outdoor_print(), and feel free to stop and peruse that verb. If you were to test the actual firework, you could infer that the outdoor_print verb is probably sending messages to nearby people who are outdoors. Don't be afraid to test objects to see what they do! Be inquisitive, because this is how you can figure out how things work. After the firework is finished exploding, it is returned to its owner's inventory (this:moveto(this.owner)).

Now, finally, we can begin to set up our virtual flower. Hopefully, as you have been reading about how these other objects work, you have been thinking about your flower as well. Besides a color, maybe you want to enhance it further. In fact, hopefully you do!

>@create $thing called a flower
<< You now have a flower (aka flower) with object number #21177 and parent generic thing (#5).
> @prop flower.color blue
<< Property added with value "blue".

Of course, your messages may differ slightly. The @create command should be familiar to you, and we inherit from the generic thing because it's simple. We then add a property called color on to the flower, and assign the default value of blue to it. Remember, the word blue is a string, so it must be quoted. We can easily change this later. Nothing too difficult here yet.

Now let's talk verbs. This is where people's eyes begin to glaze over, if they haven't already. Remember that the flower inherits all verbs and properties from the generic thing, and that you can use the @parents command to view the inheritance hierarchy. There is a verb on the root class called description() that is used whenever you look at an object. This description verb simply returns the description pproperty of the object, which you can change with the @describe command, and you can see this by listing the verb. It's so simple you might even glance right over it. But let's reprogram this verb for our flower so it returns something else.

> @verb flower:description this none this
<< Verb added (1).

This command creates a verb on the flower called description. When another program calls the :description verb on the flower, it will run this verb, not the description verb from the root class, because it's farther down the inheritance hierarchy. The phrase "this none this" after the verb name indicates the way that other players could call this verb. We'll talk about verb arguments in depth in a later section, but for now, know that this none this means that other players cannot actually use this verb. If you use the examine command to examine the flower, the description verb does not appear in the list of obvious verbs. Now let's program this verb.

@program flower:description

return "It is a pretty "+this.color+" flower.";
.

Now, look at the flower. You will see the generic description, "You see nothing special" has been replaced with the result of the verb we just entered. Pretty neat! Now, assign a different color to the flower using a single-line evaluation. Does the description change?

Stretch your thinking

  1. add some more description of the flower to it with the use of more properties. Does the flower have multiple colors? A certain aroma?
  2. List two or three other things you want your flower to do. Remember, they don't have to necessarily be limited to the abilities of a real-life flower, which are honestly limited. Do you want it to be able to move around the moo? No problem, just note it down now so it stays in your mind. Anything is possible in moo programming, even walking flowers!
  3. Evaluate a list containing the days of the week. There is a list utilities package called $list_utils. Try experimenting with some of the verbs on this package, by using your days of the week list. Of particular note are the reverse, randomly_permute, random_element, and make verbs. You will be using these utilities often!
  4. You can extract a single element of a list by using what is known as the index operator which is []. The first list item has an index of 1, and the indexes increase by 1 all the way up to the last item. The last item can also be referenced with the $ sign. To evaluate a list item, use the [] syntax, such as:
    > ;{2,4,6,8}[1]
    << =>2
    This creates a list of four even numbers, and extracts the first item of that list. IN this case, the value 2 would be returned. Work more with the index operator by extracting the final element of the list. The built-in function connected_players() returns a list of everyone currently connected, with the most recent connection as the first element. Which player was the 10th most recently connected? Which player has been connected the longest?
  5. Critical thinking: Why does the moo look for verbs on the lowest child in the object hierarchy first, then move up to the parent? What would happen if it searched for verb code starting with the parent first and moving down to the lowest child instead?
  6. What would you do if an object becomes so complex that there are too many properties and verbs to easily think about in your mind?