package arena
import (
"strconv"
"strings"
"time"
"gno.land/p/g188vafzutzvlfw6wt7u3s62xtuvm0epmu8ufmfz/gnomepit/engine"
)
const notFound = "# Not found\n\nThe arena has no page at this path.\n"
// Render serves the board of open challenges at "", one player's matches at
// "player/
", and one match at "match/".
func Render(path string) string {
switch {
case path == "":
return renderBoard()
case strings.HasPrefix(path, "player/"):
player := address(strings.TrimPrefix(path, "player/"))
if !canonical(player) {
return notFound
}
return renderPlayer(player)
case strings.HasPrefix(path, "match/"):
rules := engine.ProofOfConcept()
n, m, ok := lookup(rules, strings.TrimPrefix(path, "match/"))
if !ok {
return notFound
}
return renderMatch(m.view(rules, n, time.Now().Unix()))
}
return notFound
}
func renderBoard() string {
var b strings.Builder
b.WriteString("# Gnomepit arena\n\nOpen challenges, newest first.\n\n")
list := Open(0, maxPage)
if len(list) == 0 {
b.WriteString("No open challenge.\n")
}
for _, c := range list {
who := "open to anyone"
if c.Opponent != "" {
who = "reserved for " + c.Opponent
}
b.WriteString("- " + matchLink(c.ID) + ": " + c.Clan + ", posted by " + c.Challenger + ", " + who + ", expires " + date(c.Expires) + "\n")
}
return b.String()
}
func renderPlayer(player address) string {
var b strings.Builder
b.WriteString("# Matches of " + player.String() + "\n\n")
list := Matches(player, 0, maxPage)
if len(list) == 0 {
b.WriteString("No match.\n")
}
for _, s := range list {
b.WriteString("- " + matchLink(s.ID) + ": " + s.ChallengerClan + " against " + orNone(s.AcceptorClan) + ", " + s.Phase + "\n")
}
return b.String()
}
func renderMatch(v MatchView) string {
var b strings.Builder
b.WriteString("# Match " + v.ID + "\n\n")
b.WriteString(v.ChallengerClan + " (" + v.Challenger + ") against " + orNone(v.AcceptorClan))
if v.Acceptor != "" {
b.WriteString(" (" + v.Acceptor + ")")
}
b.WriteString("\n\n")
switch v.Phase {
case phaseOpen:
who := "open to anyone"
if v.Opponent != "" {
who = "reserved for " + v.Opponent
}
b.WriteString("Open challenge, " + who + ", until " + date(v.Deadline) + ".\n")
case phaseReveal, phasePlay:
b.WriteString("Round " + strconv.Itoa(v.Round) + ", waiting for " + v.ToMove + " until " + date(v.Deadline) + ".\n")
case phaseEnded:
b.WriteString("Over: " + outcome(v.Result, v.Reason) + ".\n")
default:
b.WriteString(strings.ToUpper(v.Phase[:1]) + v.Phase[1:] + ".\n")
}
if len(v.HP) > 0 {
b.WriteString("\n| HP after | Challenger | Acceptor |\n|---|---|---|\n")
for i, hp := range v.HP {
b.WriteString("| Round " + strconv.Itoa(i+1) + " | " + strconv.Itoa(hp[0]) + " | " + strconv.Itoa(hp[1]) + " |\n")
}
}
return b.String()
}
func outcome(result, reason string) string {
winner := "the challenger won"
switch result {
case "b":
winner = "the acceptor won"
case "draw":
winner = "a draw"
}
switch reason {
case "hp":
return winner + " on HP"
case "knockout":
return winner + " by knockout"
case "resign":
return winner + " by resignation"
}
return winner + " by forfeit"
}
func matchLink(id string) string {
return "[Match " + id + "](" + strings.TrimPrefix(self, "gno.land") + ":match/" + id + ")"
}
func date(unix int64) string {
return time.Unix(unix, 0).UTC().Format("2006-01-02 15:04 UTC")
}
func orNone(s string) string {
if s == "" {
return "nobody yet"
}
return s
}