sortviz 0.6.1 → 0.8.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ab0c658aff4dc772ecffcacb46cf611eabb3461
4
- data.tar.gz: 22438a128feac8deb9c70aee7c33d580d5faed1b
3
+ metadata.gz: 970249e15c43b4ade3c5ea494b42c41105854a23
4
+ data.tar.gz: a85bbd678f174d5efc01da4fe8d432fbbc450922
5
5
  SHA512:
6
- metadata.gz: 142a64eec59079b2e6e8af322a593226ebdbd04163b69f6f964b2bbe856db9a043626ea61e3d7761533afa833b1c300509983275f78416ad37a2ae98054fe880
7
- data.tar.gz: 76f9fed39164fbad24c24cef1891095e581eb551b8c967dfff3ed7b5973c8b561912525f3836da19a65b0fe0fd5499967992a27403a71ae37205be3ffef419c7
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
- 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).
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 # 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
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
- Because I'm a terminal guy that's why. I like to think I'm not too
61
- bad googling either and for the life of me I couldn't find anything similar
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
- options = {}
13
+ Options = Struct.new(:algorithm, :speed, :path)
14
+ args = Options.new(:'bubble-sort', 0.05)
14
15
 
15
- oparser = OptionParser.new do |opts|
16
- opts.banner = <<-HELP
17
- USAGE: sortviz [options]
16
+ @parser = OptionParser.new do |opts|
17
+ opts.banner = 'Usage: sortviz [options] sorting_algorithm_name'
18
18
 
19
- Exit: Use 'q' to exit during visualization
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::ALGORITHMS.each do |func_name, name|
36
- puts " #{name} (-s #{func_name})"
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.on('-h', '--help', 'View this help') do
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
- oparser.parse!(ARGV)
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
- 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]
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
- end
13
-
14
- define_algorithm "Bubble Sort", :bubblesort
14
+ return unsorted_list
15
+ }
15
16
  end
@@ -1,16 +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|
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 list[j-1] > list[j]
8
- list[j], list[j-1] = list[j-1], list[j]
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
- end
14
-
15
- define_algorithm "Insertion Sort", :insertionsort
14
+ return unsorted_list
15
+ }
16
16
  end
@@ -1,19 +1,18 @@
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|
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...list.size).each do |i|
9
- iMin = i if list[i] < list[iMin]
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
- list[j], list[iMin] = list[iMin], list[j]
14
- yield list, j
14
+ unsorted_list.swap!(j, iMin)
15
15
  end
16
- end
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/plugins'
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
- def init(algo)
15
- visualizer = Visualizer.new algo
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
@@ -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 redraw(partially_sorted, selected_indx)
36
- @window.clear
37
- @window.box('|', '-')
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
- @cursor.tprint(("|%02d|" % number))
44
+ draw_number(number)
45
+ draw_bar(number, i == selection)
46
+ next_bar
47
+ end
48
+
49
+ Curses.doupdate
50
+ end
47
51
 
48
- @cursor.decr_y
52
+ private
49
53
 
50
- draw_bar(number, selected_indx == i)
54
+ def clear
55
+ @window.clear
56
+ @window.box('|', '-')
57
+ end
51
58
 
52
- @cursor.move_y(@window.maxy - 1)
53
- @cursor.incr_x MARGIN
54
- end
59
+ def draw_title
60
+ @cursor.move(0, 0)
61
+ @cursor.tprint("Algorithm: #{@title}")
55
62
  end
56
63
 
57
- private
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
- @window.addstr(" ".center(4)) # 4 spaces
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
@@ -1,3 +1,3 @@
1
1
  module Sortviz
2
- VERSION = "0.6.1"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -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
- # +algo+:: <tt>Symbol</tt> A symbol representing a sorting algorithm method
22
- def initialize(algo)
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
- @algo = algo
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
- loop do
73
- Sortviz.send(@algo, @unsorted_list) do |partially_sorted, selected_indx|
74
- @canvas.redraw(partially_sorted, selected_indx)
75
- Curses.doupdate
76
- sleep SLEEP_INTERVAL
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
- end
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.6.1
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-20 00:00:00.000000000 Z
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
@@ -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