Space Game


This is a Space Game script in Tcl/Tk. What makes it different is that this project is just beginning, and the script will be shown through its development. It is a demonstration, taking a program from idea to tool. Or toy, as the case may be. Since the author is a beginning programmer, there should be plenty of learning opportunities. :)

If you want to edit the code, please do so. It is not performance tuned, clean, or very useful, yet. But if you do edit, please give us enough comments to understand what you are doing, and why. This less a product and more a learning tool.

Or so I hope....

2001-06-02: Of course, I forgot to mention the help from Miguel MS and others, as I started writing this. JCW--thanks for the Wikit, and the chat room!

2001-06-02: part2. Well, thanks to Clif Flynt and an example in his book, I've overcome the problems that prevented this from being a Tk program.

2001-06-01: Conversations in the chat room add ideas. I want people to feel free to edit this, but I would also like to track changes. So, a Code change format page.


#! /bin/env tclsh

# See Wikit page [Exec Magic] for an explanation of the above.

# Added 2 Jun 01-- Program information.
# Comments are generally good, especially when someone might look 
# at your code and wonder what you were smoking/thinking when you 
# wrote that.

# This is called vector.tcl, and takes two arguments. Each argument
# is a comma divided set of 3 numbers, in the form of 12,34,56.
# It does some error checking, and then calculates the angle of
# difference between the pair of coordinated, and calls that the
# vector. Finally, it figures the distance between the two coordinates.

# Output looks like:
# $ ./vector.tcl 12,23,34 54,43,32
# To get from 12,23,34 to 54,43,32 you vector 42,20,-2 for 46.5617869073
# The plan, and I do have one, is to make this modular, and then put
# it into Tk. This is just one part of the Space Game.

# Check for the right number of arguments, and have at least one error
# message.

set errorMsg "Please use the right number of arguments and coordinates\
    in the form of:
    $argv0 aa,bb,cc xx,yy,zz"

if { $argc != 2 } {
    puts $errorMsg

# This gave a more detailed error message.
proc coordError { badCoord } {
    puts "$badCoord is not a valid coordinate in this universe."

# I'm rather fond of this one, it took me hours to figure out.
# The first version had a second argument of the variable name
# that was to be used. However, I could not get it to work. This
# one does two tests for the if, and then returns the proper 
# list of 3 numbers if everything is right.
proc validateCoord { coord } {
    set coordName [ split $coord , ]
    if {([ llength $coordName ] != "3") || \
        ([regexp -- {[^0-9 ]}  "$coordName"])} {

        # The more detailed error message
        coordError $coord
    return $coordName

set hereNow [ lindex $argv 0  ]
set thereThen [ lindex $argv 1 ]

set hereCoord [ validateCoord $hereNow ]
set thereCoord [ validateCoord $thereThen ]

# Foreach #1
# here is the first improvement, it replaces the while loop below:
# '''why replace the other loop?  What makes this better?'''
foreach x1 $hereCoord x2 $thereCoord {
    lappend vector [expr {$x2 - $x1}]

# This replaced by Foreach #1
#set labelCoord  [ list X Y Z ]
#set counter 0
#while { $counter < [ llength $hereCoord ] } {
#       set v [ lindex $labelCoord $counter ]
#       set vector$v [ expr [ lindex $hereCoord $counter ] \
#               - [ lindex $thereCoord $counter ] ]
##       set type vector
#       set V [ expr [ lindex $hereCoord $counter ] \
#               - [ lindex $thereCoord $counter ] ]
#        set sqrAbsVector${v} [ expr abs($V) * abs($V) ]
#       incr counter

# These were early drops, though they go the script working.
# Such repetitive lines should be automated.
#set sqrAbsVectorX [ expr abs($vectorX) * abs($vectorX) ]
#set sqrAbsVectorY [ expr abs($vectorY) * abs($vectorY) ]
#set sqrAbsVectorZ [ expr abs($vectorZ) * abs($vectorZ) ]

# Foreach #2, to replace the distance math.
# I had to read up on foreach to get this.
# '''what is this foreach doing?'''
foreach {X Y Z} $vector  break 

# It was giving me errors about vector, so a temporary "puts"
#puts " After: $vector "

#The first set was given to better do the math, however, it had an
# error-- the sqrAbs${vector} would set the variable to:
#  "sqrAbs-11 -22 9"
#set sqrAbs${vector} [expr {hypot($X, [expr hypot($Y,$Z)])}]
set distance [expr {hypot($X, hypot($Y,$Z))}]
# {end of Foreach #2}

# Replaced by Foreach #2
#set distance [ expr sqrt($sqrAbsVectorX + $sqrAbsVectorY + \
 $sqrAbsVectorZ) ]

puts "To get from $hereNow to $thereThen you vector \
    ${X},${Y},${Z} for $distance"


Why not use regexp for the checking and variable assigment part? Maybe something like:

set coordset [join $argv]
if {![regexp {([0-9]{2}),([0-9]{2}),([0-9]{2}) ([0-9]{2}),([0-9]{2}),([0-9]{2})} $coordset -> x1 x2 x3 y1 y2 y3]} {
    error "$coordset is not a valid set of coordinates.\n Use: x1,y1,z1 x2,y2,z2"

this would either throw an error if the given arguments are not right coordinates or let use us $x1,2 , $y1,2 and $z1,2 for your calculations.

MG offers this alternative:

proc validateCoordMG {coord} {
    set num [scan $coord "%d,%d,%d%n" a b c size]
    if { $num != 4 || $size != [string length $coord] } {
        coordError $coord
    return [list $a $b $c]
};# validateCoordMG

This one takes 4ms to run on a valid coordinate set, as opposed to 6ms for the original code, and 40ms for the regexp above (which I edited slightly, to only work on a single coord. set and to run in a proc, using coordError instead of error to report mistakes, so I could get a more accurate comparison).