Fractalbot
From Telecomix Crypto Munitions Bureau
Fractalbot is written in ruby, as an experiment in ruby.
What does it do? It is hardcoded to connect to telecomix IRC and join the channel #test2, where users can ask it to draw ASCII fractals. Typing "!fractal help" will show the command syntax.
[edit] Conclusions
- ruby is nice
- rubys lack of types was INSANELY exhausting, had to debug all the time :O :O :O
[edit] Code
#!/usr/bin/env ruby1.9.1 require 'socket' class Fraktal def initialize(width=120, height=60, top_left=Complex(-1.5, 1.0), bottom_right=Complex(1.0,-1.0), c=nil) span_real = bottom_right.real - top_left.real span_im = (top_left.imag - bottom_right.imag ).abs @width = width @height = height @top_left = top_left @bottom_right = bottom_right @c = c # Complex(0,-1) @matrix = [] @x_step = Complex(span_real.to_f / width, 0) @y_step = Complex(0, span_im.to_f / height) @max_it = 1000 puts("pixels height: #{height}") puts("pixels width: #{width}") puts("x-stepping: #{@x_step}") puts("y-stepping: #{@y_step}") puts("top left corner: #{@top_left}") puts("bottom right corner: #{@bottom_right}") puts("real span: #{span_real}") puts("imaginary span: #{span_im}") compute end ## print the fractal def get_ascii picture = [] for y in (0..@height) picture[y] = "" for x in (0..@width) picture[y] += color( @matrix[y][x] ) end end return picture end private def compute z = @top_left for y in (0..@height) @matrix[y] = Array.new(@width) for x in (0..@width) @matrix[y][x] = iterate(z,@c) z += @x_step end z = @top_left - y * @y_step end end def iterate(z, c) c ? c : c = Complex(z) (0..@max_it).each { |n| z = z*z + c if z.abs > 4 return n end } return @max_it end ## calculate the color of the pseudopixel def color(n) n > 9 ? " " : " " + (0+n).to_s end end ################################################################################################## class Kaosbot attr_accessor :session def initialize(server="irc.telecomix.org", port=6667, nickname="kaosbot", verbosity=0) @nickname = nickname @server_name = server @server_port = port @verbosity = verbosity @threads = Array.new ( verbosity > 0 ) ? puts("verbosity: #{verbosity}") : @verbosity = verbosity end def lprint(string, level=3) if level <= @verbosity puts(string) end end def connect() new_thread = Thread.new() { lprint("connecting to #{@server_name}", 0) until @session @server_ip = IPSocket.getaddress(@server_name) lprint("Connecting to #{@server_ip}:#{@server_port}", 1) begin @session = TCPSocket.open(@server_ip, @server_port) rescue => error lprint("failed to connect, trying again..", 0) lprint("TCP Socket error --> " + error.to_s, 1) end end lprint("connected to #{@server_name} --> #{@server_ip}:#{@server_port}", 0) @session.write("NICK #{@nickname}\r\n") @session.write("USER #{@nickname} 8 d :#{@nickname}\r\n") @connected = true main_loop } @threads.push(new_thread) end def disconnect() if @connected @connected = false @session.close() lprint("disconnected.", 0) else puts("disconnect called without being connected!") end end def reconnect() if @connected lprint("reconnecting", 0) disconnect() connect() else puts("reconnect called without being connected!") end end def server_name? @server_name end def server_port? @server_port end def join(channel="#test2") @connected ? lprint("waiting for bot to connect to server..", 1) : until @connected Thread.pass end @session.write("JOIN #{channel}\n") lprint("tried to join #{channel}", 0) end def main_loop while true string = @session.gets line = string.chomp lprint(line, 3) puts "test0" primitive = /^(?<command>[A-Z]+){1}/.match(line) system = /^:(?<sender>[[:graph:]]+)[\s]+(?<cmd>[a-zA-Z0-9]{1,})\s+(?<target>[[:graph:]]+)(\s:)*(?<rest>(.*))$/.match(line) puts "test1" if primitive case primitive[:command] when "PING" lprint("received PING, sending PONG", 3) @session.write("PONG\n") when "ERROR" lprint("WARNING: Received this error-message from the server: #{line}", 0) else lprint("NOTICE: Received unknown message : #{line}", 2) end elsif system puts "test2" words = system[:rest].split(/[\s]+/) if words == nil next end puts "test3" user_msg = system[:rest].split if "PRIVMSG".match( system[:cmd] ) && user_msg if "!fractal".match( user_msg[0] ) puts "test4" parsefractal(system[:sender], system[:target], words, words.length) end end end end end def parsefractal(from, target, umsg, len) puts "from: #{from}, target: #{target}, umsg: #{umsg}, len: #{len}" if len < 2 Thread.new { print_fractal(target) } elsif /^\s*help\s*$/.match(umsg[1]) @session.write("PRIVMSG #{target} :!fractal [h=int] [w=int] [c=a+bi]\n") else c, c_re, c_im = nil, nil, nil height, width = 50, 79 matched = false umsg.each do |msg| puts "trying to match " + msg complex_regexp.match(msg) { |complex| c = Complex(complex[:re], complex[:im]) puts "this c is what we got:" + c.to_s matched = true } /^w=(?<w>[1-9]{1}[0-9]*){1}$/.match(msg) { |m| width = m[:w].to_i matched = true } /^h=(?<h>[1-9]{1}[0-9]*){1}$/.match(msg) { |m| height = m[:h].to_i matched = true } end if matched puts "printing fractal" Thread.new { print_fractal(target, width, height, c) } end end end def complex_regexp pat_begin = "^" pat_end = "$" prefix = "c=" re = "(?<re>[0-9]*(([.,]{1})[0-9]{1,12}){0,1}){1}" im = "(?<im>[+-]{1}[0-9]*(([.,]{1})[0-9]{1,12}){0,1}i{1}){1}" return Regexp.new( pat_begin + prefix + re + im + pat_end ) end def print_fractal(target, width=79, height=50, type=nil) puts "creating fractal.." fraktal = Fraktal.new(width, height, Complex(-1.8, 1.3), Complex(0.8,-1.3), type) puts "tries to get picture.." picture = fraktal.get_ascii puts "got ascii.." picture.each { |row| puts( "PRIVMSG #{target} :#{row}\n") @session.write("PRIVMSG #{target} :#{row}\n") } end end irc = Kaosbot.new("irc.telecomix.org", 6667, "kaosbot", 3) irc.connect irc.join while true Thread.pass end
[edit] How to run it
put the above code in a file, chmod+x it and type "./filename"