#!/usr/bin/ruby # A simple graph generator (for graphviz) # author: Yoan Blanc # usage: ./graph.rb # example: ./graph.rb 100 .98 | dot -Tpng -o graph.png def genGraph( size, luck ) # Generate a graph of the given size # two nodes have the luck (in %) chance to NOT have connection graph = Array.new size.times do |i| # a new array for the current cell graph[i] = Array.new # mirror (0..(i-1)).step do |j| graph[i] << graph[j][i] end # no link to self graph[i] << false # new values ((i+1)..(size-1)).step do |j| # lowing the chances graph[i] << (rand > luck) end end graph end def genLetter( size ) # Generate a serie of human readable numbers (with letters) # like a, b, c ... aa, ab, ac offset = ?a if size <= 26 ("a"..(offset + size - 1).chr) elsif size <= 26**2 div, rest = size.divmod(26).map {|x| x+offset} ("aa"..(div.chr + rest.chr)) elsif size <= 26**3 div2, rest = size.divmod(26**2) div, rest = rest.divmod(26).map {|x| x+offset} div2 += offset ("aaa"..(div2.chr + div.chr + rest.chr)) else # be a samourai (http://c2.com/cgi/wiki?SamuraiPrinciple) raise "Size of the graph is too big for the letter mapping" end end def buildDot( graph, dive ) # Build the dot semantic for GraphViz size = graph[0].length letters = genLetter( size ) dot = "graph noname {\n" dot += " center = false\n" dot += " ratio = fill\n" dot += " node [style=filled,fontsize=10,width=0.000,height=0.000]\n" # Nice colors and label (with level in it) # Not required without colors neither label nodes = letters.to_a depth = dive.length size.times do |i| dot += " #{nodes[i]}" depth.times do |n| unless dive[n].index(i).nil? color = 1.0 - n/depth.to_f dot += ' [label="'+nodes[i]+' ('+n.to_s+')",color="'+format("%.3f", color)+' 1.000 1.000"]' break end end dot += "\n" end # The connections between nodes size.times do |i| dot += " #{nodes[i]}--{" ((i+1)..size).step do |j| if graph[i][j] dot += " #{nodes[j]}" end end dot += " }\n" end dot + "}" end def diveGraph( graph, node, level) # Dive into the graph and find every nodes that are accessible for the given # node. It'll stop at the level (if targeted) that is the max depth. size = graph.length dive = Array.new level.times do |i| dive << Array.new if i == 0 # first level is self dive[i] << node else # find all nodes that are accessible at this level dive[i-1].each do |node| size.times do |j| dive[i] << j if graph[node][j] end end # Remove already known nodes dive[i].uniq! dive[i] = dive[i] - dive[0..i-1].flatten # no more, leave break if dive[i].length == 0 end end dive end # not very nice... (sorry) graph = genGraph(ARGV[0].to_i, ARGV[1].to_f) dive = diveGraph( graph, 0, 50) puts buildDot(graph, dive)