Created
September 6, 2013 03:15
-
-
Save alexdantas/6459071 to your computer and use it in GitHub Desktop.
Shows a nice screensaver on the terminal with Ruby Curses.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# | |
# pipes.rb: Displays a nice animation on the terminal, | |
# based on an old screensaver. | |
# | |
# If you want to see some action, scroll to the bottom. | |
# The main code is between "begin" and "end". | |
# | |
# This code uses Ruby and it's Curses module to display | |
# characters on the terminal. | |
# | |
# I've tried to keep the code clean but it's a mess by | |
# now. Hey, I was excited when coding, don't blame me. | |
# | |
# This code is DEFINITELY not Rubyish. Looks like a messed | |
# up C/C++ cousing. Maybe that creepy uncle who shows up | |
# at birthday parties. | |
require 'curses' # Awesome terminal-handling library | |
# Since Ruby doesn't natively supports "enums", I've | |
# used a lot of labels (those things with ':') | |
# If you find a better one, please tell me ([email protected]) | |
:direction_left | |
:direction_right | |
:direction_up | |
:direction_down | |
# Represents a single pipe, that will scroll around the | |
# screen, leaving a colourful trail behind. | |
class Pipe | |
# It's public components. | |
attr_reader :x, :y, :behaviour | |
# All possible colors that a pipe can have. | |
Colors = { | |
:black => 1, | |
:white => 2, | |
:red => 3, | |
:yellow => 4, | |
:magenta => 5, | |
:blue => 6, | |
:green => 7, | |
:cyan => 8 | |
}.freeze | |
# The possible ways a pipe can behave. | |
# Nice little feature, right? | |
Behaviour = [:dumb, # Randomly decide where to go | |
:line, # Keep making long lines | |
:curvy] # Make a lot of curves | |
# Constructor, creates a pipe. | |
def initialize(x, y, behaviour = :dumb, color = Colors[:cyan]) | |
@x = x | |
@y = y | |
@appearance = '-' | |
@previous_dir = :direction_right | |
@current_dir = :direction_right | |
@color = Curses::color_pair color | |
@behaviour = behaviour | |
end | |
# Shows the pipe on the screen. | |
# This is VERY UGLY, DAMn. | |
def print | |
# If we're changing direction it's best to use '+' instead | |
# of '-' or '|' | |
if @current_dir == :direction_right or @current_dir == :direction_left | |
if (@previous_dir == :direction_up or @previous_dir == :direction_down) | |
@appearance = '+' | |
else | |
@appearance = '-' | |
end | |
elsif @current_dir == :direction_up or @current_dir == :direction_down | |
if (@previous_dir == :direction_left or @previous_dir == :direction_right) | |
@appearance = '+' | |
else | |
@appearance = '|' | |
end | |
end | |
Curses::setpos(@y, @x) | |
Curses::attrset @color | |
Curses::addstr @appearance | |
Curses::refresh | |
end | |
# Refreshes the pipe's direction. | |
# If `change_direction` is true, forces the pipe to | |
# do it. | |
# If not, it will keep on it's current way. | |
def refresh_direction(change_direction) | |
dir = @current_dir | |
if change_direction | |
begin | |
# Getting a random direction | |
result = Random.new.rand(1..4) | |
if result == 1; dir = :direction_left | |
elsif result == 2; dir = :direction_up | |
elsif result == 3; dir = :direction_right | |
else dir = :direction_down | |
end | |
end while not is_valid_movement? dir | |
end | |
@previous_dir = @current_dir | |
@current_dir = dir | |
end | |
# Tells if we can move the cursor to the next direction. | |
# Invalid movements will be returning 180o. | |
def is_valid_movement? dir | |
if (@current_dir == :direction_right and dir == :direction_left) or | |
(@current_dir == :direction_left and dir == :direction_right) or | |
(@current_dir == :direction_up and dir == :direction_down) or | |
(@current_dir == :direction_down and dir == :direction_up) | |
return false | |
else | |
return true | |
end | |
end | |
# Actually steps the terminal a position on the screen, | |
# based on it's internal directions. | |
def move | |
if @current_dir == :direction_right; @x += 1 | |
elsif @current_dir == :direction_left; @x -= 1 | |
elsif @current_dir == :direction_up; @y -= 1 | |
elsif @current_dir == :direction_down; @y += 1 | |
end | |
out_of_screen = false | |
if @x < 0 | |
@x = Curses::cols - 1 | |
out_of_screen = true | |
elsif @x > Curses::cols - 1 | |
@x = 0 | |
out_of_screen = true | |
end | |
if @y < 0 | |
@y = Curses::lines - 1 | |
out_of_screen = true | |
elsif @y > Curses::lines - 1 | |
@y = 0 | |
out_of_screen = true | |
end | |
self.change_color if out_of_screen | |
end | |
# Randomly changes the pipe's color. | |
def change_color | |
result = Random.new.rand(Colors[:red]..Colors[:cyan]) # first..last | |
@color = Curses::color_pair result | |
# Randomly choosing between normal and bold | |
@color = @color | Curses::A_BOLD if random_bool | |
end | |
# Updates all the pipe's internal stuff. | |
def update | |
if @behaviour == :dumb | |
if random_bool | |
self.refresh_direction true | |
else | |
self.refresh_direction false | |
end | |
elsif @behaviour == :line | |
if random_bool_with_chance 0.2 | |
self.refresh_direction true | |
else | |
self.refresh_direction false | |
end | |
elsif @behaviour == :curvy | |
if random_bool_with_chance 0.8 | |
self.refresh_direction true | |
else | |
self.refresh_direction false | |
end | |
end | |
self.print | |
self.move | |
end | |
end | |
# Initializes the curses engine. | |
# ugly function is ugly D: | |
def curses_init timeout | |
Curses::init_screen | |
Curses::start_color | |
Curses::init_pair(Pipe::Colors[:black], Curses::COLOR_BLACK, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:white], Curses::COLOR_WHITE, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:red], Curses::COLOR_RED, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:yellow], Curses::COLOR_YELLOW, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:magenta], Curses::COLOR_MAGENTA, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:blue], Curses::COLOR_BLUE, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:green], Curses::COLOR_GREEN, Curses::COLOR_BLACK) | |
Curses::init_pair(Pipe::Colors[:cyan], Curses::COLOR_CYAN, Curses::COLOR_BLACK) | |
Curses::curs_set 0 | |
Curses::noecho | |
Curses::nonl | |
Curses::timeout = timeout | |
Curses::refresh | |
end | |
# Returns randomly 'true' or 'false'. | |
def random_bool | |
result = Random.new.rand(1..10) | |
if (result % 2) == 0 # is even | |
return true | |
else | |
return false | |
end | |
end | |
# Returns 'true' or 'false' with a probability of 'chance' | |
def random_bool_with_chance chance | |
result = Random.new.rand(1..100) | |
return true if (result <= (chance*100)) | |
return false | |
end | |
# Here's the main function! | |
# _ _ __ | |
# (_) / \ \ | |
# _ __ ___ __ _ _ _ __ | | | | | |
# | '_ ` _ \ / _` | | '_ \| | | | | |
# | | | | | | (_| | | | | | | | | | |
# |_| |_| |_|\__,_|_|_| |_| | | | | |
# \_ /_/ | |
begin | |
timeout = 50 # default delay in miliseconds | |
curses_init timeout | |
pipes = [Pipe.new(Curses::cols/2, Curses::lines/2, :dumb, Pipe::Colors[:cyan]), | |
Pipe.new(Curses::cols/4, Curses::lines/8, :line, Pipe::Colors[:red]), | |
Pipe.new(Curses::cols/8, Curses::lines/4, :curvy, Pipe::Colors[:magenta])] | |
while true | |
# Updating all pipes | |
for i in 0..(pipes.size-1) | |
pipes[i].update | |
end | |
# This is where the timeout delay happens. | |
case Curses::getch | |
when 'q', 'Q' # quit | |
break | |
when 'a', 'A' # faster! | |
timeout -= 10 | |
timeout = 10 if timeout < 10 | |
Curses::timeout = timeout | |
when 's', 'S' # slower! | |
timeout += 10 | |
timeout = 50 if timeout > 50 | |
Curses::timeout = timeout | |
when 'c', 'C' # clear | |
Curses::clear | |
Curses::refresh | |
end | |
end | |
exit 0 | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment