package mlp import ( "math/rand" "git.fromouter.space/mcg/draft" ) // I8PCube is a cube that uses I8Pages' pack schema. // I8Pages' cube uses different kinds of packs, so a single schema is not possible. // Therefore, I8PCube itself is not a valid set, but contains two valid sets for // both types of packs (main deck / problems) // // Infos on I8Pages' cube: // https://docs.google.com/spreadsheets/d/1Ufz4QLwLCZ1vLemAhE6cuAmu2lq_VAN7RhLO9p1opNQ type I8PCube struct { Main *I8PSet Problems *I8PSet } // I8PSet is one of the sets of packs contained in a I8PCube type I8PSet struct { Cards I8PPool Schema draft.PackSchema } // I8PType is a category of cards to be seeded into packs type I8PType string // I8PSchema is a schema to seed a single I8PCube pack type I8PSchema []I8PSchemaSlot // I8PSchemaSlot is a single slot of a I8PSchema type I8PSchemaSlot struct { Amount int Type I8PType } // All types used for seeding packs const ( I8PTypeBlue I8PType = "blue" I8PTypeOrange I8PType = "orange" I8PTypePink I8PType = "pink" I8PTypePurple I8PType = "purple" I8PTypeWhite I8PType = "white" I8PTypeYellow I8PType = "yellow" I8PTypeNone I8PType = "none" I8PTypeMulti I8PType = "multi" I8PTypeEntry I8PType = "entry" I8PTypeProblem I8PType = "problem" // Special types I8PTypeAll I8PType = "all" // all means "from any other non-problem type" ) // I8PPool is a pool of card divided into categories 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 { return &I8PCube{ Main: makeMainSet(cards, schema), Problems: makeProblemSet(cards), } } func makeMainSet(cards I8PPool, schema I8PSchema) (set *I8PSet) { // Create set with the given card pool set = &I8PSet{ Cards: I8PPool{ I8PTypeBlue: cards[I8PTypeBlue], I8PTypeOrange: cards[I8PTypeOrange], I8PTypePink: cards[I8PTypePink], I8PTypePurple: cards[I8PTypePurple], I8PTypeWhite: cards[I8PTypeWhite], I8PTypeYellow: cards[I8PTypeYellow], I8PTypeNone: cards[I8PTypeNone], I8PTypeMulti: cards[I8PTypeMulti], I8PTypeEntry: cards[I8PTypeEntry], }, } // Build PackSchema from given I8PSchema var slots []draft.PackSlot for _, slot := range schema { // Check for special cases var provider draft.CardProvider switch slot.Type { case I8PTypeAll: provider = set.ProviderOther() default: provider = set.ProviderByType(slot.Type) } // Add slot slots = append(slots, draft.PackSlot{ Amount: slot.Amount, Provider: provider, }) } set.Schema = draft.PackSchema{ Slots: slots, } return } func makeProblemSet(cards I8PPool) (set *I8PSet) { set = &I8PSet{ Cards: I8PPool{ I8PTypeProblem: cards[I8PTypeProblem], }, } set.Schema = draft.PackSchema{ Slots: []draft.PackSlot{ { Amount: 12, Provider: set.ProviderByType(I8PTypeProblem), }, }, } return } // ProviderByType provides cards from a specified category func (s *I8PSet) ProviderByType(typ I8PType) draft.CardProvider { return func(n int) (out []draft.Card) { s.shuffle(typ) if len(s.Cards[typ]) < n { n = len(s.Cards[typ]) } out, s.Cards[typ] = toDraft(s.Cards[typ][:n]), s.Cards[typ][n:] return } } // ProviderOther picks a random card for any available set (except Problems) func (s *I8PSet) ProviderOther() draft.CardProvider { choices := []I8PType{ I8PTypeBlue, I8PTypeOrange, I8PTypePink, I8PTypePurple, I8PTypeWhite, I8PTypeYellow, I8PTypeNone, I8PTypeMulti, I8PTypeEntry, } return func(n int) (out []draft.Card) { // Filter types that have enough cards for what we need // n is almost always 1, so this should not introduce bias available := []I8PType{} for _, typ := range choices { if len(s.Cards[typ]) >= n { available = append(available, typ) } } // Check if there are no pools we can pick stuff from, and exit early // THIS IS NOT NORMAL, AND YOU SHOULD FIX YOUR CUBE! if len(available) < 1 { return } typ := available[rand.Intn(len(available))] return s.ProviderByType(typ)(n) } } // PackSchema returns the pack schema for building packs from a I8PCube func (s *I8PSet) PackSchema() draft.PackSchema { return s.Schema } func (s *I8PSet) shuffle(typ I8PType) { rand.Shuffle(len(s.Cards[typ]), func(i, j int) { s.Cards[typ][i], s.Cards[typ][j] = s.Cards[typ][j], s.Cards[typ][i] }) } // DefaultI8PSchema returns I8Pages' schema as specified in his docs func DefaultI8PSchema() I8PSchema { return I8PSchema{ {Amount: 1, Type: I8PTypeBlue}, {Amount: 1, Type: I8PTypeOrange}, {Amount: 1, Type: I8PTypePink}, {Amount: 1, Type: I8PTypePurple}, {Amount: 1, Type: I8PTypeWhite}, {Amount: 1, Type: I8PTypeYellow}, {Amount: 1, Type: I8PTypeNone}, {Amount: 2, Type: I8PTypeMulti}, {Amount: 2, Type: I8PTypeEntry}, {Amount: 1, Type: I8PTypeAll}, } } // PackProvider returns a PackProvider for a given number of main and problem packs func (c *I8PCube) PackProvider(mainPacks, problemPacks int) draft.PackProvider { return func() []draft.Pack { packs := make([]draft.Pack, mainPacks+problemPacks) for main := 0; main < mainPacks; main++ { packs[main] = draft.MakePack(c.Main) } for problem := 0; problem < problemPacks; problemPacks++ { packs[mainPacks+problem] = draft.MakePack(c.Problems) } return packs } }