feat: voting procedures

This commit is contained in:
Ilia Denisov
2026-01-30 12:18:32 +03:00
parent abf72c16b4
commit 824f6609ab
12 changed files with 581 additions and 18 deletions
+301
View File
@@ -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})
}