Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

featured.gno

3.82 Kb · 145 lines
  1package golf
  2
  3import (
  4	"strconv"
  5	"time"
  6)
  7
  8// The records the hub puts forward (as the site's boards, web/lib/featured.ts,
  9// whose draw and rules these are): not the course ranking's top three, a few
 10// players worth racing today, each for a reason said beside them. One a
 11// player, drawn again each day (the same day, the same picks as the site's,
 12// but for the players the site's bot check hides: the chain knows none).
 13
 14// featuredRead is how much of the course ranking the picks look at: its first
 15// page; podium, its first places, which get no card of their own.
 16const (
 17	featuredRead = 50
 18	podium       = 3
 19)
 20
 21// pick is a player put forward, and why.
 22type pick struct {
 23	r    *row
 24	name string
 25	at   int // their place on the ranking
 26	tag  string
 27}
 28
 29// today is the day, a number: the picks hold for it and change with it (block time).
 30func today() int64 { return time.Now().Unix() / 86400 }
 31
 32// draw is a player's draw for a day: FNV-1a over their address, seeded with
 33// the day (featured.ts draw: the same numbers, so the same picks).
 34func draw(player string, day int64) uint32 {
 35	h := uint32(2166136261) ^ uint32(day)
 36	for i := 0; i < len(player); i++ {
 37		h = (h ^ uint32(player[i])) * 16777619
 38	}
 39	return h
 40}
 41
 42// featured is up to n picks from the course ranking in mode m: a fast start (a
 43// third of the course or less, at par or better), the best average against par
 44// (a third of the course played, nine holes at least), and today's picks for
 45// the rest; never the first three places.
 46func featured(m, n int) []pick {
 47	// every named row read, the first three too (the best average is the
 48	// course's, as featured.ts takes it, whoever holds it), numbered as the
 49	// boards number them: a row whose name was deleted keeps no place
 50	var rows []pick
 51	walked, at := 0, 0
 52	ranks[m].Iterate("", "", func(_, p string) bool {
 53		walked++
 54		if name := nameOf(p); name != "" {
 55			at++
 56			rows = append(rows, pick{r: rowOf(m, p), name: name, at: at})
 57		}
 58		return walked >= featuredRead
 59	})
 60	course := slots.Size()
 61	third := course / 3
 62	perHole := func(r *row) float64 { return float64(r.strokes-r.par) / float64(r.holes) }
 63	var rising, many []pick
 64	for _, x := range rows {
 65		if x.r.holes > 0 && x.r.holes <= max1(third) && x.r.strokes <= x.r.par {
 66			rising = append(rising, x)
 67		}
 68		if x.r.holes >= nine(third) {
 69			many = append(many, x)
 70		}
 71	}
 72	var sharp []pick
 73	for _, x := range many {
 74		switch {
 75		case len(sharp) == 0 || perHole(x.r) < perHole(sharp[0].r):
 76			sharp = []pick{x}
 77		case perHole(x.r) == perHole(sharp[0].r):
 78			sharp = append(sharp, x)
 79		}
 80	}
 81	day := today()
 82	var out []pick
 83	used := map[string]bool{}
 84	take := func(from []pick, tag string, again bool) {
 85		for len(out) < n {
 86			best := -1
 87			for i, x := range from {
 88				if !used[x.r.player] && x.at > podium && (best < 0 || draw(x.r.player, day) < draw(from[best].r.player, day)) {
 89					best = i
 90				}
 91			}
 92			if best < 0 {
 93				return
 94			}
 95			x := from[best]
 96			used[x.r.player] = true
 97			x.tag = tag
 98			if tag == "Today's pick" && countTag(out, tag) > 0 {
 99				x.tag = "Also today"
100			}
101			out = append(out, x)
102			if !again {
103				return
104			}
105		}
106	}
107	take(rising, "Fast start", false)
108	take(sharp, "Best average", false)
109	take(rows, "Today's pick", true)
110	return out
111}
112
113func countTag(ps []pick, tag string) int {
114	n := 0
115	for _, p := range ps {
116		if p.tag == tag {
117			n++
118		}
119	}
120	return n
121}
122
123func max1(n int) int {
124	if n < 1 {
125		return 1
126	}
127	return n
128}
129
130func nine(n int) int {
131	if n < 9 {
132		return 9
133	}
134	return n
135}
136
137// featuredRows is the hub's records to beat in mode m, as markdown lines.
138func featuredRows(m int) string {
139	s := ""
140	for _, x := range featured(m, 3) {
141		s += "- **" + x.tag + ":** " + md("@"+x.name, "/u/"+x.name) + ", " + count(x.r.holes, "hole") + " at " + vsPar(x.r.strokes-x.r.par) +
142			" (#" + strconv.Itoa(x.at) + ") · " + md("race their ghosts", playURL+"?by="+x.r.player) + "\n"
143	}
144	return s
145}