Compare commits

..

9 Commits

Author SHA1 Message Date
Bel LaPointe
94d3d97645 test fill 2024-12-15 10:23:13 -07:00
Bel LaPointe
eb3a30ec8a test hangs due to fill inf looping 2024-12-15 10:19:04 -07:00
Bel LaPointe
6340469d53 killing them 200 line functions one at a time 2024-12-15 09:55:08 -07:00
Bel LaPointe
d05ab6d0ec killing does not change assignment 2024-12-15 09:51:40 -07:00
Bel LaPointe
ae593bc092 refactor making new AllKillWords just without assignees and now 2024-12-15 09:50:34 -07:00
Bel LaPointe
ff5071215a functional looking 2024-12-15 09:49:09 -07:00
Bel LaPointe
b9d06f81ba functional looking 2024-12-15 09:47:11 -07:00
Bel LaPointe
51c7cc4496 sh 2024-12-15 09:46:21 -07:00
Bel LaPointe
d7e4196f61 refactorinnng 2024-12-15 09:44:13 -07:00
2 changed files with 290 additions and 72 deletions

View File

@@ -356,7 +356,6 @@ func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string,
if err != nil {
return err
}
now := time.Now()
event := EventAssignmentRotation{
Type: AssignmentRotation,
@@ -366,98 +365,152 @@ func (games Games) CreateEventAssignmentRotation(ctx context.Context, id string,
Word: word,
Points: points,
},
AllKillWords: make(AllKillWords),
}
toAssign := []string{}
doNotAssign := map[string]string{}
prevAllKillWords := make(AllKillWords)
for k, v := range state.Players {
v := v.KillWords
toAssign = append(toAssign, k)
doNotAssign[k] = v.Assignee
event.AllKillWords[k] = KillWords{
Global: v.Global,
Assigned: now,
Assignee: "",
Assignment: v.Assignment,
}
prevAllKillWords[k] = v.KillWords
}
if killerState, ok := state.Players[killer]; !ok {
} else if victimState, ok := state.Players[victim]; !ok {
event.AllKillWords = prevAllKillWords.ShuffleAssignees(killer, victim, word)
event.AllKillWords = event.AllKillWords.FillKillWords()
return games.createEvent(ctx, id, event)
}
func (prev AllKillWords) ShuffleAssignees(killer, victim, word string) AllKillWords {
m := prev.withoutAssignees()
if _, ok := prev[killer]; !ok {
} else if victimState, ok := prev[victim]; !ok {
} else {
event.AllKillWords[killer] = KillWords{
Global: killerState.KillWords.Global,
Assigned: now,
Assignee: victimState.KillWords.Assignee,
Assignment: killerState.KillWords.Assignment,
}
toAssign = slices.DeleteFunc(toAssign, func(s string) bool { return s == event.AllKillWords[killer].Assignee })
if killerState.KillWords.Global.Word != word {
victimState.KillWords.Assignment = Assignment{}
state.Players[victim] = victimState
}
m.assign(killer, victimState.Assignee)
}
for !func() bool {
toAssign := slices.Clone(toAssign)
doNotAssign := maps.Clone(doNotAssign)
allKillWords := maps.Clone(event.AllKillWords)
for i := range toAssign {
j := rand.Intn(i + 1)
toAssign[i], toAssign[j] = toAssign[j], toAssign[i]
allKillWords := maps.Clone(m)
unassigned := allKillWords.unassigned()
pop := func() string {
result := unassigned[0]
unassigned = unassigned[1:]
return result
}
for k, v := range allKillWords {
if k == toAssign[0] || doNotAssign[k] == toAssign[0] {
if v.Assignee != "" {
continue
}
v.Assignee = pop()
if k == v.Assignee || prev[k].Assignee == v.Assignee {
return false
}
allKillWords[k] = KillWords{
Global: v.Global,
Assigned: now,
Assignee: toAssign[0],
Assignment: v.Assignment,
}
toAssign = toAssign[1:]
allKillWords[k] = v
}
event.AllKillWords = allKillWords
if len(unassigned) > 0 {
panic(unassigned)
}
m = allKillWords
return true
}() {
}
for k, v := range event.AllKillWords {
if v.Global.Word == "" {
v.Global = KillWord{Word: event.AllKillWords.unusedGlobal(), Points: -1}
event.AllKillWords[k] = v
}
if len(v.Assignment.Public) == 0 {
v.Assignment.Public = []KillWord{}
for i := 0; i < 2; i++ {
v.Assignment.Public = append(v.Assignment.Public, KillWord{Word: event.AllKillWords.unusedPublic(), Points: 50})
event.AllKillWords[k] = v
}
}
if len(v.Assignment.Private) == 0 {
v.Assignment.Private = []KillWord{}
for i := 0; i < 2; i++ {
v.Assignment.Private = append(v.Assignment.Private, KillWord{Word: event.AllKillWords.unusedPrivate(), Points: 100})
event.AllKillWords[k] = v
}
}
return m
}
return games.createEvent(ctx, id, event)
func (m AllKillWords) assign(killer, victim string) {
v := m[killer]
v.Assignee = victim
m[killer] = v
}
func (m AllKillWords) withoutAssignees() AllKillWords {
now := time.Now()
result := make(AllKillWords)
for k := range m {
result[k] = KillWords{
Global: m[k].Global,
Assigned: now,
Assignee: "",
Assignment: m[k].Assignment,
}
}
return result
}
func (m AllKillWords) unassigned() []string {
var result []string
for k := range m {
if !slices.Contains(m.assigned(), k) {
result = append(result, k)
}
}
for i := range result {
j := rand.Intn(i + 1)
result[i], result[j] = result[j], result[i]
}
return result
}
func (m AllKillWords) assigned() []string {
var result []string
for k := range m {
v := m[k].Assignee
if v == "" {
continue
}
result = append(result, v)
}
return result
}
//go:embed holiday.txt
var wordsHoliday string
func (m AllKillWords) unusedGlobal() string {
pool := strings.Fields(wordsHoliday)
func (m AllKillWords) FillKillWords() AllKillWords {
return m.fillKillWords(
strings.Fields(wordsHoliday),
1,
strings.Fields(wordsHoliday), // TODO medium difficulty
2,
strings.Fields(wordsHoliday), // TODO hard difficulty
)
}
func (m AllKillWords) fillKillWords(
poolGlobal []string,
nPublic int,
poolPublic []string,
nPrivate int,
poolPrivate []string,
) AllKillWords {
result := maps.Clone(m)
m = result
for k, v := range m {
if v.Global.Word == "" {
v.Global = KillWord{Word: m.unusedGlobal(poolGlobal), Points: -1}
m[k] = v
}
if len(v.Assignment.Public) == 0 {
v.Assignment.Public = []KillWord{}
for i := 0; i < nPublic; i++ {
v.Assignment.Public = append(v.Assignment.Public, KillWord{Word: m.unusedPublic(poolPublic), Points: 50})
m[k] = v
}
}
if len(v.Assignment.Private) == 0 {
v.Assignment.Private = []KillWord{}
for i := 0; i < nPrivate; i++ {
v.Assignment.Private = append(v.Assignment.Private, KillWord{Word: m.unusedPrivate(poolPrivate), Points: 100})
m[k] = v
}
}
}
return m
}
func (m AllKillWords) unusedGlobal(pool []string) string {
inUse := func() []string {
result := []string{}
for _, killWords := range m {
@@ -473,9 +526,7 @@ func (m AllKillWords) unusedGlobal() string {
}
}
// TODO hard difficulty
func (m AllKillWords) unusedPrivate() string {
pool := strings.Fields(wordsHoliday)
func (m AllKillWords) unusedPrivate(pool []string) string {
inUse := func() []string {
result := []string{}
for _, killWords := range m {
@@ -493,9 +544,7 @@ func (m AllKillWords) unusedPrivate() string {
}
}
// TODO medium difficulty
func (m AllKillWords) unusedPublic() string {
pool := strings.Fields(wordsHoliday)
func (m AllKillWords) unusedPublic(pool []string) string {
inUse := func() []string {
result := []string{}
for _, killWords := range m {

View File

@@ -1,6 +1,7 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -213,3 +214,171 @@ func TestParseEvent(t *testing.T) {
})
}
}
func TestAllKillWordsFill(t *testing.T) {
kw := func(p int, w string) KillWord {
return KillWord{Word: w, Points: p}
}
kws := func(points int, w string) []KillWord {
if w == "" {
return nil
}
return []KillWord{kw(points, w)}
}
ass := func(pub, pri string) Assignment {
return Assignment{
Public: kws(50, pub),
Private: kws(100, pri),
}
}
cases := map[string]struct {
given KillWords
expect KillWords
}{
"full": {
given: KillWords{
Global: kw(-1, "global"),
Assignment: ass("pub", "pri"),
},
expect: KillWords{
Global: kw(-1, "global"),
Assignment: ass("pub", "pri"),
},
},
"no ass": {
given: KillWords{
Global: kw(-1, "global"),
Assignment: Assignment{},
},
expect: KillWords{
Global: kw(-1, "global"),
Assignment: ass("filled-public", "filled-private"),
},
},
"no pub": {
given: KillWords{
Global: kw(-1, "global"),
Assignment: ass("", "pri"),
},
expect: KillWords{
Global: kw(-1, "global"),
Assignment: ass("filled-public", "pri"),
},
},
"no pri": {
given: KillWords{
Global: kw(-1, "global"),
Assignment: ass("pub", ""),
},
expect: KillWords{
Global: kw(-1, "global"),
Assignment: ass("pub", "filled-private"),
},
},
"empty": {
given: KillWords{},
expect: KillWords{
Global: kw(-1, "filled-global"),
Assignment: ass("filled-public", "filled-private"),
},
},
"no global": {
given: KillWords{
Assignment: ass("pub", "pri"),
},
expect: KillWords{
Global: kw(-1, "filled-global"),
Assignment: ass("pub", "pri"),
},
},
}
equal := func(a, b KillWords) bool {
ba, _ := json.Marshal(a)
bb, _ := json.Marshal(b)
return bytes.Equal(ba, bb)
}
for name, d := range cases {
c := d
t.Run(name, func(t *testing.T) {
akw := make(AllKillWords)
akw[name] = c.given
akw = akw.fillKillWords(
[]string{"filled-global"},
1,
[]string{"filled-public"},
1,
[]string{"filled-private"},
)
got := akw[name]
if !equal(c.expect, got) {
t.Errorf("expected \n\t%+v but got \n\t%+v", c.expect, got)
}
})
}
}
func TestAllKillWordsUnused(t *testing.T) {
t.Run("empty", func(t *testing.T) {
akw := make(AllKillWords)
if got := akw.unusedPublic([]string{"x"}); got != "x" {
t.Error("empty playerbase didnt think only option was unused")
}
if got := akw.unusedPrivate([]string{"x"}); got != "x" {
t.Error("empty playerbase didnt think only option was unused")
}
if got := akw.unusedGlobal([]string{"x"}); got != "x" {
t.Error("empty playerbase didnt think only option was unused")
}
})
t.Run("dont return used", func(t *testing.T) {
t.Run("private", func(t *testing.T) {
akw := make(AllKillWords)
akw["k"] = KillWords{
Global: KillWord{Word: "x"},
Assignment: Assignment{
Private: []KillWord{{}, {Word: "y"}},
Public: []KillWord{{}, {Word: "x"}},
},
}
got := akw.unusedPrivate([]string{"x", "y"})
if got != "x" {
t.Error("didnt return only unused option")
}
})
t.Run("global", func(t *testing.T) {
akw := make(AllKillWords)
akw["k"] = KillWords{
Global: KillWord{Word: "y"},
Assignment: Assignment{
Private: []KillWord{{}, {Word: "x"}},
Public: []KillWord{{}, {Word: "x"}},
},
}
got := akw.unusedGlobal([]string{"x", "y"})
if got != "x" {
t.Error("didnt return only unused option")
}
})
t.Run("public", func(t *testing.T) {
akw := make(AllKillWords)
akw["k"] = KillWords{
Global: KillWord{Word: "x"},
Assignment: Assignment{
Private: []KillWord{{}, {Word: "x"}},
Public: []KillWord{{}, {Word: "y"}},
},
}
got := akw.unusedPublic([]string{"x", "y"})
if got != "x" {
t.Error("didnt return only unused option")
}
})
})
}