sortviz 0.6.1 → 0.8.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 +7 -35
- data/exe/sortviz +40 -19
- data/lib/algorithms/bubblesort.rb +12 -11
- data/lib/algorithms/insertionsort.rb +11 -11
- data/lib/algorithms/selectionsort.rb +12 -13
- data/lib/sortviz.rb +10 -3
- data/lib/sortviz/algorithms.rb +30 -0
- data/lib/sortviz/canvas.rb +30 -15
- data/lib/sortviz/core_ext.rb +17 -0
- data/lib/sortviz/version.rb +1 -1
- data/lib/sortviz/visualizer.rb +13 -11
- metadata +4 -3
- data/lib/sortviz/plugins.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 970249e15c43b4ade3c5ea494b42c41105854a23
|
4
|
+
data.tar.gz: a85bbd678f174d5efc01da4fe8d432fbbc450922
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d1545bfa711f394446dba788a9784bb045e01689f74906944e08c9d506b97afd9c69a37cf8e4629edc9c7afa9dc221a2f9a67c6c8803dce6d869f220d828167
|
7
|
+
data.tar.gz: 3c2b5d78bad824b5926debf84533a1b5fa9dcd6cbb23fddbd6738b13fec39bd11bcb46d96040e4baf51b08feba355313b1d47a466ec136546498acf97fb190ff
|
data/README.md
CHANGED
@@ -4,30 +4,13 @@ Sortviz is a small terminal program written in Ruby and uses the Curses library
|
|
4
4
|
It lets you visualize sorting algorithms, you can add more sorting algorithms
|
5
5
|
at will but for the time being it can only load the ones bundled with itself.
|
6
6
|
|
7
|
-
|
8
|
-
[lib/
|
9
|
-
[lib/algorithms](lib/algorithms).
|
7
|
+
However, for pointers about writing plugins, check
|
8
|
+
[lib/algorithms](lib/algorithms) and check the plugin system at
|
9
|
+
[lib/sortviz/algorithms.rb](lib/sortviz/algorithms.rb).
|
10
10
|
|
11
11
|
## Looks like this
|
12
12
|
![Screenshot][screenshot]
|
13
13
|
|
14
|
-
## Working with Sortviz
|
15
|
-
|
16
|
-
Some sorting algorithms operate on single lists, swapping and sifting through
|
17
|
-
them. Others operate on multiple lists, like merge sort. With any luck, you
|
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
|
-
|
20
|
-
__Generally speaking, the sorting algorithm (your code) has to yield a partially sorted list, with a current index on *every iteration*__.
|
21
|
-
|
22
|
-
For examples, check the [lib/algorithms](lib/algorithms) directory.
|
23
|
-
|
24
|
-
Some implementations like the bubble sort one at [lib/algorithms/bubblesort.rb](lib/algorithms/bubblesort.rb) generate some visual artifacts.
|
25
|
-
If you look at it, you'd notice that the index we're yielding is +1.
|
26
|
-
|
27
|
-
The reason for that is because otherwise, Sortviz will be selecting a column
|
28
|
-
that might in fact be the real current index, but the actual swapping is happening a column + 1. Which becomes confusing, so we +1 to select the
|
29
|
-
actual column that's moving around.
|
30
|
-
|
31
14
|
## Installation
|
32
15
|
|
33
16
|
This gem is not intended for your application's Gemfile, it's a
|
@@ -40,10 +23,9 @@ install it as:
|
|
40
23
|
## Usage
|
41
24
|
|
42
25
|
```shell
|
43
|
-
$ sortviz -h
|
44
|
-
$ sortviz -l # Will give you a list of available sorting algorithms
|
45
|
-
$ sortviz -s ALGO # Will start visualizing the sorting of 20 digits using that algorithm
|
26
|
+
$ sortviz -h
|
46
27
|
```
|
28
|
+
|
47
29
|
## Development
|
48
30
|
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.
|
49
31
|
|
@@ -57,18 +39,8 @@ And don't make me do your job for you. It's OSS, be reasonable.
|
|
57
39
|
|
58
40
|
## Why
|
59
41
|
|
60
|
-
|
61
|
-
|
62
|
-
when a buddy of mine that teaches CS at a university was like, I need a terminal based sorting visualizer. I'm like PSH! Like there has to be some out there, lo-and-behold, they're all web and javascript based.
|
63
|
-
|
64
|
-
I'm also using this as a demo for an interview, hope it works out. They asked
|
65
|
-
for code I'm most proud of, pretty hard question to find an answer to, as it turns out.
|
66
|
-
It's not like I write code I'm not generally proud of but yeah, was a real
|
67
|
-
bummer looking over everything I worked in, so this is my solution.
|
68
|
-
|
69
|
-
Am I proud of it? Well yes, could be way better but I'm pressed on time.
|
70
|
-
|
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.*
|
42
|
+
I blogged about this here:
|
43
|
+
[Software, a labor of love](http://hadyahmed.com/2016/06/software-a-labor-of-love/)
|
72
44
|
|
73
45
|
## Future
|
74
46
|
|
data/exe/sortviz
CHANGED
@@ -10,37 +10,58 @@
|
|
10
10
|
require 'optparse'
|
11
11
|
require "sortviz"
|
12
12
|
|
13
|
-
|
13
|
+
Options = Struct.new(:algorithm, :speed, :path)
|
14
|
+
args = Options.new(:'bubble-sort', 0.05)
|
14
15
|
|
15
|
-
|
16
|
-
opts.banner =
|
17
|
-
USAGE: sortviz [options]
|
16
|
+
@parser = OptionParser.new do |opts|
|
17
|
+
opts.banner = 'Usage: sortviz [options] sorting_algorithm_name'
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
HELP
|
22
|
-
|
23
|
-
opts.on('-s', '--sort ALGORITHM', 'Visualize sorting ALGORITHM (Default: bubblesort)') do |algo|
|
24
|
-
if Sortviz::ALGORITHMS.has_key? algo.to_sym
|
25
|
-
Sortviz.init(algo.to_sym)
|
26
|
-
else
|
27
|
-
puts "We couldn't find this algorithm, please check available algorithms through -l option"
|
28
|
-
exit
|
29
|
-
end
|
19
|
+
opts.on('-sSPEED', '--sorting-speed=SPEED', OptionParser::DecimalNumeric, 'Set the sorting speed in seconds') do |speed|
|
20
|
+
args.speed = speed
|
30
21
|
end
|
31
22
|
|
32
23
|
opts.on('-l', '--list', 'List of available sorting algorithms') do
|
33
24
|
puts "Sortviz: List of available algorithms"
|
34
25
|
puts "-------------------------------------"
|
35
|
-
Sortviz::
|
36
|
-
|
26
|
+
Sortviz::Algorithms.plugins.each do |plugin|
|
27
|
+
print "#{plugin[:display_name]} \n"
|
28
|
+
print "\t Use as: sortviz #{plugin[:name]} \n"
|
29
|
+
print "\t Author: #{plugin[:author]} \n"
|
37
30
|
end
|
31
|
+
exit
|
38
32
|
end
|
39
33
|
|
40
|
-
opts.
|
34
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
41
35
|
puts opts
|
42
36
|
exit
|
43
37
|
end
|
38
|
+
|
39
|
+
opts.on_tail('--version', 'Show version') do
|
40
|
+
puts "Sortviz v#{Sortviz::VERSION}"
|
41
|
+
exit
|
42
|
+
end
|
44
43
|
end
|
45
44
|
|
46
|
-
|
45
|
+
def show_help(message = nil)
|
46
|
+
print "#{message} \n\n" unless message.nil?
|
47
|
+
puts @parser
|
48
|
+
exit -1
|
49
|
+
end
|
50
|
+
|
51
|
+
if ARGV.empty?
|
52
|
+
show_help
|
53
|
+
else
|
54
|
+
begin
|
55
|
+
@parser.parse!(ARGV)
|
56
|
+
rescue OptionParser::MissingArgument => missing
|
57
|
+
print "Error: #{missing.to_s} \n\n"
|
58
|
+
show_help
|
59
|
+
end
|
60
|
+
|
61
|
+
algorithm = Sortviz.find_algorithm(ARGV.pop)
|
62
|
+
show_help("Error: Please specify the algorithm name") if algorithm.nil?
|
63
|
+
|
64
|
+
args.algorithm = algorithm
|
65
|
+
|
66
|
+
Sortviz.init(args)
|
67
|
+
end
|
@@ -1,15 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
Sortviz::Algorithms.define 'Bubble Sort' do
|
2
|
+
author 'ieatkimchi'
|
3
|
+
url 'https://github.com/ieatkimchi/Bubble_Sort_Ruby'
|
4
|
+
name :'bubble-sort'
|
5
|
+
|
6
|
+
sort -> (unsorted_list){
|
7
|
+
unsorted_list.each_index do |i|
|
8
|
+
(unsorted_list.length - i - 1).times do |j|
|
9
|
+
if unsorted_list[j] > unsorted_list[j + 1]
|
10
|
+
unsorted_list.swap!(j, j+1)
|
8
11
|
end
|
9
|
-
yield list, j + 1
|
10
12
|
end
|
11
13
|
end
|
12
|
-
|
13
|
-
|
14
|
-
define_algorithm "Bubble Sort", :bubblesort
|
14
|
+
return unsorted_list
|
15
|
+
}
|
15
16
|
end
|
@@ -1,16 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
Sortviz::Algorithms.define 'Insertion Sort' do
|
2
|
+
author 'Emad Elsaid'
|
3
|
+
url 'https://coderwall.com/p/z8vowg/simple-sorting-algorithms-with-ruby'
|
4
|
+
name :'insertion-sort'
|
5
|
+
|
6
|
+
sort -> (unsorted_list) {
|
7
|
+
(1...unsorted_list.size).each do |i|
|
6
8
|
j = i
|
7
|
-
while j > 0 and
|
8
|
-
|
9
|
+
while j > 0 and unsorted_list[j-1] > unsorted_list[j]
|
10
|
+
unsorted_list.swap!(j, j-1)
|
9
11
|
j = j - 1
|
10
|
-
yield list, j
|
11
12
|
end
|
12
13
|
end
|
13
|
-
|
14
|
-
|
15
|
-
define_algorithm "Insertion Sort", :insertionsort
|
14
|
+
return unsorted_list
|
15
|
+
}
|
16
16
|
end
|
@@ -1,19 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
Sortviz::Algorithms.define 'Selection Sort' do
|
2
|
+
author 'Emad Elsaid'
|
3
|
+
url 'https://coderwall.com/p/z8vowg/simple-sorting-algorithms-with-ruby'
|
4
|
+
name :'selection-sort'
|
5
|
+
|
6
|
+
sort -> (unsorted_list) {
|
7
|
+
(0...unsorted_list.size).each do |j|
|
6
8
|
# find index of minimum element in the unsorted part
|
7
9
|
iMin = j
|
8
|
-
(j+1...
|
9
|
-
iMin = i if
|
10
|
+
(j+1...unsorted_list.size).each do |i|
|
11
|
+
iMin = i if unsorted_list[i] < unsorted_list[iMin]
|
10
12
|
end
|
11
|
-
|
12
13
|
# then swap it
|
13
|
-
|
14
|
-
yield list, j
|
14
|
+
unsorted_list.swap!(j, iMin)
|
15
15
|
end
|
16
|
-
|
17
|
-
|
18
|
-
define_algorithm "Selection Sort", :selectionsort
|
16
|
+
return unsorted_list
|
17
|
+
}
|
19
18
|
end
|
data/lib/sortviz.rb
CHANGED
@@ -1,18 +1,25 @@
|
|
1
1
|
require 'curses'
|
2
2
|
require 'forwardable'
|
3
3
|
require 'sortviz/version'
|
4
|
+
require 'sortviz/core_ext'
|
4
5
|
require 'sortviz/cursor'
|
5
6
|
require 'sortviz/canvas'
|
6
7
|
require 'sortviz/visualizer'
|
7
|
-
require 'sortviz/
|
8
|
+
require 'sortviz/algorithms'
|
8
9
|
|
9
10
|
libdir = File.dirname(__FILE__)
|
10
11
|
Dir[libdir + '/algorithms/*.rb'].each {|file| require file }
|
11
12
|
|
12
13
|
module Sortviz
|
13
14
|
extend self
|
14
|
-
|
15
|
-
|
15
|
+
|
16
|
+
def find_algorithm(algorithm)
|
17
|
+
return nil if algorithm.nil?
|
18
|
+
Algorithms.plugins.find { |plugin| plugin[:name] == algorithm.to_sym }.freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def init(args)
|
22
|
+
visualizer = Visualizer.new args
|
16
23
|
visualizer.visualize
|
17
24
|
end
|
18
25
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Sortviz
|
2
|
+
class Algorithms
|
3
|
+
class << self
|
4
|
+
def define(algorithm_name, &block)
|
5
|
+
plugins << { display_name: algorithm_name }
|
6
|
+
instance_eval &block
|
7
|
+
end
|
8
|
+
|
9
|
+
def plugins
|
10
|
+
@plugins ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def sort(block)
|
14
|
+
Algorithms.plugins.last[:sort] = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def name(name)
|
18
|
+
plugins.last[:name] = name
|
19
|
+
end
|
20
|
+
|
21
|
+
def author(author)
|
22
|
+
plugins.last[:author] = author
|
23
|
+
end
|
24
|
+
|
25
|
+
def url(link)
|
26
|
+
plugins.last[:url] = link
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/sortviz/canvas.rb
CHANGED
@@ -25,45 +25,60 @@ module Sortviz
|
|
25
25
|
@screen_dim[:lines] - MARGIN,
|
26
26
|
@screen_dim[:cols] - MARGIN,
|
27
27
|
@cursor.y, @cursor.x)
|
28
|
-
@window.box('|', '-')
|
29
28
|
@window.nodelay = 1 # Non-blocking mode for #getch
|
30
29
|
end
|
31
30
|
|
32
31
|
# Draws the partially sorted list and highlights the current index
|
33
32
|
# It also attempts to center the graph in the display area, does well but
|
34
33
|
# not always, sometimes it'll be shifted to the right a bit.
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@cursor.move(0, 0)
|
40
|
-
@cursor.tprint("Algorithm: #{@title}")
|
34
|
+
def draw(partially_sorted, selection)
|
35
|
+
clear
|
36
|
+
draw_title
|
41
37
|
|
42
38
|
len = partially_sorted.join.center(4).length
|
39
|
+
# We draw bottom up, this sets our y-position at the very bottom of
|
40
|
+
# the canvas and our x-position half way through the canvas
|
43
41
|
@cursor.move(@window.maxy - 1, (@window.maxx - len) / MARGIN)
|
44
42
|
|
45
43
|
partially_sorted.each_with_index do |number, i|
|
46
|
-
|
44
|
+
draw_number(number)
|
45
|
+
draw_bar(number, i == selection)
|
46
|
+
next_bar
|
47
|
+
end
|
48
|
+
|
49
|
+
Curses.doupdate
|
50
|
+
end
|
47
51
|
|
48
|
-
|
52
|
+
private
|
49
53
|
|
50
|
-
|
54
|
+
def clear
|
55
|
+
@window.clear
|
56
|
+
@window.box('|', '-')
|
57
|
+
end
|
51
58
|
|
52
|
-
|
53
|
-
|
54
|
-
|
59
|
+
def draw_title
|
60
|
+
@cursor.move(0, 0)
|
61
|
+
@cursor.tprint("Algorithm: #{@title}")
|
55
62
|
end
|
56
63
|
|
57
|
-
|
64
|
+
def draw_number(number)
|
65
|
+
@cursor.tprint(("|%02d|" % number))
|
66
|
+
@cursor.decr_y
|
67
|
+
end
|
58
68
|
|
59
69
|
def draw_bar(height, highlighted)
|
60
70
|
attr = highlighted ? @red_highlight : Curses::A_REVERSE
|
61
71
|
attron(attr)
|
62
72
|
height.times do
|
63
|
-
@
|
73
|
+
@cursor.tprint(" ".center(4)) # 4 spaces
|
64
74
|
@cursor.decr_y
|
65
75
|
end
|
66
76
|
attroff(attr)
|
67
77
|
end
|
78
|
+
|
79
|
+
def next_bar
|
80
|
+
@cursor.move_y(@window.maxy - 1)
|
81
|
+
@cursor.incr_x MARGIN
|
82
|
+
end
|
68
83
|
end
|
69
84
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# TODO: Use refinements
|
2
|
+
class Array
|
3
|
+
def versions
|
4
|
+
@versions ||= Array.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def swap!(a, b)
|
8
|
+
self[a], self[b] = self[b], self[a]
|
9
|
+
mark_version(b)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
# idx: Current selection index
|
14
|
+
def mark_version(idx)
|
15
|
+
versions << [self.dup, idx]
|
16
|
+
end
|
17
|
+
end
|
data/lib/sortviz/version.rb
CHANGED
data/lib/sortviz/visualizer.rb
CHANGED
@@ -13,20 +13,22 @@ module Sortviz
|
|
13
13
|
class Visualizer
|
14
14
|
# Origin starts at (1, 0) not (0, 0)
|
15
15
|
ORIGIN = { y: 1, x: 5 }
|
16
|
-
SLEEP_INTERVAL = 0.05
|
17
16
|
|
18
17
|
# Initializes a new Visualizer for a sorting algorithm
|
19
18
|
#
|
20
19
|
# Params:
|
21
|
-
# +
|
22
|
-
def initialize(
|
20
|
+
# +args+:: <tt>Struct</tt> Command line args that configure how visualization will happen
|
21
|
+
def initialize(args)
|
23
22
|
setup_curses
|
24
23
|
@screen_dim = { cols: Curses.cols - ORIGIN[:x], lines: Curses.lines }
|
25
24
|
@screen = Curses.stdscr
|
26
25
|
@cursor = Cursor.new(@screen, ORIGIN)
|
27
|
-
@canvas = Canvas.new(ALGORITHMS[algo], @cursor, @screen_dim)
|
28
26
|
|
29
|
-
@
|
27
|
+
@algorithm = args.algorithm
|
28
|
+
|
29
|
+
@canvas = Canvas.new(@algorithm[:display_name], @cursor, @screen_dim)
|
30
|
+
|
31
|
+
@sorting_speed = args.speed
|
30
32
|
@unsorted_list = generate_list
|
31
33
|
end
|
32
34
|
|
@@ -69,13 +71,13 @@ module Sortviz
|
|
69
71
|
|
70
72
|
@cursor.switch_window @canvas.window
|
71
73
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
sleep
|
74
|
+
while true do
|
75
|
+
result = @algorithm[:sort].call(@unsorted_list)
|
76
|
+
result.versions.each do |version|
|
77
|
+
@canvas.draw(version[0], version[1])
|
78
|
+
sleep @sorting_speed
|
77
79
|
return if @canvas.getch
|
78
|
-
|
80
|
+
end
|
79
81
|
@cursor.switch_window @screen # Switch back to our stdscr
|
80
82
|
@cursor.restore # Restoring here, would place us right under the canvas box
|
81
83
|
@cursor.newline
|
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.8.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-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -91,9 +91,10 @@ files:
|
|
91
91
|
- lib/algorithms/insertionsort.rb
|
92
92
|
- lib/algorithms/selectionsort.rb
|
93
93
|
- lib/sortviz.rb
|
94
|
+
- lib/sortviz/algorithms.rb
|
94
95
|
- lib/sortviz/canvas.rb
|
96
|
+
- lib/sortviz/core_ext.rb
|
95
97
|
- lib/sortviz/cursor.rb
|
96
|
-
- lib/sortviz/plugins.rb
|
97
98
|
- lib/sortviz/version.rb
|
98
99
|
- lib/sortviz/visualizer.rb
|
99
100
|
- screenshot.png
|
data/lib/sortviz/plugins.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Sortviz
|
2
|
-
extend self
|
3
|
-
ALGORITHMS = {}
|
4
|
-
|
5
|
-
# Defines a new algorithm for sorting by adding some meta data to
|
6
|
-
# <tt>Sortviz::ALGORITHMS</tt> and turning the sorting algorithm method into a
|
7
|
-
# Module function of <tt>Sortviz</tt>.
|
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
|
-
#
|
12
|
-
# Params:
|
13
|
-
# +name+:: <tt>String</tt> Name of the sorting algorithm for display purposes
|
14
|
-
# +func_name+:: <tt>Symbol</tt> A symbol representation of the actual method that does
|
15
|
-
# the actual sorting. It's important that both match or else <tt>Sortviz::Visualizer</tt>
|
16
|
-
# will not be able to find it and call it.
|
17
|
-
def define_algorithm(name, func_name)
|
18
|
-
ALGORITHMS[func_name] = name
|
19
|
-
module_function func_name
|
20
|
-
end
|
21
|
-
end
|