weather.gno
2.63 Kb · 70 lines
1package golf
2
3import (
4 "strconv"
5 "time"
6
7 "gno.land/p/nym-alexiscolin000/gnogolf/course"
8 "gno.land/p/nt/ufmt/v0"
9)
10
11// The weather changes every five minutes of chain time, differs from hole to
12// hole and is the same for everyone (course.ForecastFor hashes hole and
13// period). A round is played in the weather of the period it started in, and
14// records it, so anyone can replay it.
15
16// PeriodSeconds is how long one weather lasts.
17const PeriodSeconds = 5 * 60
18
19// Period is the weather period of the chain's current block time.
20func Period() int64 { return time.Now().Unix() / PeriodSeconds }
21
22// weatherOf is the hole's weather in a period: seeded by its id, so each
23// version of a slot has weather of its own.
24func weatherOf(e *entry, h *course.Simple, period int64) course.Forecast {
25 return course.ForecastFor(e.id, e.world, h, period)
26}
27
28// forecastJSON is a forecast as an object, head its first fields if any
29// (as `"version":1,`). Zones' air and capped are not printed: only the wind
30// has them, always.
31func forecastJSON(head string, f course.Forecast) string {
32 return "{" + head + `"period":` + strconv.FormatInt(f.Period, 10) + `,"kind":` + jstr(f.Kind) +
33 `,"wind":` + jvec(f.Wind) + `,"zones":` + zonesJSON(f.Zones, false) + "}"
34}
35
36// Weather is a hole's weather in a period, as JSON: the zones a client draws
37// and the chain plays under. Its "gas" is what a commit there spends before
38// its first shot (decoding and forecast), counted against the commit's
39// budget: a client adds it to its shots' estimate. A future period panics.
40func Weather(hole string, period int64) string {
41 notAhead(period)
42 e := readHole(hole)
43 fc := weatherOf(e, e.hole(), period)
44 return forecastJSON(versionJSON+`,"gas":`+strconv.FormatInt(fixedGas(e, fc), 10)+",", fc)
45}
46
47// notAhead refuses a period still to come. It is no secret (ForecastFor is
48// pure): this only keeps reads to the weather a round can be played in.
49func notAhead(period int64) {
50 if period > Period() {
51 panic("golf: that weather has not come yet")
52 }
53}
54
55// notOver refuses a round's weather two periods or more behind: a round left
56// for later cannot be finished days on in the weather it picked.
57func notOver(period int64) {
58 if period < Period()-1 {
59 panic(ufmt.Sprintf("golf: this round's weather (period %d) is over: Reset to play again", period))
60 }
61}
62
63// playablePeriod checks the period a round starts under: the current one, or
64// the one just gone, for a round that ran over the turn of the period.
65func playablePeriod(period int64) {
66 now := Period()
67 if period != now && period != now-1 {
68 panic(ufmt.Sprintf("golf: period %d is not the current weather (%d) or the one before", period, now))
69 }
70}