mIRC Home    About    Download    Register    News    Help

Print Thread
Joined: Oct 2003
Posts: 34
S
spling Offline OP
Ameglian cow
OP Offline
Ameglian cow
S
Joined: Oct 2003
Posts: 34
OK, my problem is this, I have ~2390 variables.. ranging from 1-31000, they all start with %msgs.* and are used in a script i made that tracks how many messages i've seen from a given user. Needless to say, I HAD to make a !top10 script for it. Now, the top10 script works fine, but is 170 lines long. There has to be an easier way to figure out what the top 10 variables are other than comparing every single variable. This is what I currently have:
Code:
 on *:TEXT:!top10:* {
   if %lastupdate >= 3600 {
    set %top1 0
    set %top2 0
    set %top3 0
    set %top4 0
    set %top5 0
    set %top6 0
    set %top7 0
    set %top8 0
    set %top9 0
    set %top10 0
    msg $nick 12The top10 list has been updated.
    set %update.time $date(mm.dd.yy - h:nn.ss TT zz)
    unset %lastupdate
    inc -c %lastupdate 1
    set %users $var(%msgs.*,0)
    :top
    if %users == 0 {
      msg $nick -- 12Top10 List --
      msg $nick 1. %top1.name $+ : %top1 messages. 4/ 2. %top2.name $+ : %top2 messages. 4/ 3. %top3.name $+ : %top3 messages. 4/ 4. %top4.name $+ : %top4 messages. 4/ 5. %top5.name $+ : %top5 messages. 4/ 6. %top6.name $+ : %top6 messages. 4/ 7. %top7.name $+ : %top7 messages. 4/ 8. %top8.name $+ : %top8 messages. 4/ 9. %top9.name $+ : %top9 messages. 4/ 10. %top10.name $+ : %top10 messages.
      unset %users
      unset %temp
      unset %temp.name
      halt 
    }
    set %temp $calc($var(%msgs.*,%users))
    set %temp.name $right($var(%msgs.*,%users),$calc($len($var(%msgs.*,%users))-6))
    if %temp > %top1 && temp.name != %top1.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %top5.name
      set %top6 %top5
      set %top5.name %top4.name
      set %top5 %top4
      set %top4.name %top3.name
      set %top4 %top3
      set %top3.name %top2.name
      set %top3 %top2
      set %top2.name %top1.name
      set %top2 %top1
      set %top1.name %temp.name
      set %top1 %temp
    }
    elseif %temp > %top2 && temp.name != %top2.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %top5.name
      set %top6 %top5
      set %top5.name %top4.name
      set %top5 %top4
      set %top4.name %top3.name
      set %top4 %top3
      set %top3.name %top2.name
      set %top3 %top2
      set %top2.name %temp.name
      set %top2 %temp
    }
    elseif %temp > %top3 && temp.name != %top3.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %top5.name
      set %top6 %top5
      set %top5.name %top4.name
      set %top5 %top4
      set %top4.name %top3.name
      set %top4 %top3
      set %top3.name %temp.name
      set %top3 %temp
    }
    elseif %temp > %top4 && temp.name != %top4.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %top5.name
      set %top6 %top5
      set %top5.name %top4.name
      set %top5 %top4
      set %top4.name %temp.name
      set %top4 %temp
    }
    elseif %temp > %top5 && temp.name != %top5.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %top5.name
      set %top6 %top5
      set %top5.name %temp.name
      set %top5 %temp
    }
    elseif %temp > %top6 && temp.name != %top6.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %top6.name
      set %top7 %top6
      set %top6.name %temp.name
      set %top6 %temp
    }
    elseif %temp > %top7 && temp.name != %top7.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %top7.name
      set %top8 %top7
      set %top7.name %temp.name
      set %top7 %temp
    }
    elseif %temp > %top8 && temp.name != %top8.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %top8.name
      set %top9 %top8
      set %top8.name %temp.name
      set %top8 %temp
    }
    elseif %temp > %top9 && temp.name != %top9.name {
      set %top10.name %top9.name
      set %top10 %top9
      set %top9.name %temp.name
      set %top9 %temp
    }
    elseif %temp > %top10 && temp.name != %top10.name {
      set %top10.name %temp.name
      set %top10 %temp
    }
    dec %users 1
    goto top
  }
  else {
    msg $nick -- 12Top10 List --
    if %update.time < 60 { notice $nick Last updated: Less than a minute ago. }
    elseif %update.time == 60 { notice $nick Last updated: One minute ago. }
    else { msg $nick Last updated: $int($calc(%update.time / 60)) minutes ago. }
    msg $nick 1. %top1.name $+ : %top1 messages. 4/ 2. %top2.name $+ : %top2 messages. 4/ 3. %top3.name $+ : %top3 messages. 4/ 4. %top4.name $+ : %top4 messages. 4/ 5. %top5.name $+ : %top5 messages. 4/ 6. %top6.name $+ : %top6 messages. 4/ 7. %top7.name $+ : %top7 messages. 4/ 8. %top8.name $+ : %top8 messages. 4/ 9. %top9.name $+ : %top9 messages. 4/ 10. %top10.name $+ : %top10 messages.
  }
} 


Thanks...

Joined: Dec 2003
Posts: 219
F
Fjord artisan
Offline
Fjord artisan
F
Joined: Dec 2003
Posts: 219
Hi!

Just a quick tip: use loop conditions:

Code:
set %x 0
while (%x < 10) {
    inc %x
   set [ %top [ $+ [ %x ] ] ] 0
}




This is the same as:
Code:
    set %top1 0
    set %top2 0
    set %top3 0
    set %top4 0
    set %top5 0
    set %top6 0
    set %top7 0
    set %top8 0
    set %top9 0
    set %top10 0


And read the mIRC's help about "Hash Tables", it could be more efficient for your stuff than %variables.xxxxxxx where xxxxx are digits.
"A hash table can store an unlimited number of items regardless of the N you choose, however the bigger N is, the faster it will work, depending on the number of items stored."
"These load/save plain text to a text file, with item and data on separate lines."


Hope this helps.

Joined: Sep 2003
Posts: 4,230
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Sep 2003
Posts: 4,230
Code:
 
on *:TEXT:!top10:* {
  if %lastupdate >= 3600 {
    msg $nick 12The top10 list has been updated.
    set %update.time $date(mm.dd.yy - h:nn.ss TT zz)
    unset %lastupdate
    inc -c %lastupdate 1
    ;
    window -c @top10.sorter.window
    window -eh @top10.sorter.window
    var %i $var(%msgs.*,0)
    while (%i) {
      aline @top10.sorter.window [ [ $var(%msgs.*,%i) ] ] $var(%msgs.*,%i)
      dec %i
    }
    filter -cwwtue 1 32 @top10.sorter.window @top10.sorter.window *
    var %i = 10
    while (%i) {
      set %top [ $+ [ %i ] ] $gettok($line(@top10.sorter.window,%i),1,32)
      set %top [ $+ [ %i ] $+ ] .name $mid($gettok($line(@top10.sorter.window,%i),2-,32),7)
      dec %i
    }
    window -c @top10.sorter.window
    ;
    msg $nick -- 12Top10 List --
    msg $nick 1. %top1.name $+ : %top1 messages. 4/ 2. %top2.name $+ : %top2 messages. 4/ 3. %top3.name $+ : %top3 messages. 4/ 4. %top4.name $+ : %top4 messages. 4/ 5. %top5.name $+ : %top5 messages. 4/ 6. %top6.name $+ : %top6 messages. 4/ 7. %top7.name $+ : %top7 messages. 4/ 8. %top8.name $+ : %top8 messages. 4/ 9. %top9.name $+ : %top9 messages. 4/ 10. %top10.name $+ : %top10 messages.
    halt 

  }
  else {
    msg $nick -- 12Top10 List --
    if %update.time < 60 { notice $nick Last updated: Less than a minute ago. }
    elseif %update.time == 60 { notice $nick Last updated: One minute ago. }
    else { msg $nick Last updated: $int($calc(%update.time / 60)) minutes ago. }
    msg $nick 1. %top1.name $+ : %top1 messages. 4/ 2. %top2.name $+ : %top2 messages. 4/ 3. %top3.name $+ : %top3 messages. 4/ 4. %top4.name $+ : %top4 messages. 4/ 5. %top5.name $+ : %top5 messages. 4/ 6. %top6.name $+ : %top6 messages. 4/ 7. %top7.name $+ : %top7 messages. 4/ 8. %top8.name $+ : %top8 messages. 4/ 9. %top9.name $+ : %top9 messages. 4/ 10. %top10.name $+ : %top10 messages.
  }
} 

 

^^ still takes 12 seconds or so to run, all in loading the window!
I load the value and the value variablename into a hiden window then sort it, and read the top 10 from the window, finally closing the window.

Personally i think you need to rewrite this whole thing using a perminent hidden window, you can load and save the window to a file, for permenent value storeage, anytime you change one (if doing so i would reverse the order in the window to be name amount) you can simple uses $fline to look for relevent names etc, and a filter to sort the window.
I would not uses a hash table as you cant sort it fast, like you can a window.

Your other option is, to keep a current top ten values, everytime someones msgs.name value changes, check it agains the top ten then, its more checking in the long run, but its spread over idle time so is not noticable.

Joined: Jan 2003
Posts: 2,523
Q
Hoopy frood
Offline
Hoopy frood
Q
Joined: Jan 2003
Posts: 2,523
You can improve its performance considerably by replacing this part:
Code:
  var %i $var(%msgs.*,0)
  while (%i) {
    aline @top10.sorter.window [ [ $var(%msgs.*,%i) ] ] $var(%msgs.*,%i)
    dec %i
  }
  filter -cwwtue 1 32 @top10.sorter.window @top10.sorter.window *
  var %i = 10
  while (%i) {
    set %top [ $+ [ %i ] ] $gettok($line(@top10.sorter.window,%i),1,32)
    set %top [ $+ [ %i ] $+ ] .name $mid($gettok($line(@top10.sorter.window,%i),2-,32),7)
    dec %i
  }
with this:
Code:
  var %file = $shortfn($readini($mircini,rfiles,n1)), %i = 1
  if $ini(%file,variables) { 
    loadbuf -tvariables @top10.sorter.window %file
    filter -wwcute 2 32 @top10.sorter.window @top10.sorter.window n*=%msgs.*
  }
  else filter -fwcute 2 32 %file @top10.sorter.window % $+ msgs.*
  while %i < 11 && $line(@top10.sorter.window,%i) {
    tokenize 32 $ifmatch
    set %top $+ %i $2
    set $+(%,top,%i,.name) $gettok($1,-1,61)
    inc %i
  }
I don't see why spling needs two variables stored for each variable in top 10 though. %topN.name should be enough: one can easily retrieve the value of that with $eval(<variable name>,2). Btw, the two conditions in /while make sure that the script doesn't break if there are less than 10 variables in total (unlikely in this case, but anyway).

spling: I didn't comment the code at all, so if you need help figuring out this script, feel free to ask.


/.timerQ 1 0 echo /.timerQ 1 0 $timer(Q).com
Joined: Oct 2003
Posts: 34
S
spling Offline OP
Ameglian cow
OP Offline
Ameglian cow
S
Joined: Oct 2003
Posts: 34
WOW! I can't tell you how much this helped. Qwerty, thanks to that code you came up with, I get virtually NO lag when the list updates now, so that removes the "can-only-update-if-an-hour-has-passed" restriction I had on it. I stripped away alot of stuff, and changed it a bit. Here is what I ended with. I'd appreciate it if you could comment the code for me, because I'm interested to know how it works, I get the jist of it from reading the help files on the commands, but I'd be happy if you'd take the time to comment on the final code:
Code:
on *:TEXT:!top10:* {
  window -c @top10.sorter.window
  window -eh @top10.sorter.window
  var %file = $shortfn($readini($mircini,rfiles,n1)), %i = 1
  if $ini(%file,variables) {
    loadbuf -tvariables @top10.sorter.window %file
    filter -wwcute 2 32 @top10.sorter.window @top10.sorter.window n*=%msgs.*
  }
  else filter -fwcute 2 32 %file @top10.sorter.window % $+ msgs.*  
  while %i &lt; 11 &amp;&amp; $line(@top10.sorter.window,%i) { 
    tokenize 32 $ifmatch   
    set %top $+ %i $2  
    set $+(%,top,%i,.name) $gettok($1,-1,61)  
    inc %i 
  }
  window -c @top10.sorter.window
  msg $nick -- 12Top10 List --
  msg $nick 1. $right(%top1.name,$calc($len(%top1.name)-6)) $+ : %top1 messages.$&amp;
    4/ 2. $right(%top2.name,$calc($len(%top2.name)-6)) $+ : %top2 messages.$&amp;
    4/ 3. $right(%top3.name,$calc($len(%top3.name)-6)) $+ : %top3 messages.$&amp;
    4/ 4. $right(%top4.name,$calc($len(%top4.name)-6)) $+ : %top4 messages.$&amp;
    4/ 5. $right(%top5.name,$calc($len(%top5.name)-6)) $+ : %top5 messages.$&amp;
    4/ 6. $right(%top6.name,$calc($len(%top6.name)-6)) $+ : %top6 messages.$&amp;
    4/ 7. $right(%top7.name,$calc($len(%top7.name)-6)) $+ : %top7 messages.$&amp;
    4/ 8. $right(%top8.name,$calc($len(%top8.name)-6)) $+ : %top8 messages.$&amp;
    4/ 9. $right(%top9.name,$calc($len(%top9.name)-6)) $+ : %top9 messages.$&amp;
    4/ 10. $right(%top10.name,$calc($len(%top10.name)-6)) $+ : %top10 messages.
}

Joined: Sep 2003
Posts: 4,230
D
Hoopy frood
Offline
Hoopy frood
D
Joined: Sep 2003
Posts: 4,230
good work on the pulling it outa the ini, i looked at it, and just went, oh i cant be bothered, its gonna get complexe (i didnt know of /loadbuf -t must check on that command, im always using /filter my bad i guess ) and thought, well hes gotta be spending alot of time in this loop of his as it is, whats 12 seconds gonna matter.
I guess that type of thinking is like this "whats a little echo -a %counter gonna slow it up by" oh dear the loop runs 10,000,000 times LOL

Joined: Jan 2003
Posts: 2,523
Q
Hoopy frood
Offline
Hoopy frood
Q
Joined: Jan 2003
Posts: 2,523
Sure, let me first describe the whole idea a bit. We want to pull out all %msgs.* variables from the Variables list and dump them in a window, so then we can use /filter to sort them. One way of pulling out those vars is a while loop, which as you noticed gets slow.

But, we can take advantage of the fact that mirc stores the variables in a text file like this:
Code:
[variables]
n0=%var1 value1
n1=%var2 value2
n2=%var3 value3
....
ie in INI format, or like this:
Code:
%var1 value1
%var2 value2
%var3 value3
So, we can use /filter on that file to grab the lines we want, ie those that match %msgs.* or n*=%msgs.*

Here's the commented code:
Code:
  [color:green]; We must first find out which file mIRC uses to store the variables
  ; We can do that by reading from mirc.ini, specifically the item n1 
  ; of the [rfiles] section of mirc.ini. We store the filename in %file[/color]
  var %file = $shortfn($readini($mircini,rfiles,n1)), %i = 1
  [color:green]; The following condition checks if %file is an INI file that contains
  ; the [variables] section or a plain text (ie non-INI format) file[/color]
  if $ini(%file,variables) {
    [color:green]; If it's an INI, we use /loadbuf -t to load the [variables] section
    ; in the hidden window @top10.sorter.window. The INI that contains
    ; the variables can (remote.ini by default) can contain other sections
    ; like [users] for example. /loadbuf -t is thus very useful here[/color]
    loadbuf -tvariables @top10.sorter.window %file
    [color:green]; All variables are now dumped in the hidden window. Now we can
    ; use /filter to filter out irrelevant variables and sort the relevant
    ; ones at the same time. Read about /filter in the help file carefully
    ; to understand how this is done[/color]
    filter -wwcute 2 32 @top10.sorter.window @top10.sorter.window n*=%msgs.*
  }
  [color:green]; If the file isn't an INI, we skip the /loadbuf -t part and proceed with /filter[/color]
  else filter -fwcute 2 32 %file @top10.sorter.window % $+ msgs.*[/color]
  [color:green]; Our sorted list of %msgs.* variables is now ready. All we need to do
  ; is loop through the first 10 lines in the hidden window[/color]
  while %i &lt; 11 &amp;&amp; $line(@top10.sorter.window,%i) { 
    tokenize 32 $ifmatch 
    set %top $+ %i $2 
    [color:green]; $gettok($1,-1,61) is used to strip the "nN=" part from the
    ; variable name, if it's there. 61 is the ascii value of "=". 
    ; $gettok(n1=blah,-1,61) returns "blah"
    ; $gettok(blah,-1,61) also returns "blah"[/color]
    set $+(%,top,%i,.name) $gettok($1,-1,61) 
    inc %i 
  }






/.timerQ 1 0 echo /.timerQ 1 0 $timer(Q).com
Joined: Oct 2003
Posts: 34
S
spling Offline OP
Ameglian cow
OP Offline
Ameglian cow
S
Joined: Oct 2003
Posts: 34
ok, now i'm trying to make the script not be a !top10 script, but a !topN script. Like, !top20, !top25, etc..
Here is what I have now
Code:
 on *:TEXT:!_top*:* {
  write -dl1 topmsgs.txt
  var %number $right($1,$calc($len($1)-5))
  window -c @top10.sorter.window
  window -eh @top10.sorter.window
  var %file = $shortfn($readini($mircini,rfiles,n1)), %i = 1
  if $ini(%file,variables) {
    loadbuf -tvariables @top10.sorter.window %file
    filter -wwcute 2 32 @top10.sorter.window @top10.sorter.window n*=%msgs.*
  }
  else filter -fwcute 2 32 %file @top10.sorter.window % $+ msgs.*  
  while %i &lt; $calc(%number + 1) &amp;&amp; $line(@top10.sorter.window,%i) { 
    tokenize 32 $ifmatch   
    set %top $+ %i $2  
    set $+(%,top,%i,.name) $gettok($1,-1,61)  
    inc %i 
  }
  unset %n
  while %n != %number {
    inc %n 1
    write -n topmsgs.txt %n $+ . $right($eval(% $+ top $+ %n $+ .name,2),$calc($len($eval(% $+ top $+ %n $+ .name,2))-6)) $+ : $eval(% $+ top $+ %n $+ .name,3) messages 
    if %n != %number { write -n topmsgs.txt  4/  }
  }
  msg $nick -- 12Top $+ %number List --
  msg $nick $read(topmsgs.txt, 1)
} 
I'm always getting "Line too long" errors and stuff, so i was wondering if you guys could help me work around this problem... I think that if I made it so that it only gave a message for every 10 users, that'd be good. What do you all think? crazy

Joined: Apr 2003
Posts: 342
M
Fjord artisan
Offline
Fjord artisan
M
Joined: Apr 2003
Posts: 342
Bleh... what a beast of code. Use hash tables! First of all hash tables are automatically sorted. Just use $base to make sure each item is padded with five zeros. You don't even need to sort the list. Just search linearally from 0 to 10 (pr whatever your goal is)..

Also... add some comments so others know what your trying to accomplish. I don't know half of what your trying to do.


Beware of MeStinkBAD! He knows more than he actually does!

Link Copied to Clipboard