chiark / gitweb /
infra: Add a copy of the GPL.
[rocl] / elite.tcl
CommitLineData
1304202a 1#! /usr/bin/tclsh
b130b8f5 2#
6b8df360 3# $Id$
1304202a 4
7da7c511 5package require "elite-bits" "1.0.1"
1304202a 6
7set galaxy1 "4a5a480253b7" ;# Seed for standard galaxy 1
8
9# --- tab ARR NAME NAME ... ---
10#
11# Construct an array mapping integers 0, 1, ... to the given NAMEs, in order.
12
13proc tab {arr args} {
14 upvar 1 $arr a
15 set i 0
16 foreach v $args {
17 set a($i) $v
18 incr i
19 }
20}
21
22# --- Various standard tables ---
23
24tab government \
25 "anarchy" "feudal" "multi-government" "dictatorship" \
26 "communist" "confederacy" "democracy" "corporate state"
27
28tab economy \
29 "rich industrial" "average industrial" "poor industrial" \
30 "mainly industrial" "mainly agricultural" "rich agricultural" \
31 "average agricultural" "poor agricultural"
32
33tab gov \
34 anarchy feudal multi-gov dictator \
35 communist confed democracy corp-state
36
37tab eco \
83b4563b 38 rich-ind avg-ind poor-ind mainly-ind \
39 mainly-agri rich-agri avg-agri poor-agri
1304202a 40
6b8df360 41tab gv Ay Fl MG Dp Ct Cy Dy CS
42tab ec RI AI PI MI MA RA AA PA
43
1304202a 44set products {
45 food "Food"
46 textiles "Textiles"
47 radioactives "Radioactives"
48 slaves "Slaves"
49 liquor-wines "Liquor & wines"
50 luxuries "Luxuries"
51 narcotics "Narcotics"
52 computers "Computers"
53 machinery "Machinery"
54 alloys "Alloys"
55 firearms "Firearms"
56 furs "Furs"
57 minerals "Minerals"
58 gold "Gold"
59 platinum "Platinum"
60 gem-stones "Gem-stones"
61 alien-items "Alien items"
62}
63
64foreach p $products { set unit($p) t }
65foreach p {gold platinum} { set unit($p) kg }
66set unit(gem-stones) g
67unset p
68
69# --- galaxy N [GAL] ---
70#
71# Compute the seed of the Nth galaxy, if GAL is the seed of galaxy 1. By
72# default, GAL is the standard galaxy 1 seed.
73
74proc galaxy [list n [list g $galaxy1]] {
75 for {set i 1} {$i < $n} {incr i} {
76 set g [elite-nextgalaxy $g]
77 }
78 return $g
79}
80
81# --- foreach-world GAL ARR SCRIPT ---
82#
83# For each world in galaxy GAL (a seed), set ARR to the world information
84# and evaluate SCRIPT. The usual loop control commands can be used in
85# SCRIPT.
86
87proc foreach-world {g p act} {
88 upvar 1 $p pp
89 for {set i 0} {$i < 256} {incr i; set g [elite-nextworld $g]} {
90 elite-worldinfo pp $g
91 uplevel 1 $act
92 }
93}
94
95# --- find-world GAL PAT ---
96#
97# Return a list of seeds for the worlds in galaxy GAL (a seed) whose names
98# match the glob pattern PAT.
99
100proc find-world {g pat} {
101 set l {}
102 foreach-world $g p {
103 if {[string match -nocase $pat $p(name)]} {
104 lappend l $p(seed)
105 }
106 }
107 return $l
108}
109
110# --- destructure PAT LIST ---
111#
112# Destrcture LIST according to PAT. If PAT is a single name, set the
113# variable PAT to LIST; otherwise, if PAT is a list, each of its elements
114# must correspond to an element of LIST, so recursively destructure the
115# corresponding elements of each. It is not an error if the PAT list is
116# shorter than LIST. The special variable name `.' indicates that no
117# assignment is to be made.
118
119proc destructure {pp xx} {
120 if {![string compare $pp "."]} {
121 return
122 } elseif {[llength $pp] == 0} {
123 return
124 } elseif {[llength $pp] == 1} {
125 upvar 1 $pp p
126 set p $xx
127 } else {
128 foreach p $pp x $xx {
129 uplevel 1 [list destructure $p $x]
130 }
131 }
132}
133
1ded87ba 134# --- write-file NAME CONTENTS [TRANS] ---
135#
136# Write file NAME, storing CONTENTS translated according to TRANS (default
137# `binary'. The write is safe against errors -- we don't destroy the old
138# data until the file is written.
139
140proc write-file {name contents {trans binary}} {
141 if {[file exists $name]} {
142 if {[set rc [catch { file copy -force $name "$name.old" } err]]} {
143 return -code $rc $err
144 }
145 }
146 if {[set rc [catch {
147 set f [open $name w]
148 fconfigure $f -translation $trans
149 puts -nonewline $f $contents
150 close $f
151 } err]]} {
152 catch { close $f }
153 catch { file rename -force "$name.old" $name }
154 return -code $rc $err
155 }
156 return ""
157}
158
159# --- read-file NAME [TRANS] ---
160#
161# Read the contents of the file NAME, translating it according to TRANS
162# (default `binary').
163
164proc read-file {name {trans binary}} {
165 set f [open $name]
166 fconfigure $f -translation $trans
167 set c [read $f]
168 close $f
169 return $c
170}
171
1304202a 172# --- nearest-planet WW X Y ---
173#
174# Returns the seed of the `nearest' planet given in the worldinfo list WW to
175# the point X Y (in decilightyears).
176
177proc nearest-planet {ww x y} {
178 set min 10000
179 foreach {ss xx yy} $ww {
180 set dx [expr {abs($x - $xx)/4}]
181 set dy [expr {abs($y - $yy)/2}]
182 if {$dx > $dy} {
183 set d [expr {($dx * 2 + $dy)/2}]
184 } else {
185 set d [expr {($dx + $dy * 2)/2}]
186 }
187 if {$d < $min} {
188 set p $ss
189 set min $d
190 }
191 }
192 return $p
193}
194
1304202a 195# --- worldname W ---
196#
197# Returns the name of the world with seed W.
198
199proc worldname {w} {
200 elite-worldinfo p $w
201 return $p(name)
202}
203
204# --- shortest-path ADJ FROM TO WEIGHT ---
205#
206# Computes the shortest path and shortest distance between the worlds wose
207# seeds are FROM and TO respectively. ADJ must be an adjacency table for the
208# galaxy containing FROM and TO. WEIGHT is a command such that WEIGHT A B
209# returns the `distance' for the simple path between A and B. The return
210# value is a list P D, where D is the weight of the path found, and P is a
211# simple list of seeds for the worlds on the path. P starts with FROM and
212# ends with TO.
213
214proc shortest-path {adjvar from to weight} {
215 upvar 1 $adjvar adj
216 if {[string equal $from $to]} { return [list $to 0] }
217 set l($from) 0
218 set p($from) $from
219 set c $from
220 while 1 {
221 foreach {n x y} $adj($c) {
222 if {[info exists l($n)]} {
223 continue
224 }
225 set w [expr {$l($c) + [uplevel 1 $weight [list $c $n]]}]
226 if {![info exists ll($n)] || $w < $ll($n)} {
227 set ll($n) $w
228 set p($n) [concat $p($c) [list $n]]
229 }
230 }
231 set s [array startsearch ll]
232 if {![array anymore ll $s]} {
233 return {{} 0}
234 }
235 set c [array nextelement ll $s]
236 set w $ll($c)
237 while {[array anymore ll $s]} {
238 set n [array nextelement ll $s]
239 if {$ll($n) < $w} {
240 set c $n
241 set w $ll($n)
242 }
243 }
244 if {[string equal $c $to]} { return [list $p($to) $ll($to)] }
245 set l($c) $ll($c)
246 unset ll($c)
247 }
248}
249
250# --- weight-hops A B ---
251#
252# shortest-path weight function giving each hop the same weight.
253
254proc weight-hops {from to} {
255 return 1
256}
257
258# --- weight-fuel A B ---
259#
260# shortest-path weight function measuring the distance between FROM and TO.
261
262proc weight-fuel {from to} {
263 elite-worldinfo f $from
264 elite-worldinfo t $to
7da7c511 265 return [elite-distance $f(x) $f(y) $t(x) $t(y)]
1304202a 266}
267
268# --- weight-safety A B ---
269#
270# shortest-path weight function attempting to maximize safety of the journey
271# by giving high weight (square-law) to worlds with unstable governments.
272
273proc weight-safety {from to} {
274 elite-worldinfo t $to
275 set w [expr {8 - $t(government)}]
276 return [expr {$w * $w}]
277}
278
279# --- weight-encounters A B ---
280#
281# shortest-path weight function attempting to maximize encounters on the
282# journey by giving high weight (square law) to worlds with stable
283# governments.
284
285proc weight-encounters {from to} {
286 elite-worldinfo f $from
287 elite-worldinfo t $to
288 set w [expr {1 + $t(government)}]
289 return [expr {$w * $w}]
290}
291
292# --- weight-trading A B ---
293#
294# shortest-path weight function attempting to maximize trading opportunities
295# along the journey by giving high weight (square law) to pairs of worlds
296# with small differences between their economic statuses.
297
298proc weight-trading {from to} {
299 elite-worldinfo f $from
300 elite-worldinfo t $to
301 set w [expr {8 - abs($f(economy) - $t(economy))}]
302 return [expr {$w * $w}]
303}
304
305# --- parse-galaxy-spec G ---
306#
307# Parses a galaxy spec and returns a list containing a description of the
308# galaxy and the corresponding galaxy seed. A galaxy spec is one of:
309#
310# * a number between 1 and 8, corresponding to one of the standard
311# galaxies;
312#
313# * a 12-digit hex string, which is a galaxy seed (and is returned
314# unchanged); or
315#
316# * a string of the form S:N where S is a 12-hex-digit seed and N is a
317# galaxy number, corresponding to the Nth galaxy starting with S as
318# galaxy 1.
319#
320# If the string is unrecognized, an empty list is returned.
321
322proc parse-galaxy-spec {g} {
323 switch -regexp -- $g {
324 {^[1-8]$} { return [list $g [galaxy $g]] }
325 {^[0-9a-fA-F]{12}$} { return [list $g $g] }
326 default {
327 if {[regexp {^([0-9a-fA-F]{12}):([1-8])$} $g . b n]} {
328 return [list $g [galaxy $n $b]]
329 }
330 }
331 }
332 return {}
333}
334
335# --- parse-planet-spec G P ---
336#
337# Parses a planet spec and returns the planet seed. The planet spec P is
338# interpreted relative to galaxy G. A planet spec is one of:
339#
340# * a simple integer, corresponding to a planet number;
341#
342# * a 12-hex-digit seed, which is returned unchanged;
343#
344# * a pair of integers separated by commas, corresponding to the nearest
345# planet to those coordinates;
346#
347# * a glob pattern, corresponding to the lowest-numbered planet in the
348# galaxy whose name matches the pattern case-insensitively; or
349#
350# * a string of the form G.P where G is a galaxy spec and P is a planet
351# spec, corresponding to the planet specified by P relative to galaxy G.
352#
353# If the string is unrecognized, an empty string is returned.
354
355proc parse-planet-spec {g p} {
356 if {[regexp {^[0-9a-fA-F]{12}$} $p]} { return $p }
357 if {[regexp {^(.+)\.(.+)$} $p . g p]} {
358 set g [parse-galaxy-spec $g]
359 if {[string equal $g ""]} { return {} }
360 destructure {. g} $g
361 return [parse-planet-spec $g $p]
362 }
363 if {[regexp {^(0x[0-9a-fA-F]+|[0-9]+)$} $p]} {
364 for {set s $g; set i 0} {$i < $p} {incr i; set s [elite-nextworld $s]} {}
365 return $s
366 }
367 if {[regexp {^(0x[0-9a-fA-F]+|[0-9]+),\s*(0x[0-9a-fA-F]+|[0-9]+)$} \
368 $p . x y]} {
7da7c511 369 return [nearest-planet [elite-galaxylist $g] $x $y]
370 }
371 if {[regexp {^([^/]*)(?:/([1-9]\d*))?$} $p . p i]} {
372 if {[string equal $i ""]} { set i 1 }
373 set l [find-world $g $p]
374 if {$i <= [llength $l]} { return [lindex $l [expr {$i - 1}]] }
1304202a 375 }
1304202a 376 return {}
377}
378
379# --- in-galaxy-p G PP ---
380#
381# Returns nonzero if the planets (seeds) listed in PP are in galaxy G.
382# Doesn't mind if the planet seeds are invalid.
383
384proc in-galaxy-p {g pp} {
385 foreach-world $g i { set x($i(seed)) 1 }
386 foreach p $pp { if {![info exists x($p)]} { return 0 } }
387 return 1
388}
389
390# --- world-summary PLANET ---
391#
392# Return a one-line summary string for PLANET.
393
7da7c511 394proc world-summary {s {ind 0} {spc 0}} {
1304202a 395 global eco gov
396 elite-worldinfo p $s
7da7c511 397 set is [string repeat " " $ind]
398 set ss [string repeat " " $spc]
399 return [format "%s%-8s%s %4d %4d %-11s %-10s %2d %s" \
400 $is $p(name) $ss $p(x) $p(y) \
1304202a 401 $eco($p(economy)) $gov($p(government)) $p(techlevel) $p(seed)]
402}
403
6b8df360 404# --- world-brief PLANET ---
405#
406# Return a very brief summary string for PLANET.
407
408proc world-brief {s} {
409 global gv ec
410 elite-worldinfo p $s
411 return [format "%-8s (%s, %s, %2d)" \
412 $p(name) $ec($p(economy)) $gv($p(government)) $p(techlevel)]
413}
414
1ded87ba 415# --- jameson ARR ---
416#
417# Fill ARR with the information about commander JAMESON.
418
419proc jameson {arr} {
420 global galaxy1 products
421 upvar 1 $arr cmdr
422 array set cmdr {
423 mission 0
424 credits 1000
425 fuel 70
426 gal-number 1
427 front-laser 0x0f
428 rear-laser 0
429 left-laser 0
430 right-laser 0
431 cargo 20
432 missiles 3
433 legal-status 0
434 score 0
435 market-fluc 0
436 }
437 set cmdr(gal-seed) $galaxy1
438 foreach i {
439 ecm fuel-scoop energy-bomb energy-unit docking-computer
440 gal-hyperdrive escape-pod
441 } { set cmdr($i) 0 }
442 elite-worldinfo lave [find-world $galaxy1 "Lave"]
443 set cmdr(world-x) [expr {$lave(x)/4}]
444 set cmdr(world-y) [expr {$lave(y)/2}]
445 elite-market mkt $lave(seed) 0
446 foreach {t n} $products {
447 destructure [list . cmdr(station-$t)] $mkt($t)
448 set cmdr(hold-$t) 0
449 }
450 set cmdr(station-alien-items) 0
451}
452
1304202a 453#----- That's all, folks ----------------------------------------------------
454
7da7c511 455package provide "elite" "1.0.1"