sortviz 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +93 -0
- data/Rakefile +6 -0
- data/algorithms/bubblesort.rb +15 -0
- data/algorithms/insertionsort.rb +16 -0
- data/algorithms/selectionsort.rb +19 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/sortviz +39 -0
- data/lib/sortviz/canvas.rb +68 -0
- data/lib/sortviz/cursor.rb +113 -0
- data/lib/sortviz/plugins.rb +9 -0
- data/lib/sortviz/version.rb +3 -0
- data/lib/sortviz/visualizer.rb +92 -0
- data/lib/sortviz.rb +20 -0
- data/screenshot.png +0 -0
- data/sortviz.gemspec +26 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b212635c737033687fa8fd584a29bba5a6a4bdc1
|
4
|
+
data.tar.gz: 6786147038a1092d595facfa0df0888b5aac1515
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b407fb6f5332ecd5e45a22105585712077fbe2240db5db67324af79730e9e744344f15e302b970f70bce4bc227c4c84f01deca106365a02a2f4d9961c1c88cd
|
7
|
+
data.tar.gz: f576f741da8b1f7c958d2c932ea51b8fb646fc71585f950e05203e0cbee30a94697269df92b04c727e11cc3fd0dcdd43902ba752bbc16ffb79532f85753efd45
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
sortviz
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.1.5
|
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at hady.fathy@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Hady Ahmed
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Sortviz
|
2
|
+
|
3
|
+
Sortviz is a sorting algorithm visualizer. But here's the rub, it's terminal
|
4
|
+
based. You guessed it! This sweet little tool will visualize sorting algorithms
|
5
|
+
for your viewing pleasures, it's written in Ruby and uses Curses for the terminal work.
|
6
|
+
|
7
|
+
It also has a rather over simplified plugin architecture, more like a ruby
|
8
|
+
trick than anything. You can write your own sorting code and have it visualize
|
9
|
+
that for you. You just need to follow a general principle.
|
10
|
+
|
11
|
+
## Looks like this
|
12
|
+
![alt text][screenshot]
|
13
|
+
|
14
|
+
## Working with Sortviz
|
15
|
+
|
16
|
+
Some sorting algorithms operate on single lists, swapping and sifting through
|
17
|
+
it. 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 `algorithms` folder.
|
23
|
+
|
24
|
+
Some implementations like the bubble sort one at `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
|
+
## Installation
|
32
|
+
|
33
|
+
This gem is not intended for your application's Gemfile, it's a
|
34
|
+
ruby program packaged as a Gem:
|
35
|
+
|
36
|
+
install it as:
|
37
|
+
|
38
|
+
$ gem install sortviz
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
```shell
|
43
|
+
$ sortviz -h # Will display help
|
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
|
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
|
+
## 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
|
+
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
|
+
|
56
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/l0gicpath/sortviz. (This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct) I Awe there, bundler is really sweet, isn't it?
|
61
|
+
|
62
|
+
Seriously now, PRs are welcome, bug reports too but don't make me do your
|
63
|
+
job for you. It's OSS, be reasonable.
|
64
|
+
|
65
|
+
## Why
|
66
|
+
|
67
|
+
Because I'm a terminal guy that's why. I like to think I'm not too
|
68
|
+
bad googling either and for the life of me I couldn't find anything similar
|
69
|
+
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.
|
70
|
+
|
71
|
+
I'm also using this as a demo for an interview, hope it works out. They asked
|
72
|
+
for code I'm most proud of, pretty hard question to find an answer to, as it turns out.
|
73
|
+
It's not like I write code I'm not generally proud of but yeah, was a real
|
74
|
+
bummer looking over everything I worked in, so this is my solution.
|
75
|
+
|
76
|
+
Am I proud of it? Well yes, could be way better but I'm pressed on time.
|
77
|
+
|
78
|
+
## Future
|
79
|
+
|
80
|
+
When I started this, I wanted something that's easy to use to learn more
|
81
|
+
about sorting. So I might actually expand on that and allow a way to
|
82
|
+
write sorting algorithms using pseudocode instead of Ruby.
|
83
|
+
|
84
|
+
Should be a fun little language design exercise.
|
85
|
+
|
86
|
+
## License
|
87
|
+
|
88
|
+
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
|
+
|
93
|
+
[screenshot]: screenshot.png "Mac OSX Terminal.app xterm-256color"
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sortviz
|
2
|
+
|
3
|
+
def bubblesort(list)
|
4
|
+
list.each_index do |i|
|
5
|
+
(list.length - i - 1).times do |j|
|
6
|
+
if list[j] > list[j + 1]
|
7
|
+
list[j], list[j + 1] = list[j + 1], list[j]
|
8
|
+
end
|
9
|
+
yield list, j + 1
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
define_algorithm "Bubble Sort", :bubblesort
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Sortviz
|
2
|
+
# insertion_sort is brought in from
|
3
|
+
# https://coderwall.com/p/z8vowg/simple-sorting-algorithms-with-ruby
|
4
|
+
def insertionsort(list)
|
5
|
+
(1...list.size).each do |i|
|
6
|
+
j = i
|
7
|
+
while j > 0 and list[j-1] > list[j]
|
8
|
+
list[j], list[j-1] = list[j-1], list[j]
|
9
|
+
j = j - 1
|
10
|
+
yield list, j
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
define_algorithm "Insertion Sort", :insertionsort
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sortviz
|
2
|
+
# selection_sort is brought in from
|
3
|
+
# https://coderwall.com/p/z8vowg/simple-sorting-algorithms-with-ruby
|
4
|
+
def selectionsort(list)
|
5
|
+
(0...list.size).each do |j|
|
6
|
+
# find index of minimum element in the unsorted part
|
7
|
+
iMin = j
|
8
|
+
(j+1...list.size).each do |i|
|
9
|
+
iMin = i if list[i] < list[iMin]
|
10
|
+
end
|
11
|
+
|
12
|
+
# then swap it
|
13
|
+
list[j], list[iMin] = list[iMin], list[j]
|
14
|
+
yield list, iMin
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
define_algorithm "Selection Sort", :selectionsort
|
19
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "sortviz"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/sortviz
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require "sortviz"
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
|
8
|
+
oparser = OptionParser.new do |opts|
|
9
|
+
opts.banner = <<-HELP
|
10
|
+
USAGE: sortviz [options]
|
11
|
+
|
12
|
+
Exit: Use 'q' to exit during visualization
|
13
|
+
|
14
|
+
HELP
|
15
|
+
|
16
|
+
opts.on('-s', '--sort ALGORITHM', 'Visualize sorting ALGORITHM (Default: bubblesort)') do |algo|
|
17
|
+
if Sortviz::ALGORITHMS.has_key? algo.to_sym
|
18
|
+
Sortviz.init(algo.to_sym)
|
19
|
+
else
|
20
|
+
puts "We couldn't find this algorithm, please check available algorithms through -l option"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-l', '--list', 'List of available sorting algorithms') do
|
26
|
+
puts "Sortviz: List of available algorithms"
|
27
|
+
puts "-------------------------------------"
|
28
|
+
Sortviz::ALGORITHMS.each do |func_name, name|
|
29
|
+
puts " #{name} (-s #{func_name})"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-h', '--help', 'View this help') do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
oparser.parse!(ARGV)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Sortviz
|
2
|
+
class Canvas
|
3
|
+
extend Forwardable
|
4
|
+
def_delegators :@window,
|
5
|
+
:refresh, :clear,
|
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
|
+
|
14
|
+
attr_reader :window
|
15
|
+
|
16
|
+
def initialize(title, cursor, screen_dim)
|
17
|
+
@screen_dim = screen_dim
|
18
|
+
@cursor = cursor
|
19
|
+
@title = title
|
20
|
+
# Cache red since we'll use it to highlight later, best do it now
|
21
|
+
@red_highlighter = Curses.color_pair(Curses.const_get("COLOR_RED"))
|
22
|
+
@gray_highlighter = Curses.color_pair(GRAY_COLOR)
|
23
|
+
@window = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup
|
27
|
+
@window = Curses::Window.new(
|
28
|
+
@cursor.y + CANVAS_HEIGHT,
|
29
|
+
@screen_dim[:cols] - CANVAS_GUTTER,
|
30
|
+
@cursor.y, @cursor.x)
|
31
|
+
@window.box('|', '-')
|
32
|
+
# Update our y-position to reflect the addition of canvas and its height
|
33
|
+
@cursor.incr_y CANVAS_HEIGHT + 4
|
34
|
+
@window.nodelay = 1 # Non-blocking mode for #getch
|
35
|
+
end
|
36
|
+
|
37
|
+
def redraw(partially_sorted, selected_indx)
|
38
|
+
@window.clear
|
39
|
+
@window.box('|', '-')
|
40
|
+
@cursor.move(0, 0)
|
41
|
+
@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
|
+
|
49
|
+
@cursor.decr_y
|
50
|
+
|
51
|
+
|
52
|
+
attron(@red_highlighter) if selected_indx == i
|
53
|
+
draw_bar(n)
|
54
|
+
attroff(@red_highlighter)
|
55
|
+
|
56
|
+
@cursor.move_y(CANVAS_HEIGHT + 2)
|
57
|
+
@cursor.incr_x GUTTER
|
58
|
+
end
|
59
|
+
end
|
60
|
+
private
|
61
|
+
def draw_bar(height)
|
62
|
+
height.times do
|
63
|
+
tprint("|_ |")
|
64
|
+
@cursor.decr_y
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Sortviz
|
2
|
+
# Cursor represents a terminal cursor, can easily switch between
|
3
|
+
# different windows (curses) and can remember the last cursor position it
|
4
|
+
# was at.
|
5
|
+
# This is to wrap up the boilerplate code of moving a cursor around a terminal
|
6
|
+
# window
|
7
|
+
class Cursor
|
8
|
+
attr_reader :y, :x, :window
|
9
|
+
# Initialize a new Cursor object, follows curses practice of passing
|
10
|
+
# the y before the x coords.
|
11
|
+
# @params
|
12
|
+
# => window (Curses::Window)
|
13
|
+
# => y (Int)
|
14
|
+
# => x (Int)
|
15
|
+
# @return instance of Sortviz::Cursor
|
16
|
+
def initialize(window, y, x)
|
17
|
+
@window = window
|
18
|
+
@cached = nil
|
19
|
+
move(y, x)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Move to positions y, x
|
23
|
+
# @params
|
24
|
+
# => y (Int)
|
25
|
+
# => x (Int)
|
26
|
+
def move(y, x)
|
27
|
+
update(y, x)
|
28
|
+
apply_pos
|
29
|
+
end
|
30
|
+
|
31
|
+
# Move along the y-axis
|
32
|
+
# @params
|
33
|
+
# => val (Int) new Value of y
|
34
|
+
def move_y(val)
|
35
|
+
update(val, x)
|
36
|
+
apply_pos
|
37
|
+
end
|
38
|
+
|
39
|
+
# Move along the x-axis
|
40
|
+
# @params
|
41
|
+
# => val (Int) new Value of x
|
42
|
+
def move_x(val)
|
43
|
+
update(y, val)
|
44
|
+
apply_pos
|
45
|
+
end
|
46
|
+
|
47
|
+
# Increment the y value by val
|
48
|
+
# @params
|
49
|
+
# => val (Int) new Value of y, Default: 1
|
50
|
+
def incr_y(val = 1)
|
51
|
+
update(y + val, x)
|
52
|
+
apply_pos
|
53
|
+
end
|
54
|
+
|
55
|
+
# Decrement the y value by val
|
56
|
+
# @params
|
57
|
+
# => val (Int) new Value of y, Default: 1
|
58
|
+
def decr_y(val = 1)
|
59
|
+
update((y - val).abs, x)
|
60
|
+
apply_pos
|
61
|
+
end
|
62
|
+
|
63
|
+
# Increment the x value by val
|
64
|
+
# @params
|
65
|
+
# => val (Int) new Value of x, Default: 1
|
66
|
+
def incr_x(val = 1)
|
67
|
+
update(y, x + val)
|
68
|
+
apply_pos
|
69
|
+
end
|
70
|
+
|
71
|
+
# Decrement the x value by val
|
72
|
+
# @params
|
73
|
+
# => val (Int) new Value of x, Default: 1
|
74
|
+
def decr_x(val = 1)
|
75
|
+
update(y, (x - val).abs)
|
76
|
+
apply_pos
|
77
|
+
end
|
78
|
+
|
79
|
+
# Cache the current cursor coordinates
|
80
|
+
def cache
|
81
|
+
@cached = { y: @y, x: @x }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Restore the previously cached cursor coordinates if any are cached
|
85
|
+
def restore
|
86
|
+
return unless @cached
|
87
|
+
update(@cached[:y], @cached[:x])
|
88
|
+
@cached = nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def switch_window(new_window, coords: {})
|
92
|
+
@window = new_window
|
93
|
+
move(coords[:y], coords[:x]) unless coords.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
def tprint(string)
|
97
|
+
@window.addstr(string)
|
98
|
+
end
|
99
|
+
|
100
|
+
def newline
|
101
|
+
incr_y
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
def update(y, x)
|
106
|
+
@y, @x = y, x
|
107
|
+
end
|
108
|
+
|
109
|
+
def apply_pos
|
110
|
+
window.setpos(@y, @x)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Sortviz
|
2
|
+
# Visualizer is the entry point of our Terminal UI. That's where all the juice
|
3
|
+
# happens. It initializes the curses library, visualizes sorting,
|
4
|
+
# cleans up after we're done and controls the event loop.
|
5
|
+
class Visualizer
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :@cursor, :tprint, :newline
|
8
|
+
# All our rendering kicks off from these two values, consider them
|
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
|
13
|
+
|
14
|
+
def initialize(unsorted_list, algo)
|
15
|
+
setup_curses
|
16
|
+
# Cache our dimensions, helpful in calculations
|
17
|
+
@screen_dim = { cols: Curses.cols, lines: Curses.lines }
|
18
|
+
# Take Reference for Curses initial standard window
|
19
|
+
@screen = Curses.stdscr
|
20
|
+
@cursor = Cursor.new(@screen, TOP_MARGIN, GUTTER)
|
21
|
+
@canvas = Canvas.new(ALGORITHMS[algo], @cursor, @screen_dim)
|
22
|
+
|
23
|
+
@algo = algo
|
24
|
+
@unsorted_list = unsorted_list
|
25
|
+
end
|
26
|
+
|
27
|
+
def visualize
|
28
|
+
begin
|
29
|
+
banner
|
30
|
+
# NOTICE: :Cavet: 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
|
+
@canvas.setup
|
34
|
+
@cursor.cache # Cache our location before the visualization mess kicks in
|
35
|
+
|
36
|
+
@screen.refresh
|
37
|
+
@canvas.refresh
|
38
|
+
|
39
|
+
@cursor.switch_window @canvas.window
|
40
|
+
|
41
|
+
loop do
|
42
|
+
Sortviz.send(@algo, @unsorted_list) do |partially_sorted, selected_indx|
|
43
|
+
@canvas.redraw(partially_sorted, selected_indx)
|
44
|
+
update
|
45
|
+
return if @canvas.getch == 'q'
|
46
|
+
end
|
47
|
+
@canvas.close
|
48
|
+
|
49
|
+
@cursor.switch_window @screen # Switch back to our stdscr
|
50
|
+
@cursor.restore # Restoring here, would place us right under the canvas box
|
51
|
+
newline
|
52
|
+
tprint("Press 'q' to exit")
|
53
|
+
break if Curses.getch == 'q'
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
Curses.close_screen
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
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
|
+
def setup_curses
|
70
|
+
Curses.init_screen
|
71
|
+
Curses.noecho # As soon as we prep the terminal screen, stop echo
|
72
|
+
Curses.nonl # Line feeds and carriage return as well
|
73
|
+
Curses.curs_set(0) # Hide cursor
|
74
|
+
Curses.start_color # Kick off colored output
|
75
|
+
Curses.use_default_colors
|
76
|
+
|
77
|
+
# We need Red and Gray
|
78
|
+
Curses.init_pair(Curses::COLOR_RED, Curses::COLOR_RED, -1)
|
79
|
+
Curses.init_pair(8, 235, -1)
|
80
|
+
end
|
81
|
+
|
82
|
+
def banner
|
83
|
+
tprint("-----------------------------------------------------------------")
|
84
|
+
newline
|
85
|
+
tprint("SortViz: Sorting Algorithms Visualization, Ruby & Curses - v#{VERSION}")
|
86
|
+
newline
|
87
|
+
tprint("-----------------------------------------------------------------")
|
88
|
+
newline
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
data/lib/sortviz.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'curses'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'sortviz/version'
|
4
|
+
require 'sortviz/cursor'
|
5
|
+
require 'sortviz/canvas'
|
6
|
+
require 'sortviz/visualizer'
|
7
|
+
require 'sortviz/plugins'
|
8
|
+
|
9
|
+
dir = './algorithms'
|
10
|
+
$LOAD_PATH.unshift(dir)
|
11
|
+
Dir[File.join(dir, '*.rb')].each {|file| require File.basename(file) }
|
12
|
+
|
13
|
+
module Sortviz
|
14
|
+
extend self
|
15
|
+
def init(algo)
|
16
|
+
unsorted_list = (1..20).to_a.shuffle
|
17
|
+
visualizer = Visualizer.new unsorted_list, algo
|
18
|
+
visualizer.visualize
|
19
|
+
end
|
20
|
+
end
|
data/screenshot.png
ADDED
Binary file
|
data/sortviz.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sortviz/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sortviz"
|
8
|
+
spec.version = Sortviz::VERSION
|
9
|
+
spec.authors = ["Hady Ahmed"]
|
10
|
+
spec.email = ["hady.fathy@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Terminal based sorting algorithms visualizer}
|
13
|
+
spec.description = %q{Visualize sorting algorithms with Ruby and Curses lib}
|
14
|
+
spec.homepage = "http://hadyahmed.com/sortviz"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
25
|
+
spec.add_dependency "curses"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sortviz
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hady Ahmed
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: curses
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Visualize sorting algorithms with Ruby and Curses lib
|
70
|
+
email:
|
71
|
+
- hady.fathy@gmail.com
|
72
|
+
executables:
|
73
|
+
- sortviz
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".rspec"
|
79
|
+
- ".ruby-gemset"
|
80
|
+
- ".ruby-version"
|
81
|
+
- ".travis.yml"
|
82
|
+
- CODE_OF_CONDUCT.md
|
83
|
+
- Gemfile
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- algorithms/bubblesort.rb
|
88
|
+
- algorithms/insertionsort.rb
|
89
|
+
- algorithms/selectionsort.rb
|
90
|
+
- bin/console
|
91
|
+
- bin/setup
|
92
|
+
- exe/sortviz
|
93
|
+
- lib/sortviz.rb
|
94
|
+
- lib/sortviz/canvas.rb
|
95
|
+
- lib/sortviz/cursor.rb
|
96
|
+
- lib/sortviz/plugins.rb
|
97
|
+
- lib/sortviz/version.rb
|
98
|
+
- lib/sortviz/visualizer.rb
|
99
|
+
- screenshot.png
|
100
|
+
- sortviz.gemspec
|
101
|
+
homepage: http://hadyahmed.com/sortviz
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.6.4
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Terminal based sorting algorithms visualizer
|
125
|
+
test_files: []
|