KillerBots

Check-in [0fa2e7a927]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Added header comments.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:0fa2e7a92737e423a720ad16529d7c10c497977a
User & Date: gerald 2013-09-12 19:52:35
Context
2013-09-24
20:50
Corrections Leaf check-in: 117acc6a90 user: gerald tags: trunk
2013-09-12
19:52
Added header comments. check-in: 0fa2e7a927 user: gerald tags: trunk
2013-08-16
22:01
List to not walk into walls. check-in: bc575faeae user: gerald tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Added Copyright.txt.





















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Copyright (c) 2006-2013, Gerald W. Lester

All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this
    list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
  * Neither the name of copyright holders may be used to endorse or promote
    products derived from this software without specific prior written
    permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Changes to KillerBots.tcl.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# the next line restarts using tclsh \
exec tclsh "$0" ${1+"$@"}

##
## URL: http(s)://chiselapp.com/user/gwlester/repository/KillerBots
##

package require Tk

set baseDir [file dirname [info script]]

##
## Create the namespaces used
##
namespace eval ::gui:: {}







|







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# the next line restarts using tclsh \
exec tclsh "$0" ${1+"$@"}

##
## URL: http(s)://chiselapp.com/user/gwlester/repository/KillerBots
##

package require Tk 8.5

set baseDir [file dirname [info script]]

##
## Create the namespaces used
##
namespace eval ::gui:: {}

Changes to gui/CreatePerferencesGui.tcl.










1
2
3
4
5
6
7
..
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









proc ::gui::CreatePerferencesGui {} {
    variable windows
    variable tempSettings

    set main $windows(main)
    set w [toplevel $main.perferences]
    wm withdraw $w
................................................................................
        Robots  $f.entryRobots \
        Player  $f.entryPlayer \
    ]
    trace add variable ::gui::tempSettings write [list updateMenuButton $buttonDict ::gui::tempSettings]

}















proc updateMenuButton {buttonDict nsVarName varName index ops} {
    upvar #0 $nsVarName arrayVar

    if {![info exists arrayVar($index)]} {
        after 1 [info level 0]
    }
    if {[dict exists $buttonDict $index]} {
        [dict get $buttonDict $index] configure -image $arrayVar($index)
    } else {
        set arrayVar($index) [expr {int($arrayVar($index))}]
    }
}












proc ::gui::DisplayPerferencesGui {w} {
    variable windows
    variable tempSettings
    variable ::settings

    set simulationActive [expr {[info exists calculated(timeLeft)] && ($calculated(timeLeft) > 0.0)}]
    if {$simulationActive} {
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>













>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
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
## Name: ::gui::CreatePerferencesGui
##
## Description:
##      Create the perfernce window
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::CreatePerferencesGui {} {
    variable windows
    variable tempSettings

    set main $windows(main)
    set w [toplevel $main.perferences]
    wm withdraw $w
................................................................................
        Robots  $f.entryRobots \
        Player  $f.entryPlayer \
    ]
    trace add variable ::gui::tempSettings write [list updateMenuButton $buttonDict ::gui::tempSettings]

}

## Name: updateMenuButton
##
## Description:
##      Update the image that a button is displaying.  This is a callback from a trace.
##
## Arguments:
##      buttonDict  - the dictionary containing the button information
##      nsVarName   - the fully qualified array name
##      varName     - the local array name
##      index       - the index into the array
##      ops         - not used
##
## Returns: Nothing
##
proc updateMenuButton {buttonDict nsVarName varName index ops} {
    upvar #0 $nsVarName arrayVar

    if {![info exists arrayVar($index)]} {
        after 1 [info level 0]
    }
    if {[dict exists $buttonDict $index]} {
        [dict get $buttonDict $index] configure -image $arrayVar($index)
    } else {
        set arrayVar($index) [expr {int($arrayVar($index))}]
    }
}

## Name: ::gui::DisplayPerferencesGui
##
## Description:
##      Display the perference GUI.
##      Note -- the simulation is paused while the GUI is displayed.
##
## Arguments:
##      w - not used.
##
## Returns: Nothing
##
proc ::gui::DisplayPerferencesGui {w} {
    variable windows
    variable tempSettings
    variable ::settings

    set simulationActive [expr {[info exists calculated(timeLeft)] && ($calculated(timeLeft) > 0.0)}]
    if {$simulationActive} {

Changes to gui/HelpSystem.tcl.

1
2
3
4
5












6
7
8
9
10
11
12
..
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
package require struct::stack

struct::stack ::gui::help::helpStack
array set ::gui::help::links {}













proc ::gui::help::loadHtml {window {file {}} {push yes}} {
    global baseDir
    variable links

    if {[string equal $file {}]} {
        ::gui::help::helpStack clear
        set baseFile [::msgcat::mc index.html]
................................................................................
    } else {
        $window.toolBar.back configure -state normal
    }

    return;
}











proc ::gui::help::back {window} {
    ::gui::help::helpStack pop
    ::gui::help::loadHtml $window [::gui::help::helpStack peek] no
}












proc ::gui::help::linkNode {window node} {
    variable links

    catch {
        set links($node) [$node attribute href]
        $node dynamic set link
    }
}














proc ::gui::help::followLink {window x y} {
    variable links

    set node [$window node $x $y]
    while {![string equal $node {}] && ![info exists links($node)]} {
        set node [$node parent]
    }
    if {[info exists links($node)]} {
        loadHtml [winfo parent $window] $links($node) yes
    }
    return
}










proc ::gui::help::dismiss {} {
    variable ::gui::windows

    wm withdraw $::gui::windows(helpViewer)

    return;
}





>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>





>
>
>
>
>
>
>
>
>
>
>










>
>
>
>
>
>
>
>
>
>
>
>













>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
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
package require struct::stack

struct::stack ::gui::help::helpStack
array set ::gui::help::links {}

## Name: ::gui::help::loadHtml
##
## Description:
##      ...
##
## Arguments:
##      window  - The base window of the help system
##      file    - the file to be displayed
##      push    - if the file should be pushed on "back" statck
##
## Returns: Nothing
##
proc ::gui::help::loadHtml {window {file {}} {push yes}} {
    global baseDir
    variable links

    if {[string equal $file {}]} {
        ::gui::help::helpStack clear
        set baseFile [::msgcat::mc index.html]
................................................................................
    } else {
        $window.toolBar.back configure -state normal
    }

    return;
}

## Name: ::gui::help::back
##
## Description:
##      Handle the back button to navigate to the "previously" displayed window.
##
## Arguments:
##      window - The HTML viewer window.
##
## Returns: Nothing
##
proc ::gui::help::back {window} {
    ::gui::help::helpStack pop
    ::gui::help::loadHtml $window [::gui::help::helpStack peek] no
}

## Name: ::gui::help::linkNode
##
## Description:
##      Callback handler for when a link is found in parsing the HEML.
##
## Arguments:
##      window  - The HTML viewer window.
##      node    - the node in the DOM tree
##
## Returns: Nothing
##
proc ::gui::help::linkNode {window node} {
    variable links

    catch {
        set links($node) [$node attribute href]
        $node dynamic set link
    }
}


## Name: ::gui::help::followLink
##
## Description:
##      Follow a link and cause a new help/html page to be displayed
##
## Arguments:
##      window  - The HTML viewer window.
##      x       - the x coordinates clicked on.
##      y       - the y coordinates clicked on.
##
## Returns: Nothing
##
proc ::gui::help::followLink {window x y} {
    variable links

    set node [$window node $x $y]
    while {![string equal $node {}] && ![info exists links($node)]} {
        set node [$node parent]
    }
    if {[info exists links($node)]} {
        loadHtml [winfo parent $window] $links($node) yes
    }
    return
}

## Name: ::gui::help::dismiss
##
## Description:
##      Handle dismissing the help window.
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::help::dismiss {} {
    variable ::gui::windows

    wm withdraw $::gui::windows(helpViewer)

    return;
}

Changes to gui/createGui.tcl.

1
2
3

4
5
6
7
8
9
10
..
12
13
14
15
16
17
18











19
20
21
22
23

24
25
##
## Create all the toplevel GUI windows and a namespace to store variables in
##

array set ::gui::windows {}

##
## Load packages we are using from TkLib
##
package require autoscroll
package require tooltip
................................................................................
##
## Load images we are using
##
foreach file [glob [file join $baseDir gui icons *]] {
    image create photo [file tail [file rootname $file]] -file $file
}












proc createGui {} {
    ::gui::createMainWindow
    ::gui::createHelpWindow
    ::gui::CreatePerferencesGui
}


wm withdraw .



>







 







>
>
>
>
>
>
>
>
>
>
>




|
>
|
<
1
2
3
4
5
6
7
8
9
10
11
..
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

##
## Create all the toplevel GUI windows and a namespace to store variables in
##
wm withdraw .
array set ::gui::windows {}

##
## Load packages we are using from TkLib
##
package require autoscroll
package require tooltip
................................................................................
##
## Load images we are using
##
foreach file [glob [file join $baseDir gui icons *]] {
    image create photo [file tail [file rootname $file]] -file $file
}

##
## Name: createGui
##
## Description:
##      Create the GUIs for the KillerBots game.  The "main" toplevel GUI is
##      in its own toplevel and not ".".
##
## Arguments: None
##
## Returns: Nothing
##
proc createGui {} {
    ::gui::createMainWindow
    ::gui::createHelpWindow
    ::gui::CreatePerferencesGui

    return;
}

Changes to gui/createHelpWindow.tcl.




1

2












3
4
5
6
7
8
9
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



package require Tkhtml 3.0














proc ::gui::createHelpWindow {} {
    variable windows

    set w [toplevel $windows(main).helpViewer]
    wm withdraw $w
    set windows(helpViewer) $w
    set windows(helpViewer,displayCommand) ::gui::help::loadHtml
    wm transient $w $windows(main)
    wm title $w [::msgcat::mc helpViwer.title]
    wm protocol $w WM_DELETE_WINDOW ::gui::help::dismiss

    frame $w.toolBar
    button $w.toolBar.back \
        -command [list ::gui::help::back $w] \
        -image back
    grid configure $w.toolBar.back x -sticky ew
    grid columnconfigure $w.toolBar 2 -weight 1

    html $w.helpText \
        -xscrollcommand [list $w.hsb set] \
        -yscrollcommand [list $w.hsb set]
    scrollbar $w.hsb \
        -orient horizontal \
        -command [list $w.helpText xview]
    scrollbar $w.vsb \
        -orient vertical \
        -command [list $w.helpText yview]

    grid configure $w.toolBar -columnspan 2 -sticky ew
    grid configure $w.helpText $w.vsb -sticky nsew
    grid configure $w.hsb -sticky nsew
    grid columnconfigure $w $w.helpText -weight 1
>
>
>

>

>
>
>
>
>
>
>
>
>
>
>
>











|
|








|


|







1
2
3
4
5
6
7
8
9
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
##
## Load the packages we will require
##
package require Tkhtml 3.0
package require autoscroll

##
## Name: ::gui::createHelpWindow
##
## Description:
##      Create the Help Window. This uses the Tkhtml widget for to do the heavy
##      lifiting. It also has a pair of scrollbars that only appear when needed
##      thanks to the autoscroll package from TkLib.
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::createHelpWindow {} {
    variable windows

    set w [toplevel $windows(main).helpViewer]
    wm withdraw $w
    set windows(helpViewer) $w
    set windows(helpViewer,displayCommand) ::gui::help::loadHtml
    wm transient $w $windows(main)
    wm title $w [::msgcat::mc helpViwer.title]
    wm protocol $w WM_DELETE_WINDOW ::gui::help::dismiss

    ::ttk::frame $w.toolBar
    ::ttk::button $w.toolBar.back \
        -command [list ::gui::help::back $w] \
        -image back
    grid configure $w.toolBar.back x -sticky ew
    grid columnconfigure $w.toolBar 2 -weight 1

    html $w.helpText \
        -xscrollcommand [list $w.hsb set] \
        -yscrollcommand [list $w.hsb set]
    ::ttk::scrollbar $w.hsb \
        -orient horizontal \
        -command [list $w.helpText xview]
    ::ttk::scrollbar $w.vsb \
        -orient vertical \
        -command [list $w.helpText yview]

    grid configure $w.toolBar -columnspan 2 -sticky ew
    grid configure $w.helpText $w.vsb -sticky nsew
    grid configure $w.hsb -sticky nsew
    grid columnconfigure $w $w.helpText -weight 1

Changes to gui/createMainWindow.tcl.




1

2












3
4
5




6
7
8
9
10
11
12
13




14
15
16
17
18
19
20
..
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
..
73
74
75
76
77
78
79




80
81




82
83
84













85
86
87
88













89
90
91
92
93
94
95
...
118
119
120
121
122
123
124













125
126
127
128
129
130
131
...
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
...
229
230
231
232
233
234
235
















236
237
238



239
240
241



242
243






244
245
246
247
248
249
250



package require math














proc ::gui::createMainWindow {} {
    variable windows





    set w [toplevel .main]
    wm withdraw $w
    set windows(main) $w
    wm transient $w .
    wm title $w [::msgcat::mc main.title]
    wm protocol $w WM_DELETE_WINDOW ::gui::createMainWindowDismiss
    #wm resizable window ?width height?





    set menu $w.menubar
    menu $menu
    if {[string equal $::tcl_platform(os) Darwin]} {
        set appleMenu $menu.apple
        menu $appleMenu
        $menu add cascade -menu $appleMenu
    }
................................................................................

    $menu add cascade \
        -menu $menu.help \
        -label [::msgcat::mc main.HelpMenuLabel]
    createMainWindowHelpMenu $menu.help
    $w configure -menu $menu





    ::ttk::frame $w.frame
    grid configure $w.frame -sticky nsew
    grid columnconfigure $w $w.frame -weight 1
    grid rowconfigure $w $w.frame -weight 1

    set f $w.frame
    canvas $f.data \
        -xscrollcommand [list $f.hsb set] \
        -yscrollcommand [list $f.hsb set] \
        -width 640 \
        -height 640 \
        -scrollregion [list 0 0 640 640]
    scrollbar $f.hsb \
        -orient horizontal \
        -command [list $f.data xview]
    scrollbar $f.vsb \
        -orient vertical \
        -command [list $f.data yview]
    bind $f.data <Button-1> {::gui::placeTarget %x %y}
    set windows(dataWidget) $f.data




    ::ttk::frame $f.status
    ::ttk::label $f.status.levelLabel \
        -text [::msgcat::mc LevelLabel]
    ::ttk::entry $f.status.level \
        -state readonly \
        -textvariable ::settings(Level) \
        -width 3
................................................................................
    grid configure $f.status -columnspan 2 -sticky ew
    grid columnconfigure $f $f.data -weight 1
    grid rowconfigure $f $f.data -weight 1

    ::autoscroll::autoscroll $f.vsb
    ::autoscroll::autoscroll $f.hsb





    ::gui::createAboutWindow $w
    ::gui::drawEnclosure $w




    return;
}














proc ::gui::createMainWindowDismiss {} {
    destroy .
}














proc ::gui::createMainWindowFileMenu {menu} {
    variable windows

    set windows(main,fileMenu) $menu

    $menu add command \
        -label [::msgcat::mc FileMenu,NewGame] \
................................................................................
    } else {
       $menu add command \
            -label [::msgcat::mc Menu.Perferences] \
            -command {displayWindow perferences}
    }
}














proc ::gui::createMainWindowHelpMenu {menu} {
    if {[string equal $::tcl_platform(os) Darwin]} {
        set parent [winfo parent $menu]
        set appleMenu $parent
        append appleMenu {.apple}
        $appleMenu add command \
            -label [::msgcat::mc HelpMenu.About] \
................................................................................
            -command {displayWindow helpAbout}
    }
    $menu add command \
        -label [::msgcat::mc HelpMenu.Help] \
        -command {displayWindow helpViewer}
}













proc ::gui::createAboutWindow {main} {
    variable windows

    set windows(helpAbout) $main.helpAbout
    set windows(helpAbout,displayCommand) [list ::gui::displayAboutWindow]
    return;
}













proc ::gui::displayAboutWindow {main} {
    variable windows

    tk_messageBox \
        -default ok \
        -icon info \
        -message [::msgcat::mc helpAbout.details] \
        -parent $windows(main) \
        -title [::msgcat::mc helpAbout.title] \
        -type ok
    return;
}











proc ::gui::drawEnclosure {main} {
    variable windows

    ##
    ## Get the canvas widget
    ##
    set c $windows(dataWidget)

    ##
    ## Put the walls in
    ##
    for {set i 0} {$i < 640} {incr i 32} {
        $c create image 0 $i -image stone -anchor w -tags stone
        $c create image 640 $i -image stone -anchor e -tags stone
        $c create image $i 0  -image stone -anchor nw -tags stone
        $c create image $i 640 -image stone -anchor sw -tags stone
    }

    ##
    ## Put the water in
    ##
    $c create oval {300 300 340 340} -tags water -fill blue -outline blue
    $c create oval {140 120 170 160} -tags water -fill blue -outline blue
    $c create oval {440 520 470 560} -tags water -fill blue -outline blue
    $c create oval {440 120 470 160} -tags water -fill blue -outline blue
    $c create oval {140 520 170 560} -tags water -fill blue -outline blue

................................................................................
        set x [::math::random 40 600]
        $c create polygon [list 608 [expr {$x - 10}] 620 $x 608 [expr {$x + 10}] 608 $x] \
            -fill black -outline black -smooth true -splinesteps 20 \
            -tags [list hole popOut_588,$x]
    }
}

















proc ::gui::placeTarget {x y} {
    variable windows




    set c $windows(dataWidget)

    if {[llength [$c gettags target]]} {



        $c coords target $x $y
    } elseif {[llength [$c gettags player]]} {






        $c create image [list $x $y] -anchor c -tags target -image target
        $c lower target player
        if {![llength [after info]] && ($::calculated(timeLeft) > 0.0)} {
            doResumeSimulation
        }
    }
}
>
>
>

>

>
>
>
>
>
>
>
>
>
>
>
>



>
>
>
>



<


<

>
>
>
>







 







>
>
>
>




<







|


|





>
>
>







 







>
>
>
>
|
|
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>




>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
|


|




>
>
>
>
>
>
>
>
>
>
>
>
|












>
>
>
>
>
>
>
>
>
>
|








|









|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



>
>
>



>
>
>


>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
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
..
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
...
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
...
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
...
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
...
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
##
## Load packages we will require
##
package require math
package require autoscroll

##
## Name: ::gui::createMainWindow
##
## Description:
##      Create the main Wwndow. This uses the canvas widget for to do the heavy
##      lifiting. It also has a pair of scrollbars that only appear when needed
##      thanks to the autoscroll package from TkLib.
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::createMainWindow {} {
    variable windows

    ##
    ## Create this as a new toplevel with title and a handler for the close
    ## button.
    ##
    set w [toplevel .main]
    wm withdraw $w
    set windows(main) $w

    wm title $w [::msgcat::mc main.title]
    wm protocol $w WM_DELETE_WINDOW ::gui::createMainWindowDismiss


    ##
    ## Create the menu bar. It will have a file and a help menu. Additionally,
    ## if we are on a Mac, it will also utilize the special Apple menu.
    ##
    set menu $w.menubar
    menu $menu
    if {[string equal $::tcl_platform(os) Darwin]} {
        set appleMenu $menu.apple
        menu $appleMenu
        $menu add cascade -menu $appleMenu
    }
................................................................................

    $menu add cascade \
        -menu $menu.help \
        -label [::msgcat::mc main.HelpMenuLabel]
    createMainWindowHelpMenu $menu.help
    $w configure -menu $menu

    ##
    ## Create the canvas and its scrollbars (only used if the window is resized
    ## down).
    ##
    ::ttk::frame $w.frame
    grid configure $w.frame -sticky nsew
    grid columnconfigure $w $w.frame -weight 1
    grid rowconfigure $w $w.frame -weight 1

    set f $w.frame
    canvas $f.data \
        -xscrollcommand [list $f.hsb set] \
        -yscrollcommand [list $f.hsb set] \
        -width 640 \
        -height 640 \
        -scrollregion [list 0 0 640 640]
    ::ttk::scrollbar $f.hsb \
        -orient horizontal \
        -command [list $f.data xview]
    ::ttk::scrollbar $f.vsb \
        -orient vertical \
        -command [list $f.data yview]
    bind $f.data <Button-1> {::gui::placeTarget %x %y}
    set windows(dataWidget) $f.data

    ##
    ## Next we have a status area displaying the level and time left.
    ##
    ::ttk::frame $f.status
    ::ttk::label $f.status.levelLabel \
        -text [::msgcat::mc LevelLabel]
    ::ttk::entry $f.status.level \
        -state readonly \
        -textvariable ::settings(Level) \
        -width 3
................................................................................
    grid configure $f.status -columnspan 2 -sticky ew
    grid columnconfigure $f $f.data -weight 1
    grid rowconfigure $f $f.data -weight 1

    ::autoscroll::autoscroll $f.vsb
    ::autoscroll::autoscroll $f.hsb

    ##
    ## Create an about window that will be a slave to this window then draw the
    ## game playing area.
    ##
    ::gui::createAboutWindow
    ::gui::drawEnclosure

    ##
    ## All done.
    ##
    return;
}

##
## Name: ::gui::createMainWindowDismiss
##
## Description:
##      This handles the close button on the decoration supplied by the window
##      manager. Since this is the main window, we will shut down the
##      application when invoked. We do this by destroying the application root
##      window, ".".
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::createMainWindowDismiss {} {
    destroy .
}

##
## Name: ::gui::createMainWindowFileMenu
##
## Description:
##      Create the file menu for main Wwndow. Note that on a Mac the Perference
##      menu item is placed on the special Apple menu, all other platforms it is
##      on the file menu itself.
##
## Arguments:
##      menu -- the file menu widget
##
## Returns: Nothing
##
proc ::gui::createMainWindowFileMenu {menu} {
    variable windows

    set windows(main,fileMenu) $menu

    $menu add command \
        -label [::msgcat::mc FileMenu,NewGame] \
................................................................................
    } else {
       $menu add command \
            -label [::msgcat::mc Menu.Perferences] \
            -command {displayWindow perferences}
    }
}

##
## Name: ::gui::createMainWindowHelpMenu
##
## Description:
##      Create the help menu for main Wwndow. Note that on a Mac the About menu
##      item is placed on the special Apple menu, all other platforms it is on
##      the help menu itself.
##
## Arguments:
##      menu - the help menu widget
##
## Returns: Nothing
##
proc ::gui::createMainWindowHelpMenu {menu} {
    if {[string equal $::tcl_platform(os) Darwin]} {
        set parent [winfo parent $menu]
        set appleMenu $parent
        append appleMenu {.apple}
        $appleMenu add command \
            -label [::msgcat::mc HelpMenu.About] \
................................................................................
            -command {displayWindow helpAbout}
    }
    $menu add command \
        -label [::msgcat::mc HelpMenu.Help] \
        -command {displayWindow helpViewer}
}

##
## Name: ::gui::createAboutWindow
##
## Description:
##      Create the About Wwndow. The about information will actually be
##      displayed using a Tk Message dialog. See the displayWindow.tcl file for
##      why this will work.
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::createAboutWindow {} {
    variable windows

    set windows(helpAbout) .helpAbout
    set windows(helpAbout,displayCommand) [list ::gui::displayAboutWindow]
    return;
}

##
## Name: ::gui::displayAboutWindow
##
## Description:
##      Display the about information using a Tk Message dialog.
##
## Arguments:
##      w - the window path. This is not used but automatically passed by the
##      displayWindow utility.
##
## Returns: Nothing
##
proc ::gui::displayAboutWindow {w} {
    variable windows

    tk_messageBox \
        -default ok \
        -icon info \
        -message [::msgcat::mc helpAbout.details] \
        -parent $windows(main) \
        -title [::msgcat::mc helpAbout.title] \
        -type ok
    return;
}

##
## Name: ::gui::drawEnclosure
##
## Description:
##      Draw the basic game playing area.
##
## Arguments: None
##
## Returns: Nothing
##
proc ::gui::drawEnclosure {} {
    variable windows

    ##
    ## Get the canvas widget
    ##
    set c $windows(dataWidget)

    ##
    ## Put in the walls
    ##
    for {set i 0} {$i < 640} {incr i 32} {
        $c create image 0 $i -image stone -anchor w -tags stone
        $c create image 640 $i -image stone -anchor e -tags stone
        $c create image $i 0  -image stone -anchor nw -tags stone
        $c create image $i 640 -image stone -anchor sw -tags stone
    }

    ##
    ## Put in the water holes
    ##
    $c create oval {300 300 340 340} -tags water -fill blue -outline blue
    $c create oval {140 120 170 160} -tags water -fill blue -outline blue
    $c create oval {440 520 470 560} -tags water -fill blue -outline blue
    $c create oval {440 120 470 160} -tags water -fill blue -outline blue
    $c create oval {140 520 170 560} -tags water -fill blue -outline blue

................................................................................
        set x [::math::random 40 600]
        $c create polygon [list 608 [expr {$x - 10}] 620 $x 608 [expr {$x + 10}] 608 $x] \
            -fill black -outline black -smooth true -splinesteps 20 \
            -tags [list hole popOut_588,$x]
    }
}

##
## Name: ::gui::placeTarget
##
## Description:
##      Move the target indicator to where the user just clicked to place it. If
##      there is not a target icon, one will be created. As a side effect, if
##      the simulation has not started, it will be started. Lastly, if they have
##      not generated the player and the robots yet, then do not place the
##      target icon.
##
## Arguments:
##      x - the x position inside of the canvas to place as the target location
##      y - the y position inside of the canvas to place as the target location
##
## Returns: Nothing
##
proc ::gui::placeTarget {x y} {
    variable windows

    ##
    ## Get the canvas widget
    ##
    set c $windows(dataWidget)

    if {[llength [$c gettags target]]} {
        ##
        ## Target icon already exists, so move it
        ##
        $c coords target $x $y
    } elseif {[llength [$c gettags player]]} {
        ##
        ## Target icon does not exist, but player does -- so create it and start
        ## game if the game has not been started. NOTE -- the target icon could
        ## not be existing although the game is in progress because the player
        ## went down a hole.
        ##
        $c create image [list $x $y] -anchor c -tags target -image target
        $c lower target player
        if {![llength [after info]] && ($::calculated(timeLeft) > 0.0)} {
            doResumeSimulation
        }
    }
}

Changes to lib/loadPerferences.tcl.

1
2











3
4
5
6
7
8
9
..
11
12
13
14
15
16
17









18
19
20
21
22
23
24
..
27
28
29
30
31
32
33









34
35
36
37
38
39
40
41
42
43
44
45
package require inifile












proc loadPerferences {} {
    variable settings

    if {[file exists ~/.KillerBots/perferences.ini]} {
        set ifd [::ini::open ~/.KillerBots/perferences.ini r]
        array set settings [::ini::get $ifd Settings]
        ::ini::close $ifd
................................................................................
    } else {
        intializePerferences
        savePerferences
    }

}










proc savePerferences {} {
    variable settings

    if {![file exists ~/.KillerBots]} {
        file mkdir ~/.KillerBots
    }
     set ifd [::ini::open ~/.KillerBots/perferences.ini w]
................................................................................
    }
    array set settings [::ini::get $ifd Settings]
    ::ini::commit $ifd
    ::ini::close $ifd
    calculateSimulationConstants
}










proc intializePerferences {} {
    variable settings

    set images [lsort -dictionary [image names]]
    set settings(Robots) [lsearch -inline -glob $images {ROBOT_*}]
    set settings(Player) [lsearch -inline -glob $images {PERSON_*}]
    set settings(RefreshRate) 100
    set settings(Speed) 15.0
    set settings(Level) 1
    set settings(RobotsPerLevel) 5
    set settings(SecondsPerLevel) 120.0
}


>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>












1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
..
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
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
package require inifile

## Name: loadPerferences
##
## Description:
##      Load the saved preference if the perference file exists, otherwise
##      initialize them to their default values and save them off thus creating
##      the perference file.
##
## Arguments: None.
##
## Returns: Nothing
##
proc loadPerferences {} {
    variable settings

    if {[file exists ~/.KillerBots/perferences.ini]} {
        set ifd [::ini::open ~/.KillerBots/perferences.ini r]
        array set settings [::ini::get $ifd Settings]
        ::ini::close $ifd
................................................................................
    } else {
        intializePerferences
        savePerferences
    }

}

## Name: savePerferences
##
## Description:
##      Save the current perference setting to the perference file.
##
## Arguments: None.
##
## Returns: Nothing
##
proc savePerferences {} {
    variable settings

    if {![file exists ~/.KillerBots]} {
        file mkdir ~/.KillerBots
    }
     set ifd [::ini::open ~/.KillerBots/perferences.ini w]
................................................................................
    }
    array set settings [::ini::get $ifd Settings]
    ::ini::commit $ifd
    ::ini::close $ifd
    calculateSimulationConstants
}

## Name: intializePerferences
##
## Description:
##      Set the perference settings to their default values.
##
## Arguments: None.
##
## Returns: Nothing
##
proc intializePerferences {} {
    variable settings

    set images [lsort -dictionary [image names]]
    set settings(Robots) [lsearch -inline -glob $images {ROBOT_*}]
    set settings(Player) [lsearch -inline -glob $images {PERSON_*}]
    set settings(RefreshRate) 100
    set settings(Speed) 15.0
    set settings(Level) 1
    set settings(RobotsPerLevel) 5
    set settings(SecondsPerLevel) 120.0
}

Changes to lib/simulation.tcl.

1
2









3
4
5
6
7
8
9
..
45
46
47
48
49
50
51









52
53
54
55
56
57









58
59
60
61
62
63
64
...
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
...
149
150
151
152
153
154
155










156
157
158
159
160
161
162
...
168
169
170
171
172
173
174











175
176
177
178
179
180
181
...
212
213
214
215
216
217
218












219
220
221
222
223
224
225
...
229
230
231
232
233
234
235












236
237
238
239
240
241
242
...
264
265
266
267
268
269
270









271
272
273
274
275
276
277
278
279
280









281
282
283
284
285
286
287
288
package require math::geometry










proc doSimulationStep {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set calculated(timeLeft) [expr {max($calculated(timeLeft) - $calculated(deltaSecondsPerStep), 0.0)}]
    if {$calculated(timeLeft) <= 0.0} {
................................................................................
            -icon info \
            -parent $::gui::windows(main) \
            -title [::msgcat::mc GameOver,Title] \
            -type ok
    }
}










proc pauseSimulation {} {
    foreach id [after info] {
        after cancel $id
    }
}










proc initializeSimulation {} {
    variable settings
    variable ::gui::windows
    variable calculated

    set c $windows(dataWidget)

................................................................................
    ## Set the calculated information so we do not have to do so every simulation step
    ##
    calculateSimulationConstants

    return;
}











proc calculateSimulationConstants {} {
    variable settings
    variable calculated

    set calculated(distancePerStep) [expr {(1.0* $settings(Speed)) / $settings(RefreshRate)}]
    set calculated(afterDelay) [expr {int(1000.0 / $settings(RefreshRate))}]
    set calculated(deltaSecondsPerStep) [expr {1.0 / $settings(RefreshRate)}]

    return;
}











proc overlaps {item} {
    variable ::gui::windows

    set c $windows(dataWidget)

    return [expr {[llength [$c find overlapping {*}[$c bbox $item]]] != 1}]
}











proc getOverlaps {item} {
    variable ::gui::windows

    set c $windows(dataWidget)
    set returnDict [dict create]

    set itemId [$c find withtag $item]
................................................................................
        set type [dict get $propertyDict type]
        dict lappend returnDict $type $propertyDict
    }

    return $returnDict
}











proc getItemDict {item} {
    variable ::gui::windows

    set c $windows(dataWidget)

    set itemId [$c find withtag $item]
    set propertyDict [dict create id $itemId]
................................................................................
            set type $tag
        }
    }
    dict set propertyDict type $type
    return $propertyDict
}












proc movePlayer {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)
    if {[info exists calculated(inTunnels)]} {
................................................................................
        lassign [$c coords player] x y
        $c coords player \
            [::tcl::mathfunc::max [::tcl::mathfunc::min $x 600] 40] \
            [::tcl::mathfunc::max [::tcl::mathfunc::min $y 600] 40]
    }
}













proc moveRobots {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)

................................................................................
        return -code error  [::msgcat::mc Survived]
    }
    foreach id $robotList {
        moveRobot $id
    }
}













proc moveRobot {id} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)
    if {[info exists calculated(inTunnels)]} {
................................................................................
        foreach waterDict [dict get $overlapList water] {
            set waterId [dict get $waterDict id]
            $c addtag charged withtag $waterId
        }
    }
}










proc doPauseSimulation {} {
    variable ::gui::windows

    set fileMenu $windows(main,fileMenu)
    $fileMenu entryconfigure $windows(main,fileMenu,pauseGame) -state disabled
    $fileMenu entryconfigure $windows(main,fileMenu,resumeGame) -state normal
    pauseSimulation

}










proc doResumeSimulation {} {
    variable ::gui::windows

    set fileMenu $windows(main,fileMenu)
    $fileMenu entryconfigure $windows(main,fileMenu,pauseGame) -state normal
    $fileMenu entryconfigure $windows(main,fileMenu,resumeGame) -state disabled
    doSimulationStep
}


>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>






>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>











>
>
>
>
>
>
>
>
>
>








>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>










>
>
>
>
>
>
>
>
>








1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..
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
...
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
...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
...
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
package require math::geometry

## Name: doSimulationStep
##
## Description:
##      Run a step in the simulator.
##
## Arguments: None.
##
## Returns: Nothing
##
proc doSimulationStep {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set calculated(timeLeft) [expr {max($calculated(timeLeft) - $calculated(deltaSecondsPerStep), 0.0)}]
    if {$calculated(timeLeft) <= 0.0} {
................................................................................
            -icon info \
            -parent $::gui::windows(main) \
            -title [::msgcat::mc GameOver,Title] \
            -type ok
    }
}

## Name: pauseSimulation
##
## Description:
##      Pause the simulation.
##
## Arguments: None.
##
## Returns: Nothing
##
proc pauseSimulation {} {
    foreach id [after info] {
        after cancel $id
    }
}

## Name: initializeSimulation
##
## Description:
##      Initialize the simulation.
##
## Arguments: None.
##
## Returns: Nothing
##
proc initializeSimulation {} {
    variable settings
    variable ::gui::windows
    variable calculated

    set c $windows(dataWidget)

................................................................................
    ## Set the calculated information so we do not have to do so every simulation step
    ##
    calculateSimulationConstants

    return;
}

## Name: calculateSimulationConstants
##
## Description:
##      Caluclate several constants from the perference settings.  This is done
##      to attempt to stream line the simulation step.
##
## Arguments: None.
##
## Returns: Nothing
##
proc calculateSimulationConstants {} {
    variable settings
    variable calculated

    set calculated(distancePerStep) [expr {(1.0* $settings(Speed)) / $settings(RefreshRate)}]
    set calculated(afterDelay) [expr {int(1000.0 / $settings(RefreshRate))}]
    set calculated(deltaSecondsPerStep) [expr {1.0 / $settings(RefreshRate)}]

    return;
}

## Name: overlaps
##
## Description:
##      Check to see if this item overlaps another.
##
## Arguments:
##      item - the item id.
##
## Returns: a boolean indicating if the item overlaps another item
##
proc overlaps {item} {
    variable ::gui::windows

    set c $windows(dataWidget)

    return [expr {[llength [$c find overlapping {*}[$c bbox $item]]] != 1}]
}

## Name: getOverlaps
##
## Description:
##      Get all the items teh specified one overlaps.
##
## Arguments:
##      item - the item id.
##
## Returns: A dictionary of overlapping items
##
proc getOverlaps {item} {
    variable ::gui::windows

    set c $windows(dataWidget)
    set returnDict [dict create]

    set itemId [$c find withtag $item]
................................................................................
        set type [dict get $propertyDict type]
        dict lappend returnDict $type $propertyDict
    }

    return $returnDict
}

## Name: getItemDict
##
## Description:
##      Build a dictionary about the item from its tags.
##
## Arguments:
##      item - the item id.
##
## Returns: Dictionary of information about the item.
##
proc getItemDict {item} {
    variable ::gui::windows

    set c $windows(dataWidget)

    set itemId [$c find withtag $item]
    set propertyDict [dict create id $itemId]
................................................................................
            set type $tag
        }
    }
    dict set propertyDict type $type
    return $propertyDict
}

## Name: movePlayer
##
## Description:
##      Move the player toward its target.
##
##      NOTE -- an error is raised if the player is killed.
##
## Arguments: None.
##
## Returns: Nothing
##
proc movePlayer {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)
    if {[info exists calculated(inTunnels)]} {
................................................................................
        lassign [$c coords player] x y
        $c coords player \
            [::tcl::mathfunc::max [::tcl::mathfunc::min $x 600] 40] \
            [::tcl::mathfunc::max [::tcl::mathfunc::min $y 600] 40]
    }
}

## Name: moveRobots
##
## Description:
##      Move all of the remaining robots.
##
##      NOTE -- returns an error if all robots are dead, indicating that the
##              player survived.
##
## Arguments: None.
##
## Returns: Nothing
##
proc moveRobots {} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)

................................................................................
        return -code error  [::msgcat::mc Survived]
    }
    foreach id $robotList {
        moveRobot $id
    }
}

## Name: moveRobot
##
## Description:
##      Move the specified Robot.
##
##      NOTE -- raises an error if the movement causes the player to die.
##
## Arguments:
##      id - the robot's ID.
##
## Returns: Nothing
##
proc moveRobot {id} {
    variable settings
    variable calculated
    variable ::gui::windows

    set c $windows(dataWidget)
    if {[info exists calculated(inTunnels)]} {
................................................................................
        foreach waterDict [dict get $overlapList water] {
            set waterId [dict get $waterDict id]
            $c addtag charged withtag $waterId
        }
    }
}

## Name: doPauseSimulation
##
## Description:
##      Callback to pause the simulation.
##
## Arguments: None.
##
## Returns: Nothing
##
proc doPauseSimulation {} {
    variable ::gui::windows

    set fileMenu $windows(main,fileMenu)
    $fileMenu entryconfigure $windows(main,fileMenu,pauseGame) -state disabled
    $fileMenu entryconfigure $windows(main,fileMenu,resumeGame) -state normal
    pauseSimulation

}

## Name: doResumeSimulation
##
## Description:
##      Callback to resume the simulation.
##
## Arguments: None.
##
## Returns: Nothing
##
proc doResumeSimulation {} {
    variable ::gui::windows

    set fileMenu $windows(main,fileMenu)
    $fileMenu entryconfigure $windows(main,fileMenu,pauseGame) -state normal
    $fileMenu entryconfigure $windows(main,fileMenu,resumeGame) -state disabled
    doSimulationStep
}