Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active June 21, 2017 09:12
Show Gist options
  • Save JoshCheek/ed876305b320d847e6d0 to your computer and use it in GitHub Desktop.
Save JoshCheek/ed876305b320d847e6d0 to your computer and use it in GitHub Desktop.
A bunch of command-line progrmas for a blog
require 'io/console'
height, width = $stdout.winsize
clear_screen = "\e[1;1H\e[2J"
bg_colour = "\e[40m"
tree_colour = "\e[42m"
base_height = 3
# Go into raw mode so users can enter 1 character at a time
$stdin.raw!
at_exit { $stdin.cooked! }
# hide the cursor until program exig
print "\e[?25l"
at_exit { print "\e[?25h" }
print bg_colour, clear_screen
print height.times.map { |y| "\e[#{y+1};#{width/2}H\e[43m \e[48m" }.join
tree_height = height - 5
[0.65, 0.4, 0.33].each_with_index do |girth, i|
segment_height = (tree_height*girth).to_i
ystart = height - segment_height - base_height - (height*i*girth).to_i
segment_height.times do |offset|
print "\e[#{ystart+offset};#{width/2-offset}H#{tree_colour}#{' '*2*offset}#{bg_colour}"
end
end
$stdin.getc
require 'io/console'
def parse(ansi)
ansi.lines.map { |line| line.chomp.chars }
end
santa = parse <<-COLOURS
000070111000000000000000303000
000000011100000000000000030300
000000111110000000000000300000
011110077300000000000000337001
111111117700666666000003333330
011111111701000000660003330000
004444444444400033336633300000
000444444444000003336633300000
000030000300030000300030000000
033333333333300033003300000000
COLOURS
gift = parse <<-GIFT
1221
2222
1221
GIFT
grass = parse <<-GROUND
0000000
0000000
2222222
2222222
2222222
GROUND
house = parse <<-GIFT
3000000
4444444
4343434
4344444
2222222
GIFT
print "\e[?25l"
at_exit { print "\e[?25h" }
$stdin.raw!
at_exit { $stdin.cooked! }
print "\e[40m\e[1;1H\e[2J"
height, width = $stdout.winsize
ground = [false]*width
100.times { ground += [false]*rand(20) + [true] }
ground += [false]*width
time = delivered = missed = 0
x = y = newx = newy = 3
gifts = []
ground_height = height - 5
while missed < 20
to_print = "\e[1;1H\e[33;1m CHUCK IT DOWN THE CHIMNEY, SANTA!!\e[22;39m"
gifts.each do |y, x|
gift.each.with_index { |row, i| to_print << "\e[#{y+i};#{x}H\e[40m#{' '*row.length}\e[49m" }
end
to_print << "\e[#{y};#{x}H#{santa.map.with_index { |line, i| "\e[#{y+i};#{x}H" << line.map { |char| "\e[40m " }.join }.join}"
x = newx
y = newy
gifts.select! do |gift|
gift[0] += 1
if ground_height <= gift[0]
house_indexes = ground.map.with_index.select { |has_house, i| has_house }.map { |_, i| i }
if house_indexes.any? { |i| i == gift[1] || i-1 == gift[1] }
delivered += 1
else
missed += 1
end
false
else
gift[0] < height
end
end
ground.shift
to_print << "\e[#{ground_height};1H"
ground.map { |b| b ? house : grass }.transpose.map.with_index { |row, i|
to_print << row.flatten.take(width-1).map { |n| "\e[4#{n}m " }.join << "\e[49m\r\n"
}
to_print << "\e[#{y};#{x}H#{santa.map.with_index { |line, i| "\e[#{y+i};#{x}H" << line.map { |char| "\e[4#{char}m " }.join }.join}"
gifts.each { |y, x|
gift.each.with_index { |row, i|
to_print << "\e[#{y+i};#{x}H" << row.map { |n| "\e[4#{n}m " }.join << "\e[49m"
}
}
to_print << "\e[1;#{width-25}H\e[32mdelivered: #{delivered} \e[31mmissed: #{missed}\e[39m"
print to_print
begin
case $stdin.read_nonblock(100).to_s[-1]
when "q", "\x03" then break
when "h" then newx = x - 2
when "j" then newy = y + 1
when "k" then newy = y - 1
when "l" then newx = x + 2
when " " then gifts << [y, x]
end
rescue IO::EAGAINWaitReadable
end
sleep 0.02
end
print "\e[#{height};#{width}H"
# Go into raw mode so users can enter 1 character at a time
require 'io/console'
$stdin.raw!
at_exit { $stdin.cooked! }
# Clear the screen
print "\e[1;1H\e[2J"
# Split the screen into cartesian quadrants (ie 0,0 moves to the middle of the screen)
height, width = $stdout.winsize
width.times do |x|
# It looks fancy, but it's just the equation for a circle, modified to account for changing radius and centered in the middle of the screen
y = Math.sqrt(height**2-(2*x*height/width-height)**2).to_i/2
# Prompt user to enter a colour, quit if they press control-c
print "\e[#{height};1HEnter one of: " + (0..7).map { |n| "\e[4#{n};9#{n==7?0:7}m #{n} \e[39;49m" }.join
colour = $stdin.getc until ["\x03", "0", "1", "2", "3", "4", "5", "6", "7"].include? colour
break if colour == "\x03"
# Print a vertical line of colour
print (height/2-y..height/2+y).map { |y| "\e[#{y+1};#{x+1}H\e[4#{colour}m \e[49m" }.join
end
print "\e[#{height+1};1H\r\n"
require 'io/console'
# Read a character at a time, reset at exit
$stdin.raw!
at_exit { $stdin.cooked! }
# Hide the cursor until exit
print "\e[?25l"
at_exit { print "\e[?25h" }
# Position on screen and in the text
height, width = $stdout.winsize
buffer = []
buffer = File.read(ARGV[0]).chars if ARGV[0]
buffer_index = 0 # where we are in the input
inputs = []
# Since we haven't talked about how to do arrow keys we'll make an emacs-like editor
loop do
# Clear the screen, print the display
print "\e[1;1H" + # move to topleft
"\e[2J" + # clear to botright
buffer.join[0, width*height] + # print the text we're editing
"\e[#{1+(buffer_index/width)};#{1+(buffer_index%width)}H" + # move to cursor position
"\e[46m" + # set bg to cursor color
(buffer[buffer_index]||' ') + # print char cursor is over with bg set
"\e[49m" # unset bg colour
# get and interpret the input based on which mode we're in
input = $stdin.getc
inputs << input
case inputs.last
when "\x03" then break # control-c
when "\x02" then buffer_index -= 1 # control-b
when "\x0E" then buffer_index += width # control-n
when "\x10" then buffer_index -= width # control-p
when "\x06" then buffer_index += 1 # control-f
when "\x08", "\x7F" # control-h, delete
unless buffer_index.zero?
buffer.delete_at buffer_index-1
buffer_index -= 1
end
else buffer << input
end
buffer_index = 0 if buffer_index < 0
buffer_index = buffer.length if buffer.length < buffer_index
end
# It doesn't deal with newlines well :)
require 'io/console'
# Read a character at a time, reset at exit
$stdin.raw!
at_exit { $stdin.cooked! }
# Hide the cursor until exit
print "\e[?25l"
at_exit { print "\e[?25h" }
# Position on screen and in the text
height, width = $stdout.winsize
buffer = []
buffer = File.read(ARGV[0]).chars if ARGV[0]
buffer_index = 0 # where we are in the input
# Since we haven't talked about how to do arrow keys
# we'll make a vim-like editor, which uses hjkl in the navigation mode (called "normal" mode in vim)
mode = :navigation
cursor_colour = 6
loop do
# Clear the screen, print the display
print "\e[1;1H" + # move to topleft
"\e[2J" + # clear to botright
buffer.join[0, width*height] + # print the text we're editing
"\e[#{1+(buffer_index/width)};#{1+(buffer_index%width)}H" + # move to cursor position
"\e[4#{cursor_colour}m" + # set bg to cursor color
(buffer[buffer_index]||' ') + # print char cursor is over with bg set
"\e[49m" # unset bg colour
# get and interpret the input based on which mode we're in
input = $stdin.getc
if mode == :navigation
case input
when "q", "\e", "\x03", "\x04" then break # escape, control-c, control-d end the program
when "h" then buffer_index -= 1
when "j" then buffer_index += width
when "k" then buffer_index -= width
when "l" then buffer_index += 1
when "i" then mode = :insert
when /\d/ then cursor_colour = input.to_i
when "x" then buffer.delete_at buffer_index
when "w" then File.write(ARGV[0]||"edited", buffer.join)
end
else
case input
when "\e", "\x03", "\x04"
mode = :navigation
when "\b", "\x7F" # these both delete left on mine
buffer.delete_at buffer_index-1 unless buffer_index.zero?
buffer_index -= 1
else
buffer.insert buffer_index, input
buffer_index += 1
end
end
buffer_index = 0 if buffer_index < 0
buffer_index = buffer.length if buffer.length < buffer_index
end
require 'io/console'
height, width = $stdout.winsize
# No cursor, input 1 char at a time
print "\e[?25l"; at_exit { print "\e[?25h" }
$stdin.raw!; at_exit { $stdin.cooked! }
# Load or create the new canvas
if ARGV[0]
canvas = File.read(ARGV[0]).lines.map { |line| line.chomp.chars.map(&:to_i) }
else
canvas = Array.new(height-1) { Array.new width-1, 0 }
end
# To save it once done (doing it this way so we have access to the canvas local var)
define_method :save do
filename = ARGV[0]||'image'
File.write filename, canvas.map(&:join).join("\n")
print "\r\nSaved in #{filename.inspect}"
end
# Promt whether to save, upon exit
at_exit do
print "\e[1;1HSAVE? "
save if $stdin.getc == 'y'
end
# Edit the canvas
x = y = 0
loop do
# Draw each square the colour specified
image = canvas.map { |ns| ns.map { |n| "\e[4#{n}m " }.join }.join("\e[0m\r\n")
print "\e[2;1H#{image}\e[#{y+2};#{x+1}H\e[4#{canvas[y][x]}m#{canvas[y][x]}"
# Depending on what they press, we'll either quit, save, move, or colour the canvas
char = $stdin.getc
case char
when "\x03", "q" then break
when 's' then save
when 'h' then x -= 1
when 'j' then y += 1
when 'k' then y -= 1
when 'l' then x += 1
when /\d/ then canvas[y][x] = char.to_i
end
end
require 'io/console'
print "\e[?25l"
at_exit { print "\e[?25h" }
height, width = $stdout.winsize
width.times { |x| puts "\e[1;#{x}H.\e[1;1H#{(100.0*x/(width-1)).to_i}%"; sleep 0.01 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment