mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -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
|
|
@ -7,7 +7,9 @@ import mage.game.Seat;
|
|||
import mage.game.Table;
|
||||
import mage.game.draft.Draft;
|
||||
import mage.game.draft.DraftOptions;
|
||||
import mage.game.match.MatchOptions;
|
||||
import mage.game.match.MatchPlayer;
|
||||
import mage.game.mulligan.MulliganType;
|
||||
import mage.game.tournament.TournamentPlayer;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -99,6 +101,9 @@ public class TableView implements Serializable {
|
|||
addInfo.append("Wins:").append(table.getMatch().getWinsNeeded());
|
||||
addInfo.append(" Time: ").append(table.getMatch().getOptions().getMatchTimeLimit().toString());
|
||||
addInfo.append(" Buffer: ").append(table.getMatch().getOptions().getMatchBufferTime().toString());
|
||||
if (table.getMatch().getOptions().getMulliganType() != MulliganType.GAME_DEFAULT){
|
||||
addInfo.append(" Mulligan: \"").append(table.getMatch().getOptions().getMulliganType().toString()).append("\"");
|
||||
}
|
||||
if (table.getMatch().getFreeMulligans() > 0) {
|
||||
addInfo.append(" FM: ").append(table.getMatch().getFreeMulligans());
|
||||
}
|
||||
|
|
@ -150,10 +155,14 @@ public class TableView implements Serializable {
|
|||
if (TableState.WAITING.equals(table.getState())) {
|
||||
stateText.append(" (").append(table.getTournament().getPlayers().size()).append('/').append(table.getNumberOfSeats()).append(')');
|
||||
}
|
||||
infoText.append(" Time: ").append(table.getTournament().getOptions().getMatchOptions().getMatchTimeLimit().toString());
|
||||
infoText.append(" Buffer: ").append(table.getTournament().getOptions().getMatchOptions().getMatchBufferTime().toString());
|
||||
if (table.getTournament().getOptions().getMatchOptions().getFreeMulligans() > 0) {
|
||||
infoText.append(" FM: ").append(table.getTournament().getOptions().getMatchOptions().getFreeMulligans());
|
||||
MatchOptions tourneyMatchOptions = table.getTournament().getOptions().getMatchOptions();
|
||||
infoText.append(" Time: ").append(tourneyMatchOptions.getMatchTimeLimit().toString());
|
||||
infoText.append(" Buffer: ").append(tourneyMatchOptions.getMatchBufferTime().toString());
|
||||
if (tourneyMatchOptions.getMulliganType() != MulliganType.GAME_DEFAULT){
|
||||
infoText.append(" Mulligan: \"").append(tourneyMatchOptions.getMulliganType().toString()).append("\"");
|
||||
}
|
||||
if (tourneyMatchOptions.getFreeMulligans() > 0) {
|
||||
infoText.append(" FM: ").append(tourneyMatchOptions.getFreeMulligans());
|
||||
}
|
||||
if (table.getTournament().getTournamentType().isLimited()) {
|
||||
infoText.append(" Constr.: ").append(table.getTournament().getOptions().getLimitedOptions().getConstructionTime() / 60).append(" Min.");
|
||||
|
|
@ -162,10 +171,10 @@ public class TableView implements Serializable {
|
|||
DraftOptions draftOptions = (DraftOptions) table.getTournament().getOptions().getLimitedOptions();
|
||||
infoText.append(" Pick time: ").append(draftOptions.getTiming().getShortName());
|
||||
}
|
||||
if (table.getTournament().getOptions().getMatchOptions().isRollbackTurnsAllowed()) {
|
||||
if (tourneyMatchOptions.isRollbackTurnsAllowed()) {
|
||||
infoText.append(" RB");
|
||||
}
|
||||
if (table.getTournament().getOptions().getMatchOptions().isPlaneChase()) {
|
||||
if (tourneyMatchOptions.isPlaneChase()) {
|
||||
infoText.append(" PC");
|
||||
}
|
||||
if (table.getTournament().getOptions().isWatchingAllowed()) {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,15 @@ import java.util.stream.Collectors;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class LondonMulliganTest extends MulliganTestBase {
|
||||
|
||||
protected MulliganType getMullType() {
|
||||
return MulliganType.LONDON;
|
||||
}
|
||||
protected int getCardsPerMull() {
|
||||
return 7;
|
||||
}
|
||||
@Test
|
||||
public void testLondonMulligan_NoMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 0);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 0);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
|
|
@ -28,7 +33,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
|
||||
@Test
|
||||
public void testLondonMulligan_OneMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 0);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 0);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
Set<UUID> hand2 = new HashSet<>();
|
||||
List<UUID> discarded = new ArrayList<>();
|
||||
|
|
@ -41,14 +46,14 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.discardBottom(count -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(1, count);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
scenario.getHand().stream().limit(count).forEach(discarded::add);
|
||||
remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded)));
|
||||
return discarded;
|
||||
});
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
assertEquals(remainingHand, scenario.getHand());
|
||||
hand2.addAll(scenario.getHand());
|
||||
return false;
|
||||
|
|
@ -56,7 +61,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.run(() -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(remainingHand, new HashSet<>(scenario.getHand()));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
assertEquals(hand2, scenario.getHand());
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
});
|
||||
|
|
@ -64,7 +69,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
|
||||
@Test
|
||||
public void testLondonMulligan_TwoMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 0);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 0);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
Set<UUID> hand2 = new HashSet<>();
|
||||
Set<UUID> hand3 = new HashSet<>();
|
||||
|
|
@ -78,23 +83,23 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.discardBottom(count -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(1, count);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
scenario.getHand().stream().limit(count).forEach(discarded::add);
|
||||
remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded)));
|
||||
return discarded;
|
||||
});
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
hand2.addAll(scenario.getHand());
|
||||
return true;
|
||||
});
|
||||
scenario.discardBottom(count -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(1, count);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(discarded, scenario.getLibraryRangeSize(26, 1));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(discarded, scenario.getLibraryRangeSize(33-getCardsPerMull(), 1));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(34-getCardsPerMull(), 6)));
|
||||
discarded.clear();
|
||||
remainingHand.clear();
|
||||
scenario.getHand().stream().limit(count).forEach(discarded::add);
|
||||
|
|
@ -104,9 +109,9 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.discardBottom(count -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(1, count);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(34-getCardsPerMull(), 6)));
|
||||
discarded.clear();
|
||||
remainingHand.clear();
|
||||
scenario.getHand().stream().limit(count).forEach(discarded::add);
|
||||
|
|
@ -115,8 +120,8 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
});
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(5, 35);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(34-getCardsPerMull(), 6)));
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
hand3.addAll(scenario.getHand());
|
||||
return false;
|
||||
|
|
@ -124,8 +129,8 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.run(() -> {
|
||||
scenario.assertSizes(5, 35);
|
||||
assertEquals(remainingHand, new HashSet<>(scenario.getHand()));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(27, 6)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(34-getCardsPerMull(), 6)));
|
||||
assertEquals(hand3, scenario.getHand());
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
});
|
||||
|
|
@ -133,7 +138,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
|
||||
@Test
|
||||
public void testLondonMulligan_FreeMulligan_NoMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 1);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 1);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
|
|
@ -148,7 +153,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
|
||||
@Test
|
||||
public void testLondonMulligan_FreeMulligan_OneMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 1);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 1);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
Set<UUID> hand2 = new HashSet<>();
|
||||
scenario.mulligan(() -> {
|
||||
|
|
@ -159,19 +164,19 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
hand2.addAll(scenario.getHand());
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
return false;
|
||||
});
|
||||
scenario.run(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getHand()));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLondonMulligan_FreeMulligan_TwoMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 1);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 1);
|
||||
Set<UUID> hand1 = new HashSet<>();
|
||||
Set<UUID> hand2 = new HashSet<>();
|
||||
Set<UUID> hand3 = new HashSet<>();
|
||||
|
|
@ -184,31 +189,31 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
});
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
hand2.addAll(scenario.getHand());
|
||||
return true;
|
||||
});
|
||||
scenario.discardBottom(count -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
assertEquals(1, count);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
scenario.getHand().stream().limit(count).forEach(discarded::add);
|
||||
remainingHand.addAll(Sets.difference(scenario.getHand(), new HashSet<>(discarded)));
|
||||
return discarded;
|
||||
});
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
hand3.addAll(scenario.getHand());
|
||||
return false;
|
||||
});
|
||||
scenario.run(() -> {
|
||||
scenario.assertSizes(6, 34);
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(19, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(26, 7)));
|
||||
assertEquals(hand1, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull()*2, 7)));
|
||||
assertEquals(hand2, new HashSet<>(scenario.getLibraryRangeSize(33-getCardsPerMull(), 7)));
|
||||
assertEquals(hand3, scenario.getHand());
|
||||
assertEquals(remainingHand, new HashSet<>(scenario.getHand()));
|
||||
assertEquals(discarded, scenario.getNBottomOfLibrary(1));
|
||||
|
|
@ -217,7 +222,7 @@ public class LondonMulliganTest extends MulliganTestBase {
|
|||
|
||||
@Test
|
||||
public void testLondonMulligan_AlwaysMulligan() {
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(MulliganType.LONDON, 0);
|
||||
MulliganScenarioTest scenario = new MulliganScenarioTest(getMullType(), 0);
|
||||
scenario.mulligan(() -> {
|
||||
scenario.assertSizes(7, 33);
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.mulligan;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.basiclands.Forest;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.cards.s.Squire;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.game.Game;
|
||||
import mage.game.GameOptions;
|
||||
|
|
@ -141,7 +142,10 @@ public class MulliganTestBase {
|
|||
public static Deck generateDeck(UUID playerId, int count) {
|
||||
Deck deck = new Deck();
|
||||
Stream.generate(() -> new Forest(playerId, new CardSetInfo("Forest", "TEST", "1", LAND)))
|
||||
.limit(count)
|
||||
.limit(count/2+(count & 1)) //If odd number of cards, add one extra forest
|
||||
.forEach(deck.getCards()::add);
|
||||
Stream.generate(() -> new Squire(playerId, new CardSetInfo("Squire", "TEST", "2", LAND)))
|
||||
.limit(count/2)
|
||||
.forEach(deck.getCards()::add);
|
||||
return deck;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package org.mage.test.mulligan;
|
||||
import mage.game.mulligan.MulliganType;
|
||||
|
||||
public class SmoothedLondonMulliganTest extends LondonMulliganTest {
|
||||
@Override
|
||||
protected MulliganType getMullType() {
|
||||
return MulliganType.SMOOTHED_LONDON;
|
||||
}
|
||||
@Override
|
||||
protected int getCardsPerMull() {
|
||||
return 14;
|
||||
}
|
||||
}
|
||||
|
|
@ -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