feat: voting procedures
This commit is contained in:
@@ -94,3 +94,11 @@ func BombPlanet(p *game.Planet, power float64) {
|
||||
func (c *Cache) ListProducingPlanets() iter.Seq[uint] {
|
||||
return c.listProducingPlanets()
|
||||
}
|
||||
|
||||
func (c *Cache) VotesByRace() map[int]float64 {
|
||||
return c.votesByRace()
|
||||
}
|
||||
|
||||
func VotingWinners(calc []*VoteGroup, gameVotes float64) []int {
|
||||
return votingWinners(calc, gameVotes)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
|
||||
var (
|
||||
Race_0 = game.Race{
|
||||
ID: Race_0_ID,
|
||||
Vote: Race_0_ID,
|
||||
Name: "Race_0",
|
||||
ID: Race_0_ID,
|
||||
VoteFor: Race_0_ID,
|
||||
Name: "Race_0",
|
||||
Tech: map[game.Tech]float64{
|
||||
game.TechDrive: 1.1,
|
||||
game.TechWeapons: 1.2,
|
||||
@@ -25,9 +25,9 @@ var (
|
||||
Relations: []game.RaceRelation{{RaceID: Race_1_ID, Relation: game.RelationWar}},
|
||||
}
|
||||
Race_1 = game.Race{
|
||||
ID: Race_1_ID,
|
||||
Vote: Race_1_ID,
|
||||
Name: "Race_1",
|
||||
ID: Race_1_ID,
|
||||
VoteFor: Race_1_ID,
|
||||
Name: "Race_1",
|
||||
Tech: map[game.Tech]float64{
|
||||
game.TechDrive: 2.1,
|
||||
game.TechWeapons: 2.2,
|
||||
|
||||
@@ -58,9 +58,9 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
|
||||
}
|
||||
relations[i] = game.RaceRelation{RaceID: raceID, Relation: game.RelationWar}
|
||||
g.Race[i] = game.Race{
|
||||
ID: raceID,
|
||||
Name: races[i],
|
||||
Vote: raceID,
|
||||
ID: raceID,
|
||||
Name: races[i],
|
||||
VoteFor: raceID,
|
||||
Tech: map[game.Tech]float64{
|
||||
game.TechDrive: 1,
|
||||
game.TechWeapons: 1,
|
||||
|
||||
@@ -44,6 +44,9 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
// 15. Происходит отмена маршрутов, выходящих за зону полета кораблей.
|
||||
c.Cache.RemoveUnreachableRoutes()
|
||||
|
||||
// 16. Происходит голосование.
|
||||
winners := c.Cache.TurnCalculateVotes()
|
||||
|
||||
/*** Last steps ***/
|
||||
|
||||
// Store battles
|
||||
@@ -56,6 +59,7 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove killed ship groups
|
||||
c.Cache.DeleteKilledShipGroups()
|
||||
|
||||
@@ -63,5 +67,6 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
|
||||
// TODO: Store individual reports
|
||||
|
||||
_ = winners
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -87,13 +87,13 @@ func (c *Cache) GiveVotes(race, recipient string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.g.Race[ri].Vote = c.g.Race[rec].ID
|
||||
c.g.Race[ri].VoteFor = c.g.Race[rec].ID
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) Voted(ri int) int {
|
||||
c.validateRaceIndex(ri)
|
||||
return c.RaceIndex(c.g.Race[ri].Vote)
|
||||
return c.RaceIndex(c.g.Race[ri].VoteFor)
|
||||
}
|
||||
|
||||
func (c *Cache) UpdateRelation(ri, other int, rel game.Relation) (err error) {
|
||||
|
||||
@@ -118,6 +118,7 @@ func (c *Cache) DeleteKilledShipGroups() {
|
||||
c.unsafeDeleteShipGroup(i)
|
||||
}
|
||||
}
|
||||
// TODO: delete empty fleets
|
||||
}
|
||||
|
||||
func (c *Controller) JoinEqualGroups(raceName string) error {
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
type VoteGroup struct {
|
||||
RaceIndex []int
|
||||
Sum float64
|
||||
}
|
||||
|
||||
type VoteNode struct {
|
||||
ID int
|
||||
Ally bool
|
||||
Next *VoteNode
|
||||
}
|
||||
|
||||
func (n VoteNode) String() string {
|
||||
lh, rh := " ", "."
|
||||
if n.Ally {
|
||||
lh, rh = "{", "}"
|
||||
}
|
||||
return fmt.Sprintf("%s%d%s", lh, n.ID, rh)
|
||||
}
|
||||
|
||||
func (c *Cache) TurnCalculateVotes() []int {
|
||||
raceVotes := c.votesByRace()
|
||||
calc := GroupVotes(raceVotes, VotingGraph(c.g.Race, c.RaceIndex))
|
||||
|
||||
c.g.Votes = 0
|
||||
for ri, votes := range raceVotes {
|
||||
c.g.Race[ri].Votes = votes
|
||||
c.g.Votes += votes
|
||||
}
|
||||
|
||||
return votingWinners(calc, c.g.Votes)
|
||||
}
|
||||
|
||||
func VotingGraph(races []game.Race, raceIndex func(uuid.UUID) int) []*VoteNode {
|
||||
nodes := make([]*VoteNode, len(races))
|
||||
for ri := range races {
|
||||
// TODO: filter inactive (RIP) races
|
||||
r := &races[ri]
|
||||
if nodes[ri] == nil {
|
||||
nodes[ri] = &VoteNode{
|
||||
ID: ri,
|
||||
}
|
||||
}
|
||||
if r.VoteFor != r.ID {
|
||||
vid := raceIndex(r.VoteFor)
|
||||
if nodes[vid] == nil {
|
||||
nodes[vid] = &VoteNode{
|
||||
ID: vid,
|
||||
}
|
||||
}
|
||||
nodes[ri].Next = nodes[vid]
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (c *Cache) votesByRace() map[int]float64 {
|
||||
result := make(map[int]float64)
|
||||
for i := range c.g.Map.Planet {
|
||||
p := &c.g.Map.Planet[i]
|
||||
if p.Owner == uuid.Nil {
|
||||
continue
|
||||
}
|
||||
ri := c.RaceIndex(p.Owner)
|
||||
planetVotes := p.Votes()
|
||||
result[ri] += planetVotes
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func GroupVotes(raceVotes map[int]float64, nodes []*VoteNode) []*VoteGroup {
|
||||
votes := maps.Clone(raceVotes)
|
||||
result := make([]*VoteGroup, 0)
|
||||
chains := VotingChains(nodes)
|
||||
chainingRaces := make(map[int]bool)
|
||||
|
||||
for i := range chains {
|
||||
chain := chains[i]
|
||||
if len(chain) == 0 {
|
||||
panic("voters chain is empty")
|
||||
}
|
||||
vg := &VoteGroup{}
|
||||
for j := range chain {
|
||||
node := &chain[j]
|
||||
if node.Ally || j == len(chain)-1 {
|
||||
vg.RaceIndex = append(vg.RaceIndex, node.ID)
|
||||
}
|
||||
vg.Sum += votes[node.ID]
|
||||
votes[node.ID] = 0
|
||||
chainingRaces[node.ID] = true
|
||||
}
|
||||
// find a non-ally group (single race) which already have its votes and merge with a new VoteGroup instead of adding to result
|
||||
if i := slices.IndexFunc(result, func(v *VoteGroup) bool { return len(v.RaceIndex) == 1 && v.RaceIndex[0] == vg.RaceIndex[0] }); i >= 0 && len(vg.RaceIndex) == 1 {
|
||||
result[i].Sum += vg.Sum
|
||||
} else {
|
||||
result = append(result, vg)
|
||||
}
|
||||
}
|
||||
|
||||
for ri, votes := range votes {
|
||||
if _, ok := chainingRaces[ri]; !ok && votes > 0 {
|
||||
result = append(result, &VoteGroup{RaceIndex: []int{ri}, Sum: votes})
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func VotingChains(nodes []*VoteNode) [][]VoteNode {
|
||||
visited := make(map[int]bool)
|
||||
result := make([][]VoteNode, 0)
|
||||
for i := range nodes {
|
||||
n := nodes[i]
|
||||
if v, ok := visited[n.ID]; (ok && v) || n.Next == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
slow, fast := n, n
|
||||
cycled := false
|
||||
var cycleBound *VoteNode
|
||||
for slow != nil && fast != nil && fast.Next != nil {
|
||||
slow = slow.Next
|
||||
fast = fast.Next.Next
|
||||
|
||||
if slow == fast {
|
||||
slow = n
|
||||
|
||||
for slow != fast {
|
||||
slow = slow.Next
|
||||
fast = fast.Next
|
||||
}
|
||||
|
||||
cycled = true
|
||||
cycleBound = slow
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var current *VoteNode
|
||||
if cycled && !visited[slow.ID] {
|
||||
result = append(result, make([]VoteNode, 0))
|
||||
|
||||
result[len(result)-1] = append(result[len(result)-1], VoteNode{ID: slow.ID, Ally: true})
|
||||
visited[slow.ID] = true
|
||||
|
||||
current = slow.Next
|
||||
|
||||
for current != slow {
|
||||
visited[current.ID] = true
|
||||
result[len(result)-1] = append(result[len(result)-1], VoteNode{ID: current.ID, Ally: true})
|
||||
current = current.Next
|
||||
}
|
||||
|
||||
if n == slow {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
current = n
|
||||
var finish *VoteNode
|
||||
if cycleBound != nil {
|
||||
if cycleBound == current.Next {
|
||||
finish = current
|
||||
} else {
|
||||
finish = cycleBound
|
||||
}
|
||||
} else {
|
||||
finish = nil
|
||||
}
|
||||
|
||||
if finish != current {
|
||||
result = append(result, make([]VoteNode, 0))
|
||||
}
|
||||
|
||||
for current != finish {
|
||||
visited[current.ID] = current.ID != n.ID && current.Next != nil
|
||||
result[len(result)-1] = append(result[len(result)-1], VoteNode{ID: current.ID, Ally: false})
|
||||
current = current.Next
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func votingWinners(calc []*VoteGroup, sumVotes float64) []int {
|
||||
slices.SortFunc(calc, func(a, b *VoteGroup) int { return cmp.Compare(b.Sum, a.Sum) })
|
||||
|
||||
topVoter := calc[0]
|
||||
maxVotes := &big.Rat{}
|
||||
maxVotes.SetFloat64(topVoter.Sum)
|
||||
|
||||
winVotes := &big.Rat{}
|
||||
winVotes.SetFloat64(sumVotes)
|
||||
winVotes = winVotes.Mul(winVotes, big.NewRat(2, 3))
|
||||
|
||||
if maxVotes.Cmp(winVotes) >= 0 {
|
||||
return topVoter.RaceIndex
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package controller_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/iliadenisov/galaxy/internal/controller"
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestVotesByRace(t *testing.T) {
|
||||
c, _ := newCache()
|
||||
|
||||
c.MustPlanet(R0_Planet_0_num).Size = 450.
|
||||
c.MustPlanet(R0_Planet_0_num).Population = 450.
|
||||
|
||||
c.MustPlanet(R1_Planet_1_num).Size = 900.
|
||||
c.MustPlanet(R1_Planet_1_num).Population = 900.
|
||||
|
||||
c.MustPlanet(R0_Planet_2_num).Size = 330.
|
||||
c.MustPlanet(R0_Planet_2_num).Population = 330.
|
||||
|
||||
vbr := c.VotesByRace()
|
||||
assert.Len(t, vbr, 2)
|
||||
assert.Contains(t, vbr, Race_0_idx)
|
||||
assert.Equal(t, 0.78, vbr[Race_0_idx])
|
||||
assert.Contains(t, vbr, Race_1_idx)
|
||||
assert.Equal(t, 0.9, vbr[Race_1_idx])
|
||||
// TODO: add races without planets / dead races
|
||||
}
|
||||
|
||||
func prepareRaces() ([]game.Race, func(u uuid.UUID) int) {
|
||||
races := make([]game.Race, 20)
|
||||
raceIndex := make(map[uuid.UUID]int)
|
||||
for i := range len(races) {
|
||||
races[i].ID = uuid.New()
|
||||
races[i].VoteFor = races[i].ID
|
||||
raceIndex[races[i].ID] = i
|
||||
}
|
||||
// 0 -> 1 -> 2 -> 3
|
||||
// ^ |
|
||||
// 5 -> 6 -> 4 <--'
|
||||
races[0].VoteFor = races[1].ID
|
||||
races[1].VoteFor = races[2].ID
|
||||
races[2].VoteFor = races[3].ID
|
||||
races[3].VoteFor = races[4].ID
|
||||
races[4].VoteFor = races[2].ID
|
||||
races[5].VoteFor = races[6].ID
|
||||
races[6].VoteFor = races[4].ID
|
||||
// 7 -> 10 -> 11
|
||||
// ^
|
||||
// 8 -> 9
|
||||
races[7].VoteFor = races[10].ID
|
||||
races[10].VoteFor = races[11].ID
|
||||
races[8].VoteFor = races[9].ID
|
||||
races[9].VoteFor = races[10].ID
|
||||
// 12 -> 13
|
||||
// 13 -> 12
|
||||
races[12].VoteFor = races[13].ID
|
||||
races[13].VoteFor = races[12].ID
|
||||
// 14 -> 15 -> 16
|
||||
// ^ |
|
||||
// 17 <--'
|
||||
races[14].VoteFor = races[15].ID
|
||||
races[15].VoteFor = races[16].ID
|
||||
races[16].VoteFor = races[17].ID
|
||||
races[17].VoteFor = races[15].ID
|
||||
// 19 -> 13
|
||||
races[19].VoteFor = races[13].ID
|
||||
|
||||
return races, func(u uuid.UUID) int { return raceIndex[u] }
|
||||
}
|
||||
|
||||
func TestVotingGraph(t *testing.T) {
|
||||
races, raceIndex := prepareRaces()
|
||||
|
||||
voteNodes := controller.VotingGraph(races, raceIndex)
|
||||
|
||||
assert.Len(t, voteNodes, len(races))
|
||||
for i := range voteNodes {
|
||||
n := voteNodes[i]
|
||||
switch i {
|
||||
case 0:
|
||||
assert.Equal(t, voteNodes[1], n.Next)
|
||||
case 1:
|
||||
assert.Equal(t, voteNodes[2], n.Next)
|
||||
case 2:
|
||||
assert.Equal(t, voteNodes[3], n.Next)
|
||||
case 3:
|
||||
assert.Equal(t, voteNodes[4], n.Next)
|
||||
case 4:
|
||||
assert.Equal(t, voteNodes[2], n.Next)
|
||||
case 5:
|
||||
assert.Equal(t, voteNodes[6], n.Next)
|
||||
case 6:
|
||||
assert.Equal(t, voteNodes[4], n.Next)
|
||||
case 7:
|
||||
assert.Equal(t, voteNodes[10], n.Next)
|
||||
case 8:
|
||||
assert.Equal(t, voteNodes[9], n.Next)
|
||||
case 9:
|
||||
assert.Equal(t, voteNodes[10], n.Next)
|
||||
case 10:
|
||||
assert.Equal(t, voteNodes[11], n.Next)
|
||||
case 11:
|
||||
assert.Nil(t, n.Next)
|
||||
case 12:
|
||||
assert.Equal(t, voteNodes[13], n.Next)
|
||||
case 13:
|
||||
assert.Equal(t, voteNodes[12], n.Next)
|
||||
case 14:
|
||||
assert.Equal(t, voteNodes[15], n.Next)
|
||||
case 15:
|
||||
assert.Equal(t, voteNodes[16], n.Next)
|
||||
case 16:
|
||||
assert.Equal(t, voteNodes[17], n.Next)
|
||||
case 17:
|
||||
assert.Equal(t, voteNodes[15], n.Next)
|
||||
case 18:
|
||||
assert.Nil(t, n.Next)
|
||||
case 19:
|
||||
assert.Equal(t, voteNodes[13], n.Next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVotingChains(t *testing.T) {
|
||||
races, raceIndex := prepareRaces()
|
||||
nodes := controller.VotingGraph(races, raceIndex)
|
||||
|
||||
vc := controller.VotingChains(nodes)
|
||||
|
||||
assert.Len(t, vc, 7)
|
||||
for i := range vc {
|
||||
n := vc[i]
|
||||
switch i {
|
||||
case 0:
|
||||
assert.Len(t, n, 3)
|
||||
assert.Equal(t, 2, n[0].ID)
|
||||
assert.Equal(t, 3, n[1].ID)
|
||||
assert.Equal(t, 4, n[2].ID)
|
||||
assert.True(t, n[0].Ally)
|
||||
assert.True(t, n[1].Ally)
|
||||
assert.True(t, n[2].Ally)
|
||||
case 1:
|
||||
assert.Len(t, n, 2)
|
||||
assert.Equal(t, 0, n[0].ID)
|
||||
assert.Equal(t, 1, n[1].ID)
|
||||
assert.False(t, n[0].Ally)
|
||||
assert.False(t, n[1].Ally)
|
||||
case 2:
|
||||
assert.Len(t, n, 2)
|
||||
assert.Equal(t, 5, n[0].ID)
|
||||
assert.Equal(t, 6, n[1].ID)
|
||||
assert.False(t, n[0].Ally)
|
||||
assert.False(t, n[1].Ally)
|
||||
case 3:
|
||||
assert.Len(t, n, 3)
|
||||
assert.Equal(t, 7, n[0].ID)
|
||||
assert.Equal(t, 10, n[1].ID)
|
||||
assert.Equal(t, 11, n[2].ID)
|
||||
assert.False(t, n[0].Ally)
|
||||
assert.False(t, n[1].Ally)
|
||||
assert.False(t, n[2].Ally)
|
||||
case 4:
|
||||
assert.Len(t, n, 4)
|
||||
assert.Equal(t, 8, n[0].ID)
|
||||
assert.Equal(t, 9, n[1].ID)
|
||||
assert.Equal(t, 10, n[2].ID)
|
||||
assert.Equal(t, 11, n[3].ID)
|
||||
assert.False(t, n[0].Ally)
|
||||
assert.False(t, n[1].Ally)
|
||||
assert.False(t, n[2].Ally)
|
||||
assert.False(t, n[3].Ally)
|
||||
case 5:
|
||||
assert.Len(t, n, 2)
|
||||
assert.Equal(t, 12, n[0].ID)
|
||||
assert.Equal(t, 13, n[1].ID)
|
||||
assert.True(t, n[0].Ally)
|
||||
assert.True(t, n[1].Ally)
|
||||
case 6:
|
||||
assert.Len(t, n, 3)
|
||||
assert.Equal(t, 15, n[0].ID)
|
||||
assert.Equal(t, 16, n[1].ID)
|
||||
assert.Equal(t, 17, n[2].ID)
|
||||
assert.True(t, n[0].Ally)
|
||||
assert.True(t, n[1].Ally)
|
||||
assert.True(t, n[2].Ally)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupVotes(t *testing.T) {
|
||||
races, raceIndex := prepareRaces()
|
||||
raceVotes := make(map[int]float64)
|
||||
// [1] = 0.24
|
||||
raceVotes[0] = 0.11
|
||||
raceVotes[1] = 0.13
|
||||
|
||||
// [2,3,4] = 0.69
|
||||
raceVotes[2] = 0.22
|
||||
raceVotes[3] = 0.23
|
||||
raceVotes[4] = 0.24
|
||||
|
||||
// [6] = 0.71
|
||||
raceVotes[5] = 0.35
|
||||
raceVotes[6] = 0.36
|
||||
|
||||
// [11] = 0.843
|
||||
raceVotes[7] = 0.41
|
||||
raceVotes[9] = 0.42
|
||||
raceVotes[10] = 0.013
|
||||
|
||||
// [12,13] = 0.52
|
||||
raceVotes[12] = 0.52
|
||||
raceVotes[13] = 0.
|
||||
|
||||
// [14] = 1.04
|
||||
raceVotes[14] = 1.04
|
||||
|
||||
// [15,16,17] = 2.49
|
||||
raceVotes[15] = 1.15
|
||||
raceVotes[16] = 0.16
|
||||
raceVotes[17] = 1.18
|
||||
|
||||
// [18] = 3.18
|
||||
raceVotes[18] = 3.18
|
||||
|
||||
// [19] = 0.019
|
||||
raceVotes[19] = 0.019
|
||||
|
||||
calc := controller.GroupVotes(raceVotes, controller.VotingGraph(races, raceIndex))
|
||||
|
||||
assert.Len(t, calc, 9)
|
||||
for i := range calc {
|
||||
vg := calc[i]
|
||||
switch i {
|
||||
case 0:
|
||||
assert.ElementsMatch(t, []int{2, 3, 4}, vg.RaceIndex)
|
||||
assert.Equal(t, 0.69, vg.Sum)
|
||||
case 4:
|
||||
assert.ElementsMatch(t, []int{12, 13}, vg.RaceIndex)
|
||||
assert.Equal(t, 0.52, vg.Sum)
|
||||
case 5:
|
||||
assert.ElementsMatch(t, []int{15, 16, 17}, vg.RaceIndex)
|
||||
assert.InDelta(t, 2.49, vg.Sum, 0.001)
|
||||
default:
|
||||
assert.Len(t, vg.RaceIndex, 1)
|
||||
switch ri := vg.RaceIndex[0]; ri {
|
||||
case 1:
|
||||
assert.Equal(t, 0.24, vg.Sum)
|
||||
case 6:
|
||||
assert.Equal(t, 0.71, vg.Sum)
|
||||
case 11:
|
||||
assert.Equal(t, 0.843, vg.Sum)
|
||||
case 14:
|
||||
assert.Equal(t, 1.04, vg.Sum)
|
||||
case 18:
|
||||
assert.Equal(t, 3.18, vg.Sum)
|
||||
case 19:
|
||||
assert.Equal(t, 0.019, vg.Sum)
|
||||
default:
|
||||
assert.Failf(t, "unexpected group", "id=%v sum=%f", vg.RaceIndex, vg.Sum)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVotingWinners(t *testing.T) {
|
||||
gameVotes := 100.0
|
||||
var vg []*controller.VoteGroup
|
||||
var winners []int
|
||||
|
||||
vg = []*controller.VoteGroup{
|
||||
{Sum: 4.0, RaceIndex: []int{0}},
|
||||
{Sum: 66.65, RaceIndex: []int{1, 2}},
|
||||
{Sum: 5.0, RaceIndex: []int{3}},
|
||||
{Sum: 25.0, RaceIndex: []int{4, 5, 6}},
|
||||
}
|
||||
winners = controller.VotingWinners(vg, gameVotes)
|
||||
assert.Len(t, winners, 0)
|
||||
|
||||
vg = []*controller.VoteGroup{
|
||||
{Sum: 4.0, RaceIndex: []int{0}},
|
||||
{Sum: 66.666666666666666, RaceIndex: []int{1, 2}},
|
||||
{Sum: 5.0, RaceIndex: []int{3}},
|
||||
{Sum: 22.0, RaceIndex: []int{4, 5, 6}},
|
||||
}
|
||||
winners = controller.VotingWinners(vg, gameVotes)
|
||||
assert.ElementsMatch(t, winners, []int{1, 2})
|
||||
|
||||
vg = []*controller.VoteGroup{
|
||||
{Sum: 4.0, RaceIndex: []int{0}},
|
||||
{Sum: 3.33, RaceIndex: []int{1, 2}},
|
||||
{Sum: 66.67, RaceIndex: []int{3}},
|
||||
{Sum: 25.0, RaceIndex: []int{4, 5, 6}},
|
||||
}
|
||||
winners = controller.VotingWinners(vg, gameVotes)
|
||||
assert.ElementsMatch(t, winners, []int{3})
|
||||
}
|
||||
Reference in New Issue
Block a user