This is an old revision of the document!
Table of Contents
Programming with ULAM
If you don't have ULAM compiler, here is the portal to Install ULAM. If you already have ULAM, let's start programming. We can mkdir for our element repository. In the following examples, we use home/user/elements/ to store our source codes for elements. They are programmed and saved as .ulam files.
A Few Preliminaries
To help reduce confusion going forward, it's important to introduce a couple key terms and ideas of programming language ULAM.
Element vs Atom
In ULAM, as in many (but not all) object-oriented languages, there is a strong distinction between a description of a thing and an instance of that thing. In Java or C++, for example, the description of a thing is called a class while an instance of a thing is called an object. In ULAM, the description is called an element and the instance is called an atom: An atom is an instance of an element. If Foo
is an element, we've got the language just right if we say something like “This function creates a new Foo atom to the west”, but we'd be misusing it if we said “This function creates a new Foo element to the west.”
Capitalization Matters
In ULAM, we capitalize the first letter of all Type names. Type names include the names of the elements we write, such as the element First
below, as well as 'primitive' types like Int
, Bool
, and Void
.
Similarly, the first letter of every variable name, and of the name of every function or method (both terms mean the same thing in ULAM), must be a lowercase letter. So examples of legal variable or function names could include i
, count
, isInitted
, or cMAX_VALUE
, but not IsInitted
, Reset
, or MAX_VALUE
.
The 'First' Element
Our first ULAM element is literally named First
, and is stored in a file called First.ulam:
- First.ulam
element First{ EventWindow ew; Void behave(){ ew[1]=ew[0]; } }
The syntax of an element is reminiscent of a class in Java or other object-oriented language. Inside the main {} brackets are a combination of data members and method declarations. In element First
, we have one data member: ew
, an instance of type EventWindow
, and one method, behave
, that takes no arguments and returns Void
.
The function Void behave()
is special because it is called automatically when it is time for a First
atom to perform an event. An element is not required to provide a Void behave()
method, but if it doesn't, an instance of that element will just sit passively and do nothing whenever its turn to have an event occurs.
An instance of EventWindow
provides access to the neighborhood during an event. It has 41 cells. The event center, where an element of type First
will be found when behave()
begins, is ew[0]
. ew[1]
is the cell 1 step west of the center ew[0] (in the normal case), so the above code will duplicate the center atom along the x axis towards the west. This element behaves like an arrow in the MFM, creating a line of First
atoms, growing to the west until it runs out of space.
Compile
Because we are now in our element directory, we must specify where the ULM compiler is located. (We could also adjust our PATH environment variable in any normal 'Unixy' way.)
/home/user/ULAM/ulam1/bin/ulam -l First.ulam
Running ulam with the -h
switch will show all the switches for the ULAM compiler. The switch -l
requests the creation of a dynamically-loadable library file from all the .ulam files on the command line. The resulting library file, called libcue.so
, will be created in the /home/user/elements/.gen/bin/
folder.
Run
We also must specify where the simulator mfms
is located. The switch -ep
tells mfms
to load our ulam elements. The mfms -h can show all the switches for MFM.
/home/user/MFM/bin/mfms -ep /home/user/elements/.gen/bin/libcue.so
Here is some screenshots of the mfms simulator at startup . The simulator is an 160*96 canvas. By default the help menu will pop at startup. We can use
mfms -n
to switch it off from the beginning or use the 'h' key-command to turn it off.
The 't' command will show us the palette, the pencil tool, how to place a First
atom. How to start the simulation. Screenshot after a couple AEPS. Screenshot after many AEPS.
The 'Second' Element
In ULAM, we use structured comments before elements, not just to document the code, but actually to affect the behavior of elements.
- Second.ulam
/** The Second element demonstrates the power of structured comments in ULAM. Even though the code looks almost identical to First, its behavior is totally different! \color #2e2 \symbol S1 \symmetries all */ element Second{ Void behave(){ EventWindow ep; ep[1]=ep[0]; } }
The parameters in the structured comments can specify not only the color and/or symbol of the element, but also its “symmetries”. Symmetries determine the layout of the EventWindow coordinate axes during events, By default, the only allowed symmetry is “North goes up, East goes right”, but if we said \symmetries 90L
(90 degrees left), then North would go left and East would go up.
But that's not all: We can specify multiple symmetries in the \symmetries
declaration, in which case one of them will be picked at random before each event. Second
is an example of the most extreme case, using all
to enable all eight flips and rotations of the axes. With a random coordinate transformation on each event, the “west” neighbor might actually be any of the four sites immediately adjacent to the Second atom. Look what Second
does:
TODO Screenshots
The 'Third' Element
We already know duplicate. Then we can swap.
/** Third is a demo element. \color #f0f \symbol T1 \symmetries normal */ element Third{ Void behave(){ EventWindow ep; ep.swap(0,1); } }
The symmetries is normal. The element will swap itself with its west neighbor. So our Third element will fly along the x axis to the west.
The 'Flip' Element
This element Five will shake back and forth 1 step along x axis.
/** Fifth is a demo element. \color #606 \symbol Fv \symmetries 0 */ element Five{ Bool xFlip; Void behave(){ EventWindow ew; if (xFlip==false){ xFlip=true; ew.swap(0,4); } else{ xFlip=false; ew.swap(0,1); } } }
The xFlip is stored in the ew[0]. Make sure to change this field before swap. If we do swap first, this field will not store the value that we want it to save.
The 'Box' Element
This element Box will move in a loop of north, east, south, west. It seems like moving inside a box.
/** Box is a demo element. \color #666 \symbol Bx \symmetries normal \author Xinyu Chen \author Dave Ackley \license lgpl */ element Box{ // Typedefs typedef EventWindow.Symmetry Symmetry; // Utilities DebugUtils du; EventWindow ew; // Data members Unsigned(2) ns; Void behave(){ ew.changeSymmetry((Symmetry) (ns + 1)); if(ns == ns.maxof) ns = ns.minof; else ++ns; ew.swap(0, 1); } }
The ew.changeSymmetry()
method accepts 0,1,2,3,4,5,6,7. We use 0,1,2,3 to let the EventWindow rotate 0,90,180,270 degree to the right hand direction(clockwise). In ULAM primitive types like Unsigned
, we can specify any number of bits from 1 to 32. The type Unsigned(2)
uses two bits and has possible values 0,1,2,3. So by giving our ns
data member the type Unsigned(2)
we use as few bits as possible while providing four possible states.