good_luck_wizard 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/lib/glw/components/input.rb +21 -0
- data/lib/glw/input.rb +76 -70
- data/lib/glw/screen.rb +75 -73
- data/lib/glw/universe.rb +11 -0
- data/lib/glw/version.rb +1 -1
- data/lib/glw.rb +25 -6
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4263837619f78bee5caa53a56bfdb94459f936a9ac28d0b9295f5f495c9f8363
|
|
4
|
+
data.tar.gz: 99d4841207fd0ce336f71936fbba360256503eed43f580188dbd834b667898a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f6cf8b4eee23afe0934301b4e312a0afed3d6654c49fe59a6f1b7ee4b5b243041598f26b9fa19135145e6ac25ada4dc0292c179a0b745439cc0bb9b230c85e2
|
|
7
|
+
data.tar.gz: 577db893cc18d765420e3104a3839e1defcf71e4e7c4e2b140afe1d4e9b516203a55a755edad479b7588557f94897d231205da7be3aebaddf7fc691793ec2138
|
data/Gemfile
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
source "https://rubygems.org"
|
|
2
2
|
|
|
3
|
-
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
3
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
|
5
5
|
# Specify your gem's dependencies in good_luck_wizard.gemspec
|
|
6
6
|
gemspec
|
|
@@ -10,3 +10,4 @@ gem "rspec", "~> 3.4"
|
|
|
10
10
|
gem "standard", "~> 1.45"
|
|
11
11
|
|
|
12
12
|
gem "debug"
|
|
13
|
+
gem "benchmark-ips"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module GLW
|
|
2
|
+
module Components
|
|
3
|
+
Input = Data.define(:events) do
|
|
4
|
+
def self.make
|
|
5
|
+
new(events: [])
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def push(event)
|
|
9
|
+
events << event
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def pop
|
|
13
|
+
events.pop
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def peek
|
|
17
|
+
events.last
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/glw/input.rb
CHANGED
|
@@ -1,96 +1,102 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module GLW
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
ESCAPE_TIMEOUT = 0.02 # 20ms
|
|
7
|
-
|
|
8
|
-
BYTES = {
|
|
9
|
-
"\t" => :k_tab,
|
|
10
|
-
"\r" => :k_enter,
|
|
11
|
-
"\n" => :k_enter,
|
|
12
|
-
"\x7F" => :k_backspace,
|
|
13
|
-
"\b" => :k_backspace,
|
|
14
|
-
" " => :k_space,
|
|
15
|
-
"\x03" => :k_ctrl_c,
|
|
16
|
-
"[" => :k_left_bracket,
|
|
17
|
-
"]" => :k_right_bracket,
|
|
18
|
-
";" => :k_semicolon,
|
|
19
|
-
"'" => :k_single_quote,
|
|
20
|
-
"," => :k_comma,
|
|
21
|
-
"." => :k_period
|
|
22
|
-
}.tap do |h|
|
|
23
|
-
("a".."z").each { |c| h[c] = :"k_#{c}" }
|
|
24
|
-
("A".."Z").each { |c| h[c] = :"k_#{c}" }
|
|
25
|
-
("0".."9").each { |c| h[c] = :"k_#{c}" }
|
|
26
|
-
end
|
|
4
|
+
class Input
|
|
5
|
+
ESCAPE_TIMEOUT = 0.02 # 20ms
|
|
27
6
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
7
|
+
BYTES = {
|
|
8
|
+
"\t" => :k_tab,
|
|
9
|
+
"\r" => :k_enter,
|
|
10
|
+
"\n" => :k_enter,
|
|
11
|
+
"\x7F" => :k_backspace,
|
|
12
|
+
"\b" => :k_backspace,
|
|
13
|
+
" " => :k_space,
|
|
14
|
+
"\x03" => :k_ctrl_c,
|
|
15
|
+
"[" => :k_left_bracket,
|
|
16
|
+
"]" => :k_right_bracket,
|
|
17
|
+
";" => :k_semicolon,
|
|
18
|
+
"'" => :k_single_quote,
|
|
19
|
+
"," => :k_comma,
|
|
20
|
+
"." => :k_period
|
|
21
|
+
}.tap do |h|
|
|
22
|
+
("a".."z").each { |c| h[c] = :"k_#{c}" }
|
|
23
|
+
("A".."Z").each { |c| h[c] = :"k_#{c}" }
|
|
24
|
+
("0".."9").each { |c| h[c] = :"k_#{c}" }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize
|
|
28
|
+
@queue = nil
|
|
29
|
+
end
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
def pop
|
|
32
|
+
@queue.pop
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def empty?
|
|
36
|
+
@queue.empty?
|
|
37
|
+
end
|
|
34
38
|
|
|
35
|
-
|
|
39
|
+
def start!
|
|
40
|
+
return if @queue
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
loop do
|
|
39
|
-
byte = read_byte
|
|
42
|
+
@queue = Thread::Queue.new
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
Thread.new do
|
|
45
|
+
loop do
|
|
46
|
+
byte = read_byte
|
|
47
|
+
|
|
48
|
+
if byte == "\e"
|
|
49
|
+
handle_escape byte
|
|
50
|
+
else
|
|
51
|
+
handle_byte byte
|
|
48
52
|
end
|
|
53
|
+
rescue EOFError
|
|
54
|
+
break
|
|
49
55
|
end
|
|
50
56
|
end
|
|
57
|
+
end
|
|
51
58
|
|
|
52
|
-
|
|
59
|
+
private
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
def handle_escape(first_byte)
|
|
62
|
+
second_byte = read_byte(timeout: true)
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
else
|
|
73
|
-
@queue << :k_escape
|
|
74
|
-
handle_byte second_byte
|
|
75
|
-
handle_byte third_byte if third_byte
|
|
76
|
-
end
|
|
64
|
+
if second_byte.nil?
|
|
65
|
+
@queue << :k_escape
|
|
66
|
+
nil
|
|
67
|
+
elsif second_byte == "["
|
|
68
|
+
third_byte = read_byte(timeout: true)
|
|
69
|
+
|
|
70
|
+
case third_byte
|
|
71
|
+
when "A"
|
|
72
|
+
@queue << :k_up
|
|
73
|
+
when "B"
|
|
74
|
+
@queue << :k_down
|
|
75
|
+
when "C"
|
|
76
|
+
@queue << :k_right
|
|
77
|
+
when "D"
|
|
78
|
+
@queue << :k_left
|
|
77
79
|
else
|
|
78
80
|
@queue << :k_escape
|
|
79
81
|
handle_byte second_byte
|
|
82
|
+
handle_byte third_byte if third_byte
|
|
80
83
|
end
|
|
84
|
+
else
|
|
85
|
+
@queue << :k_escape
|
|
86
|
+
handle_byte second_byte
|
|
81
87
|
end
|
|
88
|
+
end
|
|
82
89
|
|
|
83
|
-
|
|
84
|
-
|
|
90
|
+
def handle_byte(byte)
|
|
91
|
+
return unless BYTES.key?(byte)
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
@queue << BYTES[byte]
|
|
94
|
+
end
|
|
88
95
|
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
def read_byte(timeout: false)
|
|
97
|
+
return if timeout && !IO.select([STDIN], nil, nil, 0.02)
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
end
|
|
99
|
+
STDIN.readpartial(1)
|
|
94
100
|
end
|
|
95
101
|
end
|
|
96
102
|
end
|
data/lib/glw/screen.rb
CHANGED
|
@@ -1,103 +1,105 @@
|
|
|
1
1
|
module GLW
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
fg = cell.fg_color
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
if cell.bg_color != bg
|
|
19
|
-
output << Terminal.set_bg(cell.bg_color)
|
|
20
|
-
bg = cell.bg_color
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
output << cell.char
|
|
2
|
+
class Screen
|
|
3
|
+
UpdateBlock = Data.define(:start_index, :cells) do
|
|
4
|
+
def self.make(start_index) = new(start_index, [])
|
|
5
|
+
|
|
6
|
+
def to_s(current_fg: nil, current_bg: nil)
|
|
7
|
+
output = ""
|
|
8
|
+
fg = current_fg
|
|
9
|
+
bg = current_bg
|
|
10
|
+
|
|
11
|
+
cells.each do |cell|
|
|
12
|
+
if cell.fg_color != fg
|
|
13
|
+
output << Terminal.set_fg(cell.fg_color)
|
|
14
|
+
fg = cell.fg_color
|
|
24
15
|
end
|
|
25
16
|
|
|
26
|
-
|
|
17
|
+
if cell.bg_color != bg
|
|
18
|
+
output << Terminal.set_bg(cell.bg_color)
|
|
19
|
+
bg = cell.bg_color
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
output << cell.char
|
|
27
23
|
end
|
|
24
|
+
|
|
25
|
+
[output, fg, bg]
|
|
28
26
|
end
|
|
27
|
+
end
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
def initialize
|
|
30
|
+
@render_queue = nil
|
|
31
|
+
end
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
width: IO.console.winsize[1]
|
|
36
|
-
)
|
|
33
|
+
def start!
|
|
34
|
+
@render_queue = Thread::Queue.new
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
@render_queue.push BufferState.blank(
|
|
37
|
+
height: IO.console.winsize[0],
|
|
38
|
+
width: IO.console.winsize[1]
|
|
39
|
+
)
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Thread.new do
|
|
42
|
+
STDOUT.print Terminal.hide_cursor
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
state = @render_queue.pop
|
|
44
|
+
last_state = nil
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
loop do
|
|
47
|
+
state = @render_queue.pop
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
# Throw away stale states until we have the newest one.
|
|
50
|
+
state = @render_queue.pop until @render_queue.empty?
|
|
51
|
+
|
|
52
|
+
render(last_state, state)
|
|
53
|
+
last_state = state
|
|
52
54
|
end
|
|
53
55
|
end
|
|
56
|
+
end
|
|
54
57
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
def push_state(state)
|
|
59
|
+
@render_queue.push(state)
|
|
60
|
+
end
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
private
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
def render(last_state, state)
|
|
65
|
+
updates = []
|
|
66
|
+
current_block = nil
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
state.cells.each_with_index do |cell, index|
|
|
69
|
+
next if last_state && cell == last_state.cells[index]
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
if current_block && current_block.start_index + current_block.cells.length == index
|
|
72
|
+
current_block.cells << cell
|
|
73
|
+
else
|
|
74
|
+
updates << current_block if current_block
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
end
|
|
76
|
+
current_block = UpdateBlock.new(index, [cell])
|
|
75
77
|
end
|
|
78
|
+
end
|
|
76
79
|
|
|
77
|
-
|
|
80
|
+
updates << current_block if current_block
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
current_fg = nil
|
|
83
|
+
current_bg = nil
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
STDOUT.print(
|
|
86
|
+
updates.flat_map do |block|
|
|
87
|
+
y = block.start_index / state.width
|
|
88
|
+
x = block.start_index % state.width
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
block_output, current_fg, current_bg = block.to_s(
|
|
91
|
+
current_fg:,
|
|
92
|
+
current_bg:
|
|
93
|
+
)
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
[
|
|
96
|
+
Terminal.move_to(x, y),
|
|
97
|
+
block_output
|
|
98
|
+
]
|
|
99
|
+
end.join
|
|
100
|
+
)
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
end
|
|
102
|
+
STDOUT.flush
|
|
101
103
|
end
|
|
102
104
|
end
|
|
103
105
|
end
|
data/lib/glw/universe.rb
ADDED
data/lib/glw/version.rb
CHANGED
data/lib/glw.rb
CHANGED
|
@@ -15,17 +15,36 @@ module GLW
|
|
|
15
15
|
class << self
|
|
16
16
|
def start!
|
|
17
17
|
IO.console.raw do
|
|
18
|
-
Screen.
|
|
19
|
-
Input.
|
|
18
|
+
screen = Screen.new
|
|
19
|
+
input = Input.new
|
|
20
|
+
|
|
21
|
+
screen.start!
|
|
22
|
+
input.start!
|
|
23
|
+
|
|
24
|
+
universe = Universe.new
|
|
25
|
+
|
|
26
|
+
target_fps = 20
|
|
27
|
+
frame_time = 1.0 / target_fps
|
|
20
28
|
|
|
21
29
|
loop do
|
|
22
|
-
|
|
30
|
+
frame_start = Time.now
|
|
31
|
+
|
|
32
|
+
until input.empty?
|
|
33
|
+
key = input.pop
|
|
34
|
+
|
|
35
|
+
exit(0) if key == :k_escape || key == :k_ctrl_c
|
|
36
|
+
|
|
37
|
+
universe.singletons[Components::Input].push key
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
height, width = *IO.console.winsize
|
|
41
|
+
buffer = universe.render(width:, height:)
|
|
42
|
+
screen.push_state(buffer)
|
|
23
43
|
|
|
24
|
-
|
|
44
|
+
elapsed = Time.now - frame_start
|
|
45
|
+
sleep(frame_time - elapsed) if elapsed < frame_time
|
|
25
46
|
end
|
|
26
47
|
end
|
|
27
|
-
rescue
|
|
28
|
-
raise
|
|
29
48
|
end
|
|
30
49
|
end
|
|
31
50
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: good_luck_wizard
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jared Norman
|
|
@@ -73,11 +73,13 @@ files:
|
|
|
73
73
|
- good_luck_wizard.gemspec
|
|
74
74
|
- lib/glw.rb
|
|
75
75
|
- lib/glw/buffer_state.rb
|
|
76
|
+
- lib/glw/components/input.rb
|
|
76
77
|
- lib/glw/input.rb
|
|
77
78
|
- lib/glw/random.rb
|
|
78
79
|
- lib/glw/screen.rb
|
|
79
80
|
- lib/glw/screen_cell.rb
|
|
80
81
|
- lib/glw/terminal.rb
|
|
82
|
+
- lib/glw/universe.rb
|
|
81
83
|
- lib/glw/version.rb
|
|
82
84
|
- test_input.rb
|
|
83
85
|
homepage: http://github.com/jarednorman/good_luck_wizard
|