sortviz 0.5.3 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -27
- data/exe/sortviz +7 -0
- data/lib/algorithms/selectionsort.rb +1 -1
- data/lib/sortviz/canvas.rb +34 -33
- data/lib/sortviz/cursor.rb +2 -3
- data/lib/sortviz/plugins.rb +11 -0
- data/lib/sortviz/version.rb +1 -1
- data/lib/sortviz/visualizer.rb +77 -46
- data/lib/sortviz.rb +1 -2
- data/screenshot.png +0 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a26d73bfed67f35fc0932b25232780f66317e86
|
4
|
+
data.tar.gz: 37bf5941624feb66a49d2bce915f7b30aa791103
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a450eaafc8aa96da572fbbafe76def7f3d85969cdb666aa0f60eccd5537be860ed41b43a05cbf6e79f42dea64db842001841860fa6e26fe6bdda9f9816ca2b79
|
7
|
+
data.tar.gz: 954c10988e7585ea53c4a58392bf0c8f42fd38e318358c99137485d258605acb75fd7bfb2fce87d62cb3fbad5ab1ffaf00f00f15979a9379f97b020950b4da75
|
data/README.md
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
# Sortviz
|
2
2
|
|
3
|
-
Sortviz is a
|
4
|
-
|
5
|
-
for
|
3
|
+
Sortviz is a small terminal program written in Ruby and uses the Curses library
|
4
|
+
It lets you visualize sorting algorithms, you can add more sorting algorithms
|
5
|
+
at will but for the time being it can only load the ones bundled with itself.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
For pointers on the over simplified (Quite bad too) plugin system, check
|
8
|
+
[lib/sortviz/plugins.rb](lib/sortviz/plugins.rb) and check the plugins at
|
9
|
+
[lib/algorithms](lib/algorithms).
|
10
10
|
|
11
11
|
## Looks like this
|
12
|
-
![
|
12
|
+
![Screenshot][screenshot]
|
13
13
|
|
14
14
|
## Working with Sortviz
|
15
15
|
|
16
16
|
Some sorting algorithms operate on single lists, swapping and sifting through
|
17
|
-
|
17
|
+
them. Others operate on multiple lists, like merge sort. With any luck, you
|
18
18
|
might be able to get these kind of sorting algorithms working but until I officially test against them, you're on your own.
|
19
19
|
|
20
20
|
__Generally speaking, the sorting algorithm (your code) has to yield a partially sorted list, with a current index on *every iteration*__.
|
21
21
|
|
22
|
-
For examples, check the
|
22
|
+
For examples, check the [lib/algorithms](lib/algorithms) directory.
|
23
23
|
|
24
|
-
Some implementations like the bubble sort one at
|
24
|
+
Some implementations like the bubble sort one at [lib/algorithms/bubblesort.rb](lib/algorithms/bubblesort.rb) generate some visual artifacts.
|
25
25
|
If you look at it, you'd notice that the index we're yielding is +1.
|
26
26
|
|
27
27
|
The reason for that is because otherwise, Sortviz will be selecting a column
|
@@ -44,23 +44,16 @@ $ sortviz -h # Will display help
|
|
44
44
|
$ sortviz -l # Will give you a list of available sorting algorithms
|
45
45
|
$ sortviz -s ALGO # Will start visualizing the sorting of 20 digits using that algorithm
|
46
46
|
```
|
47
|
-
|
48
|
-
__Notice: I haven't yet added the ability to load algorithms from a folder of your choosing, but its pretty much the same concept as of loading them from the `algorithms` folder__
|
49
|
-
|
50
47
|
## Development
|
51
|
-
|
52
|
-
*I'm using RVM to manage my Ruby and Gemset environments for the development of this Gem. So if you too are doing the same, careful cause I've checked in the following files `.ruby-gemset` and `.ruby-version`. If you don't know what I'm talking about, ignore this.*
|
53
|
-
|
54
48
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
55
49
|
|
56
|
-
To install this gem onto your local machine, run `bundle exec rake install
|
50
|
+
To install this gem onto your local machine, run `bundle exec rake install`
|
57
51
|
|
58
52
|
## Contributing
|
59
53
|
|
60
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/l0gicpath/sortviz.
|
54
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/l0gicpath/sortviz. Please read [Contributor Covenant](http://contributor-covenant.org) and kindly attempt to adhere to it.
|
61
55
|
|
62
|
-
|
63
|
-
job for you. It's OSS, be reasonable.
|
56
|
+
And don't make me do your job for you. It's OSS, be reasonable.
|
64
57
|
|
65
58
|
## Why
|
66
59
|
|
@@ -75,19 +68,15 @@ bummer looking over everything I worked in, so this is my solution.
|
|
75
68
|
|
76
69
|
Am I proud of it? Well yes, could be way better but I'm pressed on time.
|
77
70
|
|
78
|
-
|
71
|
+
*I also realized the notion of being proud of something is more of a process, after you've invested time and effort into something and grew it, you can't help but feel proud of it.*
|
79
72
|
|
80
|
-
|
81
|
-
about sorting. So I might actually expand on that and allow a way to
|
82
|
-
write sorting algorithms using pseudocode instead of Ruby.
|
73
|
+
## Future
|
83
74
|
|
75
|
+
I want this to be a teaching tool, so I'll be expanding this to support writing sorting code using pseudocode instead of Ruby.
|
84
76
|
Should be a fun little language design exercise.
|
85
77
|
|
86
78
|
## License
|
87
79
|
|
88
80
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
89
|
-
I wouldn't know why wouldn't you like MIT license but just in case you don't,
|
90
|
-
don't ping me saying it sucks. Ping bundler, they make it absurdly easy not to
|
91
|
-
think about licensing when creating a new Gem.
|
92
81
|
|
93
82
|
[screenshot]: screenshot.png "Mac OSX Terminal.app xterm-256color"
|
data/exe/sortviz
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
|
3
|
+
# Sortviz is a small terminal program written in Ruby and uses the Curses library
|
4
|
+
# It lets you visualize sorting algorithms
|
5
|
+
#
|
6
|
+
# Author:: Hady Ahmed (mailto:me@hadyahmed.com)
|
7
|
+
# Copyright:: Copyright (c) 2016
|
8
|
+
# License:: Distributes under MIT
|
9
|
+
|
3
10
|
require 'optparse'
|
4
11
|
require "sortviz"
|
5
12
|
|
data/lib/sortviz/canvas.rb
CHANGED
@@ -1,68 +1,69 @@
|
|
1
1
|
module Sortviz
|
2
|
+
# Canvas is the portion of the screen that contains the sorting bar charts.
|
3
|
+
# It displays the partially sorted list of numbers and redraws on every iteration
|
4
|
+
# marking the currently selected bar (current index in the partially sorted list)
|
2
5
|
class Canvas
|
3
6
|
extend Forwardable
|
4
|
-
def_delegators :@window,
|
5
|
-
|
6
|
-
:getch, :close, :attron, :attroff
|
7
|
-
def_delegators :@cursor, :tprint
|
8
|
-
|
9
|
-
CANVAS_HEIGHT = 20 # That's our drawing area
|
10
|
-
CANVAS_GUTTER = 50 # We subtract this value from Curses.cols
|
11
|
-
GUTTER = 5
|
12
|
-
GRAY_COLOR = 8
|
13
|
-
|
7
|
+
def_delegators :@window, :refresh, :getch, :close, :attron, :attroff
|
8
|
+
MARGIN = 5
|
14
9
|
attr_reader :window
|
15
10
|
|
11
|
+
# Initializes a new instance with a title to display, the current cursor
|
12
|
+
# object (+Sortviz::Cursor+) and the modified screen dimensions from the
|
13
|
+
# parent window/screen (standard screen created in +Sortviz::Visualizer+)
|
16
14
|
def initialize(title, cursor, screen_dim)
|
17
15
|
@screen_dim = screen_dim
|
18
16
|
@cursor = cursor
|
19
17
|
@title = title
|
20
|
-
|
21
|
-
@red_highlighter = Curses.color_pair(Curses.const_get("COLOR_RED"))
|
22
|
-
@gray_highlighter = Curses.color_pair(GRAY_COLOR)
|
23
|
-
@window = nil
|
18
|
+
@red_highlight = Curses.color_pair(Curses.const_get("COLOR_RED"))
|
24
19
|
end
|
25
20
|
|
21
|
+
# Does the initial setup of creating an actual curses window, adding a
|
22
|
+
# border to it and setting up non-blocking +Curses::Window#getch+
|
26
23
|
def setup
|
27
|
-
@window
|
28
|
-
@
|
29
|
-
@screen_dim[:cols] -
|
24
|
+
@window ||= Curses::Window.new(
|
25
|
+
@screen_dim[:lines] - MARGIN,
|
26
|
+
@screen_dim[:cols] - MARGIN,
|
30
27
|
@cursor.y, @cursor.x)
|
31
28
|
@window.box('|', '-')
|
32
|
-
# Update our y-position to reflect the addition of canvas and its height
|
33
|
-
@cursor.incr_y CANVAS_HEIGHT + 4
|
34
29
|
@window.nodelay = 1 # Non-blocking mode for #getch
|
35
30
|
end
|
36
31
|
|
32
|
+
# Draws the partially sorted list and highlights the current index
|
33
|
+
# It also attempts to center the graph in the display area, does well but
|
34
|
+
# not always, sometimes it'll be shifted to the right a bit.
|
37
35
|
def redraw(partially_sorted, selected_indx)
|
38
36
|
@window.clear
|
39
37
|
@window.box('|', '-')
|
38
|
+
|
40
39
|
@cursor.move(0, 0)
|
41
40
|
@cursor.tprint("Algorithm: #{@title}")
|
42
|
-
@cursor.move(CANVAS_HEIGHT + 2, GUTTER)
|
43
|
-
partially_sorted.each_with_index do |n, i|
|
44
|
-
|
45
|
-
attron(@gray_highlighter)
|
46
|
-
tprint(("|%02d|" % n))
|
47
|
-
attroff(@gray_highlighter)
|
48
41
|
|
49
|
-
|
42
|
+
len = partially_sorted.join.center(4).length
|
43
|
+
@cursor.move(@window.maxy - 1, (@window.maxx - len) / MARGIN)
|
44
|
+
|
45
|
+
partially_sorted.each_with_index do |number, i|
|
46
|
+
@cursor.tprint(("|%02d|" % number))
|
50
47
|
|
48
|
+
@cursor.decr_y
|
51
49
|
|
52
|
-
|
53
|
-
draw_bar(n)
|
54
|
-
attroff(@red_highlighter)
|
50
|
+
draw_bar(number, selected_indx == i)
|
55
51
|
|
56
|
-
@cursor.move_y(
|
57
|
-
@cursor.incr_x
|
52
|
+
@cursor.move_y(@window.maxy - 1)
|
53
|
+
@cursor.incr_x MARGIN
|
58
54
|
end
|
59
55
|
end
|
56
|
+
|
60
57
|
private
|
61
|
-
|
58
|
+
|
59
|
+
def draw_bar(height, highlighted)
|
60
|
+
attr = highlighted ? @red_highlight : Curses::A_REVERSE
|
61
|
+
attron(attr)
|
62
62
|
height.times do
|
63
|
-
|
63
|
+
@window.addstr(" ".center(4)) # 4 spaces
|
64
64
|
@cursor.decr_y
|
65
65
|
end
|
66
|
+
attroff(attr)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
data/lib/sortviz/cursor.rb
CHANGED
@@ -13,10 +13,9 @@ module Sortviz
|
|
13
13
|
# => y (Int)
|
14
14
|
# => x (Int)
|
15
15
|
# @return instance of Sortviz::Cursor
|
16
|
-
def initialize(window,
|
16
|
+
def initialize(window, origin)
|
17
17
|
@window = window
|
18
|
-
|
19
|
-
move(y, x)
|
18
|
+
move(origin[:y], origin[:x])
|
20
19
|
end
|
21
20
|
|
22
21
|
# Move to positions y, x
|
data/lib/sortviz/plugins.rb
CHANGED
@@ -2,6 +2,17 @@ module Sortviz
|
|
2
2
|
extend self
|
3
3
|
ALGORITHMS = {}
|
4
4
|
|
5
|
+
# Defines a new algorithm for sorting by adding some meta data to
|
6
|
+
# +Sortviz::ALGORITHMS+ and turning the sorting algorithm method into a
|
7
|
+
# Module function of +Sortviz+.
|
8
|
+
#
|
9
|
+
# It's a bad design, should be fixed soon with a proper plugin system as this
|
10
|
+
# way pollutes the Sortviz namespace.
|
11
|
+
# Params:
|
12
|
+
# +name+:: +String+ Name of the sorting algorithm for display purposes
|
13
|
+
# +func_name+:: +Symbol+ A symbol representation of the actual method that does
|
14
|
+
# the actual sorting. It's important that both match or else +Sortviz::Visualizer+
|
15
|
+
# will not be able to find it and call it.
|
5
16
|
def define_algorithm(name, func_name)
|
6
17
|
ALGORITHMS[func_name] = name
|
7
18
|
module_function func_name
|
data/lib/sortviz/version.rb
CHANGED
data/lib/sortviz/visualizer.rb
CHANGED
@@ -1,37 +1,67 @@
|
|
1
1
|
module Sortviz
|
2
|
-
# Visualizer is the entry point of
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# Visualizer is the entry point of Sortviz, given an algorithm name it'll
|
3
|
+
# initialize Curses library, a +Sortviz::Canvas+ with proper screen dimensions
|
4
|
+
# and a +Sortviz::Cursor+.
|
5
|
+
#
|
6
|
+
# It also generates a shuffled list of numbers counting from 1 to n, n is
|
7
|
+
# decided by Sortviz::Visualizer#generate_list which calculates the number
|
8
|
+
# of bar charts that can be drawn given the current screen size.
|
9
|
+
#
|
10
|
+
# _Notice_: Curses coordinates are treated in reverse, (y, x) and x represents
|
11
|
+
# the number of columns, y represents the number of lines so
|
12
|
+
# *(y, x)* is *(cols, lines)*. As a result of that we too, use _(y, x)_.
|
5
13
|
class Visualizer
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# our origin point instead of (0, 0)
|
10
|
-
GUTTER = 5 # We don't want to flirt too much with the terminal border
|
11
|
-
TOP_MARGIN = 1 # Same issue with the top border
|
12
|
-
SLEEP_INTERVAL = 0.3
|
14
|
+
# Origin starts at (1, 0) not
|
15
|
+
ORIGIN = { y: 1, x: 5 }
|
16
|
+
SLEEP_INTERVAL = 0.05
|
13
17
|
|
14
|
-
|
18
|
+
# Initializes a new Visualizer for a sorting algorithm
|
19
|
+
# Params:
|
20
|
+
# +algo+:: +Symbol+ A symbol representing a sorting algorithm method
|
21
|
+
def initialize(algo)
|
15
22
|
setup_curses
|
16
|
-
|
17
|
-
@screen_dim = { cols: Curses.cols, lines: Curses.lines }
|
18
|
-
# Take Reference for Curses initial standard window
|
23
|
+
@screen_dim = { cols: Curses.cols - ORIGIN[:x], lines: Curses.lines }
|
19
24
|
@screen = Curses.stdscr
|
20
|
-
@cursor = Cursor.new(@screen,
|
25
|
+
@cursor = Cursor.new(@screen, ORIGIN)
|
21
26
|
@canvas = Canvas.new(ALGORITHMS[algo], @cursor, @screen_dim)
|
22
27
|
|
23
28
|
@algo = algo
|
24
|
-
@unsorted_list =
|
29
|
+
@unsorted_list = generate_list
|
25
30
|
end
|
26
31
|
|
32
|
+
# Starts the actual visualization
|
33
|
+
# * First prints the program's banner
|
34
|
+
# * Sets up the canvas (+Sortviz::Canvas+)
|
35
|
+
# * Caches the cursor location so we can return to our original location later
|
36
|
+
# * Refreshes the current standard screen first
|
37
|
+
# * Refreshes the canvas
|
38
|
+
# * Finally we switch input to the canvas through +Sortviz::Cursor#switch_window+
|
39
|
+
#
|
40
|
+
# The order is important because curses works in such a way that the last
|
41
|
+
# thing on display is the last thing refreshed, if we had refreshed canvas
|
42
|
+
# first, it would have been drawn normally, then we refresh the standard
|
43
|
+
# screen (in which the canvas is inside) it'll do just that refreshs and overrides the canvas.
|
44
|
+
#
|
45
|
+
# === The loop
|
46
|
+
# In order to draw every returned partially sorted list from the algorithm method
|
47
|
+
# we have to loop, clearing the canvas everytime and redrawing it then telling
|
48
|
+
# Curses to apply these drawn updates. This helps with flicker but I still
|
49
|
+
# seem to be getting some of it ever since I introduced the +Curses::A_REVERSE+
|
50
|
+
# attribute.
|
51
|
+
#
|
52
|
+
# Durring the loop, we're trapped inside the sorting method, as a block of code
|
53
|
+
# that gets yielded to on every iteration of the sorting algorithm. To exit that
|
54
|
+
# at anytime during the sorting run, we capture the keyboard and return if any key is pressed,
|
55
|
+
# the canvas has +Curses::Window#getch+ on non-block mode so it doesn't block waiting for a character.
|
56
|
+
#
|
57
|
+
# Finally, we make sure both the canvas and the standard screen are closed.
|
58
|
+
# It's important to note that we also poll on keyboard after we exit the sorting method.
|
59
|
+
# In order to allow the user a moment to see the final sorted graph.
|
27
60
|
def visualize
|
28
61
|
begin
|
29
62
|
banner
|
30
|
-
# NOTICE: :caveat: It's important to setup the canvas AFTER displaying
|
31
|
-
# the banner, so the canvas would correctly calculate its offsets
|
32
|
-
# based on the current cursor position, as in, after printing banner
|
33
63
|
@canvas.setup
|
34
|
-
@cursor.cache
|
64
|
+
@cursor.cache
|
35
65
|
|
36
66
|
@screen.refresh
|
37
67
|
@canvas.refresh
|
@@ -41,51 +71,52 @@ module Sortviz
|
|
41
71
|
loop do
|
42
72
|
Sortviz.send(@algo, @unsorted_list) do |partially_sorted, selected_indx|
|
43
73
|
@canvas.redraw(partially_sorted, selected_indx)
|
44
|
-
|
45
|
-
|
74
|
+
Curses.doupdate
|
75
|
+
sleep SLEEP_INTERVAL
|
76
|
+
return if @canvas.getch
|
46
77
|
end
|
47
|
-
@canvas.close
|
48
|
-
|
49
78
|
@cursor.switch_window @screen # Switch back to our stdscr
|
50
79
|
@cursor.restore # Restoring here, would place us right under the canvas box
|
51
|
-
newline
|
52
|
-
tprint("Press
|
53
|
-
break if Curses.getch
|
80
|
+
@cursor.newline
|
81
|
+
@cursor.tprint("Press any key to exit")
|
82
|
+
break if Curses.getch
|
54
83
|
end
|
55
84
|
ensure
|
85
|
+
@canvas.close
|
56
86
|
Curses.close_screen
|
57
87
|
end
|
58
88
|
end
|
59
89
|
|
60
90
|
private
|
61
|
-
def update
|
62
|
-
# doupdate is much more efficient than multiple refreshes
|
63
|
-
# and after some testing, it seems to clear out the flicker
|
64
|
-
# caused by multiple rapid calls to refresh
|
65
|
-
Curses.doupdate
|
66
|
-
sleep SLEEP_INTERVAL
|
67
|
-
end
|
68
|
-
|
69
91
|
def setup_curses
|
70
92
|
Curses.init_screen
|
71
|
-
Curses.noecho
|
72
|
-
Curses.nonl # Line feeds and carriage return as well
|
93
|
+
Curses.noecho
|
73
94
|
Curses.curs_set(0) # Hide cursor
|
74
95
|
Curses.start_color # Kick off colored output
|
75
96
|
Curses.use_default_colors
|
76
97
|
|
77
|
-
# We need Red
|
78
|
-
Curses.init_pair(Curses::COLOR_RED, Curses::COLOR_RED
|
79
|
-
|
98
|
+
# We only need Red
|
99
|
+
Curses.init_pair(Curses::COLOR_RED, -1, Curses::COLOR_RED)
|
100
|
+
end
|
101
|
+
|
102
|
+
# We want to know how much can we draw depending on the window size
|
103
|
+
# To do that, we know that a single bar takes up 4 columns '|00|'' (bar_len)
|
104
|
+
# We also know that we add 5 columns between bars (Canvas::MARGIN)
|
105
|
+
# So we take cols/bar_len + CANVAS::MARGIN = n_bars
|
106
|
+
def generate_list
|
107
|
+
n = @screen_dim[:cols] / (4 + Canvas::MARGIN)
|
108
|
+
(1..n).to_a.shuffle
|
80
109
|
end
|
81
110
|
|
82
111
|
def banner
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
112
|
+
@cursor.tap do |c|
|
113
|
+
c.tprint("-----------------------------------------------------------------")
|
114
|
+
c.newline
|
115
|
+
c.tprint("SortViz: Sorting Algorithms Visualization, Ruby & Curses - v#{VERSION}")
|
116
|
+
c.newline
|
117
|
+
c.tprint("-----------------------------------------------------------------")
|
118
|
+
c.newline
|
119
|
+
end
|
89
120
|
end
|
90
121
|
|
91
122
|
end
|
data/lib/sortviz.rb
CHANGED
@@ -12,8 +12,7 @@ Dir[libdir + '/algorithms/*.rb'].each {|file| require file }
|
|
12
12
|
module Sortviz
|
13
13
|
extend self
|
14
14
|
def init(algo)
|
15
|
-
|
16
|
-
visualizer = Visualizer.new unsorted_list, algo
|
15
|
+
visualizer = Visualizer.new algo
|
17
16
|
visualizer.visualize
|
18
17
|
end
|
19
18
|
end
|
data/screenshot.png
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sortviz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hady Ahmed
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|