One thing you need in most games is a source of randomness. In Tcl, random numbers are provided by the expr command's rand() function; but rand() can be made considerably more convenient by some judicious wrapping. Again, this is a Tcl-oriented essay rather than a game design essay; if the details don't interest you, pass on by.
The rand() function returns a floating-point value between 0 and 1, which is handy if you're working with raw probability figures. For many applications, though, what you're really looking for is an integer in some range. Here's a function that returns a random integer between $min and $max, inclusive:
proc rand {min max} {
# Determine the number of possible values.
set numValues [expr {$max - $min + 1}]
# Get a random integer from 0 to numValues - 1
set randInt [expr {int($numValues*rand())}]
# Return a number from min to max
return [expr {$min + $randInt}]
}
# Roll a six-sided die
puts "You rolled [rand 1 6]"
rand covers many of the cases I'm interested in; however, there's a particular application of rand that deserves further elaboration: picking an element randomly from a list. For example, suppose your game includes a creature that says something different every time the player talks to it. Provide the creature with a list of statements, and pick one randomly using pickfrom:
proc pickfrom {list} {
set i [rand 0 [expr {[llength $list] - 1}]]
set result [lindex $list $i]
return $result
}
puts "The parrot said [pickfrom {
{Polly wants a cracker!}
{Shiver me timbers!}
{Squawk!}
{The treasure is hidden under the captain's pillow!}
}]"
Sometimes you want to remove the selected element from the list, so that it can't be picked again. I use this a lot when randomly placing features on a newly created map. First I build up a list of open tiles, i.e., spots where I can legally put a chest, or a monster, or the player. Then I use takefrom to randomly choose tiles from the list. Because chosen tiles are removed from the list, I don't need to worry about putting two things in the same spot. Here's what takefrom looks like. Note that instead of passing a list, I'm passing a variable that contains a list; that's necessary so that takefrom can update the variable's value.
proc takefrom {listvar} {
upvar $listvar list
set i [rand 0 [expr {[llength $list] - 1}]]
set result [lindex $list $i]
set list [lreplace $list $i $i]
return $result
}
set openlist [getopentiles $map]
set location [takefrom openlist]
puts "The player starts at $location!"
Between them, these three commands provide almost all of the randomness I need for Ramble...so far, anyway.
Posted by Will Duquette at January 1, 2005 01:25 PM