Beispiel #1
0
// TooManyMechanics vetos decks using more than 2 mechanics
func TooManyMechanics(p Probability, d deck.Deck) bool {
	count := 0

	if d.Potions() {
		count++
	}
	if d.CoinTokens() {
		count++
	}
	if d.VictoryTokens() {
		count++
	}
	if d.TavernMats() {
		count++
	}
	if d.TradeRouteMats() {
		count++
	}
	if d.NativeVillageMats() {
		count++
	}
	if d.Spoils() {
		count++
	}
	if d.Ruins() {
		count++
	}
	if d.MinusOneCardTokens() {
		count++
	}
	if d.MinusOneCoinTokens() {
		count++
	}
	if d.JourneyTokens() {
		count++
	}

	return (count > 2) && rnd.Float64() < p.WhenTooManyMechanics
}
Beispiel #2
0
func evaluateMechanicCount(d deck.Deck) uint {
	count := 0

	if d.Potions() {
		count++
	}
	if d.CoinTokens() {
		count++
	}
	if d.VictoryTokens() {
		count++
	}
	if d.TavernMats() {
		count++
	}
	if d.TradeRouteMats() {
		count++
	}
	if d.NativeVillageMats() {
		count++
	}
	if d.Spoils() {
		count++
	}
	if d.Ruins() {
		count++
	}
	if d.MinusOneCardTokens() {
		count++
	}
	if d.MinusOneCoinTokens() {
		count++
	}
	if d.JourneyTokens() {
		count++
	}

	switch count {
	case 0:
		return 100
	case 1:
		return 100
	case 2:
		return 50
	default:
		return 0
	}
}
Beispiel #3
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")
}
Beispiel #4
0
// Determine how well distributed the card costs are based on the entropy of the
// cost distribution
func evaluateCostSpread(d deck.Deck) uint {
	//  First, count up the total number of cards in the deck with each cost
	var distribution = map[int]uint{
		// coppers, curses
		0: 2,
		// estates
		2: 1,
		// silver
		3: 1,
		// duchys
		5: 1,
		// gold
		6: 1,
		// prov
		8: 1,
	}

	if d.ColoniesAndPlatinums {
		// platinum
		distribution[9]++
		// colonies
		distribution[11]++
	}

	if d.Potions() {
		distribution[4]++
	}

	for _, card := range d.Cards {
		distribution[card.CostTreasure]++
	}

	for _, card := range d.Events {
		distribution[card.CostTreasure]++
	}

	// Now, convert those counts into "probabilities" that a randomly selected
	// card will have a given cost.  For instance, if there are 10 cards in the
	// deck and exactly 2 of them cost 3 coins, there's a 20% chance of a random
	// card costing 3 coins
	//
	// With this distribution (the probability of each different coin value),
	// calculate the entropy, which describes how "random" the distribution is
	// Decks with high entropy have a broad spread of values, decks with low
	// entropy have a small spread of values.
	cards := uint(0)
	for _, n := range distribution {
		cards += n
	}
	entropy := 0.0
	maxEntropy := 0.0
	for _, n := range distribution {
		probability := float64(n) / float64(cards)
		entropy -= probability * math.Log(probability)
		maxEntropy -= 1 / float64(cards) * math.Log(1/float64(cards))
	}

	// Scale to the range 0-100 based on the fact that the entropy for this set
	// has an upper bound where each value has the same probability
	return uint(100 * (entropy / maxEntropy))
}