Example #1
0
func makeDeck(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	var (
		sets            = availableSets
		requestedSets   deck.Sets
		vetoProbability veto.Probability
		weights         score.Weights
		req             makeDeckRequest
		maxSetCount     uint
		maxScore        uint
		d               deck.Deck
	)

	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&req)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error decoding JSON body: %s", err), http.StatusBadRequest)
		return
	}
	log.Printf("makeDeck(%+v)\n", req)

	for _, set := range req.Sets {
		requestedSets.Add(set)
	}
	if !requestedSets.Empty() {
		sets.Intersect(requestedSets)
	}
	if sets.Empty() {
		http.Error(w, "Can't generate a deck from no sets", http.StatusBadRequest)
		return
	}

	maxSetCount = req.MaxSetCount
	if maxSetCount == 0 {
		maxSetCount = 2
	}

	if req.VetoProbability == nil || (req.VetoProbability == &veto.Probability{}) {
		log.Println("Using default veto probs")
		// Defaults
		vetoProbability = veto.Probability{
			WhenTooExpensive:     0.90,
			WhenNoTrashing:       0.70,
			WhenTooManyMechanics: 0.85,
			WhenTooManyAttacks:   0.85,
		}
	} else {
		vetoProbability = *req.VetoProbability
	}

	weights = req.Weights
	// Defaults
	if weights.Trashing == 0 {
		weights.Trashing = 5
	}
	if weights.Random == 0 {
		weights.Random = 5
	}
	if weights.Chaining == 0 {
		weights.Chaining = 5
	}
	if weights.CostSpread == 0 {
		weights.CostSpread = 5
	}
	if weights.SetCount == 0 {
		weights.SetCount = 5
	}
	if weights.MechanicCount == 0 {
		weights.MechanicCount = 5
	}
	if weights.Novelty == 0 {
		weights.Novelty = 5
	}

	decks := make([]deck.Deck, 0, 1000)
	count := 0
GenerateDeck:
	candidateDeck := deck.NewRandomDeck(maxSetCount, sets)
	if veto.TooExpensive(vetoProbability, candidateDeck) {
		goto GenerateDeck
	}
	if veto.NoTrashing(vetoProbability, candidateDeck) {
		goto GenerateDeck
	}
	if veto.TooManyMechanics(vetoProbability, candidateDeck) {
		goto GenerateDeck
	}
	if veto.TooManyAttacks(vetoProbability, candidateDeck) {
		goto GenerateDeck
	}
	count++

	decks = append(decks, candidateDeck)
	if len(decks) < cap(decks) {
		goto GenerateDeck
	}

	var totalCards uint
	cardCounts := make(map[deck.Card]uint, len(deck.Cards))
	for _, deck := range decks {
		for _, card := range deck.Cards {
			cardCounts[card]++
			totalCards++
		}
		for _, card := range deck.Events {
			cardCounts[card]++
			totalCards++
		}
	}
	cardProbs := make(map[deck.Card]float64, len(cardCounts))
	for card, count := range cardCounts {
		cardProbs[card] = float64(count) / float64(totalCards)
	}

	for _, candidateDeck := range decks {
		candidateScore := score.Evaluate(weights, candidateDeck, cardProbs)
		if candidateScore > maxScore {
			d = candidateDeck
			maxScore = candidateScore
		}
	}

	resp := deckResponse{
		ID:                   base64.URLEncoding.EncodeToString(d.ID()),
		Cards:                d.Cards,
		Events:               d.Events,
		ColoniesAndPlatinums: d.ColoniesAndPlatinums,
		Shelters:             d.Shelters,
		Potions:              d.Potions(),
		Spoils:               d.Spoils(),
		Ruins:                d.Ruins(),
		Hardware: deckHardware{
			CoinTokens:         d.CoinTokens(),
			VictoryTokens:      d.VictoryTokens(),
			MinusOneCardTokens: d.MinusOneCardTokens(),
			MinusOneCoinTokens: d.MinusOneCoinTokens(),
			JourneyTokens:      d.JourneyTokens(),
			TavernMats:         d.TavernMats(),
			TradeRouteMats:     d.TradeRouteMats(),
			NativeVillageMats:  d.NativeVillageMats(),
		},
	}
	enc := json.NewEncoder(w)
	_ = enc.Encode(resp)

	w.Header().Set("Content-Type", "application/json")
}