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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e30b9e51cccc9c3a759b30c48bd2ea23770181b
4
- data.tar.gz: 846ec151c0cbf34fd73d3eca5a125d18b7b40ef0
3
+ metadata.gz: 3a26d73bfed67f35fc0932b25232780f66317e86
4
+ data.tar.gz: 37bf5941624feb66a49d2bce915f7b30aa791103
5
5
  SHA512:
6
- metadata.gz: 096d616309245732d9e35b263b89b883332fb56b6b28f5c06130b59ff4231a21f08234c42db951cd4ca48cba55b3ebdeb1e0b05aad0ce0666290b66d2bce70ae
7
- data.tar.gz: 4dbc3f8bb250a8cebdd6e5d89024476274d3c485aca2eb9d718bafae4df4bb14917e48c79ef4420e9aa4611404e36cd493e2e7b125f8426fc0f4dafe1fdbf23f
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 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.
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
- 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.
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
- ![alt text][screenshot]
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
- it. Others operate on multiple lists, like merge sort. With any luck, you
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 `algorithms` folder.
22
+ For examples, check the [lib/algorithms](lib/algorithms) directory.
23
23
 
24
- Some implementations like the bubble sort one at `algorithms/bubblesort.rb` generate some visual artifacts.
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`. 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).
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. (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?
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
- Seriously now, PRs are welcome, bug reports too but don't make me do your
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
- ## Future
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
- 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.
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
 
@@ -11,7 +11,7 @@ module Sortviz
11
11
 
12
12
  # then swap it
13
13
  list[j], list[iMin] = list[iMin], list[j]
14
- yield list, iMin
14
+ yield list, j
15
15
  end
16
16
  end
17
17
 
@@ -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
- :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
-
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
- # 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
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 = Curses::Window.new(
28
- @cursor.y + CANVAS_HEIGHT,
29
- @screen_dim[:cols] - CANVAS_GUTTER,
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
- @cursor.decr_y
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
- attron(@red_highlighter) if selected_indx == i
53
- draw_bar(n)
54
- attroff(@red_highlighter)
50
+ draw_bar(number, selected_indx == i)
55
51
 
56
- @cursor.move_y(CANVAS_HEIGHT + 2)
57
- @cursor.incr_x GUTTER
52
+ @cursor.move_y(@window.maxy - 1)
53
+ @cursor.incr_x MARGIN
58
54
  end
59
55
  end
56
+
60
57
  private
61
- def draw_bar(height)
58
+
59
+ def draw_bar(height, highlighted)
60
+ attr = highlighted ? @red_highlight : Curses::A_REVERSE
61
+ attron(attr)
62
62
  height.times do
63
- tprint("|_ |")
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
@@ -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, y, x)
16
+ def initialize(window, origin)
17
17
  @window = window
18
- @cached = nil
19
- move(y, x)
18
+ move(origin[:y], origin[:x])
20
19
  end
21
20
 
22
21
  # Move to positions y, x
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Sortviz
2
- VERSION = "0.5.3"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -1,37 +1,67 @@
1
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.
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
- 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
14
+ # Origin starts at (1, 0) not
15
+ ORIGIN = { y: 1, x: 5 }
16
+ SLEEP_INTERVAL = 0.05
13
17
 
14
- def initialize(unsorted_list, algo)
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
- # Cache our dimensions, helpful in calculations
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, TOP_MARGIN, GUTTER)
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 = 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 # Cache our location before the visualization mess kicks in
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
- update
45
- return if @canvas.getch == 'q'
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 'q' to exit")
53
- break if Curses.getch == 'q'
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 # As soon as we prep the terminal screen, stop echo
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 and Gray
78
- Curses.init_pair(Curses::COLOR_RED, Curses::COLOR_RED, -1)
79
- Curses.init_pair(8, 235, -1)
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
- tprint("-----------------------------------------------------------------")
84
- newline
85
- tprint("SortViz: Sorting Algorithms Visualization, Ruby & Curses - v#{VERSION}")
86
- newline
87
- tprint("-----------------------------------------------------------------")
88
- newline
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
- unsorted_list = (1..20).to_a.shuffle
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.5.3
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-18 00:00:00.000000000 Z
11
+ date: 2016-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler