Copyright 2019 jmf License: WTFPL This code prints a Mandelbrot set in ASCII art to terminal. First of all, some variables are declared. The initial maximum number of iterations is set to 128. ~~~ 'x var 'y var 'iter var #128 'max-iter var-n #1 'zoom var-n #0 'posx var-n #0 'posy var-n ~~~ We are using fixed-point numbers with a scaling factor of 10'000. That means that the float value 1.0 is represented by the number 10'000. For that, we need a special multiplication definition. ~~~ :f* * #10000 / ; ~~~ Now the calculation of the Mandelbrot set value at a specified point is defined. ~~~ :mb:value (x_y--v) #0 !x #0 !y #-1 !iter [ @iter #1 + !iter dup-pair #2 @x * @y f* + swap (new_y) @x @x f* @y @y f* - + (new_x) !x !y @x @x f* @y @y f* + #40000 lteq? @iter @max-iter lt? and ] while drop drop @iter ; ~~~ In order to display the mandelbrot set nicely, 10 different iteration levels are filled with their equivalent ASCII signs. ~~~ :ascii-equiv (n--c) #9 * @max-iter / #0 [ #32 ] case #1 [ $. ] case #2 [ $: ] case #3 [ $- ] case #4 [ $= ] case #5 [ $+ ] case #6 [ $* ] case #7 [ $# ] case #8 [ $% ] case #9 [ $@ ] case ; ~~~ The user can change some parameters by entering key press sequences. The following code checks for those key presses... ~~~ :zoom+? dup $+ eq? [ @zoom #2 * !zoom ] if ; :zoom-? dup $- eq? [ @zoom #2 / #1 n:max !zoom ] if ; :up? dup $w eq? [ @posy #10000 @zoom / - !posy ] if ; :down? dup $s eq? [ @posy #10000 @zoom / + !posy ] if ; :left? dup $a eq? [ @posx #10000 @zoom / - !posx ] if ; :right? dup $d eq? [ @posx #10000 @zoom / + !posx ] if ; :resinc? dup $e eq? [ @max-iter #2 * !max-iter ] if ; :resdec? dup $q eq? [ @max-iter #2 / #1 n:max !max-iter ] if ; ~~~ ...and this word bundles all those together. ~~~ :input:handle c:get zoom+? zoom-? up? down? left? right? resinc? resdec? drop nl ; ~~~ A quick function draws some information about key bindings and the current zoom level, as well as the current maximum number of iterations. ~~~ :info:draw '+/-_to_zoom;_w/a/s/d_to_move s:put nl 'q/e_to_increase_decrease_resolution s:put nl 'zoom_level_ s:put @zoom n:put nl 'iterations_ s:put @max-iter n:put nl ; ~~~ Finally the actual drawing code is written. It renders to 25x80 characters. The width of 80 characters is distributed over a set width of 3.5 (35000) units, the height of 25 characters is distributed over a set height of 2 units. This results in a scaling factor of 438 for the width and 800 for the height. ~~~ :mb:draw #25 [ #80 [ I #438 * #25000 - @zoom / @posx + J #800 * #10000 - @zoom / @posy + mb:value ascii-equiv c:put ] indexed-times nl ] indexed-times ; ~~~ Before we start, the terminal is set to character-buffered. This makes it possible to react to key presses directly, instead of only after the enter key is pressed. The last function is a loop that draws the set, some info and gets key- presses. ~~~ 'stty_cbreak unix:system [ mb:draw info:draw input:handle TRUE ] while ~~~