Vitruvia (exercises and tests) is a web app that provides a gentle and interactive introduction to the abstractions found in the Bricklayer Library as well as the fundamentals of the programming language SML. At present, Vitruvia consists of 23 concepts which span Bricklayer coding levels 1 through 5. Each concept has a number of associated Vitruvia exercises.
From a structural standpoint, a Vitruvia exercise consists of
- a text-based description of a problem to be solved,
- a 2-dimensional grid (initially empty) in which unit LEGO bricks (i.e., 1×1 bricks which are also known as bit bricks) can be placed, and
- a small set of unit bricks of varying colors which can be placed in cells within the grid.
Solutions to Vitruvia exercises are 2-dimensional brick configurations. Candidate solutions can be submitted (by clicking on a submit button) to which Vitruvia will respond with either (1) correct, or (2) incorrect, at which point two options for engagement are provided: try again or show solution.
The Correspondence between Physical and Virtual Coordinates
Solutions to Vitruvia exercises can be recreated in physical space (i.e., the real world) by placing LEGO bricks on a LEGO base plate. The coordinate system used in Vitruvia (and in Bricklayer) is based on a LEGO base plate that is horizontally positioned residing in the xz-plane. For example, if you place a base plate in front of you on a table, the x-axis will run from left to right, the y-axis will go up and down (from the ceiling to the floor), and the z-axis will run from the front of the table (closest to you) to the back of the table.
If we extend this coordinate system to the world around us, then the ground we walk on will be the xz-plane, and changes in our elevation will be expressed by the y-axis. It should be noted that this type of coordinate system is used in Minecraft.
Before coding can begin, some basic understanding of coordinate spaces and brick positioning must be developed. With this objective in mind, Vitruvia concepts 1,3, and 4 establish a natural-language based vocabulary suitable for the communication of brick placement between individuals (concept 2 involves out-of-the-box thinking and is not essential to a basic Bricklayer introduction). More specifically, a means of communication is introduced whereby the construction of a 2-dimensional LEGO artifact can be unambiguously expressed as a sequence of simple English sentences. Each sentence concerns itself with the placement of a single brick having specific dimensions and color. Interactive web-based exercises associated with learning the meaning of such sentences focus on developing an understanding of 2-dimensional coordinate spaces (e.g., positions on LEGO base plates where bricks can be “put”) as well as well as the specification of LEGO brick – its dimensions and color.
At this stage, only standard shapes and a few basic colors are allowed. The Level 1 Bricklayer library provides functions for creating the following set of bricks.
The creation of artifacts using the brick dimensions and colors shown above is the focus of Vitruvia concepts 1 – 12.
Concept 1: Cell Locations
This set of Vitruvia exercises develops an understanding of cell coordinates in the xz-plane. LEGO Digital Designer (LDD) permits the creation of three dimensional structures in which the xz-plane corresponds to a (virtual) LEGO base plate.
Concept 2: Boolean Functions (can be skipped)
This set of Vitruvia exercises develop a basic understanding of Boolean functions whose inputs are cell coordinates. The idea here is to express an artifact in terms of coordinate properties. For example, a checkerboard pattern can be created by placing black 1×1 bricks at coordinates whose x and z values sum to an even integer and by placing white 1×1 bricks at coordinates whose x and z values sum to an odd integer.
Concept 3: Basic Bricks
This set of exercises develops an understanding of the specification and placement of basic LEGO bricks. A basic brick is defined by its dimensions (e.g., 2×4) and its color (e.g., RED).
Basic bricks (all standard rectangular bricks for that matter) can be understood in terms of abstractions involving a composition of 1×1 bricks. For example, a 4×2 brick is the composition of eight 1×1 bricks having the proper configuration.
By convention, Bricklayer assumes that the first number in the dimensions of a brick corresponds to its length along the x-axis and the second number corresponds to its depth along the z-axis. For example, a brick having the dimensions 4×2 will be 4 cells wide and 2 cells deep.
By convention, Bricklayer assumes the position of a brick is defined by the cell coordinate of its lower left corner. In this context, an (informal) instruction is an English sentence specifying a brick dimension, color, and location. An example of an informal instruction is:
The Vitruvia exercises in Concept 3 establish a vocabulary for specifying the placement of the basic LEGO bricks mentioned in the beginning of this section.
Concept 4: Brick Sequences
This set of Vitruvia exercises develops an understanding of the sequences of (informal) brick-placement instructions. Concept 4 is a direct extension of Concept 3 and focuses on the notion of instruction sequences .
In Level 1, concepts 5 and 6 focus on developing a simple translation of the natural-language sentences used in concepts 3 and 4 into corresponding Bricklayer function calls and function call sequences. In this translation, the important parts of a concept 3 sentence (the verb put, the brick dimensions, and the brick color) are encoded by the translation into a function name. The resulting function is parameterized only on the coordinate where the brick is to be placed. The table below gives examples of such translations.
|Concept 3 Natural Language Sentence||Bricklayer Function Call|
|Put a 4×2 RED brick at location (0,0).||put2D_4x2_RED (0,0)|
|Put a 2×3 BLUE brick at location (3,4).||put2D_2x3_BLUE (3,4)|
|Put a 1×2 GREEN brick at location (4,0).||put2D_1x2_GREEN (4,0)|
Concept 7 introduces the dynamics of overwriting – which is the placement of a brick at a location occupied by another brick. Concept 8 consists of exercises involving 2 groups of individuals, and concept 9 involves completing Bricklayer artifacts in which bricks are missing.
Concept 5: Bricklayer Functions
The set of Vitruvia exercises transitions from informal brick-placement instructions to corresponding Bricklayer/SML function calls. An example of an actual SML function call is: put2D_4x2_RED(0,0) which, when executed by Bricklayer, will “put” the lower left corner of a 4×2 red LEGO brick at coordinate (0,0).
When programming in Bricklayer in Level_1 and Level_2, only a fixed set of put2D_* functions are made available. Specifically, the put2D_* functions available are precisely those corresponding to those referenced in Vitruvia exercises.
Concept 6: Function Call Sequences
The set of Vitruvia exercises takes a formal look at the evaluation (aka execution) of a sequence of SML function calls. At this point, an exercise solution represents the effects of evaluating a sequence of Bricklayer function calls.
Concept 7: Overwriting
In a virtual space, the notion of overwriting is significant. What happens when the placement of one LEGO overlaps the placement of another? This set of Vitruvia exercises explores the effects of overwriting in Bricklayer. More specifically, overwriting is permitted and can be used to advantage when building LEGO objects.
Concept 8: Program Creation
This set of Vitruvia exercises calls for the creation of Bricklayer programs. Basic Bricklayer programs consist of sequences of put2D_* function calls. The skill that is developed here is attention to detail – are semi-colons in the right places, are function calls syntactically well-formed?
Supplemental Bricklayer assignments can be found here.
Concept 9: Debugging
As soon as the very first program is created, one is confronted with the problem of figuring out “why the didn’t work” or “why the program produced a result different than intended”. This set of exercises develops basic skills in debugging Bricklayer programs. More specifically, two LDD structures are shown – the first incorrect, the second correct. The focus of this set of Vitruvia exercises is to manually place bricks in order to transform the first solution into the second solution.
Supplemental Vitruvia content and Bricklayer assignments can be found here.
Level 2 spans Vitruvia concepts 10 – 13 and primarily focuses on using function declarations to structure the code that creates a LEGO artifact. It is through such structure that the construction of complex LEGO artifacts becomes intellectually manageable. The key ideas introduced in Level 2 are (1) nullary function declarations, and (2) parameterized function declarations. The concept of an offset is also introduced in order to increase the expressive power of nullary function calls, and to provide a framework for understanding (and transitioning to) parameterized functions. In Level 2, parameterized function declarations are exclusively parameterized on the location of a brick. Level 2 ends with an introduction to rings and circles which are Bricklayer-defined functions that accept multiple parameters in a curried fashion.
Concept 10: Nullary Functions
For lengthy put2D_* function call sequences, comprehension becomes a problem. Nullary function declarations (i.e., functions taking essentially no arguments) can be used to structure and abstract function call sequences. Such structuring of Bricklayer programs makes them easier to understand, to debug, and to modify.
This set of Vitruvia exercises develops basic skills in understanding the semantics of (user-defined) nullary function declarations and corresponding function calls.
Concept 11: Offsets
Offsets provide a Bricklayer mechanism for shifting the origin of the xz-plane. Through offsets it becomes possible to reuse a (single) sequence of put2D_* function calls in order to build copies of LEGO objects positioned at various points in the xz-plane.
Offsets share similarities to pointers and are (understandably) quite complex. This set of Vitruvia exercises develops basic skills in understanding offsets.
Concept 12: Function Parameters
In Bricklayer, function parameters can be used to play a role similar to offsets. However, function parameters are essentially stateless and are therefore (ultimately) much easier to understand and reason about than offsets. This distinction between offsets and parameters illustrates a fundamental difference between imperative and functional programming paradigms.
Through parameterized functions, it becomes possible for users to extend the set of put2D_* functions thereby creating specialty composite pieces (e.g., L-shaped bricks and nxm bricks).
The example below shows how an artifact can be created at the location (7,5) through a call to the function setOffset2D followed by a call to a user-defined nullary function.
fun thing1 () = ( put2D_1x1_BLUE(0,0); put2D_1x1_BLUE(1,2); put2D_2x1_BLUE(1,1) ); setOffset2D(7,5); thing1();
The example below shows how the same artifact of the previous example can be created at the location (7,5) through a call to a user-defined function that is parameterized on location.
fun thing2 (x,z) = ( put2D_1x1_BLUE(x,z); put2D_1x1_BLUE(x+1,z+2); put2D_2x1_BLUE(x+1,z+1) ); thing2(7,5);
This set of Vitruvia exercises develops basic skills in understanding the declaration and use of parameterized functions.
Concept 13: Circles and Rings
In Bricklayer, the fundamental brick has dimensions 1x1x1. However, Bricklayer also provides functions that construct (and “put”) composite structures such as circles and rings.
This set of Vitruvia exercises demonstrates how composite structures are created.
Level 3 coding spans Vitruvia concepts 14 – 18. Vitruvia concept 14 introduces a generalized version of the “put” function, called put2D, that is parameterized on brick dimensions, color, and location. Through the put2D function, all bricks supported by Bricklayer are now accessible. A complete listing of the pieces supported by Bricklayer can be found here. Furthermore, rectangular shapes of any size can now be created using a single function call. For example, a pink brick whose shape is 3×8 and whose location is (2,4) will be created by the following function call.
put2D (3,8) PINK (2,4)
The function put2D underscores the power of parameterization. At this stage, students are encouraged to parameterize their own user-defined function declarations on attributes other than location (e.g., on brick dimensions and color).
Concept 14: The Curried Function put2D
This set of Vitruvia exercises demonstrates the syntax and semantics of the Bricklayer function put2D. The curried function put2D is parameterized on (1) the brick dimension (i.e., its shape), (2) the brick type (e.g., color), and (3) the coordinate where the brick is to be placed. It is through the parameterization of brick type that put2D provides the ability to “put” any of the supported bricks (over 70) in the xz-plane.
Concept 15: Lines
In Bricklayer, a 1x1x1 brick can be thought of as a large pixel that also has a physical manifestation. When using such “pixels” to draw lines, the jagged nature of lines becomes quite apparent. The algorithms used by “smooth” line drawing functions, while not overly complex, are never-the-less computationally intricate.
This set of Vitruvia exercises demonstrates the syntax and of the Bricklayer function lineXZ. The exercises also give some appreciation of the semantic issues that must be confronted when drawing “smooth” lines.
Concept 16: Clipping
The purpose of evaluating a Bricklayer program is to build a LEGO structure in a virtual space, and to then display that structure in LDD. We refer to this virtual space as the “cube”. The dimensions of the cube used by a Bricklayer program must be explicitly declared within the program. The xz-plane is a cube whose height (y dimension) equals 1.
During execution, Bricklayer provides a safe interaction with the program’s cube. Specifically it will not permit an interaction with the cube that attempts to place a brick at in cell coordinate lying outside of the cube. An interesting question concerns itself with function calls that attempt to put a composite structure (e.g., a 4×2 brick) at a location where only part of the structure lies within the cube. In such situations, Bricklayer updates the cube with the portion of the structure that lies within the cube’s dimensions. This behavior is called clipping.
This set of Vitruvia exercises develops an understanding of Bricklayer’s clipping algorithm.
Concept 17: My Space
Bricklayer provides the capability of controlling which cells within a cube can be updated by a function or program. This capability can be used to partition a cube into a set of sub-cubes. Multiple people interact with a cube by executing code in their own assigned sub-cube. This allows for safe compositions of user code and provides an environment for interesting group coding projects.
This set of Vitruvia exercises develops an understanding of the syntax and semantics of functions that partition a cube.
Concept 18: Let-blocks
The compositional possibilities arising from my space functions give rise to name space issues. For example, if two programs are composed there may exist a user-defined function that is declared (albiet differently) in both programs having the same name. There are a number of manual polices that can be followed to mitigate this issue. However, a better way to resolve this problem is to declare functions in ways that suitably restrict their visibility/scope. In SML, let-blocks provide one way to restrict the visibility of user-defined functions.
This set of Vitruvia exercises develops an understanding of the syntax and semantics of let-blocks.
Level 4 coding involves the creation of three-dimensional artifacts. The three-dimensional function for placing bricks is called put.
Due to its two-dimensional nature, learning three-dimensional concepts using Vitruvia is limited. However, the Bricklayer ecosystem provides online documentation as well as coding examples that describe the syntax and semantics of three-dimensional Bricklayer functions.
In three-dimensional space, it is very easy to create artifacts that have thousands of pieces. For example, a 30x30x30 solid cube will contain 27,000 pieces. Though the rules are not hard and fast, LEGO Digital Designer (LDD) has difficulty displaying LEGO artifacts that contain more than 25K pieces. For this reason, Bricklayer introduces functions that create “hollow” shapes. For example, the function putHollow is a function that creates a hollow rectangular prism. Similar functions exist for creating, hollow spheres, cylinders, and cones.
In Level 4, a variety of Bricklayer functions accept brick lists. The elements of a brick list are randomly selected when constructing a given structure. This random selection of bricks provides a way to add “texture”. The figure below shows a minecraft-inspired scene complete with grass, a Birch tree, and the coveted diamond sword.
Concept 19: Transition to 3D
Up to this point, all the exercises involved the creation of LEGO structures in the xz-plane. We now transition to three dimensions.
This set of Vitruvia exercises develops an understanding of the syntax and semantics of the 3D Bricklayer function: put.
Level 5 coding spans Vitruvia concepts 20 – 23. These exercises introduce Boolean values, Boolean and relational operators, conditional expressions, and iterators.
Concept 20: Brick Functions and Conditional Expressions
Vitruvia concept 20 introduces relational as well as conditional expressions.
It is at this point that Bricklayer programs can incorporate control flow into their
At first blush this may seem like a long time to wait to introduce a construct as fundamental as control flow. However, it should be noted that complex LEGO artifacts such as the Wunderlich Curve, and the Menger Sponge, can be constructed without the use of control flow.
In Level 5, the power of conditional expressions is brought to life by a class of
user-defined functions called brick functions. A brick function takes a 3-dimensional
coordinate as input and returns a brick type (e.g., RED) as its output. In concept
20, exercises involve evaluating brick functions for all the cells in a given grid (e.g., 6x1x6) and placing the results of these evaluations (aka bricks) in the corresponding
cells. For example, consider a function that sums the x, y, and z values of its
input coordinate and when the sum is odd returns a white brick and otherwise returns
a black brick. Such a function will create a checkerboard pattern on the grid
whose cells to which it is applied.
This set of Vitruvia exercises develops an understanding of the syntax and semantics of the brick functions and conditional expressions.
Concept 21: Nested Conditional Expressions and the Logical Operator andalso
Vitruvia concept 21 introduces the logical operator andalso (which technically
speaking is a derived form in SML) as well as nested conditionals involving
the andalso operator. Brick functions are used as the vehicle for understanding
This set of Vitruvia exercises develops an understanding of the relationship between conditional expressions and the logical operator andalso.
Concept 22: Nested Conditional Expressions and the Logical Operator orelse
Vitruvia concept 22 introduces the logical operator orelse (which technically
speaking is a derived form in SML) as well as nested conditionals involving the
orelse operator. Again, brick functions are used as the vehicle for understanding
It should be noted that, regardless of which programming language is used,
logical expressions as well as nested conditionals introduce a dimension of complexity into a program that students of all ages generally have difficulty with. Thus, developing proficiency in their use can take a considerable amount of time.
This set of Vitruvia exercises develops an understanding of the relationship between conditional expressions and the logical operator orelse.
Concept 23: Bricklayer’s traverseWithin function
Vitruvia concept 23 introduces the iteration function traverseWithin as well as the identity brick type. At this stage of coding, it is the iterator traverseWithin that enables the effective use of brick functions within Bricklayer programs. It now becomes possible to traverse the cells in a given space multiple times. For example, the first traversal in a program may use one brick function while a second traversal may traverse the same space but use a different brick function. The idea of repeatedly traversing a space provides one mechanism for combining the effects of brick functions. In this context, the identity brick plays an important role. Unlike the EMPTY brick which, when placed at a location, has the effect of “erasing” the contents of that location, the IDENTITY brick has the effect of leaving the contents at a given location unaltered. The identity brick enables a traversal to affect a specific subset of the space traversed while leaving the rest of the space unaltered. Projecting a 2-dimensional image onto a 3-dimensional shape (shown below) is an example of a class of problems for which the identity brick is useful.
This set of Vitruvia exercises develops an understanding of the relationship between brick functions and Bricklayer’s traverseWithin function.