Add block drafting to mlp subpackage

This commit is contained in:
Hamcha 2019-06-17 22:56:05 +02:00
parent 3463fadfac
commit 7f04d7e929
Signed by: hamcha
GPG Key ID: A40413D21021EAEE
9 changed files with 316 additions and 40 deletions

33
mlp/block.go Normal file
View File

@ -0,0 +1,33 @@
package mlp
import "git.fromouter.space/mcg/draft"
// BlockPacks returns a pack provider for a block draft
func BlockPacks(block BlockID) (draft.PackProvider, error) {
var setids []SetID
switch block {
case BlockPremiere:
setids = []SetID{SetPremiere, SetCanterlotNights, SetCrystalGames, SetAbsoluteDiscord}
case BlockOdyssey:
setids = []SetID{SetEquestrialOdysseys, SetEquestrialOdysseys, SetHighMagic, SetMarksInTime}
case BlockDefenders:
setids = []SetID{SetFriendsForever, SetDefendersOfEquestria, SetSeaquestriaBeyond, SetFriendsForever}
}
sets := make([]*Set, len(setids))
for i, setid := range setids {
set, err := LoadSetMemory(setid)
if err != nil {
return nil, err
}
sets[i] = set
}
return func() []draft.Pack {
packs := make([]draft.Pack, len(sets))
for i, set := range sets {
packs[i] = draft.MakePack(set)
}
return packs
}, nil
}

64
mlp/block_test.go Normal file
View File

@ -0,0 +1,64 @@
package mlp_test
import (
"testing"
"git.fromouter.space/mcg/draft/mlp"
)
// Tests a block packprovider
func TestBlockProvider(t *testing.T) {
// Clean all loaded sets
mlp.CleanSetCache()
// Load all needed sets
sets := []mlp.SetID{
mlp.SetPremiere,
mlp.SetCanterlotNights,
mlp.SetCrystalGames,
mlp.SetAbsoluteDiscord,
mlp.SetEquestrialOdysseys,
mlp.SetHighMagic,
mlp.SetMarksInTime,
mlp.SetDefendersOfEquestria,
mlp.SetSeaquestriaBeyond,
mlp.SetFriendsForever,
}
for _, setid := range sets {
_, err := mlp.LoadSetHTTP(setid)
if err != nil {
t.Fatalf("Could not load needed set: %s", err.Error())
}
}
PR, err := mlp.BlockPacks(mlp.BlockPremiere)
if err != nil {
t.Fatalf("Could not load sets for premiere block draft: %s", err.Error())
}
EO, err := mlp.BlockPacks(mlp.BlockOdyssey)
if err != nil {
t.Fatalf("Could not load sets for odyssey block draft: %s", err.Error())
}
DE, err := mlp.BlockPacks(mlp.BlockDefenders)
if err != nil {
t.Fatalf("Could not load sets for defenders block draft: %s", err.Error())
}
// Generate packs for each block
_ = PR()
_ = EO()
_ = DE()
}
// Tests trying to make a packprovider of a block that's not loaded
func TestBlockError(t *testing.T) {
// Clean all loaded sets
mlp.CleanSetCache()
_, err := mlp.BlockPacks(mlp.BlockPremiere)
if err == nil {
t.Fatal("Could somehow generate a packprovider even though the set shouldn't be loaded")
}
if err != mlp.ErrSetNotLoaded {
t.Fatalf("Got an error but it's not the right one: %s", err.Error())
}
}

View File

@ -14,18 +14,18 @@ func TestAlternates(t *testing.T) {
// Load Premiere/CN as they have their own UR ratios // Load Premiere/CN as they have their own UR ratios
prSet, err := mlp.LoadSetHTTP(mlp.SetPremiere) prSet, err := mlp.LoadSetHTTP(mlp.SetPremiere)
if err != nil { if err != nil {
t.Errorf("Could not fetch set data: %s", err.Error()) t.Fatalf("Could not fetch set data: %s", err.Error())
} }
cnSet, err := mlp.LoadSetHTTP(mlp.SetCanterlotNights) cnSet, err := mlp.LoadSetHTTP(mlp.SetCanterlotNights)
if err != nil { if err != nil {
t.Errorf("Could not fetch set data: %s", err.Error()) t.Fatalf("Could not fetch set data: %s", err.Error())
} }
// Load set with Royal rares // Load set with Royal rares
eoSet, err := mlp.LoadSetHTTP(mlp.SetEquestrialOdysseys) eoSet, err := mlp.LoadSetHTTP(mlp.SetEquestrialOdysseys)
if err != nil { if err != nil {
t.Errorf("Could not fetch set data: %s", err.Error()) t.Fatalf("Could not fetch set data: %s", err.Error())
} }
// Find all Premiere URs // Find all Premiere URs
@ -62,7 +62,7 @@ func TestAlternates(t *testing.T) {
} }
if !prurfound { if !prurfound {
t.Errorf("No PR UR found after 1000 packs") t.Fatalf("No PR UR found after 1000 packs")
} }
// Get some CN packs and search for URs // Get some CN packs and search for URs
@ -83,7 +83,7 @@ func TestAlternates(t *testing.T) {
} }
if !cnurfound { if !cnurfound {
t.Errorf("No CN UR found after 1000 packs") t.Fatalf("No CN UR found after 1000 packs")
} }
eorrfound := false eorrfound := false
@ -98,20 +98,20 @@ func TestAlternates(t *testing.T) {
} }
if !eorrfound { if !eorrfound {
t.Errorf("No EO RR found after 100k packs") t.Fatalf("No EO RR found after 100k packs")
} }
} }
// TestPackFixedSets tries to get packs a set that isn't a true set // TestPackFixedSets tries to get packs a set that isn't a true set
// This should result in empty packs // This should result in empty packs
func TestPackFixedSets(t *testing.T) { func TestPackFixedSets(t *testing.T) {
set, err := mlp.LoadSet(mlp.SetRockNRave, []byte("{}")) set, err := mlp.LoadSetBytes(mlp.SetRockNRave, []byte("{}"))
if err != nil { if err != nil {
t.Errorf("Could not load set: %s", err.Error()) t.Fatalf("Could not load set: %s", err.Error())
} }
pack := draft.MakePack(set) pack := draft.MakePack(set)
if len(pack) != 0 { if len(pack) != 0 {
t.Errorf("Expected an empty pack but got %d cards", len(pack)) t.Fatalf("Expected an empty pack but got %d cards", len(pack))
} }
} }

View File

@ -28,13 +28,13 @@ func TestDraftI8PCube(t *testing.T) {
pack3 := draft.MakePack(cube.Problems) pack3 := draft.MakePack(cube.Problems)
if len(pack1) != 12 { if len(pack1) != 12 {
t.Errorf("Expected 12 cards in pack 1 but got %d", len(pack1)) t.Fatalf("Expected 12 cards in pack 1 but got %d", len(pack1))
} }
if len(pack2) != 12 { if len(pack2) != 12 {
t.Errorf("Expected 12 cards in pack 2 but got %d", len(pack2)) t.Fatalf("Expected 12 cards in pack 2 but got %d", len(pack2))
} }
if len(pack3) != 2 { if len(pack3) != 2 {
t.Errorf("Expected 2 cards in pack 3 but got %d", len(pack3)) t.Fatalf("Expected 2 cards in pack 3 but got %d", len(pack3))
} }
t.Logf("Cards in pack1: %s\n", pack1) t.Logf("Cards in pack1: %s\n", pack1)
@ -60,7 +60,7 @@ func TestDryCube(t *testing.T) {
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema()) cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema())
pack := draft.MakePack(cube.Main) pack := draft.MakePack(cube.Main)
if len(pack) != 11 { if len(pack) != 11 {
t.Errorf("Expected 11 cards in pack but got %d", len(pack)) t.Fatalf("Expected 11 cards in pack but got %d", len(pack))
} }
} }

View File

@ -31,3 +31,13 @@ const (
SetSeaquestriaBeyond SetID = "SB" SetSeaquestriaBeyond SetID = "SB"
SetFriendsForever SetID = "FF" SetFriendsForever SetID = "FF"
) )
// BlockID denotes a certain block
type BlockID string
// All blocks
const (
BlockPremiere BlockID = "PR" // Premiere block - PR/CN/CG/AD
BlockOdyssey BlockID = "EO" // Odyssey block - EO/HM/MT
BlockDefenders BlockID = "DE" // Defenders block - DE/SB/FF
)

View File

@ -2,6 +2,7 @@ package mlp
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -46,16 +47,65 @@ func (c Card) ToDraftCard() draft.Card {
// PowerRequirement denotes one or more power requirements, colored or not // PowerRequirement denotes one or more power requirements, colored or not
type PowerRequirement map[string]int type PowerRequirement map[string]int
// LoadSet loads a set with a specified ID from JSON var loadedSets = make(map[SetID]*Set)
func LoadSet(id SetID, setdata []byte) (*Set, error) {
// Errors
var (
ErrSetNotLoaded = errors.New("set not loaded")
)
// CleanSetCache removes all loaded sets from memory
func CleanSetCache() {
loadedSets = make(map[SetID]*Set)
}
// LoadSetData loads sets from data
func LoadSetData(sets map[SetID][]byte) error {
for setid, setdata := range sets {
_, err := LoadSetBytes(setid, setdata)
if err != nil {
return err
}
}
return nil
}
// LoadSetMemory loads a set from memory (must have been previously loaded with LoadSetData or other functions)
func LoadSetMemory(id SetID) (*Set, error) {
// Check if set is already loaded
if set, ok := loadedSets[id]; ok {
return set, nil
}
// Not loaded, return error
return nil, ErrSetNotLoaded
}
// LoadSetBytes loads a set with a specified ID from JSON
func LoadSetBytes(id SetID, setdata []byte) (*Set, error) {
// Check if set is already loaded
if set, ok := loadedSets[id]; ok {
return set, nil
}
var set Set var set Set
err := json.Unmarshal(setdata, &set) err := json.Unmarshal(setdata, &set)
set.ID = id set.ID = id
// If set loaded fine, cache it
if err == nil {
loadedSets[set.ID] = &set
}
return &set, err return &set, err
} }
// LoadSetHTTP loads a set using MCG's remote server // LoadSetHTTP loads a set using MCG's remote server
func LoadSetHTTP(id SetID) (*Set, error) { func LoadSetHTTP(id SetID) (*Set, error) {
// Check if set is already loaded
if set, ok := loadedSets[id]; ok {
return set, nil
}
// Get SetID as string and make it lowercase // Get SetID as string and make it lowercase
setid := strings.ToLower(string(id)) setid := strings.ToLower(string(id))
@ -72,5 +122,5 @@ func LoadSetHTTP(id SetID) (*Set, error) {
return nil, err return nil, err
} }
return LoadSet(id, data) return LoadSetBytes(id, data)
} }

View File

@ -10,9 +10,12 @@ import (
// TestSet retrieves a set online and generates a couple packs with it // TestSet retrieves a set online and generates a couple packs with it
// This test *requires* an internet connection! // This test *requires* an internet connection!
func TestSet(t *testing.T) { func TestSet(t *testing.T) {
// Clean all loaded sets
mlp.CleanSetCache()
deSet, err := mlp.LoadSetHTTP(mlp.SetDefendersOfEquestria) deSet, err := mlp.LoadSetHTTP(mlp.SetDefendersOfEquestria)
if err != nil { if err != nil {
t.Errorf("Could not fetch set data: %s", err.Error()) t.Fatalf("Could not fetch set data: %s", err.Error())
} }
pack1 := draft.MakePack(deSet) pack1 := draft.MakePack(deSet)
@ -20,10 +23,10 @@ func TestSet(t *testing.T) {
// Make sure both packs have the exact number of cards // Make sure both packs have the exact number of cards
if len(pack1) != 12 { if len(pack1) != 12 {
t.Errorf("Expected 12 cards in pack 1 but got %d", len(pack1)) t.Fatalf("Expected 12 cards in pack 1 but got %d", len(pack1))
} }
if len(pack2) != 12 { if len(pack2) != 12 {
t.Errorf("Expected 12 cards in pack 2 but got %d", len(pack2)) t.Fatalf("Expected 12 cards in pack 2 but got %d", len(pack2))
} }
t.Logf("Cards in pack 1: %s\n", pack1) t.Logf("Cards in pack 1: %s\n", pack1)
@ -35,6 +38,123 @@ func TestSet(t *testing.T) {
func TestWrongSet(t *testing.T) { func TestWrongSet(t *testing.T) {
_, err := mlp.LoadSetHTTP("nopenope") _, err := mlp.LoadSetHTTP("nopenope")
if err == nil { if err == nil {
t.Errorf("Expected an error but didn't get one!") t.Fatalf("Expected an error but didn't get one!")
}
}
// TestAllLoads tests all the LoadSet* functions
// This test *requires* an internet connection!
func TestAllLoads(t *testing.T) {
// Clean all loaded sets
mlp.CleanSetCache()
// Test LoadSetHTTP
_, err := mlp.LoadSetHTTP(mlp.SetAbsoluteDiscord)
if err != nil {
t.Fatalf("[LoadSetHTTP] Could not fetch set data from the internet: %s", err.Error())
}
// Load set from bytes
_, err = mlp.LoadSetBytes(mlp.SetCelestialSolstice, []byte("{}"))
if err != nil {
t.Fatalf("[LoadSetBytes] Could not load set: %s", err.Error())
}
// Load set to memory
err = mlp.LoadSetData(map[mlp.SetID][]byte{
mlp.SetRockNRave: []byte("{}"),
})
if err != nil {
t.Fatalf("[LoadSetData] Could not load set: %s", err.Error())
}
// Load set from memory
_, err = mlp.LoadSetMemory(mlp.SetRockNRave)
if err != nil {
t.Fatalf("[LoadSetMemory] Could not load set: %s", err.Error())
}
}
// TestNotLoadedErr tests that LoadSetMemory fails if set is not cached
func TestNotLoadedErr(t *testing.T) {
// Clean all sets from memory first
mlp.CleanSetCache()
// Load set from memory (should fail)
_, err := mlp.LoadSetMemory(mlp.SetFriendsForever)
if err == nil {
t.Fatal("[LoadSetMemory] Set loaded but shouldn't")
} else if err != mlp.ErrSetNotLoaded {
t.Fatalf("[LoadSetMemory] Set not loaded but error is not the right one: %s", err.Error())
}
// Actually load set
err = mlp.LoadSetData(map[mlp.SetID][]byte{
mlp.SetFriendsForever: []byte("{}"),
})
if err != nil {
t.Fatalf("[LoadSetData] Could not load set: %s", err.Error())
}
// Load set from memory (should succeed)
_, err = mlp.LoadSetMemory(mlp.SetFriendsForever)
if err != nil {
t.Fatalf("[LoadSetMemory] Could not load set: %s", err.Error())
}
}
// TestLoadCache tests caching on all Load functions that support it
func TestLoadCache(t *testing.T) {
// Clean all sets from memory first
mlp.CleanSetCache()
// Load dummy set
err := mlp.LoadSetData(map[mlp.SetID][]byte{
mlp.SetFriendsForever: []byte("{}"),
})
if err != nil {
t.Fatalf("[LoadSetData] Could not load set: %s", err.Error())
}
//
// Try all set loading functions to trigger the cache
//
_, err = mlp.LoadSetMemory(mlp.SetFriendsForever)
if err != nil {
t.Fatalf("[LoadSetMemory] Could not load set: %s", err.Error())
}
_, err = mlp.LoadSetBytes(mlp.SetFriendsForever, []byte("THIS SHOULD BE IGNORED"))
if err != nil {
t.Fatalf("[LoadSetBytes] Could not load set: %s", err.Error())
}
loadedset, err := mlp.LoadSetHTTP(mlp.SetFriendsForever)
if err != nil {
t.Fatalf("[LoadSetHTTP] Could not load set: %s", err.Error())
}
// Check that loaded set via HTTP is the dummy cache and not the real thing
if len(loadedset.Cards) != 0 {
t.Fatalf("[LoadSetHTTP] Set not loaded from cache")
}
}
// TestMalformedJSONLoad tests that LoadSetData/LoadSetBytes return an error when given malformed JSON
func TestMalformedJSONLoad(t *testing.T) {
// Clean all sets from memory first
mlp.CleanSetCache()
_, err := mlp.LoadSetBytes(mlp.SetFriendsForever, []byte("THIS SHOULD FAIL"))
if err == nil {
t.Fatalf("LoadSetBytes with invalid data succeeded but shouldn't have")
}
err = mlp.LoadSetData(map[mlp.SetID][]byte{
mlp.SetFriendsForever: []byte("THIS SHOULD FAIL"),
})
if err == nil {
t.Fatalf("LoadSetData with invalid data succeeded but shouldn't have")
} }
} }

View File

@ -18,7 +18,7 @@ func TestCreatePod(t *testing.T) {
pod := draft.MakePod(PlayersPerPod, testProvider) pod := draft.MakePod(PlayersPerPod, testProvider)
if len(pod.Players) != PlayersPerPod { if len(pod.Players) != PlayersPerPod {
t.Fatalf("Expected %d players in pod but got %d\n", PlayersPerPod, len(pod.Players)) t.Fatalf("Expected %d players in pod but got %d", PlayersPerPod, len(pod.Players))
} }
for i, player := range pod.Players { for i, player := range pod.Players {
@ -27,7 +27,7 @@ func TestCreatePod(t *testing.T) {
t.Logf(" - Pack #%d: %s", packi, pack) t.Logf(" - Pack #%d: %s", packi, pack)
} }
if len(player.Packs) != PacksPerPlayer { if len(player.Packs) != PacksPerPlayer {
t.Fatalf("Player #%d has %d packs but should have %d\n", i, PacksPerPlayer, len(player.Packs)) t.Fatalf("Player #%d has %d packs but should have %d", i, PacksPerPlayer, len(player.Packs))
} }
} }
} }
@ -51,7 +51,7 @@ func TestPick(t *testing.T) {
// Open new packs! // Open new packs!
err := pod.OpenPacks() err := pod.OpenPacks()
if err != nil { if err != nil {
t.Fatalf("Got an error while opening packs #%d: %s\n", packnum, err.Error()) t.Fatalf("Got an error while opening packs #%d: %s", packnum, err.Error())
} }
for picknum := 0; picknum < PACKSIZE; picknum++ { for picknum := 0; picknum < PACKSIZE; picknum++ {
@ -59,7 +59,7 @@ func TestPick(t *testing.T) {
// Pick first card for each player // Pick first card for each player
err := player.Pick(player.CurrentPack[0]) err := player.Pick(player.CurrentPack[0])
if err != nil { if err != nil {
t.Fatalf("Tried picking first card in pack but couldn't: %s\n", err.Error()) t.Fatalf("Tried picking first card in pack but couldn't: %s", err.Error())
} }
} }
@ -69,16 +69,15 @@ func TestPick(t *testing.T) {
// Pass packs around // Pass packs around
err := pod.NextPacks() err := pod.NextPacks()
if err != nil { if err != nil {
t.Fatalf("Got an error while passing packs: %s\n", err.Error()) t.Fatalf("Got an error while passing packs: %s", err.Error())
} }
case <-pod.ReadyNextPack: case <-pod.ReadyNextPack:
break break
default: default:
t.Fatal("ReadyNextPick/ReadyNextPack channel should trigger but hasn't\n") t.Fatal("ReadyNextPick/ReadyNextPack channel should trigger but hasn't")
} }
} }
} }
} }
func TestPodErrors(t *testing.T) { func TestPodErrors(t *testing.T) {
@ -95,17 +94,17 @@ func TestPodErrors(t *testing.T) {
// Pick a card that doesn't exist // Pick a card that doesn't exist
err := pod.Players[0].Pick(draft.Card{ID: "nana"}) err := pod.Players[0].Pick(draft.Card{ID: "nana"})
if err == nil { if err == nil {
t.Fatal("Tried picking inexistant card but it succeeded\n") t.Fatal("Tried picking inexistant card but it succeeded")
} else if err != draft.ErrNotInPack { } else if err != draft.ErrNotInPack {
t.Fatalf("Got error for wrong pick but not the right one: %s\n", err.Error()) t.Fatalf("Got error for wrong pick but not the right one: %s", err.Error())
} }
// Try getting packs from nearby players when no one is passing them // Try getting packs from nearby players when no one is passing them
err = pod.NextPacks() err = pod.NextPacks()
if err == nil { if err == nil {
t.Fatal("Tried getting inexistant packs from nearby players but it succeeded\n") t.Fatal("Tried getting inexistant packs from nearby players but it succeeded")
} else if err != draft.ErrNoPendingPack { } else if err != draft.ErrNoPendingPack {
t.Fatalf("Got error for non existant pack but not the right one: %s\n", err.Error()) t.Fatalf("Got error for non existant pack but not the right one: %s", err.Error())
} }
// Try opening more packs than each player has // Try opening more packs than each player has
@ -113,8 +112,8 @@ func TestPodErrors(t *testing.T) {
err = pod.OpenPacks() err = pod.OpenPacks()
} }
if err == nil { if err == nil {
t.Fatal("Tried opening too many packs but it succeeded\n") t.Fatal("Tried opening too many packs but it succeeded")
} else if err != draft.ErrNoPacksLeft { } else if err != draft.ErrNoPacksLeft {
t.Fatalf("Got error for too many packs but not the right one: %s\n", err.Error()) t.Fatalf("Got error for too many packs but not the right one: %s", err.Error())
} }
} }

View File

@ -31,16 +31,16 @@ func TestSetRepeatable(t *testing.T) {
pack := draft.MakePack(testSet) pack := draft.MakePack(testSet)
if len(pack) < PACKSIZE { if len(pack) < PACKSIZE {
t.Errorf("Pack expected to contain %d cards, contains %d", PACKSIZE, len(pack)) t.Fatalf("Pack expected to contain %d cards, contains %d", PACKSIZE, len(pack))
} }
// Check that all cards have something in it // Check that all cards have something in it
for i, card := range pack { for i, card := range pack {
if card.ID == "" { if card.ID == "" {
t.Errorf("Pack contains \"empty\" card") t.Fatalf("Pack contains \"empty\" card")
} }
if card.ID != "a" && card.ID != "b" && card.ID != "c" { if card.ID != "a" && card.ID != "b" && card.ID != "c" {
t.Errorf("Pack contains unexpected card %s at index %d, not contained in pool", card.ID, i) t.Fatalf("Pack contains unexpected card %s at index %d, not contained in pool", card.ID, i)
} }
} }
} }
@ -82,7 +82,7 @@ func TestAlternateProviders(t *testing.T) {
// After 500k packs, I'm expecting distribution to be within 2% // After 500k packs, I'm expecting distribution to be within 2%
if distribution < 0.29 || distribution > 0.31 { if distribution < 0.29 || distribution > 0.31 {
t.Errorf("Distribution is sketchy after 500k packs: %f%%", distribution*100) t.Fatalf("Distribution is sketchy after 500k packs: %f%%", distribution*100)
} }
} }
@ -93,14 +93,14 @@ func TestCubeOverflow(t *testing.T) {
// Pack 2 can only contain 4 cards, as there are not enough cards to fill it // Pack 2 can only contain 4 cards, as there are not enough cards to fill it
if len(pack2) >= PACKSIZE { if len(pack2) >= PACKSIZE {
t.Errorf("Pack 2 expected to contain only 4 cards, has %d", len(pack2)) t.Fatalf("Pack 2 expected to contain only 4 cards, has %d", len(pack2))
} }
// Check for duplicates // Check for duplicates
allcards := append(pack1, pack2...) allcards := append(pack1, pack2...)
uniq := sliceUniq(allcards) uniq := sliceUniq(allcards)
if len(allcards) != len(uniq) { if len(allcards) != len(uniq) {
t.Errorf("Duplicate cards found across packs") t.Fatalf("Duplicate cards found across packs")
} }
} }
@ -110,7 +110,7 @@ func TestPackString(t *testing.T) {
expected := "a b c" expected := "a b c"
if p.String() != expected { if p.String() != expected {
t.Errorf("Expected \"%s\" but got \"%s\"", expected, p) t.Fatalf("Expected \"%s\" but got \"%s\"", expected, p)
} }
} }