librarygames/stargazer/Sky.xtl

Stargazer's rules and its sky, a library: stargazer.xtl (scripted), play.xtl (the game: a sky you click) and the web page all use these. The data is in two TOML files, read with []L_IST and []T_ABLE: assets/cache/sky.toml (the Bright Star Catalog's stars and the IAU's named stars, fetched by assets/fetch.sh, not tracked) and stars.toml (the parts of the sky, the brightness bands, stars of your own). The sky is SVG written here, with the pieces of lib/Svg.xtl.

Sky units are hundredths of a degree: x = 100 (360 - ra), so right ascension grows to the left as on the sky seen from Earth, and y = 100 (90 - dec) south from the north pole. A game's state: the part of the sky (0 all of it), the star whose menu is open (0 none), the score, the number of stars n, the round's random number, then the n stars, the n results (0 not yet, 1 right, 2 wrong), then the last star answered and the star chosen for it.

source · imports t: lib/Text.xtl; sv: lib/Svg.xtl

The data

named : Box Char

value (private) · line 24

The named stars, then any of your own: name, ra, dec, magnitude and constellation, as texts.

named ← (("assets/cache/sky.toml" "star") ⎕T̲ABLE ("stars" "fields")) c̲at ("stars.toml" "star") ⎕T̲ABLE ("stars" "fields")
Used in: names, ra, dec, mag, ˡs̲ay

names : Box Char

value (private) · line 25
names ← 1 s̲elect₂ named
Used in: ˡp̲ool, ˡn̲ame

ra : Float

value (private) · line 26
ra ← 'ˢᵛn̲um e̲ach 2 s̲elect₂ named

dec : Float

value (private) · line 27
dec ← 'ˢᵛn̲um e̲ach 3 s̲elect₂ named
Used in: ˡp̲ool, ˡa̲t

mag : Float

value (private) · line 28
mag ← 'ˢᵛn̲um e̲ach 4 s̲elect₂ named
Used in: band

ʰs̲econd : Box Char -> Float

function (private) · line 30

The second and third numbers in a text (h:, private to this file).

ʰs̲econd ← { x → 2 s̲elect n̲umbers d̲isclose x }
Used in: edges, sy

ʰt̲hird : Box Char -> Float

function (private) · line 31
ʰt̲hird ← { x → 3 s̲elect n̲umbers d̲isclose x }
Used in: class

parts : Box Char

value (private) · line 33

The parts of the sky: name, ra0, ra1, dec0, dec1.

parts ← ("stars.toml" "area") ⎕T̲ABLE ("areas" "areafields")
Used in: ra0, ra1, dec0, dec1, ˡp̲arts

ra0 : Float

value (private) · line 34
ra0 ← 'ˢᵛn̲um e̲ach 2 s̲elect₂ parts
Used in: ʰi̲nRa, ˡv̲iew

ra1 : Float

value (private) · line 35
ra1 ← 'ˢᵛn̲um e̲ach 3 s̲elect₂ parts
Used in: ʰi̲nRa, ˡv̲iew

dec0 : Float

value (private) · line 36
dec0 ← 'ˢᵛn̲um e̲ach 4 s̲elect₂ parts
Used in: ˡp̲ool, ˡv̲iew

dec1 : Float

value (private) · line 37
dec1 ← 'ˢᵛn̲um e̲ach 5 s̲elect₂ parts
Used in: ˡp̲ool, ˡv̲iew

edges : Float

value (private) · line 40

The brightness bands' upper edges, each star's band (1 the brightest), and how many of each band a round takes.

edges ← 'ʰs̲econd e̲ach "stars.toml" ⎕L̲IST "bands"
Used in: band

band : Int

value (private) · line 41
band ← 1 + '+ r̲/₂ 0 + mag '> t̲able -1 d̲rop edges
Used in: ʰo̲ne, ˡn̲ew

mix : Int

value (private) · line 42
mix ← f̲loor 'ˢᵛn̲um e̲ach "stars.toml" ⎕L̲IST "mix"

sky : Box Char

value (private) · line 46

The sky behind: every catalog star to magnitude 5.5, as paths of points (one per brightness class, a round dot of a fixed size on the screen at every zoom), joined once.

sky ← "assets/cache/sky.toml" ⎕L̲IST "sky"
Used in: sx, sy, class

sx : Float

value (private) · line 47
sx ← 100 × 360 − 'ˢᵛn̲um e̲ach sky
Used in: ʰp̲oints

sy : Float

value (private) · line 48
sy ← 100 × 90 − 'ʰs̲econd e̲ach sky
Used in: ʰp̲oints

class : Int

value (private) · line 49
class ← 1 + '+ r̲/₂ 0 + ('ʰt̲hird e̲ach sky) '> t̲able 1.0 2.0 3.0 4.0
Used in: ʰp̲oints

ʰp̲oints : Int -> Char

function (private) · line 50
ʰp̲oints ← { k → ᵗj̲oin '{ j → e̲nclose "M" c̲at (ˢᵛw̲hole j s̲elect sx) c̲at " " c̲at (ˢᵛw̲hole j s̲elect sy) c̲at "h0" } e̲ach w̲here class = k }
Used in: layer

ʰw̲idth : Int -> Char

function (private) · line 51
ʰw̲idth ← { k → d̲isclose k s̲elect "7" "5.5" "4" "2.8" "1.8" }
Used in: layer

layer : Char

value (private) · line 52
layer ← ᵗj̲oin '{ k → e̲nclose @ f̲ormat< "<path d=\"{h:p_oints k}\" stroke-width=\"{h:w_idth k}\" vector-effect=\"non-scaling-stroke\"/>" } e̲ach r̲ange 5
f̲ormat< expands to
("<path d=\"" c̲at (f̲ormat (ʰp̲oints k)) c̲at "\" stroke-width=\"" c̲at (f̲ormat (ʰw̲idth k)) c̲at "\" vector-effect=\"non-scaling-stroke\"/>")
Used in: ˡd̲raw

Where things are

ˡs̲x : Num a => a -> a

function · line 58

Sky x of right ascensions, and sky y of declinations (sky units).

ˡs̲x ← { r → 100 × 360 − r }
Used in: ˡv̲iew, ˡa̲t

ˡs̲y : Num a => a -> a

function · line 61

Sky y of declinations (sky units): hundredths of a degree south of the north pole.

ˡs̲y ← { d → 100 × 90 − d }
Used in: ˡv̲iew, ˡa̲t

ʰi̲nRa : Truthy a => Int -> a

function (private) · line 64

Which named stars lie in part a's right ascensions (through 0h when ra0 is above ra1).

ʰi̲nRa ← { a →
  (a s̲elect ra0) ≤ a s̲elect ra1 ? (ra ≥ a s̲elect ra0) ∧ ra < a s̲elect ra1
  (ra ≥ a s̲elect ra0) ∨ ra < a s̲elect ra1
}
Used in: ˡp̲ool

ˡp̲ool : Int -> Int

function · line 70

The named stars in part a of the sky (0 all of it), by number.

ˡp̲ool ← { a →
  0 = a ? r̲ange t̲ally names
  w̲here (ʰi̲nRa a) ∧ (dec ≥ a s̲elect dec0) ∧ dec ≤ a s̲elect dec1
}

ˡp̲arts : Unit -> Box Char

function · line 76

The parts of the sky, by name, in the menu's order.

ˡp̲arts ← { @ → 1 s̲elect₂ parts }

ˡn̲ame : Int -> Char

function · line 79

Star i's name.

ˡn̲ame ← { i → d̲isclose i s̲elect names }

ˡv̲iew : Int -> Float

function · line 85

What the sky shows for part a: x, y, width and height (sky units), in the shape of a picture (5 by 2) with room at the top for the buttons; the whole sky (a 0) a little wider than one turn, drawn again on both sides.

ˡv̲iew ← { a →
  0 = a ? -4500.0 -1000.0 45000.0 18000.0
  r1 ← (a s̲elect ra1) + 360 × f̲loat (a s̲elect ra0) > a s̲elect ra1
  w ← 100 × r1 − a s̲elect ra0
  h ← 100 × (a s̲elect dec1) − a s̲elect dec0
  cx ← (ˡs̲x r1) + w ÷ 2
  cy ← (ˡs̲y a s̲elect dec1) + h ÷ 2
  w ← w m̲ax 2.5 × h
  h ← w ÷ 2.5
  (cx − 0.6 × w) c̲at (cy − 0.7 × h) c̲at (1.2 × w) c̲at 1.2 × h
}

ˡa̲t : Int -> Int -> Float

function · line 99

Star i's point in part a's view: x and y, x moved by a whole turn when that brings it nearer the middle of the view (the sky wraps at 0h).

ˡa̲t ← { a i →
  v ← ˡv̲iew a
  x ← ˡs̲x i s̲elect ra
  mid ← (f̲irst v) + 0.5 × 3 s̲elect v
  (x − 36000.0 × f̲loat f̲loor 0.5 + (x − mid) ÷ 36000) c̲at ˡs̲y i s̲elect dec
}

ˡc̲hoices : Int -> Int -> Int

function · line 109

Star i's choices, with the round's random number q: it and one star from each brightness band (another star, from anywhere in the sky, picked by q and i), in the order of right ascension.

ˡc̲hoices ← { q i →
  c ← i c̲at '{ b → q ʰo̲ne i c̲at b } e̲ach r̲ange t̲ally mix
  (g̲rade c s̲elect n̲eg ra) s̲elect c
}

ʰo̲ne : Int -> Int -> Int

function (private) · line 115

Band b's star for star i: a star of that band other than i, at a place fixed by q, i and b.

ʰo̲ne ← { q ib →
  l ← w̲here band = 2 s̲elect ib
  l ← (l ≠ f̲irst ib) r̲eplicate l
  (1 + (q + (7919 × f̲irst ib) + 104729 × 2 s̲elect ib) m̲od t̲ally l) s̲elect l
}
Used in: ˡc̲hoices

The game

ʰu̲pTo : Int -> a -> a

function (private) · line 125

The first n items of v, or all of them when v has fewer (a band with no star in a part of the sky: the round is filled from the rest).

ʰu̲pTo ← { n v → (n m̲in t̲ally v) t̲ake v }
Used in: ˡn̲ew

ˡn̲ew : Int -> Int

function · line 131

A new round in part a of the sky (0 all of it): its named stars in a random order, mix of each band taken in turn (bright, middling, faint), filled up from the rest when a band has too few; five, or all of them when the part has fewer.

ˡn̲ew ← { a →
  p ← ˡp̲ool a
  o ← (g̲rade r̲oll! (t̲ally p) r̲eshape 1000000) s̲elect p
  b ← o s̲elect band
  pick ← d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at d̲isclose y } r̲/ '{ k → (k s̲elect mix) ʰu̲pTo (b = k) r̲eplicate o } m̲ap r̲ange t̲ally mix
  n ← 5 m̲in t̲ally p
  stars ← n t̲ake pick c̲at (n̲ot o m̲ember? pick) r̲eplicate o
  a c̲at 0 0 c̲at n c̲at (r̲oll! 1000000) c̲at stars c̲at (n r̲eshape 0) c̲at 0 0
}

ˡs̲core : a -> a

function · line 142

The round's score.

ˡs̲core ← { s → 3 s̲elect s }

ˡm̲arks : Int -> Int

function · line 145

The round's stars, and each one's result (0 not yet, 1 right, 2 wrong).

ˡm̲arks ← { s → (4 s̲elect s) t̲ake 5 d̲rop s }

ˡr̲esults : Int -> Int

function · line 148

Each star's result in the round: 0 not yet, 1 right, 2 wrong.

ˡr̲esults ← { s → (4 s̲elect s) t̲ake (5 + 4 s̲elect s) d̲rop s }

ˡs̲tatus : (Num a, Truthy a) => Int -> a

function · line 151

0 playing, 1 when every star is answered.

ˡs̲tatus ← { s → 0 + 0 = '+ r̲/ 0 + 0 = ˡr̲esults s }
Used in: ʰr̲oundOver

ˡo̲ffered : Int -> Int

function · line 154

The open star's choices.

ˡo̲ffered ← { s → (5 s̲elect s) ˡc̲hoices (2 s̲elect s) s̲elect ˡm̲arks s }

ˡm̲enu : Int -> Float

function · line 157

The open star's menu: four rectangles beside it (Svg's menu).

ˡm̲enu ← { s → (ˡv̲iew f̲irst s) ˢᵛm̲enu (f̲irst s) ˡa̲t (2 s̲elect s) s̲elect ˡm̲arks s }
Used in: ʰo̲pen, ʰc̲hoose, b

ʰx̲s : Int -> Float

function (private) · line 159

The marked stars' x and y in their view.

ʰx̲s ← { s → '{ i → f̲irst (f̲irst s) ˡa̲t i } e̲ach ˡm̲arks s }

ʰy̲s : Int -> Float

function (private) · line 160
ʰy̲s ← { s → '{ i → 2 s̲elect (f̲irst s) ˡa̲t i } e̲ach ˡm̲arks s }

ʰs̲tarAt : Int -> Float -> Int

function (private) · line 163

Which unanswered star is at xy: the nearest within reach, 1 to n, 0 for none.

ʰs̲tarAt ← { s xy →
  u ← ˢᵛu̲nit ˡv̲iew f̲irst s
  dx ← (ʰx̲s s) − f̲irst xy
  dy ← (ʰy̲s s) − 2 s̲elect xy
  d2 ← (dx × dx) + dy × dy
  ok ← (d2 ≤ (u × 16) × u × 16) ∧ 0 = ˡr̲esults s
  0 = '+ r̲/ 0 + ok ? 0
  f̲irst g̲rade d2 + 1e12 × f̲loat n̲ot ok
}

ˡc̲lick : Int -> Float -> Int

function · line 176

A click at xy (sky units): a part's button starts a round there; in an open menu a choice answers its star; a star not yet answered opens its menu; anywhere else closes the menu.

ˡc̲lick ← { s xy →
  b ← ((ˡv̲iew f̲irst s) ˢᵛb̲uttons 1 + t̲ally ˡp̲arts @) ˢᵛh̲it xy
  b > 0 ? ˡn̲ew b − 1
  (2 s̲elect s) > 0 ? s ʰo̲pen xy
  (2 c̲at s ʰs̲tarAt xy) ʰs̲et s
}
Used in: ᵘl̲oop, t

ʰo̲pen : Int -> Float -> Int

function (private) · line 184

A click while a menu is open: a choice answers; elsewhere, another star's menu opens or the menu closes.

ʰo̲pen ← { s xy →
  k ← (ˡm̲enu s) ˢᵛh̲it xy
  k > 0 ? s ʰa̲nswer k
  (2 c̲at s ʰs̲tarAt xy) ʰs̲et s
}
Used in: ˡc̲lick

ʰs̲et : Int -> Int -> Int

function (private) · line 190

The state with item k (from 1) replaced by v: kv is k v.

ʰs̲et ← { kv s → s + ((2 s̲elect kv) − (f̲irst kv) s̲elect s) × (f̲irst kv) = r̲ange t̲ally s }

ʰa̲nswer : Int -> Int -> Int

function (private) · line 192

Choice k of the open star: right or wrong, the score, the menu closed.

ʰa̲nswer ← { s k →
  d ← 2 s̲elect s
  i ← d s̲elect ˡm̲arks s
  pick ← k s̲elect ˡo̲ffered s
  right ← pick = i
  n ← 4 s̲elect s
  t ← (5 + n + d) c̲at 2 − right
  s ← (2 c̲at 0) ʰs̲et (3 c̲at (3 s̲elect s) + right) ʰs̲et t ʰs̲et s
  (-2 d̲rop s) c̲at d c̲at pick
}
Used in: ʰo̲pen

What the game says and draws

ʰr̲oundOver : Int -> Char

function (private) · line 206

The end of a round, said after its last answer (else nothing).

ʰr̲oundOver ← { s →
  0 = ˡs̲tatus s ? ""
  @ f̲ormat< " Round over: {l:s_core s} of {4 s_elect s}. Choose a part of the sky for another round."
}
f̲ormat< expands to
" Round over: " c̲at (f̲ormat (ˡs̲core s)) c̲at " of " c̲at (f̲ormat (4 s̲elect s)) c̲at ". Choose a part of the sky for another round."
Used in: ˡs̲ay

ˡs̲ay : Int -> Char

function · line 213

What has happened, in a line: the open menu, the last answer, the round's end.

ˡs̲ay ← { s →
  (2 s̲elect s) > 0 ? "Which star is this?"
  d ← ((t̲ally s) − 1) s̲elect s
  last ← (1 + d) s̲elect 0 c̲at ˡr̲esults s
  note ← ʰr̲oundOver s
  0 = d ? "Click a ringed star: which is it?"
  i ← d s̲elect ˡm̲arks s
  1 = last ? @ f̲ormat< "Right: {l:n_ame i}, in {d_isclose i s_elect 5 s_elect_2 named}.{note}"
  @ f̲ormat< "No: that is {l:n_ame i}, not {l:n_ame (t_ally s) s_elect s}.{note}"
}
f̲ormat< expands to
("Right: " c̲at (f̲ormat (ˡn̲ame i)) c̲at ", in " c̲at (f̲ormat (d̲isclose i s̲elect 5 s̲elect₂ named)) c̲at "." c̲at (f̲ormat (note)))
f̲ormat< expands to
"No: that is " c̲at (f̲ormat (ˡn̲ame i)) c̲at ", not " c̲at (f̲ormat (ˡn̲ame (t̲ally s) s̲elect s)) c̲at "." c̲at (f̲ormat (note))

ʰr̲ingColor : Int -> Char

function (private) · line 225

A ring's color by its result (0 not yet, red; 1 right, green; 2 wrong, orange).

ʰr̲ingColor ← { r → d̲isclose (1 + r) s̲elect "#ff4d4d" "#3ddc84" "#ffa630" }
Used in: ʰr̲ings

ʰr̲ings : Int -> Float -> Box Char

function (private) · line 228

The round's stars: a ring around each (its color its result), each answered one named.

ʰr̲ings ← { s u →
  x ← ʰx̲s s
  y ← ʰy̲s s
  r ← ˡr̲esults s
  rings ← '{ k → e̲nclose @ f̲ormat< "<circle class=\"dot\" cx=\"{sv:w_hole k s_elect x}\" cy=\"{sv:w_hole k s_elect y}\" r=\"{sv:w_hole u * 13}\" fill=\"none\" stroke=\"{h:r_ingColor k s_elect r}\" stroke-width=\"{sv:w_hole u * 3}\"/>" } e̲ach r̲ange t̲ally x
  rings c̲at '{ k → (((k s̲elect x) + u × 16) c̲at ((k s̲elect y) + u × 6) c̲at u × 18) ˢᵛw̲ords ˡn̲ame k s̲elect ˡm̲arks s } e̲ach w̲here r > 0
}
f̲ormat< expands to
("<circle class=\"dot\" cx=\"" c̲at (f̲ormat (ˢᵛw̲hole k s̲elect x)) c̲at "\" cy=\"" c̲at (f̲ormat (ˢᵛw̲hole k s̲elect y)) c̲at "\" r=\"" c̲at (f̲ormat (ˢᵛw̲hole u × 13)) c̲at "\" fill=\"none\" stroke=\"" c̲at (f̲ormat (ʰr̲ingColor k s̲elect r)) c̲at "\" stroke-width=\"" c̲at (f̲ormat (ˢᵛw̲hole u × 3)) c̲at "\"/>")
Used in: ˡd̲raw

ʰb̲ar : Num a => a -> Float -> Box Char

function (private) · line 237

The parts' buttons (the part shown filled), and the score below them at the right (seven buttons fill the row).

ʰb̲ar ← { s v →
  u ← ˢᵛu̲nit v
  score ← ((f̲irst v) + (3 s̲elect v) − u × 190) c̲at ((2 s̲elect v) + u × 80) c̲at u × 22
  ((v c̲at f̲loat 1 + f̲irst s) ˢᵛl̲abeled (e̲nclose "All") c̲at ˡp̲arts @) c̲at score ˢᵛw̲ords @ f̲ormat< "Score {l:s_core s} of {4 s_elect s}"
}
f̲ormat< expands to
("Score " c̲at (f̲ormat (ˡs̲core s)) c̲at " of " c̲at (f̲ormat (4 s̲elect s)))
Used in: ˡd̲raw

ʰs̲aid : Int -> Float -> Box Char

function (private) · line 243

What happened, along the bottom.

ʰs̲aid ← { s v →
  u ← ˢᵛu̲nit v
  (((f̲irst v) + u × 12) c̲at ((2 s̲elect v) + (4 s̲elect v) − u × 16) c̲at u × 22) ˢᵛw̲ords ˡs̲ay s
}
Used in: ˡd̲raw

ʰc̲hoose : Int -> Box Char

function (private) · line 248

The open menu: the four names, each a button.

ʰc̲hoose ← { s →
  0 = 2 s̲elect s ? 0 r̲eshape e̲nclose ""
  (ˡm̲enu s) ˢᵛc̲hoices '{ i → e̲nclose ˡn̲ame i } e̲ach ˡo̲ffered s
}
Used in: ˡd̲raw

ˡd̲raw : Int -> Char

function · line 257

The picture, as SVG text: the night, the sky's stars (drawn three times, a turn apart, so a view across 0h is whole), the round's rings, the parts' buttons and the score, what happened, and the open menu. Every coordinate is computed here; the page only shows the picture.

ˡd̲raw ← { s →
  v ← ˡv̲iew f̲irst s
  u ← ˢᵛu̲nit v
  night ← v ˢᵛr̲ect "fill=\"#0b1d3a\""
  stars ← e̲nclose "<g id=\"sky\" stroke=\"#ffffff\" stroke-linecap=\"round\" fill=\"none\">" c̲at layer c̲at "</g><use href=\"#sky\" x=\"-36000\"/><use href=\"#sky\" x=\"36000\"/>"
  v ˢᵛp̲icture night c̲at stars c̲at (s ʰr̲ings u) c̲at (s ʰb̲ar v) c̲at (s ʰs̲aid v) c̲at ʰc̲hoose s
}
Used in: ᵘl̲oop, pic