From 1738ce7d198a556b8a4c7fe62e3610291e435d2b Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Mon, 27 Mar 2023 10:11:17 -0600 Subject: [PATCH] test a vote for someone saying themself --- src/device/input/parse/v01/server.go | 44 ++++++-- src/device/input/parse/v01/server_test.go | 117 +++++++++++++++++++++- 2 files changed, 147 insertions(+), 14 deletions(-) diff --git a/src/device/input/parse/v01/server.go b/src/device/input/parse/v01/server.go index ece8398..5482e4b 100644 --- a/src/device/input/parse/v01/server.go +++ b/src/device/input/parse/v01/server.go @@ -157,16 +157,14 @@ func (v01 *V01) serveGM(w http.ResponseWriter, r *http.Request) { case "/gm/rpc/vote": v01.serveGMVote(w, r) case "/gm/rpc/elect": - panic("TODO swap or shuffle") + v01.serveGMElect(w, r) case "/gm/rpc/shuffle": - v01.serveGMShuffle(w, r) - v01.cfg.Quiet = false + v01.serveGMShuffle(r) case "/gm/rpc/swap": - if errCode, err := v01.serveGMSwap(r, r.URL.Query().Get("a"), r.URL.Query().Get("b")); err != nil { + if errCode, err := v01.serveGMSwap(r.URL.Query().Get("a"), r.URL.Query().Get("b")); err != nil { http.Error(w, err.Error(), errCode) return } - v01.cfg.Quiet = false default: http.NotFound(w, r) } @@ -235,12 +233,35 @@ func (v01 *V01) serveGMFillNonPlayerAliases(w http.ResponseWriter, r *http.Reque } } +func (v01 *V01) serveGMElect(w http.ResponseWriter, r *http.Request) { + alias := r.URL.Query().Get("alias") + votes := map[string]int{} + for _, v := range v01.cfg.Users { + if v2 := strings.Split(v.Message, "//"); len(v2) > 1 { + votes[v2[1]] = votes[v2[1]] + 1 + } + } + threshold := 0.1 + float64(len(votes))/2.0 + winner := "" + for k, v := range votes { + if float64(v) > threshold { + winner = k + } + } + if winner == "" { + v01.serveGMShuffle(r) + } else if _, err := v01.serveGMSwap(winner, alias); err != nil { + v01.serveGMShuffle(r) + } + yaml.NewEncoder(w).Encode(votes) +} + func (v01 *V01) serveGMVote(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: counts := map[string]string{} for k, v := range v01.cfg.Users { - if strings.HasPrefix(v.Alias, "//") { + if strings.Contains(v.Message, "//") { counts[k] = "voted" } else { counts[k] = "voting" @@ -254,12 +275,12 @@ func (v01 *V01) serveGMVote(w http.ResponseWriter, r *http.Request) { if _, ok2 := v01.cfg.Users[candidate]; !ok || !ok2 { http.Error(w, "bad voter/candidate", http.StatusBadRequest) } - v.Alias = "//" + candidate + v.Message = strings.Split(v.Message, "//")[0] + "//" + candidate v01.cfg.Users[voter] = v } } -func (v01 *V01) serveGMShuffle(w http.ResponseWriter, r *http.Request) { +func (v01 *V01) serveGMShuffle(r *http.Request) { poolSize := len(v01.cfg.Users) if altSize := len(v01.cfg.Players); altSize > poolSize { poolSize = altSize @@ -286,12 +307,13 @@ func (v01 *V01) serveGMShuffle(w http.ResponseWriter, r *http.Request) { i += 1 } v01.servePutBroadcastValue(strings.Join(msg, ", ")) + v01.cfg.Quiet = false } -func (v01 *V01) serveGMSwap(r *http.Request, nameA, nameB string) (int, error) { +func (v01 *V01) serveGMSwap(nameA, nameB string) (int, error) { getUserNameFor := func(like string) string { for k, v := range v01.cfg.Users { - if k == like || v.Alias == like { + if k == like || v.Alias == like || (strings.Contains(v.Message, "//") && strings.Split(v.Message, "//")[0] == like) { return k } } @@ -310,5 +332,7 @@ func (v01 *V01) serveGMSwap(r *http.Request, nameA, nameB string) (int, error) { a.Player, b.Player = b.Player, a.Player v01.cfg.Users[userA] = a v01.cfg.Users[userB] = b + v01.cfg.Quiet = false + v01.servePutBroadcastValue(fmt.Sprintf(`%s is now player %v and %s is now player %v`, userA, a.Player, userB, b.Player)) return http.StatusOK, nil } diff --git a/src/device/input/parse/v01/server_test.go b/src/device/input/parse/v01/server_test.go index b4584fb..9efbc46 100644 --- a/src/device/input/parse/v01/server_test.go +++ b/src/device/input/parse/v01/server_test.go @@ -241,6 +241,10 @@ func TestServeGM(t *testing.T) { t.Run("vote", func(t *testing.T) { type result map[string]string + t.Run("cast vote", func(t *testing.T) { + t.Error("not impl") + }) + t.Run("get non vote", func(t *testing.T) { v01 := NewV01(ctx, nil) v01.cfg.Users = map[string]configUser{"bel": {}} @@ -260,7 +264,7 @@ func TestServeGM(t *testing.T) { t.Run("get mid vote", func(t *testing.T) { v01 := NewV01(ctx, nil) - v01.cfg.Users = map[string]configUser{"bel": {Alias: "//zach"}} + v01.cfg.Users = map[string]configUser{"bel": {Message: "driver//zach"}} resp := do(v01, "/gm/rpc/vote", "", "GET") var result result if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { @@ -291,7 +295,107 @@ func TestServeGM(t *testing.T) { }) t.Run("elect", func(t *testing.T) { - t.Error("not impl") + type result map[string]int + + t.Run("happy", func(t *testing.T) { + v01 := NewV01(ctx, nil) + v01.cfg.Users = map[string]configUser{ + "bel": configUser{Message: "driver//zach", Player: 1}, + "zach": configUser{Message: "pizza//bel"}, + "bill": configUser{Message: "//bel", Player: 2}, + } + resp := do(v01, "/gm/rpc/elect?alias=pizza", "") + var result result + if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { + t.Error(err) + } + if len(result) != 2 { + t.Error(result) + } else if result["bel"] != 2 { + t.Error(result) + } else if result["zach"] != 1 { + t.Error(result) + } + if v01.cfg.Users["bel"].Player != 0 { + t.Error(v01.cfg.Users["bel"].Player) + } else if v01.cfg.Users["zach"].Player != 1 { + t.Error(v01.cfg.Users["zach"].Player) + } + if v01.cfg.Users["broadcast"].Message != `bel is now player 0 and zach is now player 1` { + t.Error(v01.cfg.Users["broadcast"].Message) + } + }) + + t.Run("self", func(t *testing.T) { + v01 := NewV01(ctx, nil) + v01.cfg.Players = []configPlayer{{}} + v01.cfg.Users = map[string]configUser{ + "bel": configUser{Message: "driver//zach", Player: 1}, + "zach": configUser{Message: "//bel"}, + "bill": configUser{Message: "//bel"}, + } + resp := do(v01, "/gm/rpc/elect?alias=driver", "") + var result result + if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { + t.Error(err) + } + if len(result) != 2 { + t.Error(result) + } else if result["bel"] != 2 { + t.Error(result) + } else if result["zach"] != 1 { + t.Error(result) + } + if !strings.HasSuffix(v01.cfg.Users["broadcast"].Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].Message, ",") { + t.Error(v01.cfg.Users["broadcast"].Message) + } + assignments := map[int]int{} + for _, v := range v01.cfg.Users { + assignments[v.Player] = assignments[v.Player] + 1 + } + if len(assignments) != 2 { + t.Error(assignments) + } else if assignments[0] != 3 { + t.Error(assignments[0]) + } else if assignments[1] != 1 { + t.Error(assignments[1]) + } + }) + + t.Run("tie", func(t *testing.T) { + v01 := NewV01(ctx, nil) + v01.cfg.Players = []configPlayer{{}} + v01.cfg.Users = map[string]configUser{ + "bel": configUser{Message: "driver//zach", Player: 1}, + "zach": configUser{Message: "pizza//bel"}, + } + resp := do(v01, "/gm/rpc/elect?alias=pizza", "") + var result result + if err := yaml.Unmarshal(resp.Body.Bytes(), &result); err != nil { + t.Error(err) + } + if len(result) != 2 { + t.Error(result) + } else if result["bel"] != 1 { + t.Error(result) + } else if result["zach"] != 1 { + t.Error(result) + } + if !strings.HasSuffix(v01.cfg.Users["broadcast"].Message, `is now player 1`) || strings.Contains(v01.cfg.Users["broadcast"].Message, ",") { + t.Error(v01.cfg.Users["broadcast"].Message) + } + assignments := map[int]int{} + for _, v := range v01.cfg.Users { + assignments[v.Player] = assignments[v.Player] + 1 + } + if len(assignments) != 2 { + t.Error(assignments) + } else if assignments[0] != 2 { + t.Error(assignments[0]) + } else if assignments[1] != 1 { + t.Error(assignments[1]) + } + }) }) t.Run("shuffle", func(t *testing.T) { @@ -367,10 +471,10 @@ func TestServeGM(t *testing.T) { t.Error(i) } } - assignments := map[int]struct{}{} + assignments := map[int]int{} for _, v := range v01.cfg.Users { if v.Player > 0 { - assignments[v.Player] = struct{}{} + assignments[v.Player] = assignments[v.Player] + 1 } } lesser := c.users @@ -380,6 +484,11 @@ func TestServeGM(t *testing.T) { if len(assignments) != lesser { t.Error(assignments) } + for _, v := range assignments { + if v != 1 { + t.Error(v) + } + } }) } })