Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
Hamcha | 198f1a6704 | |
Hamcha | 69f54c08cc | |
Hamcha | 0e2c613375 | |
Hamcha | e9e5ffdada | |
Hamcha | 3184ce476a | |
Hamcha | d2a2e4526b | |
Hamcha | 37645e37f7 |
|
@ -108,7 +108,7 @@ func (set *Set) PackSchema() draft.PackSchema {
|
|||
|
||||
// ProviderByRarity returns a provider for a given rarity level from a given set
|
||||
func (set *Set) ProviderByRarity(rarity Rarity) draft.CardProvider {
|
||||
var collection []draft.Card
|
||||
collection := []draft.Card{}
|
||||
// RR flow is super easy, just pick one from our hardcoded list
|
||||
if rarity == RarityRoyalRare {
|
||||
rr, ok := royalRares[set.ID]
|
||||
|
@ -126,6 +126,10 @@ func (set *Set) ProviderByRarity(rarity Rarity) draft.CardProvider {
|
|||
}
|
||||
return func(n int) []draft.Card {
|
||||
out := make([]draft.Card, n)
|
||||
if len(collection) < 1 {
|
||||
// Something really wrong happened here
|
||||
return out
|
||||
}
|
||||
for n := range out {
|
||||
// Pick a RR at random
|
||||
idx := rand.Intn(len(collection))
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
// TestAlternates tries to get alternates (SR/UR/RR)
|
||||
// This might require a while since it needs to generate MANY packs!
|
||||
// This test *requires* an internet connection!
|
||||
func TestAlternates(t *testing.T) {
|
||||
// Load Premiere/CN as they have their own UR ratios
|
||||
prSet, err := mlp.LoadSetHTTP(mlp.SetPremiere)
|
||||
|
|
11
mlp/card.go
11
mlp/card.go
|
@ -24,6 +24,7 @@ type Card struct {
|
|||
ProblemBonus *int `json:",omitempty"`
|
||||
ProblemOpponentPower int `json:",omitempty"`
|
||||
ProblemRequirement PowerRequirement `json:",omitempty"`
|
||||
Boosted *Card `json:",omitempty"`
|
||||
}
|
||||
|
||||
// ToDraftCard converts cards to draft.Card
|
||||
|
@ -50,9 +51,19 @@ func LoadCardList(list []string, fetch bool) (out []Card, err error) {
|
|||
err = ErrInvalidCardID
|
||||
return
|
||||
}
|
||||
|
||||
// Get Set ID
|
||||
setid := SetID(strings.ToUpper(card[:2]))
|
||||
|
||||
// Check for promo card
|
||||
for _, promoid := range promoIDs {
|
||||
if strings.ToLower(card) == promoid {
|
||||
card = strings.ToLower(card)
|
||||
setid = SetPromo
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get set
|
||||
var set *Set
|
||||
if fetch {
|
||||
|
|
|
@ -57,10 +57,10 @@ const (
|
|||
type I8PPool map[I8PType][]Card
|
||||
|
||||
// MakeI8PCube takes an organized set of cards and sorts them into a draftable I8PCube
|
||||
func MakeI8PCube(cards I8PPool, schema I8PSchema) *I8PCube {
|
||||
func MakeI8PCube(cards I8PPool, schema I8PSchema, problemPackSize int) *I8PCube {
|
||||
return &I8PCube{
|
||||
Main: makeMainSet(cards, schema),
|
||||
Problems: makeProblemSet(cards),
|
||||
Problems: makeProblemSet(cards, problemPackSize),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ func makeMainSet(cards I8PPool, schema I8PSchema) (set *I8PSet) {
|
|||
return
|
||||
}
|
||||
|
||||
func makeProblemSet(cards I8PPool) (set *I8PSet) {
|
||||
func makeProblemSet(cards I8PPool, problemPackSize int) (set *I8PSet) {
|
||||
set = &I8PSet{
|
||||
Cards: I8PPool{
|
||||
I8PTypeProblem: cards[I8PTypeProblem],
|
||||
|
@ -111,7 +111,7 @@ func makeProblemSet(cards I8PPool) (set *I8PSet) {
|
|||
set.Schema = draft.PackSchema{
|
||||
Slots: []draft.PackSlot{
|
||||
{
|
||||
Amount: 12,
|
||||
Amount: problemPackSize,
|
||||
Provider: set.ProviderByType(I8PTypeProblem),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -21,7 +21,7 @@ func TestDraftI8PCube(t *testing.T) {
|
|||
mlp.I8PTypeEntry: mockCards("e1", "e2", "e3", "e4", "e5"),
|
||||
mlp.I8PTypeProblem: mockCards("P1", "P2"),
|
||||
}
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema())
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema(), 12)
|
||||
|
||||
pack1 := draft.MakePack(cube.Main)
|
||||
pack2 := draft.MakePack(cube.Main)
|
||||
|
@ -57,7 +57,7 @@ func TestDryCube(t *testing.T) {
|
|||
mlp.I8PTypeMulti: mockCards("m1", "m4"),
|
||||
mlp.I8PTypeEntry: mockCards("e1", "e4"),
|
||||
}
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema())
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema(), 12)
|
||||
pack := draft.MakePack(cube.Main)
|
||||
if len(pack) != 11 {
|
||||
t.Fatalf("Expected 11 cards in pack but got %d", len(pack))
|
||||
|
@ -93,7 +93,7 @@ func TestPodI8PCube(t *testing.T) {
|
|||
"Q9", "Q10", "Q11", "Q12",
|
||||
),
|
||||
}
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema())
|
||||
cube := mlp.MakeI8PCube(pool, mlp.DefaultI8PSchema(), 12)
|
||||
|
||||
remainingStart := i8pCountRemaining(cube)
|
||||
|
||||
|
|
17
mlp/mlp.go
17
mlp/mlp.go
|
@ -30,6 +30,7 @@ const (
|
|||
SetDefendersOfEquestria SetID = "DE"
|
||||
SetSeaquestriaBeyond SetID = "SB"
|
||||
SetFriendsForever SetID = "FF"
|
||||
SetPromo SetID = "Promo"
|
||||
)
|
||||
|
||||
// BlockID denotes a certain block
|
||||
|
@ -41,3 +42,19 @@ const (
|
|||
BlockOdyssey BlockID = "EO" // Odyssey block - EO/HM/MT
|
||||
BlockDefenders BlockID = "DE" // Defenders block - DE/SB/FF
|
||||
)
|
||||
|
||||
var allSets = []SetID{
|
||||
SetPremiere,
|
||||
SetCanterlotNights,
|
||||
SetRockNRave,
|
||||
SetCelestialSolstice,
|
||||
SetCrystalGames,
|
||||
SetAbsoluteDiscord,
|
||||
SetEquestrialOdysseys,
|
||||
SetHighMagic,
|
||||
SetMarksInTime,
|
||||
SetDefendersOfEquestria,
|
||||
SetSeaquestriaBeyond,
|
||||
SetFriendsForever,
|
||||
SetPromo,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package mlp
|
||||
|
||||
var promoIDs = []string{
|
||||
"prpf1", "prpf2", "prpf3", "prpf4", "prpf6", "cnpf3", "cnpf5", "cnpf7", "cnpf9", "cnpf11", "cnpf13", "cgpf4", "cgpf6", "cgpf8", "cgpf11", "cgpf12", "cgpf14", "gf1", "gf4", "gf5", "gf7", "gf9",
|
||||
}
|
19
mlp/set.go
19
mlp/set.go
|
@ -89,6 +89,12 @@ func LoadSetBytes(id SetID, setdata []byte) (*Set, error) {
|
|||
return &set, err
|
||||
}
|
||||
|
||||
// HTTPClient is the HTTP client used in LoadSetHTTP
|
||||
var HTTPClient = http.DefaultClient
|
||||
|
||||
// HTTPSource is the base URL for fetching sets remotely via HTTP
|
||||
var HTTPSource = "https://mcg.zyg.ovh/setdata/"
|
||||
|
||||
// LoadSetHTTP loads a set using MCG's remote server
|
||||
func LoadSetHTTP(id SetID) (*Set, error) {
|
||||
// Check if set is already loaded
|
||||
|
@ -99,7 +105,7 @@ func LoadSetHTTP(id SetID) (*Set, error) {
|
|||
// Get SetID as string and make it lowercase
|
||||
setid := strings.ToLower(string(id))
|
||||
|
||||
resp, err := http.Get("https://mcg.zyg.ovh/setdata/" + setid + ".json")
|
||||
resp, err := HTTPClient.Get(HTTPSource + setid + ".json")
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("server returned non-200 response code (%d)", resp.StatusCode)
|
||||
|
@ -114,3 +120,14 @@ func LoadSetHTTP(id SetID) (*Set, error) {
|
|||
|
||||
return LoadSetBytes(id, data)
|
||||
}
|
||||
|
||||
// LoadAllSets just loads all sets from the web
|
||||
func LoadAllSets() error {
|
||||
for _, set := range allSets {
|
||||
_, err := LoadSetHTTP(set)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
package mlp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"git.fromouter.space/mcg/draft"
|
||||
"git.fromouter.space/mcg/draft/mlp"
|
||||
"git.fromouter.space/mcg/draft/mlp/testdata"
|
||||
)
|
||||
|
||||
// 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)
|
||||
prSet, err := mlp.LoadSetHTTP(mlp.SetPremiere)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not fetch set data: %s", err.Error())
|
||||
}
|
||||
|
||||
pack1 := draft.MakePack(deSet)
|
||||
pack2 := draft.MakePack(deSet)
|
||||
pack1 := draft.MakePack(prSet)
|
||||
pack2 := draft.MakePack(prSet)
|
||||
|
||||
// Make sure both packs have the exact number of cards
|
||||
if len(pack1) != 12 {
|
||||
|
@ -34,7 +38,6 @@ func TestSet(t *testing.T) {
|
|||
}
|
||||
|
||||
// TestWrongSet tries to fetch a set that doesn't exist
|
||||
// This test *requires* an internet connection!
|
||||
func TestWrongSet(t *testing.T) {
|
||||
_, err := mlp.LoadSetHTTP("nopenope")
|
||||
if err == nil {
|
||||
|
@ -43,13 +46,12 @@ func TestWrongSet(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
_, err := mlp.LoadSetHTTP(mlp.SetFriendsForever)
|
||||
if err != nil {
|
||||
t.Fatalf("[LoadSetHTTP] Could not fetch set data from the internet: %s", err.Error())
|
||||
}
|
||||
|
@ -73,6 +75,12 @@ func TestAllLoads(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("[LoadSetMemory] Could not load set: %s", err.Error())
|
||||
}
|
||||
|
||||
// Load all remaining sets
|
||||
err = mlp.LoadAllSets()
|
||||
if err != nil {
|
||||
t.Fatalf("[LoadAllSets] Could not load remaining sets: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotLoadedErr tests that LoadSetMemory fails if set is not cached
|
||||
|
@ -157,4 +165,29 @@ func TestMalformedJSONLoad(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatalf("LoadSetData with invalid data succeeded but shouldn't have")
|
||||
}
|
||||
|
||||
_, err = mlp.LoadSetHTTP(mlp.SetID("broken"))
|
||||
if err == nil {
|
||||
t.Fatalf("LoadSetHTTP with invalid JSON succeded but shouldn't have")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
switch req.URL.Path {
|
||||
case "/nopenope.json":
|
||||
// 404
|
||||
http.Error(res, "Not found", http.StatusNotFound)
|
||||
case "/broken.json":
|
||||
// Broken response
|
||||
fmt.Fprintf(res, "{{{{")
|
||||
case "/pr.json":
|
||||
fmt.Fprintf(res, testdata.SetPremiere)
|
||||
default:
|
||||
fmt.Fprintf(res, testdata.SetFriendForever)
|
||||
}
|
||||
}))
|
||||
mlp.HTTPSource = testServer.URL + "/"
|
||||
defer testServer.Close()
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
package testdata
|
||||
|
||||
// SetFriendForever is an excerpt from Friend Forever's JSON set data
|
||||
const SetFriendForever = `{
|
||||
"Name": "Friends Forever",
|
||||
"Cards": [
|
||||
{
|
||||
"ID": "ff1",
|
||||
"Name": "Gallus",
|
||||
"Subname": "Full of Surprises",
|
||||
"Element": [
|
||||
"Loyalty"
|
||||
],
|
||||
"Keywords": [
|
||||
"Home Limit 3"
|
||||
],
|
||||
"Traits": [
|
||||
"Griffon"
|
||||
],
|
||||
"Power": 1,
|
||||
"Type": "Mane Character",
|
||||
"Text": "When you confront this card's Problem, turn this card over.",
|
||||
"Rarity": "U"
|
||||
},
|
||||
{
|
||||
"ID": "ff3",
|
||||
"Name": "Ocellus",
|
||||
"Subname": "Knowledge is Power",
|
||||
"Element": [
|
||||
"Generosity"
|
||||
],
|
||||
"Keywords": [
|
||||
"Home Limit 3"
|
||||
],
|
||||
"Traits": [
|
||||
"Changeling"
|
||||
],
|
||||
"Power": 1,
|
||||
"Type": "Mane Character",
|
||||
"Text": "When you confront this card's Problem, turn this card over.",
|
||||
"Rarity": "U"
|
||||
},
|
||||
{
|
||||
"ID": "ff4",
|
||||
"Name": "Chipcutter",
|
||||
"Subname": "Sculptor",
|
||||
"Element": [
|
||||
"Loyalty"
|
||||
],
|
||||
"Keywords": [
|
||||
"Swift"
|
||||
],
|
||||
"Traits": [
|
||||
"Pegasus",
|
||||
"Foal"
|
||||
],
|
||||
"Cost": 3,
|
||||
"Power": 2,
|
||||
"Type": "Friend",
|
||||
"Text": "",
|
||||
"Rarity": "C"
|
||||
},
|
||||
{
|
||||
"ID": "ff5",
|
||||
"Name": "Daybreaker",
|
||||
"Subname": "Blinding Light",
|
||||
"Element": [
|
||||
"Loyalty"
|
||||
],
|
||||
"Keywords": [
|
||||
"Competitive 3"
|
||||
],
|
||||
"Traits": [
|
||||
"Alicorn",
|
||||
"Royalty"
|
||||
],
|
||||
"Requirement": {
|
||||
"Loyalty": 3
|
||||
},
|
||||
"Cost": 3,
|
||||
"Power": 3,
|
||||
"Type": "Friend",
|
||||
"Text": "When you win a faceoff involving this card, score a point.",
|
||||
"Rarity": "SR"
|
||||
},
|
||||
{
|
||||
"ID": "ff131",
|
||||
"Name": "Silverstream",
|
||||
"Subname": "Everything's New!",
|
||||
"Element": [
|
||||
"Laughter"
|
||||
],
|
||||
"Keywords": [
|
||||
"Home Limit 3"
|
||||
],
|
||||
"Traits": [
|
||||
"Ally",
|
||||
"Hippogriff"
|
||||
],
|
||||
"Power": 1,
|
||||
"Type": "Mane Character",
|
||||
"Text": "Immediate: While this card is at a Problem with 2 or more other characters, turn this card over.",
|
||||
"Rarity": "UR"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
// SetPremiere is an excerpt from Premiere's JSON set data
|
||||
const SetPremiere = `{
|
||||
"Name": "Premiere",
|
||||
"Cards": [
|
||||
{
|
||||
"ID": "pr54",
|
||||
"Name": "Comet Tail",
|
||||
"Subname": "Hale Bopper",
|
||||
"Element": [
|
||||
"Magic"
|
||||
],
|
||||
"Keywords": [],
|
||||
"Traits": [
|
||||
"Unicorn"
|
||||
],
|
||||
"Cost": 1,
|
||||
"Power": 1,
|
||||
"Type": "Friend",
|
||||
"Text": "",
|
||||
"Rarity": "C"
|
||||
},
|
||||
{
|
||||
"ID": "pr55",
|
||||
"Name": "Mint Jewelup",
|
||||
"Subname": "A Cut Above",
|
||||
"Element": [
|
||||
"Magic"
|
||||
],
|
||||
"Keywords": [],
|
||||
"Traits": [
|
||||
"Unicorn"
|
||||
],
|
||||
"Requirement": {
|
||||
"Magic": 2
|
||||
},
|
||||
"Cost": 2,
|
||||
"Power": 1,
|
||||
"Type": "Friend",
|
||||
"Text": "Studious.",
|
||||
"Rarity": "C"
|
||||
},
|
||||
{
|
||||
"ID": "pr56",
|
||||
"Name": "Gyro",
|
||||
"Subname": "Poindexter",
|
||||
"Element": [
|
||||
"Magic"
|
||||
],
|
||||
"Keywords": [],
|
||||
"Traits": [
|
||||
"Earth Pony"
|
||||
],
|
||||
"Requirement": {
|
||||
"Magic": 3
|
||||
},
|
||||
"Cost": 1,
|
||||
"Power": 1,
|
||||
"Type": "Friend",
|
||||
"Text": "When you play this card, you may search your deck for an Event, reveal it, put it into your hand, and shuffle your deck.",
|
||||
"Rarity": "R"
|
||||
},
|
||||
{
|
||||
"ID": "pr77",
|
||||
"Name": "Rarity",
|
||||
"Subname": "Nest Weaver",
|
||||
"Element": [
|
||||
"Generosity"
|
||||
],
|
||||
"Keywords": [
|
||||
"Inspired"
|
||||
],
|
||||
"Traits": [
|
||||
"Unicorn"
|
||||
],
|
||||
"Requirement": {
|
||||
"Generosity": 3
|
||||
},
|
||||
"Cost": 3,
|
||||
"Power": 2,
|
||||
"Type": "Friend",
|
||||
"Text": "When you play this card, you may search your discard pile for a card and put it into your hand.",
|
||||
"Rarity": "U"
|
||||
},
|
||||
{
|
||||
"ID": "pr211",
|
||||
"Name": "Fluttershy",
|
||||
"Subname": "Monster Tamer",
|
||||
"Element": [
|
||||
"Kindness"
|
||||
],
|
||||
"Keywords": [],
|
||||
"Traits": [
|
||||
"Pegasus"
|
||||
],
|
||||
"Requirement": {
|
||||
"Kindness": 4
|
||||
},
|
||||
"Cost": 4,
|
||||
"Power": 2,
|
||||
"Type": "Friend",
|
||||
"Text": "When you play this card to a Problem, you may banish a Troublemaker there. When this card leaves that Problem, put that banished Troublemaker into play at a Problem and uncover it.",
|
||||
"Rarity": "UR"
|
||||
}
|
||||
]
|
||||
}`
|
|
@ -2,7 +2,7 @@ package mlp
|
|||
|
||||
import "git.fromouter.space/mcg/draft"
|
||||
|
||||
// Converts multiple mlp.Card to draft.Card
|
||||
// ToDraft converts multiple mlp.Card to draft.Card
|
||||
func ToDraft(cards []Card) []draft.Card {
|
||||
out := make([]draft.Card, len(cards))
|
||||
for i, card := range cards {
|
||||
|
|
Loading…
Reference in New Issue