tcl-dicebot

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


Category Internet