Plain English Programming

Having programmed for many years in many languages, I often find myself thinking in English pseudo-code, then I translate my thoughts into whatever artificial syntax I’m working with at the time. So one day I thought, “Why not simply code at a natural language level and skip the translation step?” My elder son (also a programmer) and I talked it over, and we decided to test the theory. Specifically, we wanted to know:

1. Is it easier to program when you don’t have to translate your natural-language thoughts into an alternate syntax?

2. Can natural languages be parsed in a relatively “sloppy” manner (as humans apparently parse them) and still provide a stable enough environment for productive programming?

3. Can low-level programs (like compilers) be conveniently and efficiently written in high level languages (like English)?

And so we set about developing a Plain English compiler (in Plain English) in the interest of answering those questions. And we are happy to report that we can now answer each of those three questions, from direct experience, with a resounding, “Yes!”

The Theory

Our parser operates, we believe, something like the parsing centers in the human brain. Consider, for example, a father saying to his baby son…

“Want to suck on this bottle, little guy?”

…and the kid hears…

“blah, blah, SUCK, blah, blah, BOTTLE, blah, blah.”

…but he properly responds because he’s got a “picture” of a bottle in the right side of his head connected to the word “bottle” on the left side, and a pre-existing “skill” near the back of his neck connected to the term “suck.” In other words, the kid matches what he can with the pictures (types) and skills (routines) he’s accumulated, and simply disregards the rest. Our compiler does very much the same thing, with new pictures (types) and skills (routines) being defined — not by us, but — by the programmer, as he writes new application code.

The Practice

A typical type definition looks like this:

A polygon is a thing with some vertices.

Internally, the name “polygon” is now associated with a dynamically-allocated structure that contains a doubly-linked list of vertices. “Vertex” is defined elsewhere (before or after this definition) in a similar fashion; the plural is automatically understood.

A typical routine looks like this:

To append an x coord and a y coord to a polygon:
Create a vertex given the x and the y.
Append the vertex to the polygon’s vertices.

Note that formal names (proper nouns) are not required for parameters and variables. This, we believe, is a major insight. A real-world chair or table is never (in normal conversation) called “c” or “myTable” — we refer to such things simply as “the chair” or “the table”. Likewise here: “the vertex” and “the polygon” are the most natural names for these variables.

Note also that spaces are allowed in routine and variable names (like “x coord”). It’s surprising that all languages don’t support this feature; this is the 21st century, after all. Note also that “nicknames” are also allowed (such as “x” for “x coord”). And that possessives (“polygon’s vertices”) are used in a very natural way to reference fields within records.

Note, as well, that the word “given” could have been “using” or “with” or any other equivalent since our sloppy parsing focuses on the pictures (types) and skills (routines) needed for understanding, and ignores, as much as possible, the rest.

Like a Math Book

At the lowest level, things look like this:

To add a number to another number:
Intel $8B85080000008B008B9D0C0000000103.

Note that in this case we have both the highest and lowest of languages — English and machine code (in hexadecimal) — in a single sentence. The insight here is that a program should be written primarily in a natural language, with snippets of code in more appropriate syntax as (and only as) required. Like a typical math book: mostly natural language with formula snippets interspersed.

We hope someday the technology will be extended, at the high end, to include Plain Spanish, and Plain French, and Plain German, etc; and at the the low end to include “snippet parsers” for the most useful, domain-specific languages. Español Llano, thanks to our helper Pablo in Argentina, is now up and running.

An Objection Answered

Now perhaps you’re thinking natural language programming is a silly idea. But have you considered the fact that most of the code in most programs does simple stuff like “move this over there” and “show that on the screen” — things that can be most conveniently and most naturally expressed in a natural language? Let’s consider an example we can examine in detail:

Our compiler — a sophisticated Plain-English-to-Executable-Machine-Code translator — has 3,050 imperative sentences in it.

1,306 of those (about 42%) are conditional statements, and at least half of those are trivial things like these:

If the item is not found, break.
If the compiler’s abort flag is set, exit.

The remainder of those conditional statements are slightly more complex, but all of them fit on a single line (with our font, in our editor). Here are a couple of the longer ones:

If the length is 4, attach $FF32 to the fragment’s code; exit.
If the rider’s token is any numeric literal, compile the literal given the rider; exit.

Of the remaining sentences:

272 (about 9%) are simple assignment statements:

Put the type name into the field’s type name.

202 (about 7%) are just the infrastructure for various loops:

Loop.
Get a field from the type’s fields.
[ other stuff here]
Repeat.

183 (6%) simply add something to the end of this or that list, like so:

Add the field to the type’s fields.

164 (about 5%) are trivial statements used to return boolean results, start and stop various timers, show the program’s current status, and write interesting things to the compiler’s output listing.

Say no.
Say yes.
Set the variable’s compiled flag.
Start the compiler’s timer.
Stop the compiler’s timer.
Show status “Compiling…”.
List the globals in the compiler’s listing.

119 (about 4%) advance the focus in the source code, sentences like:

Bump the rider.
Move the rider (code rules).

92 (about 3%) are used to create, destroy and keep internal indexes up to date, sentences like:

Create the type index using 7919 for the bucket count.
Index the type given the type’s name.
Destroy the type index.
58 (about 2%) are used to find things in various lists:
Find a variable given the name.
37 (about 1%) are calls to various conversion routines:
Convert the rider’s token to a ratio.

31 (about 1%) are used to generate actual machine code (plus those that appear in conditional statements, as above):

Attach $E8 and the address to the fragment.

And that accounts for 80% of the code in our compiler.

Only 57 of the remaining sentences (less than 2% of the whole) are mathematical in nature, a line here and there like these:

Add 4 to the routine’s parameter size.
Subtract the length from the local’s offset.
Multiply the type’s scale by the base type’s scale.
Calculate the length of the field’s type.
Round the address up to the nearest multiple of 4096.

And the rest are not formulaic at all. Stuff like:

Copy the field into another field.
Append the fragment to the current routine’s fragments.
Abort with “I was hoping for a definition but all I found was ‘” then the token.
Initialize the compiler.
Remove any trailing backslashes from the path name.
Reduce the monikette’s type to a type for utility use.
Eliminate duplicate nicknames from the type’s fields.
Prepend “original ” to the term’s name.
Extend the name with the rider’s token.
Unquote the other string.
Read the source file’s path into the source file’s buffer.
Generate the literal’s name.
Extract a file name from the compiler’s abort path.
Write the compiler’s exe to the compiler’s exe path.
Swap the monikettes with the other monikettes.
Skip any leading noise in the substring.
Scrub the utility index.
Fill the compiler’s exe with the null byte given the compiler’s exe size.
Position the rider’s token on the rider’s source.
Pluralize the type’s plural name.
Link.
Finalize the compiler.
Check for invalid optional info on the type.

And that’s why we say that most of what most programs do is easy stuff, stuff that can be conveniently expressed in a natural language. And that, in turn, is why we like programming in Plain English: the thoughts in our heads are typed in as Plain English “pseudo code” and, with a tweak here and there, that pseudo code actually compiles and runs. And is self-documenting, to boot.

Another Objection Answered

You may be thinking that natural language is just too verbose for programming. But is it really that bad? Let’s consider a couple of examples. In a traditional programming langauge, we might draw a box using a statement like this:

substring.draw ( box, color, source.text.font, source.text.alignment ) ;

Which is 10 words and 11 punctuation marks: 21 total elements.

The Plain English equivalent would be:

Draw the substring in the box with the color and the source’s text’s font and alignment.

Which is 16 words and 3 punctuation marks: 19 total elements.

Admittedly, the Plain English version requires a few more easy-to-type alphabetic characters (it’s difficult to say exactly how many since traditional coders put spaces in different places); but that’s a small price to pay for not having to learn (or think in) an artificial syntax.

Here’s another example:

if ( ! source.colorized ( ) ) color = black ;

Which is 5 words and 8 punctuation marks: 13 total elements.

Compared with the Plain English:

If the source is not colorized, put black into the color.

Which is 11 words and 2 punctuation marks: 13 total elements.

Again, it’s mostly a matter of whether you like to type words or (specialized) punctuation. And whether you like to think in two different syntactical and grammatical forms simultaneously. And whether you want your code to be self-documenting. And whether you want code that’s friendly for beginners. And whether you want to code in a language (like English) that will still be in common use 100 years from now. Personally, we think you may have lost some human perspective if you’ve come to think that “(!source.colorized())” is a good way of saying anything!

The Prototype

If you’re interested, you can download the whole shebang here:

www.osmosian.com/cal-4700.zip

It’s a small Windows program, less than a megabyte in size. But it’s a complete development environment, including a unique interface, a simplified file manager, an elegant text editor, a handy hexadecimal dumper, a native-code-generating compiler/linker, and even a wysiwyg page layout facility (that we used to produce the documentation). It is written entirely in Plain English. The source code (about 25,000 sentences) is included in the download. No installation is necessary; just unzip. Start with the “instructions.pdf” in the “documentation” directory and before you go ten pages you won’t just be writing “Hello, World!” to the screen, you’ll be re-compiling the entire thing in itself (in less than three seconds on a bottom-of-the-line machine from Walmart).

Thanks for your time and interest.


Gerry Rzeppa
Grand Negus of the Osmosian Order of Plain English Programmers


Dan Rzeppa
Prime Assembler of the Osmosian Order of Plain English Programmers

Teaching Kids to Program

We take a different approach to teaching kids how to program here at the Osmosian Order of Plain English Programmers.

The Interface

The first thing we do is remove all unnecessary and distracting clutter. This, for example, is what our full-screen Integrated Development Environment (IDE) looks like:

Alphabetical menus and a status message field at the top, work area in the middle, and tabs at the bottom. No window borders, no widgets, no icons, no tool bars, and no palettes. We thus encourage the student to focus on the content in the foreground, not the background (which we leave in the background where it belongs). Whoops! Almost forgot. No scroll bars. Ever. The right mouse button is used to shove content around — horizontally, vertically, and/or diagonally — and an Incremental Find facility is used to “leap” in large files (ala the late, great Jef Raskin). The Home, End, Page Up, Page Down and Arrow keys are also used, intuitively, to change the focus as necessary.

The file system is exposed to the programmer using full path names. This is the only view the programmer is given: there are no icons, and no alternate views in “open” and “save as” dialog boxes (because we don’t use or need dialog boxes). The students are thus introduced to the exact syntax that they will need to reference files in their programs, and they only have to maintain a single mental image of the file system in their budding brains.

Our student’s applications are also clutter-free. When they clear the screen, it is erased to black, top-to-bottom and side-to-side. Their applications are real, native programs, with access to the whole screen (and computer), so their dreams can come to life exactly as they imagined them. No sandboxes.

The Language

The second difference in our approach is that we program in natural, English-language sentences. Our “keywords” are articles (like aanthe and some), and prepositions (like inof and with, etc) rather than obscure words like CONST and EVAL and INSTANCEOF (which, technically, aren’t real words at all).

We also make a point of concentrating on semantics (WHAT is being said) rather than syntax (HOW it is being said). Routines in Plain English typically have multiple headers so they can be called in various ways. This routine, for example…

To erase the screen;
To blank out the screen;
To wipe off the screen;
To clear the screen:
Unmask everything.
Draw the screen’s box with the black color and the black color.
Refresh the screen.
Put the screen’s box into the context’s box.

…can be called four different ways:

Erase the screen.
Blank out the screen.
Wipe off the screen.
Clear the screen.

The skilled teacher, familiar with the way his students normally speak, typically provides pre-coded libraries of commonly used routines with headers in the “local dialect.” Students are also encouraged to teach the compiler to speak (and think) as they do, by adding headers to existing libraries, and by developing libraries of their own.

The Depth

The third difference in our approach is that we we use the same interface and language for both novices and experts alike. This is a system for kids, but it isn’t just a system for kids. We used this very interface and language ourselves to conveniently and efficiently create the whole shebang: uncluttered desktop, simplified file manager, elegant text editor, handy hexadecimal dumper, native-code-generating compiler/linker, and wysiwyg page layout facility for documentation and other creative writing and drawing tasks. So when the student is ready to dig deeper, he can simply dig. He doesn’t have to invest in a new shovel or find another plot of land. It’s all in one place, from “Hello, World!” to the machine code.

Hello World!

Speaking of “Hello, World!”, this is a version we often use to illustrate the fundamental concepts of sequence, conditional statements, and looping. This is what we tell the students to type in:

To run:
Start up.
Clear the screen.
Use medium letters. Use the fat pen.
Pick a really dark color.
Loop.
Start in the center of the screen.
Turn left 1/32 of the way.
Turn right. Move 2 inches. Turn left.
Write “HELLO WORLD”.
Refresh the screen.
Lighten the current color about 20 percent.
Add 1 to a count. If the count is 32, break.
Repeat.
Wait for the escape key.
Shut down.

And this is what they see on the screen when they run their programs (using the Run command, which is conveniently and intuitively located under the “R” menu):

Desert Landscapes

Programming is a textual, left-brain kind of activity. But students learn best when both sides of their brains are engaged. So most of our introductory exercises produce inspiring, graphical outputs to enliven both hemispheres and the corpus callosum that connects them. Here’s another sample program:

To run:
Start up.
Draw a desert landscape.
Wait for the escape key.
Shut down.

To draw a desert landscape:
Clear the screen.
Draw the sky.
Draw the sun.
Draw the birds.
Draw the sand.

To draw the sky:
Use the lightest sky blue pen.
Imagine a line across the middle of the screen’s box.
Loop.
Draw the line.
Refresh the screen.
Darken the current color about 3 percent.
Move the line up 1 pixel.
If the line is above the screen’s box’s top, break.
Repeat.

To draw the sun:
Pick a spot anywhere in the top middle 1/4 of the screen’s box.
Make a dot between 1/4 inch and 1 inch wide.
Center the dot on the spot.
Draw the dot with the lightest yellow color.

To draw the birds:
Pick a spot in the screen’s box about 1 inch above the middle.
Use the black pen.
Loop.
Move to the spot.
Face east.
Pick a width between 1/8 inch and 1/4 inch.
Draw a quarter circle given the width.
Turn around.
Draw another quarter circle given the width.
Move the spot about 1/2 inch in any direction.
Add 1 to a count. If the count is 3, break.
Repeat.

To draw the sand:
Use the lightest orange pen.
Imagine a line across the middle of the screen’s box.
Loop.
Draw the line.
Refresh the screen.
Darken the current color about 3 percent.
Move the line down 1 pixel.
If the line is below the screen’s box’s bottom, break.
Repeat.

Note the seamless integration of vector graphics and turtle graphics. Here’s the kind of artwork that program produces:

Optical Illusions

Optical illusions are always fun and inspiring. This is one of my personal favorites:

To run:
Start up.
Draw the crooked line illusion.
Wait for the escape key.
Shut down.

To draw the crooked line illusion:
Clear the screen.
Use the fat pen.
Imagine a big box 4 inches smaller than the screen’s box.
Imagine a line across the top of the big box.
Imagine a small box 1/2 inch by 1/2 inch.
Move the small box to the top left corner of the big box.
Loop.
Draw and fill the small box with the white color.
Move the small box right 1 inch.
Refresh the screen.
If the small box is still in the big box, repeat.
Move the small box close to the left side of the big box.
Draw the line with the red color.
Move the line down 1/2 inch.
Move the small box down 1/2 inch.
If the small box is still in the big box, repeat.
Draw the line with the red color.
Use medium letters.
Write “THE RED LINES ARE ALL STRAIGHT AND PARALLEL.”
with the gold pen at the bottom of the screen’s box.
Refresh the screen.

You’re going to need a ruler to check out this result:

Conclusion

It is our belief that the programmer of the future will program his machines much as we “program” an Amazon Echo or an Apple Siri or a Google Home or a Microsoft Cortana today. When, for example, my wife says:

“Echo, set a timer for 12 minutes. Then play a song by the Beatles.”

She’s writing and executing a short program, in English. Now if the devices listed above were themselves programmed in English, well, then we’d really be on the right track. Our Plain English prototype is evidence of the feasibility of such an approach. After all, if we can conveniently write a complete and efficient Compiler and IDE in English, why not an automated assistant? This is the 21st century, last I checked. Why shouldn’t both users and programmers be speaking to our machines in the same language we use to speak to each other?

Sudoku Solver

This is a Sudoku puzzle:

The object is to get one, and only one, of each digit in each row, column, and 3-by-3 block.

This is what it looks like after my Plain English program solves it:

And this is the Plain English routine that does the deed:

To solve a sudoku puzzle:
Get a blank cell. If the blank cell is nil, exit.
Loop.
Add 1 to a number. If the number is greater than 9, exit.
If the number is not valid in the blank, repeat.
Put the number into the blank.
Solve the puzzle. If the sudoku puzzle is solved, break.
Erase the blank cell.
Repeat.
Display message “Solved!”.

Technically speaking, it’s a “recursive backtracking” routine. It tries a number in a blank cell, then calls itself to try another number in another blank cell. If everything works out, we’re done. If we get stuck, it works backward, erasing the mistakes and trying again with a different starting number. It’s fun to watch; you just draw the puzzle on the screen right before each recursive call.

I noticed that there are other articles on various sites that use a similar algorithm, and I thought a brief comparison might be instructive and edifying. Below is a C/C++ equivalent of the above Plain English routine that I found:

boolSolveSudoku(intgrid[N][N])
{
    introw, col;
    // If there is no unassigned loc, we are done
    if(!FindUnassignedLocation(grid, row, col))
       returntrue; // success!
    // consider digits 1 to 9
    for(intnum = 1; num <= 9; num++)
    {
        // if looks promising
        if(isSafe(grid, row, col, num))
        {
            // make tentative assignment
            grid[row][col] = num;
            // return, if success, yay!
            if(SolveSudoku(grid))
                returntrue;
            // failure, unmake & try again
            grid[row][col] = UNASSIGNED;
        }
    }
    returnfalse; // this triggers backtracking
}

I showed the two versions to my wife, and she said, “Kind of makes you wonder why some people say that Plain English is too verbose!” So we did some counting:

Lines of code: Plain English, 10; C/C++, 27
(more than double in the C/C++ version)

Characters: Plain English, 334; C/C++, 681
(more than double in the C/C++ version)

Punctuation Marks: Plain English, 19; C/C++, 70
(more than three times as many in the C/C++ version)

Conclusion? Plain English is easier to learn, easier to remember, easier to think about, easier to type, easier to read. And reasonably compact (more compact in some cases, like the one above).

Hiding in Plain Sight

One of these pictures contains a hidden message 83 characters long:

Externally and internally, however, they are both very similar. Externally, they are what you see above. Internally, each of them is just a very long string of characters, where each character corresponds to a pixel in the image. There are just 27 allowed characters (numbers 64 to 90 on the ASCII chart):

@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

“@” means black, and “Z” means white, with the letters in-between representing increasingly lighter shades of gray. The definition of the image on the left looks like this in our program:

The cipher is a string equal to “NNNNNOOOOOOOPP…”

It’s much longer, of course. 82,944 bytes, to be exact. This is the routine we use to draw it on the screen:

To display a cipher in a box (as a picture):
Put the cipher’s first into a byte pointer.
Put the box’s left-top into a spot.
Loop.
If the spot’s y is the box’s bottom, break.
Put the byte pointer’s target into a byte.
Convert the byte to a color.
Draw the spot with the color.
Add 1 to the byte pointer. If the byte pointer is greater than the cipher’s last, break.
Move the spot right 1 pixel.
If the spot’s x is the box’s right,
put the box’s left into the spot’s x; move the spot down 1 pixel; repeat.
Repeat.
Refresh the screen.

And this is the routine that we use to encode a message into a cipher image:

To encode a message in a cipher given a key:
Put the message’s first into a message byte pointer.
Put the cipher’s first into a cipher byte pointer.
Loop.
If the message byte pointer is greater than the message’s last, break.
Add the key to the cipher byte pointer.
If the cipher byte pointer is greater than the cipher’s last, break.
If the message byte pointer’s target is the space byte,
put the at-sign byte into the cipher byte pointer’s target.
If the message byte pointer’s target is not the space byte,
put the message byte pointer’s target into the cipher byte pointer’s target.
Add 1 to the message byte pointer.
Repeat.

We thus replace some of the bytes in the cipher image’s string with the characters of the hidden message. Since the message affects only a very small percentage of the image’s characters, and since the characters in the message come from the same subset of the ASCII chart, and since we choose a “key” that will spread the message as thinly as possible across the entire image, the visual effect is very hard to detect. In this case, there are 83 characters in the message. Can you spot 83 differences in the images above?

To get the message back out, we use this routine:

To decode a cipher into a message given a key:
Clear the message.
Put the cipher’s first into a byte pointer.
Loop.
Add the key to the byte pointer.
If the byte pointer is greater than the cipher’s last, break.
If the byte pointer’s target is the at-sign byte,
append the space byte to the message; repeat.
Append the byte pointer’s target to the message.
Repeat.

So let’s find out what that hidden message says. Here’s the test routine:

To run:
Start up.
Clear the screen to the tan color.
Imagine a box 3 inches by 3 inches in the middle of the screen.
Move the box left 1-5/8 inches.
Display the cipher in the box (as a picture).
Encode the message in the cipher given 997.
Move the box right 3-1/4 inches.
Display the cipher in the box (as a picture).
Decode the cipher into a decoded message given 997.
Write the decoded message below the box.
Refresh the screen.
Wait for the escape key.
Shut down.

Note that, in Plain English, “1-5/8 inches” means “one and five-eighths inches”, not “one minus five eighths inches”. Likewise for “3-1/4”.

Note also that we use 997 for the key because the message is 83 characters long, which is roughly 1/1000 of the 82,944 characters in the image, and 997 is the largest prime less than 1000.

The output on the screen looks like this:

If we want the “defects” in the encoded cipher to be less noticeable, we can use a shorter message or a larger picture.

On a personal side note, I must say I’m struck by the quality of the images. After all, we’re using a trivial data structure (a string) and only 27 shades of gray. Who wudda thunk?

Bresenham’s Circle Drawing Algorithm

A Quick Comparison

There are lots of great articles about Bresenham’s Circle Drawing Algorithm. Here, for instance. So I won’t describe the theory in this article. Instead, I’ll show a Plain English implementation of the same, and compare it with this C version from the aforementioned article:

#include (some obscure library name)
#include (some obscure library name)
#include (some obscure library name)
 
// Function to put pixels
// at subsequence points
voiddrawCircle(intxc, intyc, intx, inty)
{
    putpixel(xc+x, yc+y, RED);
    putpixel(xc-x, yc+y, RED);
    putpixel(xc+x, yc-y, RED);
    putpixel(xc-x, yc-y, RED);
    putpixel(xc+y, yc+x, RED);
    putpixel(xc-y, yc+x, RED);
    putpixel(xc+y, yc-x, RED);
    putpixel(xc-y, yc-x, RED);
}
 
// Function for circle-generation
// using Bresenham's algorithm
voidcircleBres(intxc, intyc, intr)
{
    intx = 0, y = r;
    intd = 3 - 2 * r;
    while(y >= x)
    {
        // for each pixel we will
        // draw all eight pixels
        drawCircle(xc, yc, x, y);
        x++;
 
        // check for decision parameter
        // and correspondingly
        // update d, x, y
        if(d > 0)
        {
            y--;
            d = d + 4 * (x - y) + 10;
        }
        else
            d = d + 4 * x + 6;
        drawCircle(xc, yc, x, y);
        delay(50);
    }
}
 
 
// driver function
intmain()
{
    intxc = 50, yc = 50, r2 = 30;
    intgd = DETECT, gm;
    initgraph(&gd, &gm, "");  // initialize graph
    circleBres(xc, yc, r);    // function call
    return0;
}

Now here’s the Plain English version:

To run:
Start up.
Clear the screen.
Draw a circle using the screen’s center and 3 inches.
Refresh the screen.
Wait for the escape key.
Shut down.

To draw a circle with a center spot and a radius:
Put the radius and 0 into a spot.
Put the radius times -2 plus 1 pixel with 1 pixel into a change pair.
Loop.
If the spot’s y is greater than the spot’s x, break.
Plot eight spots given the center and the spot.
Add 1 pixel to the spot’s y.
Add the change’s y to some error twips.
Add 2 pixels to the change’s y.
If the error times 2 plus the change’s x is less than or equal to 0, repeat.
Subtract 1 pixel from the spot’s x.
Add the change’s x to the error.
Add 2 pixels to the change’s x.
Repeat.

To plot eight spots given a center spot and a spot:
Privatize the spot.
Loop.
Plot the center plus the spot.
Negate the spot’s x. Plot the center plus the spot.
Negate the spot’s y. Plot the center plus the spot.
Negate the spot’s x. Plot the center plus the spot.
Negate the spot’s y.
Flip the spot. If a counter is past 1, break.
Repeat.

Lines of code: Plain English, 33; C, 56.
Almost 70% more in the C version.

Characters: Plain English, 1048; C, 1209.
About 15% more in the C version.

Now please don’t get me wrong. C is a great language if you don’t mind memorizing (and/or looking up) how to type things like:

initgraph(&gd, &gm, "");

It just seems kind of foreign and unnatural to me. After all, my native tongue is English, and when I’m thinking about a program, I typically think in English “pseudo-code.” The cool thing about Plain English is that the pseudo-code I had in mind actually runs.

Bear with me, please. This next part is really interesting.

Mathemagician Pie

As you may know from a previous article of mine, I joined the Mathemagician’s Club in my youth. This is my membership card:

But I quickly became an outcast for taking Kronecker’s side in the Kronecker/Cantor kerfuffle. Which is why, decades later, Plain English is still an integer-only language. Baby steps toward a more rational and natural mathematics. And with the algorithms above, we now have what we need to take another baby step.

Pi, in the mathemagician’s world, is defined as the circumference of a circle, c, divided by the circle’s diameter, d, thus:

Pi = c/d

It is a nasty, transcendental (ie, unreal) number that can’t be fully calculated by anyone or anything. But using the routines above, we are able to calculate precise values for c/d, because we’re dealing, not with mathemagical abstractions, but with real-life pixels that we can actually see and count (using whole numbers).

The question is, How should we measure the circumference of a circle? I know of two, obvious, whole-number methods: (1) we can count the pixels we draw, or (2) we can calculate the sum of the Minkowski distances between those pixels. These are the figures we get for circles of increasing diameter using each method:

If we figure the circumference using a pixel count circumference, we get a rational value for Pi that quickly converges to 2 and 5/6. On the other hand, if we calculate the circumference by tracing a Minkowski path through those pixels, we get a rational value for Pi that is very close to 4. This is exactly what we’d expect. The Pixel Count Pi is a little less than the mathemagician’s value, and the Minkowski Pi is a little more. But both are rational numbers based on real-world pixels.

A “pixelated” circle, I hope you can see, is not an approximation of a mathemagician’s “ideal” circle; that’s putting the cart before the horse. It’s the mathemagician’s imaginary circle that approximates the real-life pixelated circle. Which, as we’ve just seen, can be efficiently drawn, and measured, and analyzed, in Plain English, using whole numbers only.

Call me crazy, but don’t call me late for dinner!

Painting Like Monet

Claude Monet (1840-1926) was a French impressionist painter known for the “dab, dab, dab” style of his paintings. I can imagine him at work, peering at a spot on his subject, mixing an appropriate color of paint, then dabbing the corresponding spot on his canvas.

Let’s take a look at a Plain English program that does essentially the same thing as ol’ Monsieur Monet. We begin with some type definitions:

A subject is a string.
A model is a picture.
A painting is a picture.
A frame is a box.

And that leads us to the general outline of the program as a whole:

To run:
Start up.
Put “Purple Grapes” into a subject.
Find a model of the subject on Google.
Paint a painting using the model.
Reveal the painting with the subject as the title.
Wait for the escape key.
Shut down.

The critical routine is the one that does the actual painting. This is it:

To paint a painting using a model:
Resize the model to 4 inches by 4 inches.
Center the model in the screen’s box.
Move the model left 3 inches.
Draw the canvas.
Draw the model.
Loop.
Pick a spot anywhere near the model’s box.
Mix a color given the spot.
Move the spot right 6 inches.
Dab the color on the spot.
If a counter is past 30000, break.
Repeat.
Make a frame 4 inches by 4 inches.
Center the frame in the screen’s box.
Move the frame right 3 inches.
Outdent the frame 1/4 inch.
Draw the frame with the black color and the clear color.
Refresh the screen.
Extract the painting given the frame.

It begins by setting up the model a little left of center on the screen. Then it loops around, painting. Like Monsieur Monet, it picks spot on (or near) the model, mixes up an appropriate color, and dabs it on the corresponding spot on the canvas on right side of the screen.

This is the routine that does the actual dabbing:

To dab a color on a spot:
Pick an ellipse’s left-top within 3/32 inch of the spot.
Pick the ellipse’s right-bottom within 3/32 inch of the spot.
Draw the ellipse with the color and the color.

This is a screenshot of our Purple Grapes model (left) and painting (right) after about 2,000 such dabs:

Colors are matched exactly, unless they are very very light, in which case a color that will better match the background canvas is chosen. This is the routine that does the mixing:

To mix a color given a spot:
Get the color given the spot.
If the color is not very very light, exit.
Pick the color between the lightest gray color and the white color.

And here is a screenshot of the finished work, after 30,000 dabs, with a frame and a title:

Since we fetch our models from Google, the program is capable of painting almost any imaginable subject. (We don’t have to worry about usage issues, by the way, because this kind of transformative art falls under the “Fair Use” section of the Copyright Act of 1976, 17 U.S.C. § 107.) Here, for example, is a painting of a forest path:

And here is a painting of one of a geek’s most indispensable accessories:

Dab, dab, dab.

The Amazing MultiMaze

A MultiMaze is a maze with multiple, player-selected beginnings and endings. Here is a sample:

The object is to connect each green dot with one, and only one, red dot (with or without overlapping paths).

The Plain English program that generates MultiMazes is described below. We use the Aldous-Broder algorithm because it always generates a perfect maze, ie, a maze with without loops or closed circuits, and without any inaccessible areas. It is also a uniform maze generator, which means it generates all possible mazes of a given size with equal probability. And it’s a very simple algorithm that requires no extra storage or stack to implement. It’s not super fast, but it is reasonably fast, typically generating a full-page maze in less than 3 seconds.

The data structures are almost trivial. We define a maze in our program like this:

A maze is a thing with a width, a height, a cell size and some cells.

And a cell is defined this way:

A cell is a thing with a box,
a left flag, a top flag, a right flag, a bottom flag,
a visited flag, a start flag and an end flag.

A neighbor is a cell.

Each cell’s box describes its position in the maze’s “array”. The left, top, right, and bottom flags indicate whether or not the corresponding side of the cell is open or closed; the visited flag is required by the Aldous-Broder algorithm, and the start and end flags indicate which cells will be marked with a green or red dot. (By the way, that first definition fit on one line on the screen when I wrote it because we use a tight, proportionally-spaced font and our uncluttered editor always spans the entire width of the screen. Turns out the things that make Plain English sentences easy-to-read — like one thought per line — are not the same things that make artificial, mathematically-influenced programming languages easy-to-read.)

The main routine in our program reads like this:

To run:
Start up.
Clear the screen with the tan color.
Create a maze 10 inches by 8 inches using 1/4 inch for the cells.
Display the maze with title and instructions.
Destroy the maze.
Wait for the escape key.
Shut down.

Most of that is self-explanatory, so I’ll jump to the interesting stuff. This is the top-level create routine:

To create a maze a width by a height using a size for the cells:
Allocate memory for the maze.
Put the width into the maze’s width.
Put the height into the maze’s height.
Put the size into the maze’s cell size.
Create some cells for the maze.
Apply the Aldous-Broder algorithm to the maze.
Select starting and ending cells in the maze.

The latter three lines do most of the work. The first calls this routine to create the maze’s cells:

To create some cells for a maze:
If a spot’s left is greater than the maze’s width, break.
If the spot’s top is greater than the maze’s height,
add the maze’s cell size to the spot’s left; repeat.
Create a cell of the maze’s cell size at the spot.
Append the cell to the maze’s cells.
Add the maze’s cell size to the spot’s left.
If the spot’s left is less than the maze’s width, repeat.
Put 0 into the spot’s left.
Add the maze’s cell size to the spot’s top.
Repeat.

Note that the cells are stored in a doubly-linked list, not in an array (as you might expect), because Plain English does not have a native array type (arrays are not native to English grammar).

This is the routine that creates an individual cell:

To create a cell of a size at a spot:
Allocate memory for the cell.
Put the spot and the spot plus the size into the cell’s box.
Set the cell’s left flag.
Set the cell’s top flag.
Set the cell’s right flag.
Set the cell’s bottom flag.
Clear the cell’s visited flag.

Once we’ve got our cells, we connect them in the right spots using the Aldous-Broder algorithm I mentioned earlier. This is the Plain English implementation:

To apply the Aldous-Broder algorithm to a maze:
Pick a cell in the maze. Set the cell’s visited flag.
Loop.
If all of the maze’s cells have been visited, break.
Pick a neighbor of the cell in the maze.
If the neighbor’s visited flag is not set,
connect the cell with the neighbor.

Set the neighbor’s visited flag.
Put the neighbor into the cell.
Repeat.

I won’t explain what that does because I’d simply be repeating the “code” in this paragraph. Instead, I’ll show you the support routines that make that work. Like this one:

To pick a neighbor of a cell in a maze:
Void the neighbor.
Loop.
Pick a number between 1 and 4.
If the number is 1,
find the neighbor above the cell in the maze.

If the number is 2,
find the neighbor to the right of the cell in the maze.

If the number is 3,
find the neighbor below the cell in the maze.

If the number is 4,
find the neighbor to the left of the cell in the maze.

If the neighbor is nil, repeat.

A neighbor might be above, below, to the right, or to the left of the current cell. We want to pick one of those, if it exists, randomly. So we pick a number between 1 and 4 (corresponding to above, right, below, and left) and see what we can find there. If we fail to find a neighbor in that relative location, we try again and again until we succeed. After all, every cell has at least 2 neighbors.

The routine below is typical of the four “find” routines called by the above routine:

To find a neighbor above a cell in a maze:
Void the neighbor.
Loop.
Get the neighbor from the maze’s cells.
If the neighbor is the cell, repeat.
If the neighbor is nil, break.
If the neighbor’s bottom line is the cell’s top line, break.
Repeat.

Now this is how we connect one cell with another. Cells start with all of their wall flags set, so we simply have to remove the appropriate walls (in both cells) to make a path:

To connect a cell with a neighbor:
If the cell’s top line is the neighbor’s bottom line,
clear the cell’s top flag;
clear the neighbor’s bottom flag; exit.

If the cell’s right line is the neighbor’s left line,
clear the cell’s right flag;
clear the neighbor’s left flag; exit.

If the cell’s bottom line is the neighbor’s top line,
clear the cell’s bottom flag;
clear the neighbor’s top flag; exit.

If the cell’s left line is the neighbor’s right line,
clear the cell’s left flag;
clear the neighbor’s right flag; exit.

We know the passed cells are neighbors, but we didn’t keep track of their relative locations, so we check all four. (And again, all those IFs fit on one line each on the screen in our editor.)

And that’s it for the Aldous-Broder algorithm, except to see if we’re done, which is this routine’s job:

To decide if all of some cells have been visited:
Get a cell from the cells.
If the cell is nil, say yes.
If the cell’s visited flag is not set, say no.
Repeat.

We now have a perfectuniform maze, and we just have to select some starting and ending points. We pick eight of each, almost at random. I say almost because we always select the top left cell as a starting point, and the bottom right cell as an ending point, to make sure that there is a least one long path available for the player.

To select starting and ending cells in a maze:
Set the maze’s cells’ first’s start flag.
Set the maze’s cells’ last’s end flag.
Loop.
Pick a cell in the maze.
If the cell’s start flag is set, repeat.
If the cell’s end flag is set, repeat.
Add 1 to a count. If the count is greater than 14, break.
If the count is odd, set the cell’s start flag; repeat.
If the count is even, set the cell’s end flag; repeat.

And that’s the gist of the whole program. A couple of side notes on Plain English looping, however, might be helpful here.

1. When there is no explicit LOOP label, REPEAT means “repeat from the top of the routine”.

2. BREAK always passes control the first statement following the last REPEAT.

3. Looping through lists almost always happens like this:

To draw some cells:
Get a cell from the cells.
If the cell is nil, break.
Draw the cell with the black pen.
Repeat.
Refresh the screen.

If a nil pointer is passed to the “Get” routine, the first item on the list, if any, is returned. “A cell” in this case, indicates a new local variable initialized to zero, so that’ll work. If a non-nil pointer is passed to the “Get” routine, the next item on the list, if any, is returned. A nil pointer is returned when the whole list has been processed. All of that is done, not by the compiler, but by a generic routine in our standard “noodle” library:

To get a thing from some things:
If the things are empty, void the thing; exit.
If the thing is nil, put the things’ first into the thing; exit.
Put the thing’s next into the thing.

But that’s all stuff for other articles. Back to the maze. This is how we actually draw those cells, taking all of those little flags into account:

To draw a cell with a pen:
If the cell’s left flag is set,
draw the cell’s left line with the pen.
If the cell’s top flag is set,
draw the cell’s top line with the pen.

If the cell’s right flag is set,
draw the cell’s right line with the pen.

If the cell’s bottom flag is set,
draw the cell’s bottom line with the pen.

Put the cell’s width divided by 3 into a width.
If the cell’s start flag is set,
draw a dot the width wide at the cell’s box’s center with the dark green pen.
If the cell’s end flag is set,
draw another dot the width wide at the cell’s box’s center with the dark red pen.

Almost forgot! This is how we pick a random cell anywhere in the maze:

To pick a cell in a maze:
Pick a number between 1 and the maze’s cells’ count.
Void the cell.
Loop.
Get the cell from the maze’s cells.
If the cell is nil, break.
Add 1 to a count. If the count is the number, break.
Repeat.

And that’s all I have to say about that. I’m speechless. Amazing.