 |
WolfScript Lite: a short tutorial
By Q'willard
(Published with permission)
At the beginning of the help file for WolfScript, the author writes:
"We apologize, that this introduction is terribly bad. Please look at the example bots to learn WolfScript Lite better."
And, well, they're right. It's terrible. If you didn't know what you were looking at to start with, you'd be utterly lost. That's probably why you're looking for a tutorial.
What is WolfScript Lite
Let's not be shy about it. WolfScript Lite (is there a WolfScript Heavy? Could be, but I've not found it) is a programming language. Scripting languages, and there are a lot of them, are simple, sometimes simplified programming languages. So, let's start with a warning:
Warning:
If you want nothing to do with programming, proceed no further.
OK, the warning out of the way, let me emphasize that scripting languages are simplified programming languages, generally geared for a specific purpose. You may have heard of Perl, or JavaScript, or VBScript, which are all scripting languages that are simplified ways of expressing sequences of actions, including some controlling logic, for telling a computer what you want it to do. WolfScript is another one of these, specifically for the purpose of interfacing with FurBot and making it a bit more powerful and flexible.
As a language (albeit a programming language), it has both a vocabulary and a syntax. The vocabulary is the words that can be used to make up expressions in the language (the words in its dictionary, so to speak), and the syntax is the rules governing how the vocabulary can be legally strung together to make sense. (Making sense is called semantics, but we won't say more about that.)
Actually, it's not this neat, since various items of vocabulary (such as the specific procedures and functions) carry their own syntactical requirements. So, we'll kinda munge them together.
The help file for WolfScript Lite actually tells you the vocabulary and the syntax, pretty much (there's one known important error which I'll tell you about later). There's also more there than I'll cover in this tutorial, namely all the database stuff. That actually makes FurBot and WolfScript interesting, but will be left for a later time.
WolfScript General Syntax
The syntax is really quite simple, except that each piece of the vocabulary has special rules. In general, though, a statement in a script starts at the left margin and continues to the end of the line.
A line starts with either a procedure, a comment, or a control clause word. There is no ending punctuation for statements; they just end at the end of a line, and the next line begins the next statement.
A function cannot start a line, but must occur within the context of a procedure. You'll see this later.
That's all I'm going to say about syntax, at least for now. You'll see how this works out as we move through the vocabulary.
WolfScript Vocabulary
The WolfScript vocabulary consists of variables, constants, procedures, and words indicating the beginning and end of control clauses (think of them as prepositions).
Variables and Constants
Variables and constants are names of things. There are really only two kinds of constants: numbers and strings.
Numbers are simply the natural numbers (or the natural numbers plus 0, if you're a purist): 0, 1, 2, 3, 4, … (WolfScript might allow for decimal fractions, like 1.3, and such can be created, but you probably won't need them, so let's pretend they're not there, at least for now.)
Strings are sequences of characters. If there are any spaces in the string, the string must be enclosed in braces ( {} ), like: {This is a string of characters with spaces.} If there are no spaces (like if it's a single word), the braces can be omitted, at least sometimes, but it's better not to; just always use the braces.
Variables are holders for strings or numbers. Variables are useful because they are a name for something that remains consistent, even though its contents may change. For example, in a game you might want to refer to player1, even though the furre that is player1 may change with each round of the game. In FurBot, variables are enclosed in square brackets, like [player1]. In WolfScript, they are prefixed with a pound sign (#), like #player1.
Variables must both be defined and have their value set. To define a variable, use the &newvar procedure, for example:
&newvar #player1
To set the value of a variable, use the &set procedure:
&set #player1 {AshtarRose}
This assigns the string of characters that is Ashtar's name to the variable #player1.
As a convenience, you can assign a value to a variable at the same time you define it, by including the value on the line:
&newvar #player1 {AshtarRose}
Once the variable has a value, that value can change as often as is necessary, but the variable remains the same.
Variables must be defined in each script; they do not carry over between scripts. You can, however, retrieve the value from a FurBot variable and store it in a WolfScript variable. To do this, use the &getvar function. This looks a little strange, but it is used like this:
&set #player1 &getvar({[player]})
The [player] is just a FurBot variable, any FurBot variable. Around that are the braces, marking that as a string, which is what it is in WolfScript. The parentheses are required because of WolfScript function syntax, which we'll get to shortly.
(If you want to load the value of a WolfScript variable back into a FurBot variable, use &setvar procedure: &setvar {[playerName]} {Felorin} )
Notice that there are no spaces in the &getvar phrase. There could be if there were spaces in the FurBot variable name, but then only in the name. There cannot be a space, for example, between &getvar and the first parenthesis. WolfScript is very unforgiving about extra spaces, so be careful.
Except for operations on variables, that's about all there is. We'll see how variables and constants are used as we discuss procedures and functions.
Comments
When writing sections of WolfScript, it can be useful to "comment your code." Comments can explain what the script does, can explain what variables are expected to hold, and so on. Put comments in freely. The syntax is easy:
// comment
The double-slash (//) must be at the beginning of a line, and the whole line becomes a comment. This means that you cannot put a comment after a function or procedure, but it must be on a line all by itself. Easy.
Procedures
Procedures are operations that produce an effect. We've already seen two, &newvar and &set, which create a new variable and set a variables value, respectively. Others you will use frequently are &whisper and &say, which have the effect of sending a whisper or saying something in Furcadia.
Procedure names start with an ampersand (&), followed by the name of the procedure (no spaces in the name or between the ampersand and the name.
Following a procedure name may be (and usually are) one or more an arguments, each separated from the procedure name or from each other by a single space. Again, WolfScript is very unforgiving about extra spaces, so one only. (If there are two, WolfScript assumes that the argument that should have been after the first is empty, and you will get messed up results.)
Procedures that take a single argument usually take a string argument. For example, to say something you use the &say procedure followed by a string:
&say {I just want to say that it is a
pleasure to know you.}
(Editor's note: That command must be written in a single line!)
This does the typical Furcadia thing of saying "I just want to say that it is a pleasure to know you."
A one-argument procedure that takes a number is &sleep, which causes FurBot to kill time for some number of seconds:
&sleep 5
This is useful for forcing a delay before going on to the next action. You do need to be careful with it, though, because FurBot stops all processing while sleeping. You can't even do operations in the FurBot console during sleep.
A 2-argument procedure that you will use is &whisper. The first argument must be the name of a furre, and the second argument is what you will whisper, so it will be a string. Typically, the furre name will come in from a FurBot variable, such as [player], so you will have something like this:
&newvar #thePlayer &getvar({[player]})
&whisper #thePlayer {Thank you for joining
the game.}
(Editor's note: &whisper command must be written in a single line!)
As in &whisper, several procedures take a furre name. If you want to specify the furre name, rather than rely on the value of a variable, the rules are the same as in Furcadia. In particular, to specify a multi-word furre name, use | between the words where a space would normally be. So, Lady Damina would be written Lady|Damina.
That's enough to give you the idea for now.
Functions
Functions are operations that return a value.
Function names start with an ampersand (&), followed by the name of the procedure, and then followed by arguments enclosed in parentheses. (No spaces in the name, between the ampersand and the name, or before the first parenthesis).
There must always be a pair of parentheses in a function statement, even though sometimes there are no arguments. For example, there are a few date and time functions that return a number, such as:
&minute()
which returns the number of the minute, 0-59. You can use these for various purposes, open to your imagination.
Functions can never stand-alone. There is, after all, no point in returning a value unless the script does something with it. Minimally, you might assign the returned value to a variable, storing it for later use:
&set #currentMinute &minute()
We'll look at functions more closely while solving some standard problems.
Control Clauses
Without some sort of control, the procedures in a script would simply be executed one after another, starting at the top of the script until it ran out of instructions. This makes for a pretty stupid script, really. The power of a scripting language really comes in with the ability to control which procedures are executed and when. Controlling the execution of procedures puts real logic into your script. This is done using control clauses (or control structures).
There are two kinds of control clause: IF clauses and FOR clauses
If Clauses
"If" clauses provide for conditionally executing procedures. The clause tells WolfScript to test to see if some condition holds and, if it does, then execute a collection of procedures. If the condition does not hold, those procedures are skipped.
This is done by placing the procedures between a pair of keywords: either &ifn … &endif if the conditional test is on a number, or &ifs … &endif if the conditional test is on a string. The conditional test has a result of either "true" or "false." If true, then the statements following the &ifn or &ifs up to the &endif are all executed. If the result of the test is false, then all the statements to the &endif are skipped, and processing continues from there.
The test is specified like this. Following the &ifn or &ifs is a variable, which must hold a number for &ifn or a string for &ifs. Then comes a test operator, which I'll describe in a minute. Finally is a value (constant, variable, or function) to test against.
IMPORTANT: The first argument must ALWAYS be a VARIABLE.
For example:
&ifn #someNumber > 5
tests to see if a number stored in #someNumber is greater than 5. If it is, then the statements following the &ifn are executed.
For numeric conditionals, the test operators available are:
| = | equal to |
| ! | not equal to |
| <> | not equal to |
| < | less than |
| > | greater than |
| <= | less than or equal to |
| >= | greater than or equal to |
String conditionals work based on whether two strings are the same (equal) or not. For example, in PSR_Bot, I use a string conditional in tallying results, depending on whether a player's response is "paper", "scissors", or "rock". So, part of it looks like:
&ifs #p1choice = {paper}
For string conditionals, the only test operators are:
| = | same |
| ! | different |
| <> | different |
Following the condition line you can have any number of other statements, including additional If clauses.
At the end of the list of statements, put the &endif statement. This tells WolfScript that this is the end of the conditional processing. Each &ifn or &ifs must have a matching &endif. Without it, statements just keep being processed until there are no more, which is usually a bad thing.
Here're a few lines out of PSR showing nested conditionals:
&ifs #p1c = {paper}
&ifs #p2c = {rock}
&say {Paper covers rock.}
&set #announcement #p1
&join #announcement { wins.}
&say #announcement
&endif
&ifs #p2c = {scissors}
&say {Scissors cut paper}
&set #announcement #p2
&join #announcement { wins.}
&say #announcement
&endif
&endif
Notice that I indent the statements within a conditional. This helps make it obvious where a conditional begins and ends. Each &endif is indented the same as its matching &ifs, again to make it easy to see which beginnings and endings match up. (You can probably guess what my variables stand for, but I'm not going to tell you.)
For Clauses
A FOR clause repeats a set of actions some number of times and then stops. It does this by specifying a start number (in a variable), and an ending number (in another variable). Each time it executes the actions, the start number is increased by 1 until it equals the ending number. When the two are equal, that's the last time the actions are performed.
The clause begins with an &for statement, followed by a variable with a value assignment, the word "to", and another variable. Then there is a series of statements to perform each time through. Finally, to show where the clause ends, is the &endfor statement.
More precisely, the structure is:
&for #variable1=0 to #variable2
actions
&endfor
Look at the first argument here: #variable1=0. You have to be careful with this. Notice especially that there are no spaces on either side of the equal sign. The first argument variable has to have its value assigned to it like this. You can start counting at 0, 1, 2, or 10000, but you have to set the start value here.
The second variable argument will have its value assigned somewhere else, before you get to the &for statement. It's value doesn't change as the clause is looped through time after time, like the first variable's value is.
Each time through the loop, the first variable's value is increased (stepped) by 1. This cannot be changed in WolfScript, as it can in other environments. That's ok, you can live with it (you may need to do a little arithmetic).
So, if you want to use a loop to count down to the launch of a star ship to escape Furcadia, you might try:
&newvar #countDown
&newvar #maxCount 10
&for #counter=0 to #maxCount
&set #countDown #maxCount
&sub #countDown #counter
&say #countDown
&sleep 1
&endfor
Note that the actions are always executed at least once, even if the first variable is already equal to or greater than the second variable. So, set up your loops with this in mind. If it's a problem, you may need to put the loop inside a conditional, which will allow you to skip the loop entirely if conditions aren't right.
Putting it together
Here are some more extended examples of how to use some of WolfScript's procedures and functions.
Working with Strings
String manipulation functions can be really useful in parsing strings that come in from Furcadia, and for building reply strings. For example, you might want to repeat the part of a string that follows a specific word in the string. Roseate and Bubbles, for example, accept multi-word descriptions, so they need to repeat the description. But that means finding a specific position in a string and capturing the next one or more words.
Just for the sake of demonstration, suppose we have a string {one two three four five}, and we only want to repeat the part of the string after "two" and before "five." First, we put the string in a variable and then look in the string for an occurrence of "two":
&newvar #myString {one two three four five}
&newvar #startPosition
&instr(1 #myString {two})
(Editor's note: &instr(1 #myString {two}) belongs to the previous line, and must be written in a single line in the code!)
The function &instr takes three arguments. The first one is the position in the string where we want to start looking. We want to start at the beginning, so we start at position 1. The second argument is the string we're looking in, and the third is the string we're looking for. (Since two is a single word string, we could have omitted the braces, but I recommended against that earlier, just for clarity.) This returns a number, in this case 5, which is the place in the string where "two" starts, and stores it in #startPosition.
What we want in #startPosition, though, is the position of the next word, which is the position of "two" plus 4 (three for "two" and one for the space). To do this, use the &add procedure, which changes the value of a variable:
&add &startPosition 4
This will make the new value of &startPosition to be 9, which is the starting position of "three," the first word after "two." (Note: In most programming languages, arithmetic operations like this are functions that return a value that we then assign to a variable. WolfScript doesn't work this way, but changes the value right in the variable being operated on.)
Now we want the ending position, or the position where "five" starts. We can get that in the same way:
&newvar #endPosition
&instr(1 #myString {five})
(Editor's note: &instr(1 #myString {five}) belongs to the previous line, and must be written in a single line in the code!)
The position is 20, which is assigned to #endPosition. What's nice about handling the numbers this way is that we, the programmers, don't ever need to know what the position is. The script deals with the details.
To get the part of the string, WolfScript as a &mid() function, which returns part of a string starting at a position and continuing on for some length, or number of characters. Unfortunately, it doesn't take an ending position, so we need to do a little arithmetic. We know the length is less than 20, and in fact less by the start position. So, let's define another variable to hold the length and do the arithmetic:
&newvar #length 20
&sub #length #startPosition
This will give us the length up to the start of "five." Notice, though, that this will include the space following "four." If we don't want to include that space we need to adjust down by one:
&sub #length 1
Now we can use just the part of the string we want, perhaps to say something in Furcadia:
&say &mid(#myString #startPosition #length)
The result should be that your furre will say "three four".
Reference
Here's a shortened, but enhanced, version of the WolfScript Procedures and Functions summaries, hopefully a little more intelligible. Items in square brackets ([]) are optional. I've omitted the database commands and a couple other advanced commands to make it a little less overwhelming. Plus, I've not figured them all out. (Someday, perhaps.)
Procedures
&newvar #varname [contents]
Creates a new variable #varname. The contents may be left blank or specified:
&newvar #player
&newvar #player {sanctimonious}
&set #varname contents
Sets the contents of variable #varname to be contents:
&set #player {kiric}
&whisper name text
Whispers the text to the furre name. The text is a string, so use {} around it, especially if it has spaces. For multi-word furre names, use the | instead of spaces.
&say text
Says the specified text. The text is a string, so use {} around it.
&do text
Does the specified text (a string).
&desc text
Changes the bot's description to the specified text.
&disconnect
Disconnects the bot from Furcadia.
&eject name
Ejects furre name, if the bot is the dream owner or has control shared with it.
&emit text
Emits the text, if the bot is the dream owner or has control shared with it.
&look name
Looks at furre name. This can be used to load values into the [desc], [color], and [descname] keywords in FurBot. These keywords currently need to be used in a separate command, since they are not updated in time to be used immediately. Even calling a procedure isn't enough, unfortunately. Some experimentation will be necessary to get them to work properly.
&l name
Same as &look
&roll rollstr
Rolls dices as specified in the rollstr and reports the total. The rollstring is in the form #1d#2, where #1 is the number of dice and #2 is the number of faces on each die. E.g., &roll 2d6 returns a total in the range 1 - 12.
&rollSEP rollstr
Same and &roll, but says "ROLL" instead.
&movebot text
Moves the bot. Text is a string consisting of move commands (7, 9, 1, 3). E.g., &movebot {77993311} makes the bot run in a circle.
| 1, 3, 7, 9 | Moves the bot around |
| <, > | Turns the bot around |
| u | Uses item (CTRL-U) |
| g | Gets item (CTRL-G) |
| s | Returns to Vinca (CTRL-S) |
| l | Lays down |
&join #variable string
Appends string to #variable in string form. If #variable is a number, it is handled as a string, so if #variable = 12 and the string is {34}, &join makes #variable to be {1234}
&add #variable value
Increases the value of #variable by adding value to it.
&sub #variable value
Decreases the value of #variable by subtracting value from it.
&div #variable value
Changes the value of #variable by dividing it by value.
&mul #variable value
Changes the value of #variable by multiplying it by value.
&sleep seconds
Waits the number of seconds specified. FurBot is totally idle during sleep.
&setvar varnamestr contents
Sets the value of the FurBot variable in varnamestr. The syntax is a bit strained, but, for the FurBot variable [playerName], "&setvar {[playerName]} Felorin" sets the value of [playerName] for use outside WolfScript.
Functions
&getvar(varnamestr)
Returns the value of the FurBot variable in varnamestr. The syntax is a bit strained, but, for the FurBot variable [player], &getvar({[player]}) returns the value of [player] for use in WolfScript.
&time()
Returns the time in format hh:mm:ss AM/PM, e.g. 2:44:05 PM
&date()
Returns the date in format mm/dd/yy, e.g., 7/4/00.
&hour()
Returns the hour number, on a 24-hour clock. E.g., 2:00 PM is hour 14.
&minute()
Returns the minute in the hour (0-59), e.g., 44.
&month()
Returns the number of the month, e.g., July is 7.
&mday()
Returns the number of the day, e.g., 4
&year()
Returns the current year number, e.g., 2000
&shortyear()
Returns the last two digits of the current year, e.g., 00 for 2000.
&wdayn()
Returns the number of the day in the week, starting with Monday. E.g., Tuesday is 2.
&wday()
Returns the name of the day of the week, e.g., Tuesday (Bug: Furbot thinks 7/4/00 is a Wednesday.)
&rand(maxvalue)
Returns a random number in a decimal expansion, e.g., 0.9250904 or 4.304218E-02. (maxvalue seems to be optional at this time, but leaving it empty appears to produce more repeat values. At any rate, it does not specify a maximum value.)
&left(string length)
Returns a string of length characters, starting at the left (first character) of string. E.g., &left({This is only a test} 3) returns "Thi".
&right(string length)
Returns a string of length characters, starting at the right (last character) of string. E.g., &right({This is only a test} 3) returns "est".
&mid(string startposition length)
Returns a string of length characters, starting at the startposition character, of string. E.g., &mid({This is only a test} 3 5) returns "is is".
&mid2(string startposition)
Returns a string, starting at the startposition character of string, to the end. E.g., &mid2({This is only a test} 3) returns "is is only a test".
&instr(startposition string1 string2)
Checks whether string2 occurs in string1, starting the search at startposition. If the string is found, it returns the position in the string where it is found. Returns zero if the string is not found. E.g., &instr(1 {This is only a test} {is}) returns 3. (Note: the WolfScript help had commas in there. That's an error.)
&ucase(string)
Returns the string with all characters in uppercase.
&lcase(string)
Returns the string with all characters in lowercase.
&len(string)
Returns the length of the string.
Back to FurBot Online
|