diff --git a/projects/snow/README.md b/projects/snow/README.md new file mode 100644 index 0000000..8e7f9cb --- /dev/null +++ b/projects/snow/README.md @@ -0,0 +1,140 @@ +# Retro Snow + +In this project, you will create a retro animation using terminal escape +sequences, that mimics falling snow. This is, in fact, a trivial +particle system. Sophisticated particle systems are a routine part of +visual effects in television and film as well as crucial tools in +scientific computing. + +## A Particle + +A single particle looks like this: + +```c++ +struct Particle { + int32_t line; + int32_t column; +}; +``` + +Thus, a particle is made up of two 32 bit integers side-by-side. The +first provides the line of the particle, the second its column. + +## Screen Size + +A default terminal window measures 80 characters across and 24 lines +down. The origin is (1, 1) located at the top left corner. + +## Cursor Movement + +Snow flakes (particles) will be positioned using an old school VT100 +escape sequence. This sequence is: + +```text +ESC [ line ; column H +``` + +with no spaces. + +Using `printf()`, a template string can be employed: + +```text +"\033[%d;%dH" +``` + +In C++, you'd do something like this: + +```c++ +cout << "\033[" << line << ";" << column << "H"; +``` + +Note that there are no new lines being printed since this would in +itself move the cursor to the left edge of the next line and could +even cause scrolling - a disaster in this project. + +## Erasing the Screen + +To erase the screen, use this escape sequence after first having moved +to screen position (1, 1): + +```text +"\033[2J" +``` + +## A C++ Object To Use As A Model + +The following C++ can be used to start your thinking about how to go +about writing this project. + +```c++ +struct Particle { + int32_t line; + int32_t column; + void Step(); + void Render(); + void Reset(); +}; +``` + +### `Step()` + +All particles use the `Step()` function to move into the future. The +`line` is increased by one and the `column` can either remain unchanged, +move to the left or move to the right. + +If the particle's `line` exceeds 24, the particle is `Reset()`. + +### `Render()` + +To draw a particle, `Move()` to its location then print a "*" all +without emitting a new line. Before printing the "*", check to ensure +both the `line` and `column` are within the boundaries of the default +terminal window (i.e. 1 ≤ line ≤ 24 and 1 ≤ column ≤ 80). +If the particle's position is outside this range, don't print anything. + +### `Reset()` + +A particle's initial `column` can be anywhere from 1 to 80 inclusive. +Special care is given to setting the particle's initial `line`. +Specifically, always place the particle above the visible screen... +chosen randomly from -1 to -48. This allows for sufficient spacing so +that the snow does not appear to fall in clumps. + +## The Storm + +A single snow flakes does not a blizzard make. Therefore, allocate a +large number of them - perhaps 150 to 200 flakes. Since assembly +language doesn't have actual C++ objects, each flake occupies only +the 2 `int32_t` values. Suppose *n* is the number of flakes you choose +to model. Then allocate *2 \* 4 \* n* bytes using `malloc()`. Since +you will then `Reset()` every one of them, initializing the newly +allocated memory is not needed. + +Then, you'll need a `StepAll()` and a `RenderAll()`. + +## The Main Loop + +* Position cursor at (1, 1). + +* Erase the screen. + +* `StepAll()` + +* `RenderAll()` + +* Position cursor at (1, 1) again and print a new line. + +* Delay a short time. In my implementation, I using `usleep()` to delay +217 microseconds. + +Note only one new line is printed per frame - this is used to force all +output to be flushed to the terminal. + +## Desired Outcome + +![movie](./snow.gif) + +## Solution + +The solution is [here](./main.s). + diff --git a/projects/snow/main.s b/projects/snow/main.s index 2d6a91f..b314c1d 100644 --- a/projects/snow/main.s +++ b/projects/snow/main.s @@ -2,7 +2,7 @@ .align 2 .text - .equ NUM_FLAKES, 150 + .equ NUM_FLAKES, 250 .equ MAX_COLUMN, 80 .equ MAX_LINE, 24 .equ MAX_LINE_X2, 48 diff --git a/projects/snow/snow.gif b/projects/snow/snow.gif new file mode 100644 index 0000000..4eaca5d Binary files /dev/null and b/projects/snow/snow.gif differ