tty-fireworks 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tty-fireworks.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mark Lorenz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Tty::Fireworks :fireworks:
2
+ > Fireworks for your terminal
3
+
4
+
5
+ ## Installation
6
+ ```bash
7
+ gem install tty-fireworks
8
+ ````
9
+
10
+ ## Usage
11
+ - `q` quits
12
+ - `c` clears the screen
13
+ - all other keys fire a salvo
14
+
15
+ ## Contributing
16
+
17
+ 1. Fork it
18
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
19
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
20
+ 4. Push to the branch (`git push origin my-new-feature`)
21
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/fireworks ADDED
@@ -0,0 +1,65 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ require 'curses'
6
+ require 'io/console'
7
+
8
+ YMAX, XMAX = STDIN.winsize
9
+ CLOCK = 0.1
10
+ PADDING = 20
11
+
12
+ require 'curses_lib'
13
+ require 'launch'
14
+ require 'bang'
15
+ require 'launch_view'
16
+ require 'bang_view'
17
+
18
+ Curses::init_screen
19
+
20
+ # yes, I am bad, and I want to date your daughter.
21
+ class Range
22
+ def any
23
+ rand(self)
24
+ end
25
+ end
26
+
27
+ Launch.fire
28
+
29
+ Thread.new do
30
+ loop do
31
+ Curses::refresh
32
+ Curses::clear
33
+ sleep CLOCK
34
+ end
35
+ end
36
+
37
+ loop do
38
+ char = STDIN.getch
39
+ exit if char == "q"
40
+ Curses::clear if char == "c"
41
+ Launch.fire
42
+ end
43
+
44
+ # http://www.petesqbsite.com/sections/tutorials/tuts/perspective.html
45
+ # ----------------------------------------
46
+ # Formula to solve Sx
47
+ # ----------------------------------------
48
+ # Ez = distance from eye to the center of the screen
49
+ # Ex = X coordinate of the eye
50
+ # Px = X coordinate of the 3D point
51
+ # Pz = Z coordinate of the 3D point
52
+ # Ez*(Px-Ex)
53
+ # Sx = ----------------------- + Ex
54
+ # Ez+Pz
55
+ #
56
+ # ----------------------------------------
57
+ # Formula to solve Sy
58
+ # ----------------------------------------
59
+ # Ez = distance from eye to the center of the screen
60
+ # Ey = Y coordinate of the eye
61
+ # Py = Y coordinate of the 3D point
62
+ # Pz = Z coordinate of the 3D point
63
+ # Ez*(Py-Ey)
64
+ # Sy = ------------------- + Ey
65
+ # Ez+Pz
data/lib/bang.rb ADDED
@@ -0,0 +1,53 @@
1
+ module Bang
2
+ class BaseFrame # you know you love it, "base" ;-)
3
+ attr_reader :particles
4
+ attr_reader :speed
5
+ attr_reader :time_to_live
6
+ attr_reader :last_frame
7
+ end
8
+
9
+ class InitialFrame < BaseFrame
10
+ SPEEDRANGE = (3..8)
11
+ TIMETOLIVERANGE = (5..15)
12
+
13
+ def initialize origin
14
+ @speed = SPEEDRANGE.any
15
+ @time_to_live = TIMETOLIVERANGE.any
16
+ @particles = Array.new(10, {x: origin[:x], y: origin[:y], z: origin[:z]})
17
+ @last_frame = self
18
+ end
19
+
20
+ def alive?
21
+ true
22
+ end
23
+ end
24
+
25
+ class Frame < BaseFrame
26
+
27
+ def initialize last_frame
28
+ @angle_increment = 360 / last_frame.particles.size
29
+
30
+ @speed = last_frame.speed
31
+ @time_to_live = last_frame.time_to_live - 1
32
+ @last_frame = last_frame
33
+ @particles = calc_particles
34
+ end
35
+
36
+ def alive?
37
+ !time_to_live.zero?
38
+ end
39
+
40
+ private
41
+
42
+ def calc_particles
43
+ # calculation intentionally not a linearly growning circle
44
+ @last_frame.particles.map.with_index do |particle, idx|
45
+ {
46
+ x: Math.cos(@angle_increment * idx) * CLOCK * speed + particle[:x],
47
+ y: Math.sin(@angle_increment * idx) * CLOCK * speed + particle[:y],
48
+ }
49
+ end
50
+ end
51
+
52
+ end
53
+ end
data/lib/bang_view.rb ADDED
@@ -0,0 +1,54 @@
1
+ require_relative './curses_lib'
2
+
3
+ module Bang
4
+ def self.fire(origin)
5
+ Thread.new do
6
+ view = Bang::View.new(origin)
7
+ loop do
8
+ break unless view.render
9
+ sleep CLOCK
10
+ end
11
+ end
12
+ end
13
+
14
+ class View
15
+ include CursesLib
16
+
17
+ def initialize origin
18
+ @last_frame = Bang::InitialFrame.new(origin)
19
+ end
20
+
21
+ def render
22
+ @last_frame = Bang::Frame.new(@last_frame)
23
+
24
+ frames = [ @last_frame.last_frame.last_frame,
25
+ @last_frame.last_frame,
26
+ @last_frame ]
27
+
28
+ frames.each do |frame|
29
+ frame.particles.each do |part|
30
+ color do
31
+ Curses::setpos *translate(part)
32
+ Curses::addstr "*"
33
+ end
34
+ end
35
+ end
36
+ return @last_frame.alive?
37
+ end
38
+
39
+ def translate(part)
40
+ x = part[:x]
41
+ y = YMAX - part[:y]
42
+ [y, x]
43
+ end
44
+
45
+ def color
46
+ options = [ BLUE_COLOR, CYAN_COLOR, GREEN_COLOR,
47
+ MAGENTA_COLOR, RED_COLOR, YELLOW_COLOR,
48
+ WHITE_COLOR ]
49
+
50
+ attrib(Curses::color_pair(options.shuffle.first)) { yield }
51
+ end
52
+
53
+ end
54
+ end
data/lib/curses_lib.rb ADDED
@@ -0,0 +1,38 @@
1
+ module CursesLib
2
+
3
+ Curses.nl
4
+ Curses.curs_set(0) # don't need to see that
5
+
6
+ Curses.start_color
7
+
8
+ BLUE_COLOR = 30
9
+ Curses.init_pair BLUE_COLOR, Curses::COLOR_BLUE, Curses::COLOR_BLACK
10
+ CYAN_COLOR = 40
11
+ Curses.init_pair CYAN_COLOR, Curses::COLOR_CYAN, Curses::COLOR_BLACK
12
+ GREEN_COLOR = 50
13
+ Curses.init_pair GREEN_COLOR, Curses::COLOR_GREEN, Curses::COLOR_BLACK
14
+ MAGENTA_COLOR = 60
15
+ Curses.init_pair MAGENTA_COLOR, Curses::COLOR_MAGENTA, Curses::COLOR_BLACK
16
+ RED_COLOR = 70
17
+ Curses.init_pair RED_COLOR, Curses::COLOR_RED, Curses::COLOR_BLACK
18
+ YELLOW_COLOR = 80
19
+ Curses.init_pair YELLOW_COLOR, Curses::COLOR_YELLOW, Curses::COLOR_BLACK
20
+ WHITE_COLOR = 90
21
+ Curses.init_pair WHITE_COLOR, Curses::COLOR_WHITE, Curses::COLOR_BLACK
22
+
23
+ ORANGE = 20
24
+ ORANGE_COLOR = 20
25
+ Curses.init_color ORANGE, 254, 254, 0
26
+ Curses.init_pair ORANGE_COLOR, ORANGE, Curses::COLOR_BLACK
27
+
28
+ def attrib *attributes
29
+ attributes.each do |attribute|
30
+ Curses.attron attribute
31
+ end
32
+ yield
33
+ attributes.each do |attribute|
34
+ Curses.attroff attribute
35
+ end
36
+ end
37
+
38
+ end
data/lib/launch.rb ADDED
@@ -0,0 +1,86 @@
1
+ module Launch
2
+ GRAVITY = 9.801
3
+
4
+ class BaseFrame
5
+ attr_accessor :position
6
+ attr_accessor :speed
7
+ attr_accessor :time_to_explosion
8
+ attr_accessor :last_frame
9
+ end
10
+
11
+ class InitialFrame < BaseFrame
12
+ EXPLOSIONRANGE = (20..30)
13
+ YSPEEDMAX = Math.sqrt(2 * (YMAX-PADDING) * GRAVITY)
14
+ XSPEEDRANGE = (0..3)
15
+ YSPEEDRANGE = (5..YSPEEDMAX)
16
+ ZSPEEDRANGE = (0.1..0.2)
17
+
18
+ def initialize
19
+ start_x = (PADDING..XMAX-PADDING).any
20
+ @position = {x: start_x, y: 0, z: 0}
21
+ @speed = {x: XSPEEDRANGE.any, y: YSPEEDRANGE.any, z: ZSPEEDRANGE.any}
22
+ @time_to_explosion = EXPLOSIONRANGE.any
23
+ @last_frame = self
24
+ end
25
+
26
+ def alive?
27
+ true
28
+ end
29
+ end
30
+
31
+ class Frame < BaseFrame
32
+ DUDHEIGHT = 2
33
+
34
+ def initialize previous
35
+ @last_frame = previous
36
+ calc_speed
37
+ calc_position
38
+ @time_to_explosion = @last_frame.time_to_explosion - 1
39
+ try_bang
40
+ end
41
+
42
+ def alive?
43
+ !time_to_explosion.zero?
44
+ end
45
+
46
+ private
47
+
48
+ def dud?
49
+ position[:y] < DUDHEIGHT && !alive?
50
+ end
51
+
52
+ def try_bang
53
+ Bang.fire(position) if !alive? && !dud?
54
+ end
55
+
56
+ def calc_position
57
+ self.position = xyz do |coord|
58
+ previous_terms[coord]
59
+ end
60
+ position.merge! y: (position[:y] + acceleration_term)
61
+ end
62
+
63
+ def calc_speed
64
+ self.speed = xyz do |coord|
65
+ last_frame.speed[coord]
66
+ end
67
+ speed.merge! y: (speed[:y] - GRAVITY*CLOCK)
68
+ end
69
+
70
+ def previous_terms
71
+ xyz do |coord|
72
+ last_frame.position[coord] + last_frame.speed[coord]*CLOCK
73
+ end
74
+ end
75
+
76
+ def acceleration_term
77
+ 0.5+GRAVITY*CLOCK**2
78
+ end
79
+
80
+ def xyz
81
+ [:x, :y, :z].inject({}) do |memo, coord|
82
+ memo.update Hash[coord, yield(coord)]
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,49 @@
1
+ require_relative './curses_lib'
2
+ module Launch
3
+ def self.fire
4
+ Thread.new do
5
+ view = Launch::View.new
6
+ loop do
7
+ break unless view.render
8
+ sleep CLOCK
9
+ end
10
+ end
11
+ end
12
+
13
+ class View
14
+ include CursesLib
15
+
16
+ def initialize
17
+ @last_frame = Launch::InitialFrame.new
18
+ end
19
+
20
+ def render
21
+ burnt do
22
+ Curses::setpos *translate_frame
23
+ Curses::addstr "."
24
+ end
25
+
26
+ @last_frame = Launch::Frame.new(@last_frame)
27
+ white do
28
+ Curses::setpos *translate_frame
29
+ Curses::addstr "."
30
+ end
31
+ return @last_frame.alive?
32
+ end
33
+
34
+ def translate_frame
35
+ x = @last_frame.position[:x]
36
+ y = YMAX - @last_frame.position[:y]
37
+ [y, x]
38
+ end
39
+
40
+ def white
41
+ attrib(Curses::A_BOLD) { yield }
42
+ end
43
+
44
+ def burnt
45
+ attrib(Curses::color_pair(ORANGE_COLOR)) { yield }
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "tty-fireworks"
7
+ gem.version = "0.0.1"
8
+ gem.authors = ["Mark Lorenz"]
9
+ gem.email = ["markjlorenz@dapplebeforedawn.com"]
10
+ gem.description = %q{Fireworks in your terminal}
11
+ gem.summary = %q{For those extra special commits}
12
+ gem.homepage = "https://github.com/dapplebeforedawn/ruby-fireworks"
13
+
14
+ gem.files = `git ls-files`.split($/)
15
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tty-fireworks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark Lorenz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-06 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Fireworks in your terminal
15
+ email:
16
+ - markjlorenz@dapplebeforedawn.com
17
+ executables:
18
+ - fireworks
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - bin/fireworks
28
+ - lib/bang.rb
29
+ - lib/bang_view.rb
30
+ - lib/curses_lib.rb
31
+ - lib/launch.rb
32
+ - lib/launch_view.rb
33
+ - tty-fireworks.gemspec
34
+ homepage: https://github.com/dapplebeforedawn/ruby-fireworks
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: For those extra special commits
58
+ test_files: []