background begin bg_mode bitand bitor bitshift bitxor blessed? blessprop break breakpoints
fabs fail fg_mode findnext firstdescr flag? flags float float? floor fmod fmtstring for force force_level foreach foreground fork frand ftostr ftostrc
if ignore_add ignore_del ignoring? inf instances instr instring int int? interp intostr is_set? ispid? itoc
jmp
kill
lastdescr ldup libraries listeners loc localvar location lock? locked? log log10 loop-example1 loop-example2 loop-example3 loops lreverse lvar
match max_variable_count mcp_bind mcp_register mcp_register_event mcp_send mcp_supports me midstr mlevel mode modf movepennies moveto mucker levels multitasking
name name-ok? newexit newobject newpassword newplayer newprogram newroom next nextdescr nextentrance nextowned nextprop not notify notify_except notify_exclude number?
objmem odrop ofail ok? online online_array or osucc over owner
queue
random read read_wants_blanks recycle reflist_add reflist_del reflist_find regexp regsub remove_prop repeat reverse rinstr rinstring rmatch room? rot rotate round rsplit
tan testlock textattr then thing? time timefmt timer_start timer_stop timesplit timestamps toadplayer tokensplit tolower toupper tread trig trigger truename try
unblessprop uncompile unparselock unparseobj until userlog
var var! variable version
watchpid while wizcall
xor xyz_to_polar
arrive compilation connect flags libraries listeners mucker levels multitasking
There are now four levels of MUCKERs in fb4.0. Level zero is a non-mucker. They cannot use the editor, and MUF programs owned by them cannot be run.
Level one MUCKER's are apprentices. Their powers are restricted as they cannot get information about any object that is not in the same room they are. ie: OWNER, NAME, LOCATION, etc all fail if the object isn't in the same room as the player. Level one MUCKER programs always run as if they are set SETUID. NOTIFY, NOTIFY_EXCEPT, and NOTIFY_EXCLUDE will refuse to send messages to rooms the user is not in. Level one programs cannot use ADDPENNIES. Level one programs don't list DARK objects or rooms in the contents of a room, unless they are controlled by the program owner. Additionally, level one programs have an absolute instruction limit that is the same size as the PREEMPT instruction limit. This is usually around 20,000 instructions.
Level two MUCKERs are also called Journeymen. Their permissions are equivalent to the permissions for a normal MUCKER under older versions of the server. Level two programs can run as many as four times the number of instructions that a preempt program could. This is usually around 80,000 instructions.
Level three MUCKERs are referred to as Masters. They can use the con- nection info primitives (ie: CONDBREF, ONLINE, etc.), read the EXITS list of any room, use NEXTPROP on objects, can use NEWROOM, NEWOBJECT, NEWEXIT, and COPYOBJ without limitations, can use QUEUE and KILL, and can override the permissions restrictions of MOVETO. You only give a player MUCKER level 3 if they are very trusted. There is no absolute instruction count limit for level three or above, except for programs running in PREEMPT mode.
A player who is wizbitted is effectively Mucker Level 4. MUCKER level four is required for the RECYCLE primitive, the CONHOST primitive, the FORCE primitive, and the SETOWN primitive. ML4 also allows overriding of permissions of the SET* primitives, and property permissions. Props not listed by NEXTPROP with ML3 are listed with ML4. Programs running ML4 do not even have instruction limits on PREEMPT mode programs.
The MUCKER level permissions that a program runs at is the lesser of it's own MUCKER level and the MUCKER level of it's owner. If it is owned by a player who is MUCKER level 2, and it is MUCKER level 3, then it runs at Mucker level 2. The one exception to this is programs owned by a Wizard player. They run at Mucker level 2 if the program itself is not wizbit, and at Mucker level 4 if the program IS set wizbit.
Mucker level is referred to in flags lists by M# where the # is the Mucker level. Level zero objects don't show a flag for it. Example:
Revar(#37PM3)
In verbose flags lists, Mucker levels greater than zero are shown by MUCKER# where # is the mucker level.
To set a level on a player or program, use the level number as the flag name. MUCKER is the same as 2, and !MUCKER is the same as 0. Example:
@set Revar=2
A player may set the MUCKER level on a program they own to any level lower than or equal to their own level, and a wizard may set a program or player to any MUCKER level. When a program is created, it is automatically set to the same MUCKER level as the creating player. When a program is loaded from an old db, if it is Mucker Level 0, it is upgraded to Mucker Level 2.
Programs run by @locks, @descs, @succs, @fails, and @drops default to the preempt mode when they run. Programs run by actions linked to them default to running in foreground mode. QUEUEd program events, such as those set up by _listen, _connect, _disconnect, etc, and those QUEUEd by other programs default to running in background mode. (NOTE: these programs cannot be changed out of background mode)
1) Create a program with several useful generic subroutines. 2) DOCUMENT those subroutines in a commented out header in the prog. 3) @set <program>=_docs:<command to list those DOCS you made> 4) Set the version number of the code in the library with $version 1.000 This lets you keep track of the code revision installed, separately from the version of the library calling API. 5) Set the library API version number with $lib-version 1.000 This is the version of the calling interface for this library. Remember to up this version when you change this library's calls. Remember that 1.19 is less than 1.2, so use numbers like 1.002. 6) For each function you want to be externally callable, do the following: a) Declare it PUBLIC b) Create the caller macro with either: $libdef FUNCNAME or $pubdef EXPORTEDNAME "$lib/THISLIB" match "FUNCNAME" call 7) Make sure the program is set LINK_OK and VIEWABLE. 8) Globally register the program with the @register command with a prefix of "lib/". ie: @reg lib-strings.muf=lib/strings 9) You're done!Currently standard libraries:
$lib/strings Functions for manipulating strings. $lib/props Routines for searching for properties, or setting them. $lib/lmgr Standard list manager routines. $lib/stackrng Routines to handle variable sized ranges on the stack. $lib/edit String range editing and manipulation routines. $lib/editor Standard user text editor. $lib/mesg Standard message manager routines. $lib/mesgbox Routines for handling lists of messages. $lib/match Object or string matching routines. $lib/reflist Dbref-list management routines. $lib/index Hashed linked list manager with partial key matching. $lib/gui Routines to make MCP's GUI package easier to use. $lib/optionsgui Given datums, auto-generates a GUI to edit them.
$ifdef __fuzzball__ me @ "This is a FuzzBall Muck server.!" notify $endif
$ifdef __muckname=FurryMUCK me @ "Helloooooo Furry!" notify $endif
$author Revar Desmera<EMAIL_ADDRESS>denote multiple authors, separate them with commas. ie:
$author Revar Desmera<EMAIL_ADDRESS>Foxen<EMAIL_ADDRESS>you don't want to include your email address, just omit it. ie:
$author Revar, Foxen
/_defs/desc: "_/de" getpropstr /_defs/setpropstr: dup if 0 addprop else pop remove_prop then /_defs/setpropval: dup if "" swap addprop else pop remove_prop then /_defs/setprop: dup int? if setpropval else setpropstr thenthen if a program contained '$include #345' in it, then all subsequent references to 'desc', 'setpropstr', 'setpropval', and 'setprop' would be expanded to the string values of their respective programs. For example, 'desc' would be replaced throughout the program with '"_/de" getpropstr' NOTE: You cannot have a slash in a definition name. ie: The property
_defs/a/b:foowill NOT make a definition named a/b.
$pubdef : Clears the _defs/ propdir on the program. $pubdef <defname> Clears the _defs/<defname> prop on the prog. $pubdef <defname> <rest_of_line> Sets _defs/<defname> prop to <rest_of_line>. $pubdef \<defname> <rest_of_line> Sets _def/<defname> if not already set.For example:
$pubdef tell me @ swap notifywould put a property named '_defs/tell' on the program object, with the value 'me @ swap notify'. Def names cannot have ':' nor '/' in them.
$libdef myfuncwould put a prop named '_defs/myfunc' on the program object, with the value '<this_programs_dbref> "myfunc" call'
DEFNAME Is true if DEFNAME was $defined earlier, whatever the value. DEFNAME=VALUE Is true if DEFNAME exists and its value equals VALUE. DEFNAME>VALUE Is true if DEFNAME exists and is greater than VALUE. DEFNAME<VALUE Is true if DEFNAME exists and is less than VALUE.
The semantics of greater than and less than is the same as that used by the strcmp string comparison command. There must be no spaces between the DEFNAME, comparator, and VALUE. They must be run together as one word.
There is no >= or <= comparator available, so to make such a comparison you need to use $ifndef with the opposite comparator. Ie, to check if FOO is greater than or equal to 3, use:
$ifndef FOO<3 BAR $enddefCompiler directives are nestable also. For examples:
$ifdef __version>Muck2.2fb3.5 $def envprop .envprop $endif $define ploc $ifdef proplocs .proploc $else owner $endif $enddef
DEFNAME Is true if DEFNAME was $defined earlier, whatever the value. DEFNAME=VALUE Is true if DEFNAME exists and its value equals VALUE. DEFNAME>VALUE Is true if DEFNAME exists and is greater than VALUE. DEFNAME<VALUE Is true if DEFNAME exists and is less than VALUE.The semantics of greater than and less than is the same as that used by the strcmp string comparison command. There must be no spaces between the DEFNAME, comparator, and VALUE. They must be run together as one word.
The <truebranch> will be compiled if the given program's $version is greater than or equal to the given <float>, using a floating point number comparison. Otherwise, the <falsebranch>, if given, will be compiled.
The <truebranch> will be compiled if the given program's $version is less than the given <float>, using a floating point number comparison. Otherwise, the <falsebranch>, if given, will be compiled.
The <truebranch> will be compiled if the given program's $lib-version is greater than or equal to the given <float>, using a floating point number comparison. Otherwise, the <falsebranch>, if given, will be compiled.
The <truebranch> will be compiled if the given program's $lib-version is less than the given <float>, using a floating point number comparison. Otherwise, the <falsebranch>, if given, will be compiled.
The <truebranch> will be compiled if the given object exists and is a program. Otherwise, the <falsebranch>, if given, will be compiled.
The <truebranch> will be compiled if the given object does not exist, or if it is not a program. Otherwise, the <falsebranch>, if given, will be compiled.
If the given program is given as 'this', then this will check if the given callable function was declared earlier in this compiling program.
The <truebranch> will be compiled if the given program exists, is a program, and has a given callable function (declared with PUBLIC or WIZCALL) that this compiling program has permission to call. Otherwise, the <falsebranch>, if given, will be compiled.
If the given program is given as 'this', then this will check if the given callable function was declared earlier in this compiling program.
The <truebranch> will be compiled if the given program does not exist, is not a program, or does not have a given callable function (declared with PUBLIC or WIZCALL) that this compiling program has permission to call. Otherwise, the <falsebranch>, if given, will be compiled.
This specifies that comments are in the old style that is terminated by the first end-paren ')' in the comment. COMMENT_RECURSE
This specifies that comments are in the new recursive style where any parens '(' in a comment must be matched by end-parens ')'. COMMENT_LOOSE
This is the default comment style, where the compiler tries to compile comments as if in comment_recurse mode, but will fall back to comment_strict mode if a comment fails to have balancing parens.
$define addprop over over or if \addprop else pop pop remove_prop $enddefso that all the 'addprop's in the program will be expanded to the definition, but the 'addprop' in the definition will not try to expand recursively. It will call the actual addprop.
depth dup dupn ldup lreverse over pick pop popn put reverse rot rotate swap { }
"a" "b" "c" "d" 4 rotatewould leave
"b" "c" "d" "a"on the stack.
"a" "b" "c" "d" -4 rotatewould leave
"d" "a" "b" "c" on the stack.
"a" "b" "c" "d" "e" 3 putwould return on the stack:
"a" "e" "c" "d"
"a" "b" "c" "d" "e" 4 reversewould return on the stack:
"a" "e" "d" "c" "b"
"a" "b" "c" "d" "e" 4 lreversewould return on the stack:
"a" "e" "d" "c" "b" 4
! @ command loc localvar lvar me trigger var var! variable
"me" match me !The ME global variable is the same variable as '0 variable'
"me" match location loc !The LOC global variable is the same variable as '1 variable'
trig trigger !The TRIGGER global variable is the same variable as '2 variable' The value of TRIGGER will be #-1 for AUTOSTART programs,
When used outside of a function, the compiler allows the use of <name> as a global variable in all functions defined after the var declaration. This variable dataspace is shared between ALL muf programs called in this process. This means that if program A declares a global variable, then calls program B, and program B also declares a global variable, then changes its value, then the global variable declared in program A will also have been changed. For this reason, this usage of VAR is deprecated and shoud NOT be used. Use LVAR instead. This usage is only kept around for backwards compatability with some old icky programs that used this feature to pass data between some programs.
: myfunction "Hello World!" var! foo me @ foo @ notify ;
me @and
0 variable @will do the same thing (returning the user's dbref). User-defined variables are numbered sequentially starting at 4 by the compiler. Note that these variable numbers can be used even if variables have not been formally declared. This command has been deprecated, and is not recommended to use. It's original purpose was to allow MUF to do small arrays. Since MUF now has an intrinsic array stack item type, this should no longer be used.
< <= = > >= address? and array? dbref? dictionary? float? int? lock? not or string? xor
Integer 0 Float 0.0 DBRef #-1 String ""
Integer 0 Float 0.0 DBRef #-1 String ""
Integer 0 Float 0.0 DBRef #-1 String ""
Integer 0 Float 0.0 DBRef #-1 String ""
' begin break call cancall? continue else execute exit for foreach if interp jmp loop-example1 loop-example2 loop-example3 loops public repeat then until while wizcall
: countforever ( i -- ) 1 + dup intostr .tell 'countforever jmp ;A better ways to do the same thing with looping primitives would be:
: countforever ( i -- ) begin 1 + dup intostr .tell repeat ;
Loops, TRY-CATCH-ENDCATCH's, and IF-ELSE-THEN's can all be nested in each other as much as you want.
Within a loop, even within IF-ELSE-THEN structures within the loop structure, you can place WHILE, CONTINUE, or BREAK statements. There is no limit as to how many, or in what combinations these instructions are used.
A WHILE statement checks to see if the top value on the stack is false. If it is, then execution breaks out of the innermost loop and resumes after the matching REPEAT or UNTIL statement.
The CONTINUE statement causes the loop to jump back to the beginning of its next iteration, after the BEGIN, FOR, or FOREACH.
The BREAK statement forces execution to break out of the innermost loop, resuming after the matching REPEAT or UNTIL.
Note: You can nest loops complexly, but WHILE, BREAK, and CONTINUE statements only refer to the innermost loop structure.
0 begin 1 + me @ over intostr notify dup 10 = untilHow to count from 1 to 10 using a FOR-REPEAT loop:
1 10 1 for intostr me @ swap notify repeat
1 5 1 for "" swap 1 -1 for intostr strcat repeat me @ swap notify repeatExample of a complex (if pointless) FOR loop:
10 -10 -2 for me @ over intostr notify dup -5 > while dup 0 = if pop continue then dup -3 = if pop break then not untilExample of a FOREACH loop:
{ "index1" "value1" "index2" "value2" "index3" "value3" }dict foreach " = " swap strcat strcat me @ swap notify repeat
#888 "functionname" CALL
#888 "functionname" CALL
notify notify_except notify_exclude read read_wants_blanks tread userlog
#0 #1 #23 #7 3 "Hi!" notify_exclude would send "Hi!" to everyone in room #0 except for players (or objects) #1, #7, and #23. _listener's will not be triggered by a notify_exclude if the program they would run is the same as the current program running.
"__tread" timer_start { "TIMER.__tread" "READ" }list event_waitfor
swap pop "READ" strcmp if "" 0 else read 1 "__tread" timer_stop then
% * + ++ - -- / abs bitand bitor bitshift bitxor getseed int random setseed sign srand
float1 @ float2 @ - fabs epsilon < if "Equivalent" then
When you are working with numbers that have exponent parts that may not be near e+00, you should do a relative comparison instead:
float1 @ float2 @ - float1 @ / fabs epsilon < if "Equivalent" then
DIV_ZERO - (0) Division by zero attempted. NAN - (1) Result was not a number. IMAGINARY - (2) Result would be imaginary. FBOUNDS - (3) Floating-point inputs were out of range. IBOUNDS - (4) Calculation resulted in an integer overflow or underflow.
"Foobar" 3 strcut returns
"Foo" "bar" If i is zero or greater than the length of s, returns a null string in the first or second position, respectively.
"testing" 2 3 midstr will return the value "est".
"Hello world" " " explodewill result in
"world" "Hello" 2on the stack. (Note that if you read these items off in order, they will come out "Hello" first, then "world".) For TinyMUCK 2.2, s2 may be any length. But "" (null string) is not an acceptable string for parameter s2.
"alpha, beta, gamma" ", " explode_arraywill result in a list array that is identical to:
{ "alpha" "beta" "gamma" }lists2 cannot be a null string, but it can be of any non-zero length. When s2 is more than one character long, the delimiter is considered to be the entire s2 string. The delimiters are removed from the results.
"HEY_YOU_THIS_IS" " " "_" substresults in
"HEY YOU THIS IS"s2 and s3 may be of any length.
The first format substitution in the format string will use the topmost stack value. The next format substitution will use the next item down the stack, and so on.
The start of a format substitution in the string is noted by a '%'. If a literal '%' is needed in the string, a '%%' may be used. The format of a substitution is as follows: '%[-,|][+, ][0][number][.number]type' Where 'number' is an integer value, and 'type' is one of the following identifiers:
i - integer argument s - string argument d - dbref number, in the form of #123 D - dbref name reference; given a dbref, will print the associated name for that object - terminates on bad reference l - pretty-lock, given a lock, will print the description f - float in xxx.yyy form e - float in x.yyEzz form g - shorter of forms e or f ~ - default representation of any stack type ? - unknown type argument, will print a string stating what the variable type is
A '-' at the start of a format substitution indicates the field will be left justified. A '|' at the start indicated the field will be centered. A '+' forces the + sign to appear for positive numbers. A space forces a blank in front of positive numbers. (This is the default.) A leading 0 will force the field to be padded on the left with 0's instead of spaces. If you use a '*' in place of either the width or precision format fields, then that integer number will be obtained from the stack. You may specify a negative fieldwidth to obtain left justification of that field. You may not specify a negative precision.
Unlike FMTSTRING, the format string needs to have key markers to specify which key of each input dictionary to use to get the data for each format field. You specify the key markers in each format subsititution by adding a '[KEY]' before the substitution type letter. For example, the code:
{ { "username" "Johnny" "count" 4 "object" #18 4 pi }dict { "username" "Ghaladahsk_Fadja" "count" 123 "object" #97 }dict }list "%-16.15[username]s %3[count]i %5[object]d %4.2[4]g" array_formatstrings { me @ }list array_notify... would show the following output to the user:
Johnny 4 #18 3.14 Ghaladahsk_Fadj 123 #97 0.00
Note that if a key does not exist in an input dictionary, then that format field will be assumed to either be 0, #-1, or a null string, as apropriate for the format type.
Numeric keys can be referred to as well as string keys. This also means that you may use a list of list arrays as input, instead of a list of dictionaries.
See FMTSTRING for a full description of normal substitution format codes. Note that unlike FMTSTRING, the format strings cannot use calculated field widths or precisions via '*'.
Hint: If you want to use this format method on a single dictionary, you can do something like:
pid getpidinfo (to give us a dictionary of data.) 1 array_make (to make a list with that as its single entry) "%-40[player]D %6[pid]i %[called_prog]D" array_fmtstrings 0 [] (to get the formatted result string.)
me @ "%N has lost %p marbles." pronoun_subwould return:
"Igor has lost his marbles."if the player's name was Igor and his sex were male. d does not have to be a player for the substitutions to work.
The substitutions are:
%a/%A for absolute possessive (his/hers/its, His/Hers/Its) %s/%S for subjective pronouns (he/she/it, He/She/It) %o/%O for objective pronouns (him/her/it, Him/Her/It) %p/%P for possessive pronouns (his/her/its, His/Her/Its) %r/%R for reflexive pronouns (himself/herself/itself, Himself/Herself/Itself) %n/%N for the player's name.
if it comes across a %X substitution, where X is any character not listed in the above substitutions table, it will search down the environment tree from d to try to find the appropriate %X property for use in substitution.
reset bold dim uline flash reverse black red yellow green cyan blue magenta white bg_black bg_red bg_yellow bg_green bg_cyan bg_blue bg_magenta bg_white
* A '?' matches any single character.
* A '*' matches any number of any characters.
* '{word1|word2|etc}' will match a single word, if it is one of those
given, separated by | characters, between the {}s. A word ends with
a space or at the end of the string. The given example would match
either the words "word1", "word2", or "etc".
{} word patterns will only match complete words: "{foo}*" and "{foo}p"
do not match "foop" and "*{foo}" and "p{foo}" do not match "pfoo".
{} word patterns can be easily meaningless; they will match nothing
if they:
(a) contains spaces,
(b) do not follow a wildcard, space or beginning of string,
(c) are not followed by a wildcard, space or end of string.
* If the first char of a {} word set is a '^', then it will match a single
word if it is NOT one of those contained within the {}s. Example:
'{^Foxen|Fiera}' will match any single word EXCEPT for Foxen or Fiera.
* '[aeiou]' will match a single character as long as it is one of those
contained between the []s. In this case, it matches any vowel.
* If the first char of a [] char set is a '^', then it will match a single
character if it is NOT one of those contained within the []s. Example:
'[^aeiou]' will match any single character EXCEPT for a vowel.
* If a [] char set contains two characters separated by a '-', then it will
match any single character that is between those two given characters.
Example: '[a-z0-9_]' would match any single character between 'a' and
'z', inclusive, any character between '0' and '9', inclusive, or a '_'.
* The '\' character will disable the special meaning of the character that
follows it, matching it literally.
Example patterns:
"d*g" matches "dg", "dog", "doog", "dorfg", etc.
"d?g" matches "dog", "dig" and "dug" but not "dg" or "drug".
"M[rs]." matches "Mr." and "Ms."
"M[a-z]" matches "Ma", "Mb", etc.
"[^a-z]" matches anything but an alphabetical character.
"{Moira|Chupchup}*" matches "Moira snores" and "Chupchup arghs."
"{Moira|Chupchup}*" does NOT match "Moira' snores".
"{Foxen|Lynx|Fier[ao]} *t[iy]ckle*\?" Will match any string starting
with 'Foxen', 'Lynx', 'Fiera', or 'Fiero', that contains either 'tickle'
or 'tyckle' and ends with a '?'.
"exit" or "e" "muf" or "program" or "f"
"player" or "p" "thing" or "t"
"room" or "r" Type's case is ignored.
$def REG_ICASE 1 (Case insensitive match)
$def REG_EXTENDED 4 (Allows PCRE comments, etc. See PCRE docs.)
$def REG_ICASE 1 (Case insensitive match)
$def REG_ALL 2 (Substitute all matches, rather than just the first)
$def REG_EXTENDED 4 (Allows PCRE comments, etc. See PCRE docs.) Ie: to replace all words in a string with 'yadda', ignoring case, you would use the following code:
"[a-z]+" "yadda" REG_ICASE REG_ALL + REGSUB
"ab//cd/'efg'hi//jk'lm" "'" "/" TOKENSPLIT returns the values:
"ab/cd'efg" "hi//jk'lm" "'"
getlockstr locked? parselock prettylock setlockstr testlock unparselock
{ "one" 2 "three" 3.14159 }list "... " array_joinwill result in a single string: "one... 2... three... 3.14159"
{ #1 " " 2 " three " 3.14159 }list array_joinwill result in a single string like: "Wizard 2 three 3.14159"
arr1 @ { "foo" 2 "bar" }list array_nested_getis roughly equivalent to
arr1 @ "foo" [] dup if 2 [] dup if "bar" [] then then
{ }dict "qux" swap { "foo" "bar" "baz" }list array_nested_set 4 swap { "foo" 2 "clam" }list array_nested_setwould return the same array as
{ "foo" { "bar" { "baz" "qux" }dict 2 { "clam" 4 }dict }dict }dict
{ "foo" { "bar" { "baz" "qux" }dict 2 { "clam" 4 }dict }dict }dict { "foo" 2 }list array_nested_delwould return the same array as
{ "foo" { "bar" { "baz" "qux" }dict }dict }dict
"index0" "value0" "index1" "value1" 2
"index0" "index1" 2
"value0" "value1" 2
The SortType argument is an integer, and its default value of 0 means that the sorting should be case sensitive and in ascending order. You can change either of those by using one or more of the following inserver $defines BITORed or added together:
$def SORTTYPE_CASEINSENS 1 (Sort is to be case insensitive.)
$def SORTTYPE_DESCENDING 2 (Sort is to be in reversed order.)
$def SORTTYPE_SHUFFLE 4 (Randomize list completely.)
The above sort types can be added or bitor'ed together to get the following inserver $defines as listed below.
$def SORTTYPE_CASE_ASCEND 0 (Case sensitive and ascending order.)
$def SORTTYPE_NOCASE_ASCEND SORTTYPE_CASEINSENS
$def SORTTYPE_CASE_DESCEND SORTTYPE_DESCENDING
$def SORTTYPE_NOCASE_DESCEND SORTTYPE_CASEINSENS SORTTYPE_DESCENDING +
{
{ "name" "One" "num" 1 }dict
{ "name" "Two" "num" 2 }dict
{ "name" "Three" "num" 3 }dict
}list
SORTTYPE_DESCENDING "num" ARRAY_SORT_INDEXED ...would return an array of dictionaries, sorted in descending order by the value of their "num" entries. ie, the same as:
{
{ "name" "Three" "num" 3 }dict
{ "name" "Two" "num" 2 }dict
{ "name" "One" "num" 1 }dict
}list
This can be used with an array of list arrays, just like with an array of dictionaries. For list arrays, you just use an integer for the index. NOTE: arrays that don't have an item matching the given index, will be sorted as lesser than arrays that do. See ARRAY_SORT for more information on sort ordering.
{ #5 #10 #15 #10 #20 }list #10 array_findvalwill return a list containing 1 and 3, being the keys (indexes) of the matching items.
{ #5 #10 #15 #10 #20 }list #10 array_excludevalwill return a list containing 0, 2 and 4, being the keys (indexes) of the items that didn't match. If you want the values, use ARRAY_DIFF or ARRAY_EXTRACT. Ie:
{ #5 #10 #15 #10 #20 }list { #10 }list array_diffor
{ #5 #10 #15 #10 #20 }list dup #10 array_excludeval array_extractwhich will both return a list containing #5, #15, and #20. The main difference between these two methods is that ARRAY_DIFF will remove any duplicate values in the remaining items, and the ordering of the resulting items will have been sorted. Using ARRAY_EXTRACT, ordering and duplicate values are preserved.
a) If the object being moved is !JUMP_OK and is it being moved by someone
other than the object's owner, then the moveto fails.
b) If the object being moved is a person and either the source or
destination rooms (if not owned by the person being moved) are
!JUMP_OK, the moveto fails.
c) If the object being moved is not a player, is owned by the owner of
either the source or destination rooms, and either room where the
ownership matches is !JUMP_OK, the moveto fails.
The moveto succeeds under any other circumstances. MOVETO rules follow the permissions of the current effective userid. MOVETO will run programs in the @desc and @succ/@fail of a room when moving a player.
You can start a search with d1 set to #-1. If d2 is #-1 then ownership checks will not be performed. However, only programs with a mucker level of 3 or better will be allowed to perform non-owner-specific searches, or searches with an owner different from the effective UID of the program. If s1 is an empty string, name checks will not be performed. If s2 is a null string, then flags will not be checked.
The s1 name pattern differs from that used by @find, @owned, etc. in that those commands implicitly treat any patterns as if it has a * before and after it. This primitive does NOT. So for this primitive, "*.muf" would match only objects whose name ends in ".muf".
The s2 string is a flagslist that is in the same format as that used by the @find, @owned, etc. commands. ie: "F3!D" will match all muf program objects in the database that are mucker level 3, and not set debug.
If there are no more objects in the database that might match all the search criteria, then #-1 is returned. Otherwise, the next matching object is returned. This primitive is used like this:
#-1 begin me @ "*.muf" "F" FINDNEXT dup while dup unparseobj .tell repeat
You can check the "interactive" flag to see if a player is currently in a program's READ, or if they are in the MUF editor. The "Truewizard" flag will check for a W flag with or without the QUELL set. The "Mucker" flag returns the most significant bit of the mucker level and the "Nucker" flag returns the least significant bit. (Use MLEVEL instead.)
(str) dumpwarn_mesg - Message to warn of a coming DB dump
(str) deltawarn_mesg - Message to warn of a coming delta dump
(str) dumpdeltas_mesg - Message telling of a delta dump
(str) dumping_mesg - Message telling of a DB dump
(str) dumpdone_mesg - Message notifying a dump is done
(str) penny - A single currency
(str) pennies - Plural currency
(str) cpenny - Capitolized currency
(str) cpennies - Capitolized plural currency
(str) muckname - The name of the MUCK
(str) rwho_passwd - Password for RWHO servers (Wizbit only)
(str) rwho_server - RWHO server to connect to (Wizbit only)
(str) huh_mesg - Message for invalid commands
(str) leave_mesg - Message given when QUIT is used
(str) idle_boot_mesg - Message given to an idle booted user
(str) register_mesg - Message for a failed 'create' at login
(str) playermax_warnmesg - Message warning off too many connects
(str) playermax_bootmesg - Error given when a player cannot connect
(time) rwho_interval - Interval between RWHO updates
(time) dump_interval - Interval between dumps
(time) dump_warntime - Warning prior to a dump
(time) monolithic_interval - Max time between full DB dumps
(time) clean_interval - Interval between unused object purges
(time) aging_time - When an object is considered old and unused
(time) maxidle - Maximum idle time allowed
(int) max_object_endowment - Max value of an object
(int) object_cost - Cost to create an object
(int) exit_cost - Cost to create an exit
(int) link_cost - Cost to link an exit
(int) room_cost - Cost to dig a room
(int) lookup_cost - Cost to lookup a player name
(int) max_pennies - Max number of pennies a player can own
(int) penny_rate - Rate for finding pennies
(int) start_pennies - Starting wealth for new players
(int) kill_base_cost - Number of pennies for a 100 percent chance
(int) kill_min_cost - Minimum cost for doing a kill
(int) kill_bonus - Bonus for a successful kill
(int) command_burst_size - Maximum number of commands per burst
(int) commands_per_time - Commands per time slice after burst
(int) command_time_msec - Time slice length in milliseconds
(int) max_delta_objs - Max percent of changed objects for a delta
(int) max_loaded_objs - Max percent of the DB in memory at once
(int) max_force_level - Maximum number of forces within one command
(int) max_process_limit - Total processes allowed
(int) max_plyr_processes - Processes allowed for each player
(int) max_instr_count - Max preempt mode instructions
(int) instr_slice - Max uninterrupted instructions per time slice
(int) mpi_max_commands - Max number of uninterruptable MPI commands
(int) pause_min - Pause between input and output servicing
(int) free_frames_pool - Number of program frames pre-allocated
(int) listen_mlev - Minimum MUCKER level for _listen programs
(int) playermax_limit - Manimum allowed connections
(ref) player_start - The home for players without a home
(bool) use_hostnames - Do reverse domain name lookup
(bool) log_commands - The server logs commands (Wizbit only)
(bool) log_failed_commands - The server logs failed commands (Wizbit only)
(bool) log_programs - The server logs programs (Wizbit only)
(bool) dbdump_warning - Warn about coming DB dumps
(bool) deltadump_warning - Warn about coming delta dumps
(bool) periodic_program_purge - Purge unused programs from memory
(bool) support_rwho - Use RWHO server
(bool) secure_who - WHO works only in command mode
(bool) who_doing - Server support for @doing
(bool) realms_control - Support for realm wizzes
(bool) allow_listeners - Allow listeners
(bool) allow_listeners_obj - Objects can be listeners
(bool) allow_listeners_env - Listeners can be up the environment
(bool) allow_zombies - Zombie objects allowed
(bool) wiz_vehicles - Only wizzes can make vehicles
(bool) force_mlev1_name_notify - M1 programs forced to show name on notify
(bool) restrict_kill - Can only kill KILL_OK players
(bool) registration - Only wizzes can create players
(bool) teleport_to_player - Allow use of exits linked to players
(bool) secure_teleport - Check teleport permissions for personal exits
(bool) exit_darking - Players can set exits dark
(bool) thing_darking - Players can set objects dark
(bool) dark_sleepers - Sleepers are effectively dark
(bool) who_hides_dark - Dark players are hidden (Wizbit only)
(bool) compatible_priorities - Backwards compatibility for exit priorities
(bool) do_mpi_parsing - Parse MPI strings in messages
(bool) look_propqueues - Look triggers _lookq propqueue
(bool) lock_envcheck - Locks will check the environment
(bool) diskbase_propvals - Allow diskbasing of property values
(bool) idleboot - Enable or disable idlebooting
(bool) playermax - Enable or disable connection limit
"name" The name of the @tune setting.
"group" The logical group that this parameter belongs to.
"type" One of "string", "integer", "timespan", "dbref", or "boolean".
"mlev" The mucker level required to read the value of this sysparm.
"value" The value of the sysparm. May be an int, string, or dbref. If a given entry is of the "dbref" type, it will also have the extra field:
"objtype" The type of object the dbref is restricted to. Can be one of
the strings "player", "thing", "room", "exit", "program",
"garbage", or "any".
$define setdesc "_/de" swap 0 addprop $enddef
$define setsucc "_/sc" swap 0 addprop $enddef
$define setfail "_/fl" swap 0 addprop $enddef
$define setdrop "_/dr" swap 0 addprop $enddef
$define setosucc "_/osc" swap 0 addprop $enddef
$define setofail "_/ofl" swap 0 addprop $enddef
$define setodrop "_/odr" swap 0 addprop $enddef
date gmtoffset sleep systime systime_precise time timefmt timesplit
%% -- "%"
%a -- abbreviated weekday name.
%A -- full weekday name.
%b -- abbreviated month name.
%B -- full month name.
%C -- "%A %B %e, %Y"
%c -- "%x %X"
%D -- "%m/%d/%y"
%d -- month day, "01" - "31"
%e -- month day, " 1" - "31"
%h -- "%b"
%H -- hour, "00" - "23"
%I -- hour, "01" - "12"
%j -- year day, "001" - "366"
%k -- hour, " 0" - "23"
%l -- hour, " 1" - "12"
%M -- minute, "00" - "59"
%m -- month, "01" - "12"
%p -- "AM" or "PM"
%R -- "%H:%M"
%r -- "%I:%M:%S %p"
%S -- seconds, "00" - "59"
%T -- "%H:%M:%S"
%U -- week number of the year. "00" - "52"
%w -- week day number, "0" - "6"
%W -- week# of year, starting on a monday, "00" - "52"
%X -- "%H:%M:%S"
%x -- "%m/%d/%y"
%y -- year, "00" - "99"
%Y -- year, "1900" - "2155"
%Z -- Time zone. "GMT", "EDT", "PST", etc.
background bg_mode compile compiled? fg_mode foreground fork getpidinfo getpids instances ispid? kill mode pid pr_mode preempt program_getlines program_setlines queue setmode uncompile
CALLED_DATA - The string the program was queued with, if any
CALLED_PROG - The program dbref# of the process' current call level.
CPU - The amount of CPU time used up by the process.
DESCR - The descriptor that called the program.
INSTCNT - The number of instructions run so far.
NEXTRUN - When the process is due to run again.
PID - That process ID.
PLAYER - The player dbref that PID belongs to.
STARTED - Systime in seconds when the process started.
SUBTYPE - Additional information about the process.
TRIG - The trigger that called that process.
TYPE - What type of process it is. MUF/MPI.
event_count event_exists event_send event_wait event_waitfor timer_start timer_stop watchpid
MCP.com-belfry-image Where pkg is 'com-belfry-image' & mesg is ''
MCP.com-belfry-image-view Where pkg is 'com-belfry-image' & mesg is 'view' The context for MCP events is a dictionary containing the following keys:
descr Holds the integer descriptor of the sending connection.
package Holds the string package name the MCP mesg is sent for.
message Holds the string message name within that package.
args Holds a dictionary, containing the message arguments.
Each mesg arg has an array list of strings as a value. Read events have eventID strings of just "READ". The context of Read events is the integer descriptor of the first waiting line to be READ. To get the actual line sent by the user you need to use the READ primitive. Process exit events have eventID strings that are created by prepending "PROC.EXIT." to the pid of the watched process that exited. The context is the pid of the process that exited.
mcp_bind mcp_register mcp_register_event mcp_send mcp_supports
names list of user-viewable pane names for D_TABBED & D_HELPER dlogs.
panes list of pane ids for D_TABBED and D_HELPER dialogs.
width requested width of dialog in pixels.
height requested height of dialog in pixels.
resizable allows resizing in "x", "y", or "both" dimensions.
minwidth minimum width of window in pixels.
minheight minimum height of window in pixels.
maxwidth maximum width of window in pixels.
maxheight maximum height of window in pixels.
Takes the following arguments:
"values" list of values or text to insert.
"before" position in widget to insert before. Defaults to "end". "delete" Deletes text or list items from the given control.
Takes the following arguments if the widget is a listbox:
"items" The list of listbox items to delete, given by index.
Takes the following arguments if the widget is not a listbox:
"first" gives the first item to delete.
"last" gives the last item of the range to delete. If not given,
this defaults to the same value as "first". "select" Selects text or list items in the given control.
Takes the following arguments if the widget is a listbox:
"items" The list of listbox items to select, given by index.
All other items will be deselected.
Takes the following arguments if the widget is not a listbox:
"first" gives the first character to select.
"last" gives the last character of the range to select. If not
given, this defaults to the same value as "first". "show" Scrolls the given item or character into view.
Takes the following arguments:
"position" The item or character position to make visible. "cursor" Moves the cursor of the given control.
Takes the following arguments:
"position" The item or character to move the cursor to or before.
value Holds the value of the control.
valname Specifies the name used with GUI_VALUE_GET/SET, to refer to the
value for this control. Multiple controls can share the same
valname, in the case of radio buttons and the like.
report If set to 1, image, checkbox, and radiobutton controls will send
a 'buttonpress' event when the user clicks on them. All other
control types send a 'valchanged' gui event when their value
is changed If the value is changed several times within a few
seconds, only the final value is guaranteed to have a valchanged
event. It is possible to receive valchanged events even though
the value appears to have not changed.
pane The pane in which to place this control. Some controls, like
the notebook or frame controls, create new panes in which you
can place controls.
newline If 1, next control will be at the beginning of the next row.
If 0, next control will be in the next column to the right,
taking into account colspan and colskip. Defaults to 1.
sticky Which sides of the cell this control sticks to. This is a
string with any or all of the letters 'n', 's', 'e', or 'w',
corresponding to north, south, east, and west.
row If given, forces the control to be placed in the given row.
column If given, forces the control to be placed in the given column.
rowspan Number of rows this control spans across. Defaults to 1.
colspan Number of columns this control spans across. Defaults to 1.
colskip Number of columns to skip before placing this control.
Defaults to 0.
minwidth Minimum width of control in pixels.
minheight Minimum width of control in pixels.
hweight How much the starting column of this control should expand.
When filling unused space, if there are two columns, one with
hweight 2 and the other with hweight 1, then the column with
hweight 2 will be expanded twice as much as the other column.
The default hweight is 0. Note that the last control in the
given column sets the hweight for the column. If no hweights
are given in a pane, then if the pane is forced to be wider
than the contained controls, the empty space will be placed
in the right side of the pane.
vweight How much the starting row of this control should expand.
When filling unused space, if there are two rows, one with
vweight 2 and the other with vweight 1, then the row with
vweight 2 will be expanded twice as much as the other row.
The default hweight is 0. Note that the last control in the
given row sets the vweight for the row. If no vweights
are given in a pane, then if the pane is forced to be taller
than the contained controls, the empty space will be placed
in the bottom of the pane.
toppad Number of pixels padding to add above this control.
leftpad Number of pixels padding to add to the left of this control.
text The text to display as the user-visible name of this menu. For
example: "Settings", "Help", etc.
pane The id of the menu in which to place this menu as a cascading
menu. If specified as "", then this is a toplevel menu.
value Contains the text to be displayed.
justify Which way to justify the text, if word wrapped. Can be one of
left, right, or center.
maxwidth The max width in pixels for the label, before it word wraps.
value Holds the url of the image to be displayed.
width The width of the image in pixels. This is required.
height The height of the image in pixels. This is required.
height Number of pixels of vertical thickness of the horizontal line.
width Number of pixels of horizontal thickness of the vertical line.
text The text label that will show on the button face.
width Number of characters text the button should be able to hold.
dismiss If 1, the dialog will be dismissed when this button is pressed.
Defaults to 1.
text The text label that will show to the right of the checkbox.
onvalue The value this control should have when checked.
offvalue The value this control should have when not checked.
value The current state/value of the control. This should match either
the value of the "onvalue" or "offvalue" argument.
text The text label that will show to the right of the radio button.
valname The name used to refer to the value of this radio button group.
A radio button group is defined as radio buttons that share the
same valname.
selvalue The value of this radio button group, when this is selected.
value The current state/value of the radio button group. This should
match one of the selvalues of a radio button in this group.
text The text label that will show to the left of the control.
Defaults to "" (no label). If there is a label, then this
control will be split across two columns, with the label in
the first one, the edit control in the second.
value The current contents/value of the control.
maxlen The maximum string length allowed. Defaults to 1024 chars.
width Number of average character widths wide this control should be.
Defaults to 40 characters wide.
height Number of lines high this control will be. Defaults to 1.
If this is greater than 1, then text will wrap as you type it,
but you still won't be able to insert carriage returns.
value The current contents/value of the control.
width Number of average character widths wide this control should be.
Defaults to 40 characters wide.
height Number of lines high this control will be. Defaults to 1.
font Specifies whether the font used is "fixed" or "proportional".
text The text label that will show to the left of the control.
Defaults to "" (no label). If there is a label, then this
control will be split across two columns, with the label in
the first one, the combobox control in the second.
value The current contents/value of the control.
width Number of average character widths wide this control should be.
Defaults to 20 characters wide.
options Contains a list of entries for the combobox pulldown menu, one
per line.
editable If 1, the user can edit the value arbitrarily. If 0, then the
user can only pick a value from the combobox pulldown menu.
Defaults to 0
sorted Causes the contents of the pulldown menu to be auto-sorted.
value The list of the currently selected list items, given as a list
of integers that refer to list positions. 0 is the first item.
font Specifies whether the font used is "fixed" or "proportional".
width Number of average character widths wide this control should be.
Defaults to 40 characters wide.
height Number of lines high this control will be. Defaults to 10.
options The list of entries for the listbox contents, one per line.
selectmode One of "single", "multiple", or "extended". Specifies how
list item selection is performed, and whether more than one item
can be selected at a time.
value The current integer value of the control.
text If given, makes a label with the given text next to the spinner.
minval The minimum integer value that this control can be set to.
maxval The maximum integer value that this control can be set to.
width Number of digits that this control should be able to hold.
Defaults to 12 digits.
value The current floating point value of the control.
text If given, makes a label with the given text next to the scale.
minval The minimum value that this control can be set to.
maxval The maximum value that this control can be set to.
resolution This is the smallest change available via the slider.
Defaults to 1.
bigincrement How much to change the value when the user clicks in the
bar on either side of the slider. Defaults to 10.
digits The number of significant digits of resolution for the value.
orient The orientation, "horiz" or "vert". (Defaults to horiz.)
length The length of the long axis of the scale, in pixels.
Defaults to 100 pixels.
width The length of the short axis of the scale, in pixels.
Defaults to 15 pixels.
text If set, the frame, as a groupbox, will have the given label.
visible If set to 1, causes a border to be drawn around the frame.
collapsible This, is set to 1, allows the group box to be collapsed.
collapsed If the frame is collapsible, this sets the state to collapsed.
panes The list of pane ids to create. Those are used by other
controls, when you wish to specify which pane the control
is in.
names The list of names that should be displayed on the pane's tabs.
The first name should be for the first pane, the second name
should be for the second name, etc.
width The minimum width of the notebook's client area, in pixels.
height The minimum height of the notebook's client area, in pixels.
abort checkargs try
On an error, the CATCH statement will pop off all stack items that were not locked, to return the stack to a known state. The stack will then be unlocked, and the thrown error string will be pushed onto the stack. Finally, the code inside the CATCH-ENDCATCH block will then be executed. If you have nested TRY-CATCH blocks, then the most recent TRY-CATCH block will catch the error thrown. If the error is thrown in another called function or program, then those calls are unwound back to the most recent TRY-CATCH block.
The CATCH_DETAILED statement can be used in place of the CATCH statement. It behaves identically to CATCH, except it pushes a dictionary onto the stack, instead of a string. This dictionary holds much more detailed information about the thrown exception. The following dictionary entries are currently supported:
"error" The error message string, like what CATCH returns.
"instr" The name string of the instruction which threw the error.
"line" The integer program line number where the error was thrown.
"program" The dbref of the program in which the error was thrown.
The ABORT primitive may be used to throw an arbitrary error anywhere. If you need to re-propagate an error, you can use ABORT. Example code:
: get_a_prop ( d s -- s )
2 TRY
getpropstr
CATCH
pop (We got an error trying to read the prop. Ignore it.)
""
ENDCATCH
;
a - function address.
d - dbref. (#-1, #-2, #-3 are okay)
D - valid, non-garbage dbref. (#-1, #-2 NOT allowed. #-3 is okay)
e - exit dbref. (#-1, #-2 allowed)
E - exit dbref. (#-1, #-2 NOT allowed)
f - program dbref. (#-1, #-2 allowed)
F - program dbref. (#-1, #-2 NOT allowed)
i - integer.
l - lock boolean expression.
p - player dbref. (#-1, #-2 allowed)
P - player dbref. (#-1, #-2 NOT allowed)
r - room dbref. (#-1, #-2 allowed) (#-3 is a room)
R - room dbref. (#-1, #-2 NOT allowed) (#-3 is a room)
s - string.
S - non-null string.
t - thing dbref. (#-1, #-2 allowed)
T - thing dbref. (#-1, #-2 NOT allowed)
v - local or global variable.
? - any stack item type. Tests can be repeated multiple times by following the test with a number. ie: '"i12" checkargs' would test the stack for 12 integers. The last test in the string expression will be done on the top stack item. Tests are done from the top of the stack down, in order, so the last test that fails in a string expression will be the one that the Program Error will be given for. ie: '"sdSi" checkargs' will test that the top stack item is an integer, then it tests that the next item down is a non-null string, then it tests the third item from the top to see if it is a dbref, and lastly it tests to make sure that the 4th item from the top is a string. Spaces are ignored, so "s d i" is the same as "sdi". However, multipliers are ignored if they follow a space, so "s 4d i" is also the same as "sdi". This is because you are basically telling it to repeat the space 4 times, and since spaces are ignored, it has no effect. If you have a function that takes a stack item of any type, you can use the "?" test. "?" will match a string, integer, dbref, or any other type. Since sometimes arguments are passed in ranges, such as the way that the explode primitive returns multiple strings with an integer count on top, there is a way to group arguments, to show that you expect to receive a range of that type. ie: '"{s}" checkargs' would test the stack for a set of strings like '"first" "second" "third" "fourth" 4' where the top stack item tells how many strings to expect within the range. Sometimes a function takes a range of paired arguments, such as: '"one" 1 "two" 2 "three" 3 "four" 4 4' where the count on the top of the range refers to the number of pairs. To test for the range given above, you would use '"{si}" checkargs' to tell it that you want to check for a range of paired strings and integers. You can group as many argument tests together in a range as you would like. ie: you could use "{sida}" as an expression to test for a range of related strings, integers, dbrefs, and function addresses. Since the argument multipliers refer to the previous test OR range, you can test for two string ranges with the test '"{s}2" checkargs'. ie: It would succeed on a stack of: '"one" "two" "three" 3 "four" "five" 2'. '"{s2}" checkargs', however, would test for one range of paired strings. ie: It would succeed with a stack of: '"one" "1" "two" "2" "three" "3" 3'. If, for some reason, you need to pass a range of ranges to a function, you can test for it by nesting the braces. ie: '"{{s}}" checkargs' Now, as one last example, the primitive notify_exclude, if we were to test the arguments passed to it manually, would use the test '"R{p}s" checkargs' to test for a valid room dbref, a range of player dbrefs or #-1s, and a string.
breakpoints debug notation debug_line debug_off debug_on debugger debugger_break debugger_commands
When you enter the debugger, the program counter will be placed at the entry point of the MUF. This should always be the start of a function. From there, you may set breakpoints, examine or change the stack or variables, list lines of code, step through, or continue execution.
Every time execution halts and returns you to the debugger, the argument stack is displayed, as well as a dump of the current line. If execution is at the beginning of the line, the actual line of source will be displayed, otherwise the compiled instructions will be displayed with the current instruction marked by surrounding braces.
finish completes execution of current function.
step [NUM] executes one (or NUM, 1) lines of muf.
stepi [NUM] executes one (or NUM, 1) muf instructions.
next [NUM] like step, except skips CALL and EXECUTE.
nexti [NUM] like stepi, except skips CALL and EXECUTE.
break LINE# sets breakpoint at given LINE number.
break FUNCNAME sets breakpoint at start of given function.
breaks lists all currently set breakpoints.
delete NUM deletes breakpoint by NUM, as listed by 'breaks'
where [LEVS] displays function call backtrace of up to num levels deep.
stack [NUM] shows the top num items on the stack.
print v# displays the value of given global variable #.
print lv# displays the value of given local variable #.
trace [on|off] turns on/off debug stack tracing.
list [L1,[L2]] lists source code of given line range.
list FUNCNAME lists source code of given function.
listi [L1,[L2]] lists instructions in given line range.
listi FUNCNAME lists instructions in given function.
words lists all function word names in program.
words PATTERN lists all function word names that match PATTERN.
exec FUNCNAME calls given function with the current stack data.
prim PRIMITIVE executes given primitive with current stack data.
push DATA pushes an int, dbref, var, or string onto the stack.
pop pops top data item off the stack.
help displays this help screen.
quit stop execution here.
A breakpoint is triggered after execution of the program has continued with either 'continue', 'finish', or while in a word called when 'next' or 'nexti' is used.
NUM: An integer. (As in 5 or -154)
#dbref: An object dbref. (As in #1 or #63516)
"Text": A string. (As in "Wizard" or "Hello world!") If it is
followed by _, there is more in the string than shown.
[Lock]: A lock in it's native structure. (As in [#0|!#0])
VNUM: Variable number NUM. (As in V0 or V10)
LVNUM: Local variable number NUM. (As in LV2 or LV5)
SVNUM: Scoped function variable number NUM. (As in SV2 or SV5)
(word): The start of a word. (As in (do-something))
'word: The address of word. (As in 'do-something)
'#dbref'word: The address of word in another program.
(As in '#2345'do-something)
EXEC->word: A subroutine call to word. (As in EXEC->do-something)
JMP->word: A jump to word. (As in JMP->do-something)
JMP->lineNUM: An internal jump used by IF-ELSE-THEN and loops.
(As in JMP->line9)
IF->lineNUM: An internal IF jump. If the top stack item is 0, the jump
occurs, otherwise this is just a no-op. (As in IF->line9)
???: An unknown instruction type. (As in ???)
???<file:line>: An instruction that has been freed. If you see this please
report it to the current server code maintainer.
(As in ???<p_db.c:1042>) Some tokens are just used as placeholders, and disappear as soon as the MUF is compiled. These are BEGIN, and THEN. In addition, FOR loops will produce at least two extra instruction, and an IF->lineNUM. FORITER, the iteration counter, and FORPOP, which cleans up from the for loop. If you JMP or EXIT out of a word from nested FOR loops, you will see a FORPOP before the JMP or EXIT for each nexted FOR loop.
force force_level version


Project Overview:
The software creates high–quality haptic feedback from audio or video frames on the fly without any code.
Feel what you see, physically feel the motion in anything you watch, recorded or live.
Simple workflow helps to connect the device to any video streaming platform and set up the supported device as the user feels and likes.
Feelme AI is a real-time motion tracking system that analyses motion in ANY online video content and automatically controls users device follow the action as you watch it.
What does the project solve:
A lack of interactive content available online. Interactive content was created manually by adding so-called subtitles to the video. The number of such videos was also limited.
Users are often deterred from purchasing an interactive pleasure product simply due to the lack of variety of free content OR the high cost of a
monthly premium on adult sites that provide interactive content. FeelMe was developed to turn an entire website’s
content to interactive content, simply by subscribing to one of our packages.
Technical Solution:
The solution was found in a process of training of Neural network. Neural network is a function that can predict result based on incoming data.
Network consists of network architecture (set of layers) and weights.
The architecture is defined by an ML Engineer. The weights are defined using process called training.
Our networks receive frames from the videos and produce subtitles (in FunScript ).
In order to train the network, we need plenty of videos with subtitles. Training is a process of finding optimal weights which produce the result as close as possible to the existing subtitles.
Technical Discussions:
At the design stage of a project, it is essential to know all the technical limitations. Since this is not a standard project (like messenger or note app) a good experience depends on how the technical capabilities of the project are transferred to a good UX.
Technical limitations lead to problems that can be possibly solved by good UX such as:
- "No visible action" scenes can cause frustration of the users because of the way they experience adult devices.
- High expectations on accuracy can lower the loyalty to the brand as the estimated neural network accuracy is 75-80%.
Information architecture:
Information architecture and navigation are the primary ways users find what they need from an application. So they should be intuitive and minimal.
I started with implementing the basic principle of hierarchy with developing an information sequence.
I combine all the information and actions that are going to be embed into Feelme AI site.
From there, I build a design hierarchy that links my pages in a consistent and logical way.
Hierarchy is another key UX design principle that is often overlooked. At its simplest level,
this approach means thinking through all the features and information that Feelme AI (and also mobile App)
will contain. I see it as an important part to use this approach early in the design phase.
This will allow potential users to navigate Feelme AI more easily and find what they are looking for.
User Test:
I wanted to get users' feedback not only regarding the user experience on the Feelme AI website but also users' feedback regarding the accuracy
of the Neural Network and their expectations regarding the movements of the connected device. It's important to remember that the most beautiful design in the world is worthless if your customers can't use it.
I prepared task scenarios to cover the objectives defined before.
Main questions that I had at this stage are:
- How easy was it to download and set up the FeelMe Video Watcher extension?
- What type of content/media did you watch?
- How would you rate the general experience of using FeelMe AI
- What did you like the most about using FeelMe AI?
- What did you like the least about using FeelMe AI?
- How accurate was FeelMe AI (syncing Keon with the video movement) overall?
- Did the functionalities - intensity, pause button, and ambient movement - add value to your experience? Please list the
ones that added value.
- How frequently would you use FeelMe AI?
- What functionalities would make the experience better?


if-elif-else
for
def print_hello (name):
"""
This is a function which prints hello to a user, but does not
return anything.
"""
print(f"Hello, {name}")
value = print_hello('Bob') # value will be `None`
a = True
b = False
c = true # This will try to use a variable named `true`!
d = false # This will try to use a variable named `false`!
# Logical AND
print(True and True) # => True
print(True and False) # => False
print(False and False) # => False
# Logical OR
print(True or True) # => True
print(True or False) # => True
print(False or False) # => False
# Logical NOT
print( not True) # => False
print( not False and True) # => True
print( not True or False) # => False
None
False
''
[]
()
set()
range(0)
print( 3 ) # => 3
print(int( 19 )) # => 19
print(int()) # => 0
print(2.24) # => 2.
print(2.) # => 2.
print(float()) # => 0.
print(27e-5) # => 0.
# Integer to Float
print( 17 ) # => 17
print(float( 17 )) # => 17.
# Float to integer
print(17.0) # => 17.
print(int(17.0)) # => 17
# Float and integer to string
print(str(17.0) + ' and ' + str( 17 )) # => 17.0 and 17
print(17.0 + ' and ' + 17 )
# TypeError: unsupported operand type(s) for +: 'float' and 'str'
# Escaping single quote
'Jodi asked, "What\'s up, Sam?"'
print('''My instructions are very long so to make them
more readable in the code I am putting them on
more than one line. I can even include "quotes"
of any kind because they won't get confused with
the end of the string!''')
def print_hello (name):
"""
This is a docstring that explains what the function does
It can be multiple lines, handy!
You can use any combination of ' and " in these because
python is looking for the ending triple " characters
to determine the end.
"""
print(f"Hello, {name}")
print(len("Spaghetti")) # => 9
# Normal indexing
print("Spaghetti"[ 0 ]) # => S
print("Spaghetti"[ 4 ]) # => h
# You can use negative indexes to start at the end.
print("Spaghetti"[-1]) # => i
print("Spaghetti"[-4]) # => e
# return a series of characters
print("Spaghetti"[ 1 : 4 ]) # => pag
print("Spaghetti"[ 4 :-1]) # => hett
print("Spaghetti"[ 4 : 4 ]) # => (empty string)
print("Spaghetti"[: 4 ]) # => Spag
print("Spaghetti"[:-1]) # => Spaghett
print("Spaghetti"[ 1 :]) # => paghetti
print("Spaghetti"[-4:]) # => etti
# indexing past the beginning or end gives an error
print("Spaghetti"[ 15 ]) # => IndexError: string index out of range
print("Spaghetti"[-15]) # => IndexError: string index out of range
# but ranges past the beginning or end do not.
print("Spaghetti"[: 15 ]) # => Spaghetti
print("Spaghetti"[ 15 :]) # => (empty string)
print("Spaghetti"[-15:]) # => Spaghetti
print("Spaghetti"[:-15]) # => (empty string)
print("Spaghetti"[ 15 : 20 ]) # => (empty string)
print("Spaghetti".index("h")) # => 4
print("Spaghetti".index("t")) # => 6
print("Spaghetti".count("h")) # => 1
print("Spaghetti".count("t")) # => 2
print("Spaghetti".count("s")) # => 0
print('''We choose to go to the moon in this decade and do the other things,
not because they are easy, but because they are hard, because that goal will
serve to organize and measure the best of our energies and skills, because that
challenge is one that we are willing to accept, one we are unwilling to
postpone, and one which we intend to win, and the others, too.
'''.count('the ')) # => 4
print("gold" + "fish") # => goldfish
print("s"* 5 ) # => sssss
first_name = "Billy"
last_name = "Bob"
# Using the format function
print('Your name is {0} {1}'.format(first_name, last_name)) # => Your name is Billy Bob
# Using the `f` format flag on the string
print(f'Your name is {first_name} {last_name}') # => Your name is Billy Bob
s = "Hello" s.upper() "HELLO"
s = "Hello" s.lower() "hello"
s = "Hello" s.islower() False
s = "hello" s.islower() True
s = "Hello" s.isupper() False
s = "HELLO" s.isupper() True
s = "Hello" s.startswith("He") True
s = "Hello" s.endswith("lo") True
s = "Hello World" s.split() ["Hello", "World"]
s = "i-am-a-dog" s.split("-") ["i", "am", "a", "dog"]
a = 7
b = 'Marbles'
print(a) # => 7
print(b) # => Marbles
# You can do assignment chaining
count = max = min = 0
print(count) # => 0
print(max) # => 0
print(min) # => 0
print( not a == b) # => True
# This breaks
print(a == not b) # Syntax Error
# This fixes it
print (a == ( not b)) # => False
+=
-=
*=
/=
%=
**=
//=
if name == 'Monica':
print('Hi, Monica.')
elif age < 12 :
print('You are not Monica, kiddo.')
else :
print('You are neither Monica nor a little kid.')
# Looping over a string
for c in "abcdefg":
print(c)
# Looping over a range
print('My name is')
for i in range( 5 ):
print('Carlita Cinco (' + str(i) + ')')
# Looping over a list
lst = [ 0 , 1 , 2 , 3 ]
for i in lst:
print(i)
# Looping over a dictionary
spam = {'color': 'red', 'age': 42 }
for v in spam.values():
print(v)
# Loop over a list of tuples and
# Destructuring to values
# Assuming spam.items returns a list of tuples
# Each containing two values (k, v)
for k, v in spam.items():
print('Key: ' + k + ' Value: ' + str(v))
spam = 0
while True:
print('Hello, world.')
spam = spam + 1
if spam < 5 :
continue
break
# Basic function with no arguments and no return value
def printCopyright ():
print("Copyright 2020. Me, myself and I. All rights reserved.")
# Function with positional parameters and a return value
def average (num1, num2):
return (num1/num2)
# Calling it with positional arguments
print(average( 6 , 2 )) # => 3.
# Calling it with keyword arguments
# (note that order doesn't matter)
print(average(num2= 2 , num1= 6 ));
# Default parameters
# Here the string "Hello" is the default for `saying`
def greeting (name, saying="Hello"):
print(saying, name)
greeting("Monica") # => Hello Monica
greeting("Monica", saying="Hi") # => Hi Monica
# A common 'gotcha' is using an mutable object for a default parameter.
# Python doesn't do what you expect. All invocations of the function
# reference the same mutable object
# Everytime we call this we use the exact same `itemList` list
def appendItem (itemName, itemList = []):
itemList.append(itemName)
return itemList
print(appendItem('notebook')) # => ['notebook']
print(appendItem('pencil')) # => ['notebook', 'pencil']
print(appendItem('eraser')) # => ['notebook', 'pencil', 'eraser']
toUpper = lambda s: s.upper()
toUpper('hello') # => HELLO
const toUpper = s => s.toUpperCase();
toUpper('hello'); // # => HELLO
average( 1 )
# => TypeError: average() missing 1 required positional argument: 'num2'
average( 1 , 2 , 3 )
# => TypeError: average() takes 2 positional arguments but 3 were given
def add (a, b, *args):
# args is a tuple of the rest of the arguments
total = a + b;
for n in args:
total += n
return total
# args is None
add( 1 , 2 ) # Returns 3
# args is (4, 5)
add( 2 , 3 , 4 , 5 ) # Returns 14
def print_names_and_countries (greeting, **kwargs):
# kwargs is a dictionary of the rest of the keyword arguments
for k, v in kwargs.items():
print(greeting, k, "from", v)
# kwargs would be:
# {
# 'Monica': 'Sweden',
# 'Charles'='British Virgin Islands',
# 'Carlo':'Portugal
# }
print_names_and_countries("Hi",
Monica="Sweden",
Charles="British Virgin Islands",
Carlo="Portugal")
# Prints
# Hi Monica from Sweden
# Hi Charles from British Virgin Islands
# Hi Carlo from Portugal
def example2 (arg_1, arg_2, *args, kw_1="shark", kw_2="blowfish", **kwargs):
pass
# Can be made with square brackets
empty_list = []
departments = ['HR','Development','Sales','Finance','IT','Customer Support']
# list built-in function makes a list too
specials = list()
# You can use `in` to test if something is in the list
print( 1 in [ 1 , 2 , 3 ]) #> True
print( 4 in [ 1 , 2 , 3 ]) #> False
# With curlies
a = {'one': 1 , 'two': 2 , 'three': 3 }
# With the dict built-in function
b = dict(one= 1 , two= 2 , three= 3 )
# You can use the `in` operator with dictionaries too
print( 1 in { 1 : "one", 2 : "two"}) #> True
print("1" in { 1 : "one", 2 : "two"}) #> False
print( 4 in { 1 : "one", 2 : "two"}) #> False
# Using curlies (dont' confuse this with dictionaries)
school_bag = {'book','paper','pencil','pencil','book','book','book','eraser'}
# Using the set() built in
school_bag = set('book','paper','pencil','pencil','book','book','book','eraser')
# You can use the `in` operator with sets
print( 1 in { 1 , 1 , 2 , 3 }) #> True
print( 4 in { 1 , 1 , 2 , 3 }) #> False
# With parenthesis
time_blocks = ('AM','PM')
# Without parenthesis
colors = 'red','blue','green'
numbers = 1 , 2 , 3
# with the tuple buit-in function which can also be used to
# convert things to tuples
tuple('abc') # returns ('a', 'b', 'c')
tuple([ 1 , 2 , 3 ]) # returns (1, 2, 3)
# you can use the `in` operator with tuples
print( 1 in ( 1 , 2 , 3 )) #> True
print( 4 in ( 1 , 2 , 3 )) #> False
range( 5 ) # [0, 1, 2, 3, 4]
range( 1 , 5 ) # [1, 2, 3, 4]
range( 0 , 25 , 5 ) # [0, 5, 10, 15, 20]
range( 0 ) # [ ]
def isOdd (num):
return num % 2
filtered = filter(isOdd, [ 1 , 2 , 3 , 4 ])
# It returns a filter iterable object
# but we can cast it to a list
print(list(filtered)) # => [1, 3]
def toUpper (str):
return str.upper()
upperCased = map(toUpper, ['a','b','c'])
print(list(upperCased)) # => ['A','B','C']
sortedItems = sorted(['Banana', 'orange', 'apple'])
print(list(sortedItems)) # => ['Banana', 'apple', 'orange']
# Notice Banana is first because uppercase letters come first
# Using a key function to control the sorting and make it sort
# so the case doesn't matter
sortedItems = sorted(['Banana', 'orange', 'apple'], key=str.lower)
print(list(sortedItems)) # => ['apple', 'Banana', 'orange']
# Reversing the sort
sortedItems = sorted(['Banana', 'orange', 'apple'], key=str.lower, reverse=True)
print(list(sortedItems)) # => ['orange', 'Banana', 'apple']
quarters = ['First', 'Second', 'Third', 'Fourth']
print(enumerate(quarters))
print(enumerate(quarters, start= 1 ))
( 0 , 'First'), ( 1 , 'Second'), ( 2 , 'Third'), ( 3 , 'Fourth')
( 1 , 'First'), ( 2 , 'Second'), ( 3 , 'Third'), ( 4 , 'Fourth')
keys = ("Name", "Email")
values = ("Bob"<EMAIL_ADDRESS>zipped = zip(keys, values)
print(list(zipped))
# => [('Name', 'Bob'), ('Email', 'Bob@bob.com')]
# You can zip more than two
x_coords = [ 0 , 1 , 2 , 3 , 4 ]
y_coords = [ 2 , 3 , 5 , 3 , 5 ]
z_coords = [ 3 , 5 , 2 , 1 , 4 ]
coords = zip(x_coords, y_coords, z_coords)
print(list(coords))
# => [(0, 2, 3), (1, 3, 5), (2, 5, 2), (3, 3, 1), (4, 5, 4)]
len([ 1 , 2 , 3 ]) # => 3
len(( 1 , 2 , 3 )) # => 3
len({
'Name': 'Bob',
'Email'<EMAIL_ADDRESS>}) # => 2
max( 1 , 4 , 6 , 2 ) # => 6
max([ 1 , 4 , 6 , 2 ]) # => 6
min( 1 , 4 , 6 , 2 ) # => 1
min([ 1 , 4 , 6 , 2 ]) # => 1
sum([ 1 , 2 , 3 ]) # => 6
any([True, False, False]) # => True
any([False, False, False]) # => False
any([True, False, False]) # => False
any([True, True, True]) # => False
user = {
'Name': 'Bob',
'Email'<EMAIL_ADDRESS>}
dir(user)
# => ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__',
# '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__',
# '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__',
# '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__',
# '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy',
# 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault',
# 'update', 'values']
math
| __init.py
| addition.py
| subtraction.py
| multiplication.py
| division.py
# addition.py
# We can import `add` from other places because it's a `name` and is
# AUTOMATICALLY exported
def add (num1, num2):
return num1 + num
# This imports the `add` function
# and now it's also re-exported in here as well!
from .addition import add
# These import and re-export the rest of the functions from the sub modules
from .subtraction import subtract
from .division import divide
from .multiplication import multiply
# This will load and execute the `math/__init__.py` file
# and give us an object with the exported names in `math/__init.py__`
import math
print(math.add( 1 , 2 )) # => 3
# This imports JUST the add from `math/__init__.py`
from math import add
print(add( 1 , 2 )) # => 3
# This skips importing from `math/__init__.py` (although it still runs)
# and imports directly from the addition.py file
from math.addition import add
print(add( 1 , 2 )) # => 3
# this imports all the functions individually from `math/__init.py`
from math import add, subtract, multiply, divide
print(add( 1 , 2 )) # => 3
print(subtract( 2 , 1 )) # => 1
# This imports `add` and renames it to `addSomeNumbers`
from math import add as addSomeNumbers
print(addSomeNumbers( 1 , 2 )) # => 3
class AngryBird :
# Slots optimize property access and memory usage
# and prevent you from arbitrarily assigning new properties to the instance
__slots__ = ['_x', '_y']
# constructor
def __init__ (self, x= 0 , y= 0 ):
# Doc string
"""
Construct a new AngryBird by setting its position to (0, 0).
"""
## Instance variables
self._x = x
self._y = y
# Instance method
def move_up_by (self, delta):
self._y += delta
# Getter
@property
def x (self):
return self._x
# Setter
@x.setter
def x (self, value):
if value < 0 :
value = 0
self._x = value
@property
def y (self):
return self._y
@y.setter
def y (self, new_y):
self._y = new_y
# Dunder Repr... called by `print`
def __repr__ (self):
return f"<AngryBird ({self._x}, {self._y})>"
newList = [value loop condition]
squares = []
for i in range( 10 ):
squares.append(i** 2 )
print(squares)
# Prints [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# value = i ** 2
# loop = for i in range(10)
squares = [i** 2 for i in range( 10 )]
print(list(squares))
# Prints [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
sentence = 'the rocket came back from mars'
vowels = [character for character in sentence if character in 'aeiou']
print(vowels)
# Prints ['e', 'o', 'e', 'a', 'e', 'a', 'o', 'a']
person = {
'name': 'Corina',
'age': 32 ,
'height': 1.
}
# This loops through and capitalizes the first letter of all the keys
newPerson = { key.title(): value for key, value in person.items() }
# Prints {'Name': 'Corina', 'Age': 32, 'Height': 1.4}
virtualenv
pipenv shell
python someprogram.py
pipenv run python someprogram.py
pipenv install flask
from flask import Flask
app = Flask(__name__)
pipenv run flask run
# config.py
class Config :
SOME_CONFIG_VARIABLE = 'Some value'
# app.py
# Import the config class
from config import Config
app = Flask(__name__)
# Load the config into Flask.
app.config.from_object(Config)
app.config['SOME_CONFIG_VARIABLE']
# Examples
@app.route('/')
def index ():
"""Put code here to execute when `/` is visited"""
pass
@app.route('/somepath')
def some_path ():
"""Put code here to execute when `/somepath` is visited"""
pass
# the <id> parameter will be captured and passed into the function as the first
# argument
@app.route('/item/<id>')
def item (id):
return f'
Item {id}
'
# You can also specify the type of the parameter by prepending it with the type
# and a colon
@app.route('/item/<int:id>')
def item (id):
return f'
Item {id}
'
@app.before_request
def before_request_function ():
print("before_request is running")
@app.after_request
def after_request_function (response):
print("after_request is running")
return response
@app.before_first_request
def before_first_function ():
print("before_first_request happens once")
http://localhost: 5000 /static/styles/main.css
├── Pipfile
├── Pipfile.lock
├── app <- directory where Flask is created
│ ├── __init__.py <- file in which Flask is created
│ ├── routes.py
│ ├── static <- static files served from here
│ │ └── styles
│ │ └── main.css
│ └── templates
│ └── main.html
└── app_loader.py
from flask_wtf import FlaskForm
class SampleForm (FlaskForm):
class SampleForm (FlaskForm):
name = StringField('Name')
from app.sample_form import SampleForm
# Create an instance of our form
form = SampleForm()
# And pass it to the view template
return render_template('form.html', form=form)
<form action="" method="post" novalidate>
{{ form.csrf_token }}

{{ form.name.label }}
{{ form.name(size=32) }}

{{ form.submit() }}

</form>
form.name(size= 32 , class_='name')
@app.route('/submit', methods=['POST'])
def handle_form_submit ():
if form.validate_on_submit():
# Do something with the form data.
# and return something
return
# You can put code here to handle what happens when
# the form fails validation, like redirecting or rendering
# the form again.
return
# This one prints out ALL the hidden fields including the CSRF that are
# defined on the form class
{{ form.hidden_tag() }
# While this one only prints out the CSRF token hidden field
{{ form.csrf_token() }}
class MyForm (FlaskForm):
field1 = StringField()
# admin.py
from flask import Blueprint
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
from admin import admin_bp
app = Flask()
app.register_blueprint(admin_bp)
@admin_bp.route('/', methods=('GET', 'POST'))
def admin_index ():
return
from flask import Flask, session
# To set something in the session
session['key'] = value
# To get something from the session
session.get('key')
# to remove something from the session
session.pop('key')
@app.route('/')
def index ():
return render_template('index.html', sitename='My Sample')
<title>{{ sitename }}</title>
{% include 'file.html' %}
import psycopg
CONNECTION_PARAMETERS = {
'dbname': 'psycopg_test_db',
'user': 'psycopg_test_user',
'password': 'password',
}
with psycopg2.connect(**CONNECTION_PARAMETERS) as conn:
print(conn.get_dsn_parameters())
import psycopg
CONNECTION_PARAMETERS = {
'dbname': 'psycopg_test_db',
'user': 'psycopg_test_user',
'password': 'password',
}
with psycopg2.connect(**CONNECTION_PARAMETERS) as conn:
print(conn.get_dsn_parameters())
with psycopg2.connect(**CONNECTION_PARAMETERS) as conn:
with conn.cursor() as curs:
curs.execute('SELECT manu_year, make, model FROM cars;')
cars = curs.fetchall()
for car in cars:
print(car) # (1993, 'Mazda', 'Rx7')
def print_all_cars ():
with psycopg2.connect(**CONNECTION_PARAMETERS) as conn:
with conn.cursor() as curs:
curs.execute('SELECT manu_year, make, model, owner_id FROM cars;')
cars = curs.fetchall()
for car in cars:
print(car)
print_all_cars()
# Output:
# (1993, 'Mazda', 'Rx7', 1)
# ...additional cars
from sqlalchemy import create_engine
engine = create_engine("postgresql://sqlalchemy_test:password@localhost/sqlalchemy_test")
from sqlalchemy import create_engine
db_url = "postgresql://sqlalchemy_test:password@localhost/sqlalchemy_test"
engine = create_engine(db_url)
with engine.connect() as connection:
result = connection.execute("""
SELECT o.first_name, o.last_name, p.name
FROM owners o
JOIN ponies p ON (o.id = p.owner_id)
""")
for row in result:
print(row["first_name"], row["last_name"], "owns", row["name"])
engine.dispose()
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
db_url = "postgresql://sqlalchemy_test:password@localhost/sqlalchemy_test"
engine = create_engine(db_url)
SessionFactory = sessionmaker(bind=engine)
session = SessionFactory()
# Do stuff with the session
engine.dispose()
# ponies.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
Base = declarative_base()
class Pony (Base):
__tablename__ = 'ponies'
id = Column(Integer, primary_key=True)
name = Column(String( 255 ))
birth_year = Column(Integer)
breed = Column(String( 255 ))
owner_id = Column(Integer, ForeignKey("owners.id"))
# owner.py
from .models import db
class Pony (db.Model):
__tablename__ = 'ponies'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String( 255 ))
birth_year = db.Column(db.Integer)
breed = db.Column(db.String( 255 ))
owner_id = db.Column(db.Integer, db.ForeignKey("owners.id"))
# The one
class Owner (db.Model):
__tablename__ = "owners"
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String( 255 ))
last_name = db.Column(db.String( 255 ))
email = db.Column(db.String( 255 ))
# ponies belong to an owner
ponies = db.relationship("Pony", back_populates="owner")
# The Many
class Pony (db.Model):
__tablename__ = "ponies"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String( 255 ))
birth_year = db.Column(Integer)
breed = db.Column(db.String( 255 ))
# The pony contains an owner_id foreign key
owner_id = db.Column(db.Integer, db.ForeignKey("owners.id"))
# An owner has many ponies
owner = db.relationship("Owner", back_populates="ponies")
# We define the foreign keys on our join table, which joins the Ponies
# to thier Handlers.
pony_handlers = db.Table(
"pony_handlers",
db.Column("pony_id", db.ForeignKey("ponies.id"), primary_key=True),
db.Column("handler_id", db.ForeignKey("handlers.id"), primary_key=True)
# Inside the Pony class...
handlers = db.relationship("Handler",
secondary=pony_handlers,
back_populates="ponies")
# Inside the Handler class...
ponies = db.relationship("Pony",
secondary=pony_handlers,
back_populates="handlers")
owner.ponies.append(pony)
print(pony.owner) # Returns None
you = Owner(first_name="your first name",
last_name="your last name",
email="your email")
your_pony = Pony(name="your pony's name",
birth_year= 2020 ,
breed="whatever you want",
owner=you)
# Note, id will be None until we commit
print(you.id) # > None
print(your_pony.id) # > None
session.add(you) # Connects you and your_pony objects
session.commit() # Saves data to the database
# After commiting the ids exist
print(you.id) # > 4 (or whatever the new id is)
print(your_pony.id) # > 4 (or whatever the new id is)
db.session.add(you) # Connects you and your_pony objects
db.session.commit() # Saves data to the database
print(your_pony.birth_year) # > 2020
# Updating is just like setting a property
your_pony.birth_year = 2019
# The pony instance updates immediately
print(your_pony.birth_year) # > 2019
# but the database doesn't update until we commit!
session.commit()
print(your_pony.birth_year) # > 2019
# Just passing the owner instance to delete, deletes it, but....
db.session.delete(you)
# It doesn't actually change the database until you commit!
db.session.commit()
class Owner (db.Model):
__tablename__ = 'owners'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String( 255 ))
last_name = db.Column(db.String( 255 ))
email = db.Column(db.String( 255 ))
# This is a relationship between Ponies and Owner.
# We have set it to cascase and delete orphans so
# when we delete an owner all the ponies related to
# that owner will be deleted
ponies = db.relationship("Pony",
back_populates="owner",
cascade="all, delete-orphan")
pony_query = session.query(Pony)
print(pony_query)
pony_id_4_query = session.query(Pony).get( 4 )
# This plain SQLAlchemy query:
pony = session.query(Pony).get( 4 );
# Can be re-written as:
pony = Pony.query.get( 4 )
owner_query = Owner.query(Owner.first_name, Owner.last_name)
.order_by(Owner.last_name)
print(owner_query)
pony_query = Pony.query.filter(Pony.name.like("%u%"))
pony_query = Pony.query
.filter(Pony.name.ilike("%u%"))
.filter(Pony.birth_year < 2015 )
ponies = Pony.query.all()
for pony in ponies:
print(pony.name)
pony_query = Pony.query
print(pony_query.count())
hirzai_owners = Owner.query \
.join(Pony) \
.filter(Pony.breed == "Hirzai")
for owner in hirzai_owners:
print(owner.first_name, owner.last_name)
for owner in Owner.query:
print(owner.first_name, owner.last_name)
for pony in owner.ponies:
print(pony.name)
owners_and_ponies = Owner.query.options(joinedload(Owner.ponies))
for owner in owners_and_ponies:
print(owner.first_name, owner.last_name)
for pony in owner.ponies:
print(pony.name)
pipenv install Flask psycopg2-binary \
SQLAlchemy Flask-SQLAlchemy
from config import Config
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config.from_object(Config)
# We are creating the DB in app.py after creating the app.
# So we can just pass our app to SQLAlchemy
db = SQLAlchemy(app)
# models.py
from flask_sqlalchemy import SQLAlchemy
# notice we create the db instance without passing it app
db = SQLAlchemy()
# app.py
from flask import Flask
from .config import Configuration
# The act of importing this creates the db instance
from .models import db
# We create our app here
app = Flask(__name__)
app.config.from_object(Configuration)
# We use init_app and pass it the app
db.init_app(app)
Pony.query.get( 4 )
Pony.query.get_or_404( 4 )
db.session.add(owner)
db.session.commit()
pipenv install alembic
pipenv run alembic init <directory-name>
import os
config.set_main_option("sqlalchemy.url", os.environ.get("DATABASE_URL"))
file_template = %% (year) d %% (month) .2d %% (day) .2d_ %% (hour) .2d %% (minute) .2d %% (second) .2d_ %% (slug) s
pipenv run alembic revision -m "create the owners table"
pipenv run alembic upgrade head
pipenv run alembic downgrade <revision hash>
pipenv run alembic downgrade base
pipenv run alembic history
revision = 'ddbf30c38165'
down_revision = 'e363377eb6d7'
-- ae1027a6acf (Team A's most recent)
/
< -- 1975ea83b712 <--
\
-- 27c6a30d7c24 (Team B's most recent)
FAILED: Multiple head revisions are present for given argument 'head';
please specify a specific target revision, '<branchname>@head' to
narrow to a specific head, or 'heads' for all heads
pipenv run alembic merge -m "merge contracts and devices" ae1027 27c6a
pipenv install alembic Flask-Migrate
# app/__init__.py
from app.models import db
from flask import Flask
from config import Config
# We have to import flask_migrate
from flask_migrate import Migrate
import os
app = Flask(__name__)
# Load our config, make sure you set DATABASE_URL as flask migrate
# uses it as well
app.config.from_object(Config)
db.init_app(app)
# And we have to do this to configure Flask Migrate. It needs to know about
# both our app and our db object
Migrate(app, db)
pipenv run flask db init
pipenv run flask db --help
Usage: flask db [OPTIONS] COMMAND [ARGS]...
Perform database migrations.
Options:
--help Show this message and exit.
Commands:
branches Show current branch points
current Display the current revision for each database.
downgrade Revert to a previous version
edit Edit a revision file
heads Show current available heads in the script directory
history List changeset scripts in chronological order.
init Creates a new migration repository.
merge Merge two revisions together, creating a new revision file
migrate Autogenerate a new revision file (Alias for 'revision...
revision Create a new revision file.
show Show the revision denoted by the given symbol.
stamp 'stamp' the revision table with the given revision; don't run...
upgrade Upgrade to a later version
pipenv run flask db migrate -m "create owners table"


| Larissa |
Diam Lorem Incorporated |
Rimouski |
December 17th, 2013 |
$59,551.42 |
| Martina |
Vestibulum PC |
Dietzenbach |
January 21st, 2004 |
$56,364.26 |
| Mariam |
Per Inceptos Hymenaeos Inc. |
Appelterre-Eichem |
July 2nd, 2007 |
$56,969.65 |
| Lamar |
Enim Diam Institute |
Alcalá de Henares |
November 10th, 2015 |
$97,614.15 |
| Emerson |
Magna Phasellus Corporation |
Kidderminster |
June 28th, 2005 |
$184,630.32 |
| Abel |
Et Libero Associates |
Montaldo Bormida |
September 7th, 2016 |
$79,165.03 |
| Linus |
Morbi Institute |
Borno |
September 4th, 2013 |
$737,084.34 |
| Shaine |
Maecenas Corp. |
Angleur |
July 7th, 2011 |
$98,935.36 |
| Noah |
Convallis Est Associates |
St. Albert |
January 22nd, 2000 |
$125,297.34 |
| Edan |
Nec LLP |
Norman Wells |
May 12th, 2012 |
$268,738.17 |
| Winter |
Vivamus Nibh Institute |
Kamarhati |
December 31st, 2003 |
$077,759.87 |
| Donna |
Neque Sed PC |
Ashoknagar-Kalyangarh |
March 26th, 2013 |
$395,671.49 |
| Hope |
Nunc Ullamcorper Eu Institute |
Curon Venosta/Graun im Vinschgau |
April 29th, 2013 |
$413,663.84 |
| Lysandra |
Nunc Incorporated |
North Las Vegas |
December 15th, 2006 |
$36,625.73 |
| Lester |
Donec Elementum Lorem Company |
Molina |
October 16th, 2005 |
$85,300.38 |
| Lila |
Et Magnis Dis Ltd |
Ranchi |
January 14th, 2010 |
$75,908.34 |
| Destiny |
Integer Associates |
Stonehaven |
July 19th, 2003 |
$110,504.85 |
| Tobias |
Pede Blandit Consulting |
Aix-en-Provence |
June 14th, 2012 |
$780,320.18 |
| Addison |
Aliquet Associates |
Kidwelly |
November 25th, 2007 |
$636,785.81 |
| Carl |
Eu Arcu Morbi Corporation |
Papudo |
May 24th, 2000 |
$10,512.48 |
| Indira |
Proin Nisl Consulting |
Sperlinga |
March 23rd, 2008 |
$133,819.74 |
| Preston |
Nisl Quisque Inc. |
Burdinne |
April 12th, 2008 |
$736,544.08 |
| Wing |
Purus Nullam PC |
Chastre |
December 15th, 2014 |
$810,872.61 |
| Carson |
Integer Vulputate Corporation |
Muridke |
September 16th, 2013 |
$819,881.70 |
| Burton |
Enim Consulting |
New Orleans |
December 10th, 2009 |
$20,528.66 |
| Henry |
Tellus Suspendisse Limited |
Hénin-Beaumont |
June 5th, 2007 |
$892,303.87 |
| Hayden |
Malesuada PC |
Abbotsford |
July 3rd, 2010 |
$88,521.46 |
| Chiquita |
Dolor Vitae Corporation |
Dublin |
July 3rd, 2013 |
$459,578.10 |
| Olga |
Orci Corp. |
Colombo |
October 15th, 2014 |
$1,009.43 |
| Meghan |
Maecenas Foundation |
Coventry |
March 14th, 2016 |
$93,679.83 |
| Quintessa |
Dui Nec Tempus Ltd |
Marcedusa |
July 1st, 2006 |
$985,245.40 |
| Adena |
Neque Nullam Corporation |
Neusiedl am See |
June 9th, 2013 |
$262,727.14 |
| Francis |
Consectetuer Euismod Consulting |
Bondo |
September 2nd, 2006 |
$221,293.44 |
| Tobias |
Lorem Donec Limited |
Brampton |
April 30th, 2006 |
$661,314.70 |
| Emerson |
Lorem Limited |
Irvine |
July 19th, 2003 |
$46,741.66 |
| Allistair |
Enim Sit Amet Corporation |
Huntley |
October 26th, 2007 |
$96,770.71 |
| Philip |
Habitant Morbi Tristique Company |
Lede |
January 2nd, 2015 |
$485,292.39 |
| Erasmus |
Ad Inc. |
Holywell |
May 18th, 2013 |
$089,276.76 |
| Herman |
Purus PC |
Salerno |
December 25th, 2011 |
$506,785.31 |
| Travis |
Ante Inc. |
Grafton |
February 18th, 2013 |
$509,585.54 |
| Jaime |
Sed Hendrerit A Corporation |
Beerse |
October 17th, 2006 |
$780,839.78 |
| Libby |
Pede Nec Ante Associates |
Bad Neuenahr-Ahrweiler |
September 15th, 2002 |
$86,363.97 |
| Cheyenne |
Rutrum Lorem Ac Foundation |
Ayr |
September 12th, 2007 |
$92,683.35 |
| Ashely |
Lacinia At Limited |
North Berwick |
January 15th, 2010 |
$77,223.68 |
| Steven |
Lobortis Incorporated |
Bergen |
August 11th, 2008 |
$173,421.15 |
| Samantha |
Ut Mi Duis Consulting |
Olympia |
November 1st, 2000 |
$610,615.49 |
| Tanek |
Sed Consulting |
Primavera |
December 24th, 2006 |
$531,186.93 |
| Cally |
Dapibus Foundation |
Ottawa-Carleton |
August 26th, 2010 |
$55,844.38 |
| Ulysses |
Sit Amet Inc. |
Unnao |
May 13th, 2011 |
$31,900.35 |
| Kay |
Pharetra Associates |
Kukatpalle |
November 6th, 2003 |
$32,124.01 |
| McKenzie |
Tellus Industries |
Lasne |
June 27th, 2004 |
$764,333.54 |
| Abraham |
Eu Sem PC |
Sombreffe |
June 21st, 2007 |
$55,023.44 |
| Hu |
Semper Nam Corp. |
Crescentino |
August 29th, 2011 |
$981,628.21 |
| Barbara |
Vitae Sodales Nisi Industries |
Merksplas |
June 21st, 2014 |
$735,343.29 |
| Davis |
Enim Sit Amet Corp. |
Great Yarmouth |
May 8th, 2009 |
$977,281.22 |
| Kai |
Gravida Mauris Ut LLP |
Viggianello |
January 24th, 2004 |
$648,933.38 |
| Bernard |
Risus A Ultricies Company |
Denver |
April 23rd, 2012 |
$47,200.87 |
| Tasha |
Diam Limited |
Arvier |
July 15th, 2014 |
$97,905.24 |
| Kim |
In Consequat Enim LLC |
Hannche |
January 10th, 2002 |
$772,549.86 |
| Francis |
Cum Industries |
Plainevaux |
July 6th, 2002 |
$16,200.60 |
| Haley |
Ante Ltd |
Detroit |
March 31st, 2008 |
$951,003.36 |
| Lucas |
Bibendum Ullamcorper Duis Industries |
Opheylissem |
September 9th, 2000 |
$307,455.84 |
| Ina |
Sodales Purus PC |
Andernach |
August 22nd, 2010 |
$785,285.49 |
| Candace |
At Inc. |
Neustadt am Rübenberge |
October 28th, 2001 |
$26,699.45 |
| Bruno |
Eros Nam Consequat Foundation |
Mount Gambier |
April 1st, 2001 |
$33,748.19 |
| Armand |
Hendrerit A Associates |
Ipswich |
June 19th, 2005 |
$15,127.34 |
| Solomon |
Velit Institute |
Santa Inês |
December 1st, 2015 |
$973,534.98 |
| Brock |
Ultricies Dignissim Lacus Incorporated |
Nedlands |
June 13th, 2007 |
$67,421.95 |
| Kylan |
Phasellus Incorporated |
Surat |
May 9th, 2014 |
$930,454.95 |
| Tyrone |
Scelerisque Sed Industries |
Niel-bij-As |
January 23rd, 2007 |
$97,941.04 |
| Wesley |
Enim LLC |
Villa Alemana |
November 15th, 2000 |
$24,009.66 |
| Zenia |
Pede Sagittis Company |
Castelnovo del Friuli |
January 17th, 2015 |
$46,382.42 |
| Carson |
Eget Company |
Valladolid |
February 14th, 2003 |
$89,360.60 |
| Wallace |
Proin Eget Ltd |
Sint-Pieters-Leeuw |
October 9th, 2016 |
$42,754.66 |
| Adele |
Molestie Ltd |
Sesto Campano |
January 29th, 2016 |
$203,840.80 |
| Gabriel |
Commodo Ltd |
Alviano |
June 22nd, 2005 |
$4,489.87 |
| Shad |
Velit Quisque Varius Ltd |
Olcenengo |
June 4th, 2003 |
$70,382.52 |
| Brielle |
Elit Curabitur Institute |
Martelange |
May 18th, 2012 |
$4,281.00 |
| Ivy |
Orci Luctus Et Ltd |
Rum |
September 1st, 2008 |
$59,423.15 |
| Ryan |
Ornare Fusce Mollis Foundation |
Champion |
March 16th, 2005 |
$94,056.99 |
| Raven |
Mauris Vestibulum Industries |
Meduno |
June 8th, 2006 |
$02,976.28 |
| Macon |
Tortor At Risus Corp. |
Sellano |
May 25th, 2000 |
$678,861.04 |
| Hop |
Mauris Ut Corp. |
Tiel |
January 28th, 2011 |
$529,032.75 |
| Joel |
Parturient Montes Incorporated |
Viña del Mar |
August 12th, 2014 |
$430,336.17 |
| Dale |
Donec Luctus Consulting |
St. Petersburg |
March 29th, 2002 |
$50,806.22 |
| Sacha |
Et Netus LLP |
Drayton Valley |
June 26th, 2001 |
$33,257.82 |
| Zachery |
Tempor Diam Dictum Foundation |
West Vancouver |
April 3rd, 2003 |
$98,970.97 |
| Samantha |
Augue Eu PC |
Bossut-Gottechain |
February 13th, 2013 |
$65,573.53 |
| Kelly |
Nunc Foundation |
Whitchurch-Stouffville |
October 28th, 2001 |
$25,788.39 |
| Kimberly |
Eget Lacus Mauris Company |
Awka |
July 26th, 2002 |
$816,683.58 |
| Brian |
Vitae Risus Ltd |
Poggio San Marcello |
July 2nd, 2010 |
$14,877.34 |
| Hilel |
Est Mauris Eu Ltd |
Oetingen |
September 11th, 2000 |
$353,733.25 |
| Jessamine |
Quam Limited |
Koersel |
April 24th, 2004 |
$339,697.16 |
| Gareth |
Bibendum Company |
Oban |
November 30th, 2010 |
$899,960.57 |
| Barbara |
In Faucibus Consulting |
Cairns |
April 27th, 2008 |
$3,114.10 |
| Hayley |
Vivamus Sit LLP |
Graneros |
January 12th, 2008 |
$510,112.94 |
| Berk |
Dolor Dapibus Gravida Institute |
Heppignies |
December 28th, 2006 |
$81,131.52 |
| Valentine |
Semper Foundation |
Baisy-Thy |
August 19th, 2004 |
$81,308.45 |
| Kimberley |
Non Egestas A Inc. |
Fremantle |
February 7th, 2016 |
$218,565.75 |
| Hammett |
Dui LLC |
Zuccarello |
June 27th, 2010 |
$219,811.59 |


"The Siege" is already under attack by Arab anti-defamation groups, thanks to its alarming -- and misleading -- trailer which shows U.S. army troops rounding up Arabs and forcing them into internment camps ringed by barbed-wire fences. However, anyone who shows up to protest what they assume is going to be an ultra-right-wing fantasy about the benefits of martial law will feel rather silly if they actually watch the film, which all but bends over backward to portray Middle Easterners in a favorable light. "The word 'Arab' is not synonymous with the word 'terrorist'," an off-screen voice assures us at one point. But at the same time, "The Siege" also errs in making most of its Arab and Arab-American characters faceless and nameless. Rather than being depicted as real people, they're shown largely as a mass of bodies being pushed around, waiting to be rescued by sympathetic outsiders. It's almost as silly as the infamous "Cry Freedom," which tried to relate the story of Steven Biko through the eyes of a white English family. In many ways, "The Siege" is a movie operating at cross-purposes with itself, a tell-tale sign that too many writers worked over the script. In this case, three are credited, although the writing is so uneven you can bet many more cooks stirred this particular potboiler. Thanks largely to director Edward Zwick ("Glory," "Courage Under Fire"), "The Siege" does manage to pull off a few genuinely startling moments, such as when a meeting of leaders is mysteriously disrupted by one pager after another going off, or when tanks roll through the streets of New York. In the film, New York is targeted by terrorists, and FBI agent Anthony Hubbard (Denzel Washington) and his partner Frank Haddad (Tony Shaloub) are forced to accept the assistance of mysterious Elise Kraft (Annette Bening), who's had more than diplomatic relations with some Arab suspects. In fact, Elise's idea of pillow talk includes asking about the location of terrorist cells in the vacinity of Manhattan. When the combined efforts of the FBI and the CIA fail, the President sends General Deveraux (Bruce Willis) and scores of troops to seal off Brooklyn and enforce martial law. Though Deveraux appears to be level-headed at first, soon he's become a maniac who bellows "I am the law" and feels no shame about torturing civilians. Consistency is not one of "The Siege"'s strong suits. Then again, the movie is more successful at creating tension than in building credible characters, which sometimes leaves Washington and Bening looking borderline silly as they try to rationalize Hubbard and Kraft's schizoid personalities. As in too many of his other recent appearances, Willis is present only to preen, in this case underneath one of the worst toupees since Burt Reynolds' heyday. Perhaps "The Siege" should be picketed -- by former fans anxious to see Willis break out of his terminal self-adoration and begin acting again. James Sanford
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Dashboard
(current)
Info
Account
School Name
School Address
School Website
Principle Name
Principle Email
Other important websites