so that debugging/checking can be done easily, I will provide a working sample of what
gets $::fd line ;#-- read a line from socket
would have in it when I want this bot to trigger its actions.
:[email protected] PRIVMSG #help :1d20 this is the message portion of the raw data
heres the IRC bot.
set ::server irc.sorcery.net set ::chan #omen-heart set ::me "dicebot_[expr round([expr rand() * 10000])]" proc random {sides} { ############################################## #generates random numbers given 'sides' sided# #dice works # # Sides=number of sides to a dice # ############################################## return [expr round([expr rand() * $sides])] } proc roller {name number sides} { ######################################################### #calls random proc 'number times and returns the results# ######################################################### set incriment 0 set rolled "$name rolled" set total 0 while {$incriment != $number} { append rolled " " set temp [random $sides] append rolled $temp incr incriment set total [expr $temp+$total] ######### } append rolled " total= $total" return $rolled } proc matcher {input} { ###################################################################################################### #proc searches for a string matching (1d20, ect) and returns it if it exists, otherwise, it returns 0# # works! # # rolls using this dicebot must have the rolling info seperated by a comma # ###################################################################################################### if {[regexp {[0-9]*[dD][0-9]+} $input]} { split $input " " return [set input [lindex $input 0]] } return 0 } ########################################################################### #preceding function splits the output of matcher (x*d/Dx* into a list # #that contains only the two rolling elements (number sides) # ########################################################################### ############################# ##Start IRC client software## ############################# proc roll_formatter {input} { set input [string tolower $input] set input [split $input d] set number [lindex $input 0] set sides [lindex $input 1] return "$number $sides" } proc recv {} { gets $::fd line ;#-- read a line from socket if {[lindex [split $line] 0] eq "PING"} { send "PONG [lindex [split $line] 1]" return #ping/pong relationship with server } if {[regexp {:([^!]*)![^ ].* +PRIVMSG ([^ :]+) +:(.*)} $line} { set temp [split $line :] set message [split [lindex $temp 2] " "] set name_long [lindex $temp 1] set name_short [split $name_long !] ; set name_short [lindex $name_short 0] set room [split $line #] ; set room [lindex $room 1] ; set room [split $room " "] ; set room "#[lindex $room 0]" set room [split $line #] ; set room [lindex $room 1] ; set room [split $room " "] ; set room "#[lindex $room 0]" #splits the text received in $line into a few parts, the most important being # the roll that was called #and the message that called it (store in $message ex: 1d20 this is a roll) if {[regexp {[0-9]*[dD][0-9]+} $message]} { #call the dicebot when $message starts with a dicebot call set roll [matcher $message] set roll [roll_formatter $roll] set roll [roller $name_short [lindex $roll 0] [lindex $roll 1]] send "PRIVMSG $::chan :$roll" } } } proc send {str} { ####################################################################### # A very simple wrapper for sending a string, not forgetting to flush;# # is called from within post procedure # ####################################################################### puts $::fd $str flush $::fd } # And now, the action begins with "logging in" to the IRC server set ::fd [socket $::server 6667] send "NICK $::me" send "USER $::me 0 * :PicoIRC user" after 500 send "JOIN $::chan" # If the socket gets readable, recv will be called fileevent $::fd readable recv
davou I've edited the function roll_formatter down so that it's much shorter... I've gotten the bot to connect to IRC, and join the channel mentioned in $::chan, but it leaves just as fast as it gets in... does anyone know why it's doing this? - RS: Easiest way to find out is to add puts $line right after the gets $::fp line and see what comes in.
Just a few comments on the Tcl usage...
No need to nest the expr's in:
set ::me "dicebot_[expr round([expr rand() * 10000])]" return [expr round([expr rand() * $sides])]
Just do:
set ::me "dicebot_[expr round(rand() * 10000)]" return [expr round(rand() * $sides)]
Also:
set incriment 0 set rolled "$name rolled" set total 0 while {$incriment != $number} { append rolled " " set temp [random $sides] append rolled $temp incr incriment set total [expr $temp+$total] }
This looks awfully like it should be a for loop. Also, the double append is redundant:
set total 0 for {set i 0 } { $i < $number } { incr i } { set rolled "$name rolled" set temp [random $sides] append rolled " $temp" set total [expr $temp+$total] }
And lastly, I believe your random proc contains an error - its return value is 0 to $sides rather than 1 to $sides, so maybe use:
proc random {sides} { return [expr int(rand() * $sides) + 1] }
instead?
Lars H: A few more comments on the Tcl usage (and the comments upon that). First, it probably doesn't make much difference in this case, but will generally be better off if you make it a habit to brace your expr-essions. Second, instead of combining
append rolled " " append rolled $temp
into
append rolled " $temp"
it may be better to combine it into
append rolled " " $temp
The difference is that the middle command copies the contents of $temp twice: once to build the string $temp-with-a-space-in-front and a second time when appending that to $rolled. The difference is negligible when $temp is short, but can be noticed in cases where $temp is very long. (Giving append several arguments also reduces the nesting in the code, which can sometimes make it easier to read.)
The two lines
set room [split $line #] ; set room [lindex $room 1] ; set room [split $room " "] ; set room "#[lindex $room 0]" set room [split $line #] ; set room [lindex $room 1] ; set room [split $room " "] ; set room "#[lindex $room 0]"
also seem rather strange. The second seems entirely redundant and the first (grabbing what's between the first # and the first space thereafter) could probably be more easily expressed using regexp. If you want to keep using split and lindex, then consider combining them, like so:
set room [lindex [split $line #] 1] ; set room "#[lindex [split $room " "] 0]"
The cost for setting variables is not always negligible.
Yeah, you're absolutely right about the split/combined append:
#!/bin/env tclsh set max [lindex $argv 0] proc testSplit { } { global mystr set addstr "x" append addstr " " $mystr } proc testComb { } { global mystr set addstr "x" append addstr " $mystr" } for { set i 0 } { $i < 1048576 } { incr i } { append origstr "x" } set mystr $origstr puts "testSplit : [time testSplit $max]" set mystr $origstr puts "testComb : [time testComb $max]"
Running it under Tcl8.0 gives:
$ ./testAppend 1000 testSplit : 1034 microseconds per iteration testComb : 2238 microseconds per iteration
And again under Tcl8.4:
$ ./testAppend 1000 testSplit : 1051 microseconds per iteration testComb : 2331 microseconds per iteration
So the overhead for the combined approach doubles the running time.
See also irc