render.gno
3.42 Kb · 129 lines
1package arena
2
3import (
4 "strconv"
5 "strings"
6 "time"
7
8 "gno.land/p/g188vafzutzvlfw6wt7u3s62xtuvm0epmu8ufmfz/gnomepit/engine"
9)
10
11const notFound = "# Not found\n\nThe arena has no page at this path.\n"
12
13// Render serves the board of open challenges at "", one player's matches at
14// "player/<address>", and one match at "match/<id>".
15func Render(path string) string {
16 switch {
17 case path == "":
18 return renderBoard()
19 case strings.HasPrefix(path, "player/"):
20 player := address(strings.TrimPrefix(path, "player/"))
21 if !canonical(player) {
22 return notFound
23 }
24 return renderPlayer(player)
25 case strings.HasPrefix(path, "match/"):
26 rules := engine.ProofOfConcept()
27 n, m, ok := lookup(rules, strings.TrimPrefix(path, "match/"))
28 if !ok {
29 return notFound
30 }
31 return renderMatch(m.view(rules, n, time.Now().Unix()))
32 }
33 return notFound
34}
35
36func renderBoard() string {
37 var b strings.Builder
38 b.WriteString("# Gnomepit arena\n\nOpen challenges, newest first.\n\n")
39 list := Open(0, maxPage)
40 if len(list) == 0 {
41 b.WriteString("No open challenge.\n")
42 }
43 for _, c := range list {
44 who := "open to anyone"
45 if c.Opponent != "" {
46 who = "reserved for " + c.Opponent
47 }
48 b.WriteString("- " + matchLink(c.ID) + ": " + c.Clan + ", posted by " + c.Challenger + ", " + who + ", expires " + date(c.Expires) + "\n")
49 }
50 return b.String()
51}
52
53func renderPlayer(player address) string {
54 var b strings.Builder
55 b.WriteString("# Matches of " + player.String() + "\n\n")
56 list := Matches(player, 0, maxPage)
57 if len(list) == 0 {
58 b.WriteString("No match.\n")
59 }
60 for _, s := range list {
61 b.WriteString("- " + matchLink(s.ID) + ": " + s.ChallengerClan + " against " + orNone(s.AcceptorClan) + ", " + s.Phase + "\n")
62 }
63 return b.String()
64}
65
66func renderMatch(v MatchView) string {
67 var b strings.Builder
68 b.WriteString("# Match " + v.ID + "\n\n")
69 b.WriteString(v.ChallengerClan + " (" + v.Challenger + ") against " + orNone(v.AcceptorClan))
70 if v.Acceptor != "" {
71 b.WriteString(" (" + v.Acceptor + ")")
72 }
73 b.WriteString("\n\n")
74 switch v.Phase {
75 case phaseOpen:
76 who := "open to anyone"
77 if v.Opponent != "" {
78 who = "reserved for " + v.Opponent
79 }
80 b.WriteString("Open challenge, " + who + ", until " + date(v.Deadline) + ".\n")
81 case phaseReveal, phasePlay:
82 b.WriteString("Round " + strconv.Itoa(v.Round) + ", waiting for " + v.ToMove + " until " + date(v.Deadline) + ".\n")
83 case phaseEnded:
84 b.WriteString("Over: " + outcome(v.Result, v.Reason) + ".\n")
85 default:
86 b.WriteString(strings.ToUpper(v.Phase[:1]) + v.Phase[1:] + ".\n")
87 }
88 if len(v.HP) > 0 {
89 b.WriteString("\n| HP after | Challenger | Acceptor |\n|---|---|---|\n")
90 for i, hp := range v.HP {
91 b.WriteString("| Round " + strconv.Itoa(i+1) + " | " + strconv.Itoa(hp[0]) + " | " + strconv.Itoa(hp[1]) + " |\n")
92 }
93 }
94 return b.String()
95}
96
97func outcome(result, reason string) string {
98 winner := "the challenger won"
99 switch result {
100 case "b":
101 winner = "the acceptor won"
102 case "draw":
103 winner = "a draw"
104 }
105 switch reason {
106 case "hp":
107 return winner + " on HP"
108 case "knockout":
109 return winner + " by knockout"
110 case "resign":
111 return winner + " by resignation"
112 }
113 return winner + " by forfeit"
114}
115
116func matchLink(id string) string {
117 return "[Match " + id + "](" + strings.TrimPrefix(self, "gno.land") + ":match/" + id + ")"
118}
119
120func date(unix int64) string {
121 return time.Unix(unix, 0).UTC().Format("2006-01-02 15:04 UTC")
122}
123
124func orNone(s string) string {
125 if s == "" {
126 return "nobody yet"
127 }
128 return s
129}