Let it snow()

Hey ChatGPT. It’s 2024, the tree has grown, and other trees have started to grow around it. Also someone has put flashing lights on the trees…

using REPL

function snow(io = stdout)
    # Grab terminal dimensions
    h, w = displaysize(io)
    iob = IOBuffer()
    ioc = IOContext(IOContext(iob, io), :displaysize=>(h,w))
    
    # Clear the screen, move cursor up
    print(io, repeat("\n", h), "\e[$(h)A\e[1G")

    # The grid that holds falling snow
    air = ones(Int, w, h)

    # Snowflake characters (index 1 is empty space)
    flakes = [" ", "*", "❄︎", "❅", "❆"]

    # Small sinusoidal helper to vary snowfall probability
    scsin(t) = ((sin(t) / 2) + 0.5) * (0.1 / 3)
    likelihood(t) = scsin(t) + scsin(t * 1.00001) + scsin(t * 0.9999)

    # Print a single tree line with random “lights”
    function printtreeline(ioc, length_of_line)
        for i in 1:length_of_line
            if rand() < 0.12
                # White light
                printstyled(ioc, "o", color=:white, bold=true)
            else
                # Standard green branch
                printstyled(ioc, "x", color=:green)
            end
        end
    end

    # Draw a single “pine” at (base_row, center_col) with specified height + trunk
    function draw_tree(ioc, base_row, center_col; height=12, trunk_height=3)
        # Print each level from top to bottom
        for lvl in 1:height
            row = base_row - height + lvl
            col = center_col - (lvl - 1)
            
            print(ioc, "\e[$(row);$(col)H")
            printtreeline(ioc, 2*lvl - 1)
        end
        
        # Print trunk
        for trunk_lvl in 1:trunk_height
            row = base_row + trunk_lvl
            print(ioc, "\e[$(row);$(center_col)H")
            printstyled(ioc, "│", color=:reverse)
        end
    end

    try
        while true
            # Update the snowfall in 'air'
            for x in 1:w, y in h:-1:1
                air[x,y] = if y == 1
                    # Spawn new snowflakes at top
                    rand() < likelihood(time()) ? rand(2:length(flakes)) : 1
                elseif y == h
                    # Accumulate at bottom
                    ((rand() < 0.95 && air[x,y] > 1) ||
                     (air[x, y-1] > 1 && rand() < 0.2)) ? 2 : 1
                elseif all(>(1), air[x, y:end]) # melt pile sometimes
                    rand() < 0.95 ? 2 : 1
                elseif (air[x, y-1] > 1 && all(>(1), air[x, (y+1):end]))
                    rand() < 0.1 ? 2 : 1
                else
                    # Normal falling
                    air[x, y-1]
                end
            end

            # Print updated snow
            foreach(space -> print(ioc, flakes[space]), air)

            # Draw main (bigger) tree
            main_tree_height = 14
            trunk_height     = 4
            main_base_row    = h - 2  
            main_center_col  = div(w, 2)
            draw_tree(ioc, main_base_row, main_center_col; 
                      height=main_tree_height, trunk_height=trunk_height)

            # Draw some smaller trees around it
            draw_tree(ioc, main_base_row-2, main_center_col - 20; height=10, trunk_height=3)
            draw_tree(ioc, main_base_row-2, main_center_col + 20; height=10, trunk_height=3)
            draw_tree(ioc, main_base_row-6, main_center_col - 35; height=8,  trunk_height=2)
            draw_tree(ioc, main_base_row-6, main_center_col + 35; height=8,  trunk_height=2)

            # Move cursor home
            print(ioc, "\e[H")

            # REPL banner + prompt
            @static VERSION > v"1.11.0-0" ? REPL.banner(ioc) : Base.banner(ioc)
            printstyled(ioc, "julia> ", color = :green, bold = true)
            println(ioc, "snow()")

            # Flush buffer
            print(io, String(take!(iob)))

            # Slight pause
            sleep(1/8)
        end

    catch e
        isa(e, InterruptException) || rethrow()
    end
    nothing
end
24 Likes