tty-fireworks 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []