chiark / gitweb /
Variable fuel range for connectivity map and pathfinder.
[rocl] / elite.tcl
CommitLineData
1304202a 1#! /usr/bin/tclsh
b130b8f5 2#
3# $Id: elite.tcl,v 1.2 2003/02/25 00:25:38 mdw Exp $
1304202a 4
5package require "elite-bits" "1.0.0"
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 \
38 rich-ind ave-ind poor-ind mainly-ind \
39 mainly-agri rich-agri ave-agri poor-agri
40
41set products {
42 food "Food"
43 textiles "Textiles"
44 radioactives "Radioactives"
45 slaves "Slaves"
46 liquor-wines "Liquor & wines"
47 luxuries "Luxuries"
48 narcotics "Narcotics"
49 computers "Computers"
50 machinery "Machinery"
51 alloys "Alloys"
52 firearms "Firearms"
53 furs "Furs"
54 minerals "Minerals"
55 gold "Gold"
56 platinum "Platinum"
57 gem-stones "Gem-stones"
58 alien-items "Alien items"
59}
60
61foreach p $products { set unit($p) t }
62foreach p {gold platinum} { set unit($p) kg }
63set unit(gem-stones) g
64unset p
65
66# --- galaxy N [GAL] ---
67#
68# Compute the seed of the Nth galaxy, if GAL is the seed of galaxy 1. By
69# default, GAL is the standard galaxy 1 seed.
70
71proc galaxy [list n [list g $galaxy1]] {
72 for {set i 1} {$i < $n} {incr i} {
73 set g [elite-nextgalaxy $g]
74 }
75 return $g
76}
77
78# --- foreach-world GAL ARR SCRIPT ---
79#
80# For each world in galaxy GAL (a seed), set ARR to the world information
81# and evaluate SCRIPT. The usual loop control commands can be used in
82# SCRIPT.
83
84proc foreach-world {g p act} {
85 upvar 1 $p pp
86 for {set i 0} {$i < 256} {incr i; set g [elite-nextworld $g]} {
87 elite-worldinfo pp $g
88 uplevel 1 $act
89 }
90}
91
92# --- find-world GAL PAT ---
93#
94# Return a list of seeds for the worlds in galaxy GAL (a seed) whose names
95# match the glob pattern PAT.
96
97proc find-world {g pat} {
98 set l {}
99 foreach-world $g p {
100 if {[string match -nocase $pat $p(name)]} {
101 lappend l $p(seed)
102 }
103 }
104 return $l
105}
106
107# --- destructure PAT LIST ---
108#
109# Destrcture LIST according to PAT. If PAT is a single name, set the
110# variable PAT to LIST; otherwise, if PAT is a list, each of its elements
111# must correspond to an element of LIST, so recursively destructure the
112# corresponding elements of each. It is not an error if the PAT list is
113# shorter than LIST. The special variable name `.' indicates that no
114# assignment is to be made.
115
116proc destructure {pp xx} {
117 if {![string compare $pp "."]} {
118 return
119 } elseif {[llength $pp] == 0} {
120 return
121 } elseif {[llength $pp] == 1} {
122 upvar 1 $pp p
123 set p $xx
124 } else {
125 foreach p $pp x $xx {
126 uplevel 1 [list destructure $p $x]
127 }
128 }
129}
130
131# --- worldinfo GAL ---
132#
133# Return a list describing the worlds in galaxy GAL (a seed). The list
134# contains a group of three elements for each world: the seed, x and y
135# coordinates (in decilightyears).
136
137proc worldinfo {g} {
138 foreach-world $g p {
139 lappend i $p(seed) $p(x) $p(y)
140 }
141 return $i
142}
143
144# --- world-distance X Y XX YY ---
145#
146# Computes the correct game distance in decilightyears between two worlds,
147# one at X, Y and the other at XX, YY.
148
149proc world-distance {x y xx yy} {
150 set dx [expr {abs($x - $xx)/4}]
151 set dy [expr {abs($y - $yy)/4}]
152 return [expr {4 * floor(sqrt($dx * $dx + $dy * $dy))}]
153}
154
155# --- nearest-planet WW X Y ---
156#
157# Returns the seed of the `nearest' planet given in the worldinfo list WW to
158# the point X Y (in decilightyears).
159
160proc nearest-planet {ww x y} {
161 set min 10000
162 foreach {ss xx yy} $ww {
163 set dx [expr {abs($x - $xx)/4}]
164 set dy [expr {abs($y - $yy)/2}]
165 if {$dx > $dy} {
166 set d [expr {($dx * 2 + $dy)/2}]
167 } else {
168 set d [expr {($dx + $dy * 2)/2}]
169 }
170 if {$d < $min} {
171 set p $ss
172 set min $d
173 }
174 }
175 return $p
176}
177
178# --- adjacency WW ADJ [D] ---
179#
180# Fill in the array ADJ with the adjacency table for the worlds listed in the
181# worldinfo list WW. That is, for each world seed S, ADJ(S) is set to a
182# worldinfo list containing the worlds within D (default 70) decilightyears
183# of S.
184
185proc adjacency {p adj {d 70}} {
186 upvar 1 $adj a
187 array set a {}
188 foreach {s x y} $p {
189 set done($s) 1
190 lappend a($s)
191 foreach {ss xx yy} $p {
192 if {[info exists done($ss)]} { continue }
193 if {abs($x - $xx) > $d + 10 || abs($y - $yy) > $d + 10 ||
194 [world-distance $x $y $xx $yy] > $d} { continue }
195 lappend a($s) $ss $xx $yy
196 lappend a($ss) $s $x $y
197 }
198 }
199}
200
201# --- worldname W ---
202#
203# Returns the name of the world with seed W.
204
205proc worldname {w} {
206 elite-worldinfo p $w
207 return $p(name)
208}
209
210# --- shortest-path ADJ FROM TO WEIGHT ---
211#
212# Computes the shortest path and shortest distance between the worlds wose
213# seeds are FROM and TO respectively. ADJ must be an adjacency table for the
214# galaxy containing FROM and TO. WEIGHT is a command such that WEIGHT A B
215# returns the `distance' for the simple path between A and B. The return
216# value is a list P D, where D is the weight of the path found, and P is a
217# simple list of seeds for the worlds on the path. P starts with FROM and
218# ends with TO.
219
220proc shortest-path {adjvar from to weight} {
221 upvar 1 $adjvar adj
222 if {[string equal $from $to]} { return [list $to 0] }
223 set l($from) 0
224 set p($from) $from
225 set c $from
226 while 1 {
227 foreach {n x y} $adj($c) {
228 if {[info exists l($n)]} {
229 continue
230 }
231 set w [expr {$l($c) + [uplevel 1 $weight [list $c $n]]}]
232 if {![info exists ll($n)] || $w < $ll($n)} {
233 set ll($n) $w
234 set p($n) [concat $p($c) [list $n]]
235 }
236 }
237 set s [array startsearch ll]
238 if {![array anymore ll $s]} {
239 return {{} 0}
240 }
241 set c [array nextelement ll $s]
242 set w $ll($c)
243 while {[array anymore ll $s]} {
244 set n [array nextelement ll $s]
245 if {$ll($n) < $w} {
246 set c $n
247 set w $ll($n)
248 }
249 }
250 if {[string equal $c $to]} { return [list $p($to) $ll($to)] }
251 set l($c) $ll($c)
252 unset ll($c)
253 }
254}
255
256# --- weight-hops A B ---
257#
258# shortest-path weight function giving each hop the same weight.
259
260proc weight-hops {from to} {
261 return 1
262}
263
264# --- weight-fuel A B ---
265#
266# shortest-path weight function measuring the distance between FROM and TO.
267
268proc weight-fuel {from to} {
269 elite-worldinfo f $from
270 elite-worldinfo t $to
271 return [world-distance $f(x) $f(y) $t(x) $t(y)]
272}
273
274# --- weight-safety A B ---
275#
276# shortest-path weight function attempting to maximize safety of the journey
277# by giving high weight (square-law) to worlds with unstable governments.
278
279proc weight-safety {from to} {
280 elite-worldinfo t $to
281 set w [expr {8 - $t(government)}]
282 return [expr {$w * $w}]
283}
284
285# --- weight-encounters A B ---
286#
287# shortest-path weight function attempting to maximize encounters on the
288# journey by giving high weight (square law) to worlds with stable
289# governments.
290
291proc weight-encounters {from to} {
292 elite-worldinfo f $from
293 elite-worldinfo t $to
294 set w [expr {1 + $t(government)}]
295 return [expr {$w * $w}]
296}
297
298# --- weight-trading A B ---
299#
300# shortest-path weight function attempting to maximize trading opportunities
301# along the journey by giving high weight (square law) to pairs of worlds
302# with small differences between their economic statuses.
303
304proc weight-trading {from to} {
305 elite-worldinfo f $from
306 elite-worldinfo t $to
307 set w [expr {8 - abs($f(economy) - $t(economy))}]
308 return [expr {$w * $w}]
309}
310
311# --- parse-galaxy-spec G ---
312#
313# Parses a galaxy spec and returns a list containing a description of the
314# galaxy and the corresponding galaxy seed. A galaxy spec is one of:
315#
316# * a number between 1 and 8, corresponding to one of the standard
317# galaxies;
318#
319# * a 12-digit hex string, which is a galaxy seed (and is returned
320# unchanged); or
321#
322# * a string of the form S:N where S is a 12-hex-digit seed and N is a
323# galaxy number, corresponding to the Nth galaxy starting with S as
324# galaxy 1.
325#
326# If the string is unrecognized, an empty list is returned.
327
328proc parse-galaxy-spec {g} {
329 switch -regexp -- $g {
330 {^[1-8]$} { return [list $g [galaxy $g]] }
331 {^[0-9a-fA-F]{12}$} { return [list $g $g] }
332 default {
333 if {[regexp {^([0-9a-fA-F]{12}):([1-8])$} $g . b n]} {
334 return [list $g [galaxy $n $b]]
335 }
336 }
337 }
338 return {}
339}
340
341# --- parse-planet-spec G P ---
342#
343# Parses a planet spec and returns the planet seed. The planet spec P is
344# interpreted relative to galaxy G. A planet spec is one of:
345#
346# * a simple integer, corresponding to a planet number;
347#
348# * a 12-hex-digit seed, which is returned unchanged;
349#
350# * a pair of integers separated by commas, corresponding to the nearest
351# planet to those coordinates;
352#
353# * a glob pattern, corresponding to the lowest-numbered planet in the
354# galaxy whose name matches the pattern case-insensitively; or
355#
356# * a string of the form G.P where G is a galaxy spec and P is a planet
357# spec, corresponding to the planet specified by P relative to galaxy G.
358#
359# If the string is unrecognized, an empty string is returned.
360
361proc parse-planet-spec {g p} {
362 if {[regexp {^[0-9a-fA-F]{12}$} $p]} { return $p }
363 if {[regexp {^(.+)\.(.+)$} $p . g p]} {
364 set g [parse-galaxy-spec $g]
365 if {[string equal $g ""]} { return {} }
366 destructure {. g} $g
367 return [parse-planet-spec $g $p]
368 }
369 if {[regexp {^(0x[0-9a-fA-F]+|[0-9]+)$} $p]} {
370 for {set s $g; set i 0} {$i < $p} {incr i; set s [elite-nextworld $s]} {}
371 return $s
372 }
373 if {[regexp {^(0x[0-9a-fA-F]+|[0-9]+),\s*(0x[0-9a-fA-F]+|[0-9]+)$} \
374 $p . x y]} {
375 return [nearest-planet [worldinfo $g] $x $y]
376 }
377 set l [find-world $g $p]
378 if {[llength $l]} { return [lindex $l 0] }
379 return {}
380}
381
382# --- in-galaxy-p G PP ---
383#
384# Returns nonzero if the planets (seeds) listed in PP are in galaxy G.
385# Doesn't mind if the planet seeds are invalid.
386
387proc in-galaxy-p {g pp} {
388 foreach-world $g i { set x($i(seed)) 1 }
389 foreach p $pp { if {![info exists x($p)]} { return 0 } }
390 return 1
391}
392
393# --- world-summary PLANET ---
394#
395# Return a one-line summary string for PLANET.
396
397proc world-summary {s} {
398 global eco gov
399 elite-worldinfo p $s
400 return [format "%-12s %4d %4d %-11s %-10s %2d %s" \
401 $p(name) $p(x) $p(y) \
402 $eco($p(economy)) $gov($p(government)) $p(techlevel) $p(seed)]
403}
404
405#----- That's all, folks ----------------------------------------------------
406
407package provide "elite" "1.0.0"