mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
Add Smoothed London Mulligan option (#10965)
* Add Smoothed London Mulligan (similar to but weaker than MTGA's) * Make SmoothedLondonMulligan extend LondonMulligan instead of copying code * modified to be have no effect within +1/-1 of the expected lands fixed tests by always putting nonchosen hand on the bottom * Inherit the primary mulligan logic as well, add comments * Make drawHand public and part of Mulligan, use it on opening 7 use Card::isLand instead of reimplementing it, remove unused imports Use standard spacing * Better account for half-land MDFCs * Don't count TDFCs as half-lands * Remove "crossover_point" calculation to make algorithm clearer * Genericize the tests, undo changed access that's no longer needed, unbox bool * Use standard case in function naming * Add Override * Add mulligan type to TableView info, add tourneyMatchOptions local variable
This commit is contained in:
parent
a7c77a8895
commit
c50e913398
10 changed files with 153 additions and 41 deletions
|
|
@ -1263,7 +1263,7 @@ public abstract class GameImpl implements Game {
|
|||
player.initLife(this.getStartingLife());
|
||||
}
|
||||
if (!gameOptions.testMode) {
|
||||
player.drawCards(startingHandSize, null, this);
|
||||
mulligan.drawHand(startingHandSize, player, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ public class LondonMulligan extends Mulligan {
|
|||
newHandSize +
|
||||
(newHandSize == 1 ? " card" : " cards"));
|
||||
}
|
||||
player.drawCards(numCards, null, game);
|
||||
drawHand(numCards, player, game);
|
||||
|
||||
while (player.canRespond() && player.getHand().size() > newHandSize) {
|
||||
Target target = new TargetCardInHand(new FilterCard("card (" + (player.getHand().size() - newHandSize) + " more) to put on the bottom of your library"));
|
||||
|
|
|
|||
|
|
@ -91,4 +91,7 @@ public abstract class Mulligan implements Serializable {
|
|||
return freeMulligans;
|
||||
}
|
||||
|
||||
public void drawHand(int numCards, Player player, Game game){
|
||||
player.drawCards(numCards, null, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ public enum MulliganType {
|
|||
VANCOUVER("Vancouver"),
|
||||
PARIS("Paris"),
|
||||
LONDON("London"),
|
||||
SMOOTHED_LONDON("Smoothed London"),
|
||||
CANADIAN_HIGHLANDER("Canadian Highlander");
|
||||
|
||||
private final String displayName;
|
||||
|
|
@ -24,6 +25,8 @@ public enum MulliganType {
|
|||
return new CanadianHighlanderMulligan(freeMulligans);
|
||||
case VANCOUVER:
|
||||
return new VancouverMulligan(freeMulligans);
|
||||
case SMOOTHED_LONDON:
|
||||
return new SmoothedLondonMulligan(freeMulligans);
|
||||
default:
|
||||
case LONDON:
|
||||
return new LondonMulligan(freeMulligans);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class ParisMulligan extends Mulligan {
|
|||
.append(deduction == 0 ? " for free and draws " : " down to ")
|
||||
.append((numCards - deduction))
|
||||
.append(numCards - deduction == 1 ? " card" : " cards").toString());
|
||||
player.drawCards(numCards - deduction, null, game);
|
||||
drawHand(numCards - deduction, player, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
package mage.game.mulligan;
|
||||
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.ModalDoubleFacedCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SmoothedLondonMulligan extends LondonMulligan {
|
||||
|
||||
public SmoothedLondonMulligan(int freeMulligans) {
|
||||
super(freeMulligans);
|
||||
}
|
||||
|
||||
SmoothedLondonMulligan(final SmoothedLondonMulligan mulligan) {
|
||||
super(mulligan);
|
||||
}
|
||||
|
||||
private static double countLands(Collection<Card> cards, boolean library){
|
||||
double land_count = 0;
|
||||
for (Card card : cards){
|
||||
if (card.isLand()) {
|
||||
land_count += 1;
|
||||
} else if (card instanceof ModalDoubleFacedCard && ((ModalDoubleFacedCard)card).getRightHalfCard().isLand()){
|
||||
if (library) { //count MDFCs with a nonland front and a land back as:
|
||||
land_count += 0.5;//half a land in a library
|
||||
} else if (RandomUtil.nextBoolean()){
|
||||
land_count += 1; //randomly as a land or nonland in a hand
|
||||
// This avoids the bias problem where adjusting the deck land ratio to be (integer vs X.5)/7
|
||||
// can greatly affect the chance of drawing an MDFC
|
||||
}
|
||||
}
|
||||
}
|
||||
return land_count;
|
||||
}
|
||||
@Override
|
||||
public void drawHand(int numCards, Player player, Game game){
|
||||
List<Card> library = player.getLibrary().getCards(game);
|
||||
if (library.size() >= numCards*2 && numCards > 1) {
|
||||
double land_ratio = countLands(library, true) / (double) library.size();
|
||||
Set<Card> hand1 = player.getLibrary().getTopCards(game, numCards);
|
||||
Set<Card> hand2 = player.getLibrary().getTopCards(game, numCards * 2);
|
||||
hand2.removeAll(hand1);
|
||||
double hand1_ratio = countLands(hand1, false) / (double) numCards;
|
||||
double hand2_ratio = countLands(hand2, false) / (double) numCards;
|
||||
//distance = max(0,abs(land_ratio-hand_ratio)-0.15)+random()*0.3
|
||||
//Where land_ratio is (deck lands/deck size) and hand_ratio is (hand lands/hand size)
|
||||
//Keeps whichever hand's distance is smaller. Note that a 1-land difference is 1/7 = 0.143
|
||||
//So -0.15 means that there's no change in relative probabilities if within +1/-1 of the expected amount
|
||||
double hand1_distance = Math.max(0,Math.abs(land_ratio - hand1_ratio)-0.15)+RandomUtil.nextDouble()*0.3;
|
||||
double hand2_distance = Math.max(0,Math.abs(land_ratio - hand2_ratio)-0.15)+RandomUtil.nextDouble()*0.3;
|
||||
//game.debugMessage("1: "+hand1_ratio+", 2 = "+hand2_ratio+", expected = "+land_ratio);
|
||||
//game.debugMessage("hand1: "+hand1_distance+", hand2: "+hand2_distance);
|
||||
if (hand1_distance < hand2_distance) {
|
||||
player.drawCards(numCards, null, game);
|
||||
player.putCardsOnBottomOfLibrary(new CardsImpl(hand2), game, null, false);
|
||||
//These are immediately shuffled away, but needed for consistent testing
|
||||
} else {
|
||||
player.putCardsOnBottomOfLibrary(new CardsImpl(hand1), game, null, false);
|
||||
player.drawCards(numCards, null, game);
|
||||
}
|
||||
player.shuffleLibrary(null, game);
|
||||
} else { //not enough cards in library or hand, just do a normal draw instead
|
||||
player.drawCards(numCards, null, game);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmoothedLondonMulligan copy() {
|
||||
return new SmoothedLondonMulligan(this);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue