4b10 Fractalbot - Telecomix Crypto Munitions Bureau

Fractalbot

From Telecomix Crypto Munitions Bureau

Jump to: navigation, search

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"

Personal tools
0