One problem, many solutions

One problem, many solutions Mercutio Thu, 2011-05-05 22:41

This book is a collection of the column pages posted that pose a Softcode problem, then contains a solution, and asks other users to post their version of the code.

The meaning of this column is to teach softcode, and show off useful code-snippets that optimize against buffer length and function calls, or show useful logic or rarely used functions.

But more importantly, it shows different ways of tackling things, and how to solve certain very common problems.

A Random Player

A Random Player Mercutio Thu, 2011-05-05 23:31

This is perhaps a fairly simple thing, but does have a semi-decent amount of use on games:

Out of all the players on your game, pick one. That's it. Just pick one player, but do it /at random/.

One way of doing this is as follows:

lsearch(all,type,players,start,rand(nlsearch(all,type,players)),count,1)

It works on the logic that nlsearch() knows how many players there are, and get a random value based on that.

For the lsearch, we then start at the dbref-count that it gives, and grab just 'one value' from that number on. Aka, that very same dbref. This requires that other restrictions, like - checking if they have an approved flag, must be done within /both/ searches.

How would you do this?

Census

Census Mercutio Fri, 2011-04-29 23:36

If a game has all approved characters carrying:

&attribute player=one-word-value

Where 'one-word-value' can only be one of:

word1, word2, word3, word4

And you wanted to create a census list that outputs:

word1: player, player2, and player3
word2: player6, and player 8
word3: player5, and player7
word4: player9

How would you go about this?

One option with the named registers now, would be to do:

null(iter(lsearch(all,type,player),setq(get(%i0/attribute),setunion(%q<[get(%i0/attribute)]>,%i0))))
[iter(word1 word2 word3 word4,%i0: [itemize(%q<%i0>)])]

What other ways can you think of?

Census: Advanced

Census: Advanced Mercutio Thu, 2011-05-05 10:27

In this post we handled a simple census command with limited attribute values. But what if the attribute value is /not/ pre-defined.

So...

&attribute player=value

And assume that one can only use letters, numbers or spaces

@attribute/limit attribute=^[a-zA-Z0-9 ]+$

And we wanted a census command that outputs:

value 1: player, player2, and player3
value 2: player6, and player 8
value 3: player5, and player7
value 4: player9

One way, /abusing/ named registers (which will fail after X amount of unique named registers have been used up - as defined in @config) - is to do the following:

null(iter(lsearch(all,type,player,elock,attribute:*),setq(setr(a,edit(get(%i0/attribute),%b,_)),setunion(%q<%qa>,%i0))[setq(tlist,setunion(%q,%qa))]))

[iter(%q,edit(%i0,_,%b): [itemize(%q<%i0>)],,%r)]

How would /you/ do this?

Checking against interesting numbers

Checking against interesting numbers Mercutio Wed, 2012-03-21 11:49

Let's say you have a list of 8 numbers. And you want a boolean returned... because all numbers must resolve:

eq(bound(%0,128,255),%0)

With other words:

128 128 128 251 128 128 204 255 --> 1
128 128 128 312 128 128 204 255 --> 0

How would you go about doing this?

Sketch offers the bit function:

lmath(band,128 %0)

How would you do this?

Dynamic Value List, and Last Value Containment

Dynamic Value List, and Last Value Containment Mercutio Fri, 2010-10-01 14:41

I pose you a problem:

Input:  foo:1 bar:2 zen:2 foo:44 meow:cat bar:meow
Output: foo:44 zen:2 meow:cat bar:meow (in any order)

Basically, unique and last-value matches for key:value.
Then I made it a tad harder, and demanded that the delimiter was chr(160) (Non-breaking space)

So... what ways can this be solved?

Mike:

setq(0,revwords(%0,chr(160)),1,regeditall(sort(%0,chr(160)),(\\w+):\\w+( \\1:\\w+)*,$1))[squish(iter(%q1,grab(%q0,%i0:*,chr(160)),chr(160),%r),%r)]

setq(X,chr(160))[iter(squish(fold(#lambda/if(match(\%0,first(\%1,:):*,%qX),,\%1)%qX\%0,revwords(%0,%qX),,%qX),%qX),%i0,%qX,%r)]

Mercutio:

iter(setunion(iter(setr(1,%0),first(%i0,:),chr(160)),),grab(revwords(%q1,chr(160)),%i0:*,chr(160)),,%r)

Javelin:

[setq(x,chr(160))][squish([iter(reverse(%0,%qx),if(member(%q1,after(%i0,:)),,setq(1,%q1 [after(%i0,:)],0,%q0 %i0)),%qx,%qx)][reverse(%q0)])] 

Trinsec: (doesn't handle [chr(160)] )

[If( Words(%0),   SetQ(A, MatchAll(%0,[First(First(%0),:)]:*) )   [SetQ(L,%qL [Elements(%0,Last(%qA))])]   [U(%#/LIST`TRINSEC, LDelete(%0,%qA) )]   ,   %qL)] 

As you can tell, many solutions to one problem. I think it'd be interesting to have some kind of weekly problem - that people can comment on with solutions to the problem.

How would you do this?

Handling Large Lists, and the Buffer Limit

Handling Large Lists, and the Buffer Limit Mercutio Fri, 2011-05-06 10:48

Let's assume you have a large player base, with say... ~255 players that actively get used. And let's assume they are all in an attribute, except for the staffers. This way, during this exercise, there will be a stable amount of output.

Now, let's assume we wanna display information about these players, and stick them into an align()ed area. Most new coders would use iter() or map() to show them all. But they run into a problem! Due to the way align() works, one reaches the buffer limit extremely fast, and one will be cut off around #118 or so.

Sure, one can increase the buffer length to twice its size by altering mushtype.h, but that is nowhere near ideal, and will crash your game over a 2x increase. So... how do you handle this?

The Attribute

&masterlist OBJ=#10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 #22 #23 #24 #25 #26 #27 #28 #29 #30 #31
 #32 #33 #34 #35 #36 #37 #38 #39 #40 #41 #42 #43 #44 #45 #46 #47 #48 #49 #50 #51 #52 #53 #54 #55 #56 #57
 #58 #59 #60 #61 #62 #63 #64 #65 #66 #67 #68 #69 #70 #71 #72 #73 #74 #75 #76 #77 #78 #79 #80 #81 #82 #83
 #84 #85 #86 #87 #88 #89 #90 #91 #92 #93 #94 #95 #96 #97 #98 #99 #100 #101 #102 #103 #104 #105 #106 #107
 #108 #109 #110 #111 #112 #113 #114 #115 #116 #117 #118 #119 #120 #121 #122 #123 #124 #125 #126 #127
 #128 #129 #130 #131 #132 #133 #134 #135 #136 #137 #138 #139 #140 #141 #142 #143 #144 #145 #146 #147
 #148 #149 #150 #151 #152 #153 #154 #155 #156 #157 #158 #159 #160 #161 #162 #163 #164 #165 #166 #167
 #168 #169 #170 #171 #172 #173 #174 #175 #176 #177 #178 #179 #180 #181 #182 #183 #184 #185 #186 #187
 #188 #189 #190 #191 #192 #193 #194 #195 #196 #197 #198 #199 #200 #201 #202 #203 #204 #205 #206 #207
 #208 #209 #210 #211 #212 #213 #214 #215 #216 #217 #218 #219 #220 #221 #222 #223 #224 #225 #226 #227
 #228 #229 #230 #231 #232 #233 #234 #235 #236 #237 #238 #239 #240 #241 #242 #243 #244 #245 #246 #247
 #248 #249 #250 #251 #252 #253 #254 #255

You can easily output this with:

@fo me=&masterlist OBJ=[iter(lnum(10,255),#%i0)]

The Align Code

&align OBJ=align(30 47,name(%0),%0)

One way to get around this, is to use an @command instead of using just functions. Because of the way @dolist works, each time you make it run its command list, it's a new queue buffer. So the solution is as follows:

@dolist get(OBJ/masterlist)=@pemit target=u(OBJ/align,%iL)

How would you do this?

Playing with numbers: Missing numbers

Playing with numbers: Missing numbers Mercutio Thu, 2011-05-19 22:48

Let's say you have the following list of numbers:

1 2 4 7

And you want it to output the 'missing numbers' in between. Like so:

3 5 6

The first thing to look at would probably be setdiff(), a useful function that compares two lists, and
returns the values that were in one list, but not in the other.

Raevnos@M*U*S*H offers the following solution:

letq(D, firstof(%1, %b), setdiff(lnum(lmath(min, %0, %qD), lmath(max, %0, %qD), %qD), %0, %qD, n))

How would you do this?

Playing with numbers: Number range creator

Playing with numbers: Number range creator Mercutio Tue, 2011-05-24 19:17

In this post we created a function that will change a set of number ranges into a simple list of numbers. But what if we wanted to do it the other way around!?

So, if we have the following list:

5 7 8 9 3 6 12 13 14

How do we get to:

3 5-9 12-14

One way of doing this, as taken from M*U*S*H's snippet machine in #0, is the following:

&ATTR1 OBJ=trim(fold(me/ATTR2,unique(sort(%0,n))))
&ATTR2 OBJ=if(eq(%1,inc(last(last(%0),-))),strcat(extract(%0,1,dec(words(%0))),%b,first(last(%0),-),-,%1),%0 %1)

How would you do this?

Playing with numbers: Number range expander

Playing with numbers: Number range expander Mercutio Tue, 2011-05-24 19:11

There's plenty of programs where the user will sometimes expect it, or it would be convenient, if something accepted a number range. For example: +bbread 5/6-9, if they want to read bboard 5, posts 6, 7, 8 and 9.

So, say we have an unsorted number range as follows:

5 7-10 3 8-9

And we want that to output:

3 5 7 8 9 10

One way of going about this, is to do as follows:

unique(sort(iter(%0,lnum(max([first(%i0,-)],[last(%i0,-)]),min([first(%i0,-)],[last(%i0,-)])))))

How would you do this?

Rooms with Columned Players

Rooms with Columned Players Mercutio Sat, 2011-05-14 14:39

Let's assume a game wants their conformat's players to be
displayed in three columns, thus as follows:

Players:
Player 1                  Player 2                  Player 3
Player 4                  Player 5                  Player 6

The way to display each single name is:

&conformat`name OBJ=left(name(%0),25)

The logic behind using 25 is because a name default can be 24 characters.

Here is one way to do this:

&conformat OBJ=step(conformat`display,lvplayers(me),3,,%r)
&conformat`display OBJ=align(25 25 25,u(conformat`name,%0),u(conformat`name,%1),u(conformat`name,%2))

How would you do this?

The Where Command

The Where Command Mercutio Wed, 2011-05-04 22:02

There are god knows how many where commands out there. But what many will often want is a where command that lists things with the location once, then all the players at that location?

Thus as follows:

location 1              player 1, player3, and player 5
location 2              player 2, and player 6
location 3              player 4

Let's leave the 'Unfindable' case out of this for now.

Here is a sample code on how to use fold() to solve this problem!

First, set the fold attribute. We're not going to mess with #lambda here.

&fold OBJ=%0 [switch(loc(last(%0)),loc(%1),%1,%r[name(loc(%1))]: %1)]
&cmd`where OBJ=$@where:@pemit %#=regeditall(iter(fold(me/fold,sort(lwho(),loc),#-1),align(40 37,first(%i0,:),itemize(iter(rest(%i0,:),name(%i0),,|),|)),%r,%r),^\\s+|\\s+$,)

How would you do this?