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
prSet, err := mlp.LoadSetHTTP(mlp.SetPremiere)
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)
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
eoSet, err := mlp.LoadSetHTTP(mlp.SetEquestrialOdysseys)
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
@ -62,7 +62,7 @@ func TestAlternates(t *testing.T) {
}
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
@ -83,7 +83,7 @@ func TestAlternates(t *testing.T) {
}
if !cnurfound {
t.Errorf("No CN UR found after 1000 packs")
t.Fatalf("No CN UR found after 1000 packs")
}
eorrfound := false
@ -98,20 +98,20 @@ func TestAlternates(t *testing.T) {
}
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
// This should result in empty packs
func TestPackFixedSets(t *testing.T) {
set, err := mlp.LoadSet(mlp.SetRockNRave, []byte("{}"))
set, err := mlp.LoadSetBytes(mlp.SetRockNRave, []byte("{}"))
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)
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)
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 {
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 {
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)
@ -60,7 +60,7 @@ func TestDryCube(t *testing.T) {
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema())
pack := draft.MakePack(cube.Main)
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"
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 (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
@ -46,16 +47,65 @@ func (c Card) ToDraftCard() draft.Card {
// PowerRequirement denotes one or more power requirements, colored or not
type PowerRequirement map[string]int
// LoadSet loads a set with a specified ID from JSON
func LoadSet(id SetID, setdata []byte) (*Set, error) {
var loadedSets = make(map[SetID]*Set)
// 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
err := json.Unmarshal(setdata, &set)
set.ID = id
// If set loaded fine, cache it
if err == nil {
loadedSets[set.ID] = &set
}
return &set, err
}
// LoadSetHTTP loads a set using MCG's remote server
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
setid := strings.ToLower(string(id))
@ -72,5 +122,5 @@ func LoadSetHTTP(id SetID) (*Set, error) {
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
// This test *requires* an internet connection!
func TestSet(t *testing.T) {
// Clean all loaded sets
mlp.CleanSetCache()
deSet, err := mlp.LoadSetHTTP(mlp.SetDefendersOfEquestria)
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)
@ -20,10 +23,10 @@ func TestSet(t *testing.T) {
// Make sure both packs have the exact number of cards
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 {
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)
@ -35,6 +38,123 @@ func TestSet(t *testing.T) {
func TestWrongSet(t *testing.T) {
_, err := mlp.LoadSetHTTP("nopenope")
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)
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 {
@ -27,7 +27,7 @@ func TestCreatePod(t *testing.T) {
t.Logf(" - Pack #%d: %s", packi, pack)
}
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!
err := pod.OpenPacks()
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++ {
@ -59,7 +59,7 @@ func TestPick(t *testing.T) {
// Pick first card for each player
err := player.Pick(player.CurrentPack[0])
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
err := pod.NextPacks()
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:
break
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) {
@ -95,17 +94,17 @@ func TestPodErrors(t *testing.T) {
// Pick a card that doesn't exist
err := pod.Players[0].Pick(draft.Card{ID: "nana"})
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 {
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
err = pod.NextPacks()
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 {
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
@ -113,8 +112,8 @@ func TestPodErrors(t *testing.T) {
err = pod.OpenPacks()
}
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 {
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)
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
for i, card := range pack {
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" {
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%
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
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
allcards := append(pack1, pack2...)
uniq := sliceUniq(allcards)
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"
if p.String() != expected {
t.Errorf("Expected \"%s\" but got \"%s\"", expected, p)
t.Fatalf("Expected \"%s\" but got \"%s\"", expected, p)
}
}