Skip to content

Search for good deck#59

Open
MaximeBastion wants to merge 16 commits into
minimal-creatures-and-lands-only-no-abilitiesfrom
search-for-good-deck
Open

Search for good deck#59
MaximeBastion wants to merge 16 commits into
minimal-creatures-and-lands-only-no-abilitiesfrom
search-for-good-deck

Conversation

@MaximeBastion
Copy link
Copy Markdown
Collaborator

@MaximeBastion MaximeBastion commented Aug 2, 2023

Adds a script to make decks fight each other using the random strategy until a "champion deck" is found.

Looks for an arena champion deck until one is found.
A champion deck is a deck that is tested as significantly better than a random deck consecutive_test_wins_threshold
times in a row.
To identify whether a deck is significantly better than another, we run up to games_limit_per_test games between the
two decks and stop as soon as the probability for the record obtained to happen under the assumption of 50% winrate
is lower than p_value.

The script can be run with

PYTHONPATH=. python scripts/search_for_good_deck.py

Or on Windows

.\with_pythonpath.bat . .\scripts\search_for_good_deck.py

Output examples (40 cards, p_value = 0.05, up to 50 games per match-up):

Consecutive test wins of deck 0 in the arena is 25
Willow Elf - {G} - (1/1)
Bear Cub - {1}{G} - (2/2)
Swordwise Centaur - {G}{G} - (3/2)
Terrain Elemental - {1}{G} - (3/2)
Leatherback Baloth - {G}{G}{G} - (4/5)
Nessian Courser - {2}{G} - (3/3)
Orazca Frillback - {2}{G} - (4/2)
Axebane Beast - {3}{G} - (3/4)
Nettle Swine - {3}{G} - (4/3)
Durkwood Boars - {4}{G} - (4/4)
Gigantosaurus - {G}{G}{G}{G}{G} - (10/10)
Nema Siltlurker - {4}{G} - (3/5)
Pheres-Band Centaurs - {4}{G} - (3/7)
Redwood Treefolk - {4}{G} - (3/6)
Spined Wurm - {4}{G} - (5/4)
Spined Wurm - {4}{G} - (5/4)
Scaled Wurm - {7}{G} - (7/6)
Forest    23

Looks like a solid vanilla deck to me, mostly good stats for the cost

Consecutive test wins of deck 0 in the arena is 50
Balduvian Bears - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Elvish Warrior - {G}{G} - (2/3)
Kalonian Tusker - {G}{G} - (3/3)
Kalonian Tusker - {G}{G} - (3/3)
Treetop Warden - {1}{G} - (2/2)
Alpine Grizzly - {2}{G} - (4/2)
Colossodon Yearling - {2}{G} - (2/4)
Nema Siltlurker - {4}{G} - (3/5)
Panther Warriors - {4}{G} - (6/3)
Panther Warriors - {4}{G} - (6/3)
Kindercatch - {3}{G}{G}{G} - (6/6)
Vastwood Gorger - {5}{G} - (5/6)
Enormous Baloth - {6}{G} - (7/7)
Forest    15
Elvish Ranger - {2}{G} - (4/1)
Gnarled Mass - {1}{G}{G} - (3/3)
Leatherback Baloth - {G}{G}{G} - (4/5)
Leatherback Baloth - {G}{G}{G} - (4/5)
Leatherback Baloth - {G}{G}{G} - (4/5)
Spined Karok - {2}{G} - (2/4)
Spined Karok - {2}{G} - (2/4)
Broodhunter Wurm - {3}{G} - (4/3)
Golden Bear - {3}{G} - (4/3)
Garruk's Gorehorn - {4}{G} - (7/3)
Plated Wurm - {4}{G} - (4/5)
Silverback Ape - {3}{G}{G} - (5/5)
Tusked Colossodon - {4}{G}{G} - (6/5)
Vastwood Gorger - {5}{G} - (5/6)
Vorstclaw - {4}{G}{G} - (7/7)
Enormous Baloth - {6}{G} - (7/7)
Ancient Brontodon - {6}{G}{G} - (9/9)
Forest    21

Also, started to explore looking for a good deck by upgrading one over and over, by changing one card at random and checking if the new deck beats the old one.

@MaximeBastion
Copy link
Copy Markdown
Collaborator Author

Among 10 champions found with

        champions_to_find=10,
        consecutive_test_wins_threshold=25,
        games_limit_per_test=1000,
        p_value=0.05,

We see that green is super dominant, which makes a lot of sense since it has the best vanilla creatures (3/3 for 2, 4/5 for 3, 10/10 for 5:
Color distribution:

8G
1W
1R
Function 'search_for_arena_winners' executed in 5030.6 seconds
Trained Jackal - {G} - (1/2)
Runeclaw Bear - {1}{G} - (2/2)
Swordwise Centaur - {G}{G} - (3/2)
Alpine Grizzly - {2}{G} - (4/2)
Centaur Courser - {2}{G} - (3/3)
Colossodon Yearling - {2}{G} - (2/4)
Colossodon Yearling - {2}{G} - (2/4)
Leatherback Baloth - {G}{G}{G} - (4/5)
Axebane Beast - {3}{G} - (3/4)
Nettle Swine - {3}{G} - (4/3)
Southern Elephant - {3}{G} - (3/4)
Colossapede - {4}{G} - (5/5)
Durkwood Boars - {4}{G} - (4/4)
Garruk's Gorehorn - {4}{G} - (7/3)
Gigantosaurus - {G}{G}{G}{G}{G} - (10/10)
Gigantosaurus - {G}{G}{G}{G}{G} - (10/10)
Hollowhenge Beast - {3}{G}{G} - (5/5)
Meng Huo's Horde - {4}{G} - (4/5)
Moss Monster - {3}{G}{G} - (3/6)
Redwood Treefolk - {4}{G} - (3/6)
Barbtooth Wurm - {5}{G} - (6/4)
Nyxborn Colossus - {3}{G}{G}{G} - (6/7)
Vastwood Gorger - {5}{G} - (5/6)
Quilled Slagwurm - {4}{G}{G}{G} - (8/8)
Ancient Brontodon - {6}{G}{G} - (9/9)
Ancient Brontodon - {6}{G}{G} - (9/9)
Forest    14

Norwood Ranger - {G} - (1/2)
Norwood Ranger - {G} - (1/2)
Willow Elf - {G} - (1/1)
Woodland Druid - {G} - (1/2)
Balduvian Bears - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Kalonian Tusker - {G}{G} - (3/3)
Colossodon Yearling - {2}{G} - (2/4)
Gorilla Warrior - {2}{G} - (3/2)
Leatherback Baloth - {G}{G}{G} - (4/5)
Leatherback Baloth - {G}{G}{G} - (4/5)
Orazca Frillback - {2}{G} - (4/2)
Trained Armodon - {1}{G}{G} - (3/3)
Axebane Beast - {3}{G} - (3/4)
Broodhunter Wurm - {3}{G} - (4/3)
Durkwood Boars - {4}{G} - (4/4)
Feral Krushok - {4}{G} - (5/4)
Nema Siltlurker - {4}{G} - (3/5)
Redwood Treefolk - {4}{G} - (3/6)
Thornhide Wolves - {4}{G} - (4/5)
Craw Wurm - {4}{G}{G} - (6/4)
Whiptail Wurm - {6}{G} - (8/5)
Scaled Wurm - {7}{G} - (7/6)
Forest    16

Barbary Apes - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Cylian Elf - {1}{G} - (2/2)
Cylian Elf - {1}{G} - (2/2)
Elvish Warrior - {G}{G} - (2/3)
Terrain Elemental - {1}{G} - (3/2)
Leatherback Baloth - {G}{G}{G} - (4/5)
Murasa Brute - {2}{G} - (3/3)
Orazca Frillback - {2}{G} - (4/2)
Colossapede - {4}{G} - (5/5)
Colossapede - {4}{G} - (5/5)
Durkwood Boars - {4}{G} - (4/4)
Garruk's Gorehorn - {4}{G} - (7/3)
Grizzled Outrider - {4}{G} - (5/5)
Grizzled Outrider - {4}{G} - (5/5)
Hollowhenge Beast - {3}{G}{G} - (5/5)
Hollowhenge Beast - {3}{G}{G} - (5/5)
Nema Siltlurker - {4}{G} - (3/5)
Silverback Ape - {3}{G}{G} - (5/5)
Thornhide Wolves - {4}{G} - (4/5)
Craw Wurm - {4}{G}{G} - (6/4)
Enormous Baloth - {6}{G} - (7/7)
Enormous Baloth - {6}{G} - (7/7)
Enormous Baloth - {6}{G} - (7/7)
Whiptail Wurm - {6}{G} - (8/5)
Forest    15

Trained Jackal - {G} - (1/2)
Woodland Druid - {G} - (1/2)
Elvish Warrior - {G}{G} - (2/3)
Runeclaw Bear - {1}{G} - (2/2)
Runeclaw Bear - {1}{G} - (2/2)
Treetop Warden - {1}{G} - (2/2)
Gnarled Mass - {1}{G}{G} - (3/3)
Harrier Naga - {2}{G} - (3/3)
Harrier Naga - {2}{G} - (3/3)
Nessian Courser - {2}{G} - (3/3)
Orazca Frillback - {2}{G} - (4/2)
Ferocious Zheng - {2}{G}{G} - (4/4)
Golden Bear - {3}{G} - (4/3)
Rowan Treefolk - {3}{G} - (3/4)
Rumbling Baloth - {2}{G}{G} - (4/4)
Garruk's Gorehorn - {4}{G} - (7/3)
Hollowhenge Beast - {3}{G}{G} - (5/5)
Silverback Ape - {3}{G}{G} - (5/5)
Alpha Tyrranax - {4}{G}{G} - (6/5)
Canopy Gorger - {4}{G}{G} - (6/5)
Nyxborn Colossus - {3}{G}{G}{G} - (6/7)
Tusked Colossodon - {4}{G}{G} - (6/5)
Tusked Colossodon - {4}{G}{G} - (6/5)
Enormous Baloth - {6}{G} - (7/7)
Forest    16

Woodland Druid - {G} - (1/2)
Forest Bear - {1}{G} - (2/2)
Forest Bear - {1}{G} - (2/2)
Forest Bear - {1}{G} - (2/2)
Alpine Grizzly - {2}{G} - (4/2)
Alpine Grizzly - {2}{G} - (4/2)
Alpine Grizzly - {2}{G} - (4/2)
Gnarled Mass - {1}{G}{G} - (3/3)
Gnarled Mass - {1}{G}{G} - (3/3)
Gnarled Mass - {1}{G}{G} - (3/3)
Gorilla Warrior - {2}{G} - (3/2)
Leatherback Baloth - {G}{G}{G} - (4/5)
Nessian Courser - {2}{G} - (3/3)
Nettle Swine - {3}{G} - (4/3)
Order of the Sacred Bell - {3}{G} - (4/3)
Rumbling Baloth - {2}{G}{G} - (4/4)
Blanchwood Treefolk - {4}{G} - (4/5)
Garruk's Gorehorn - {4}{G} - (7/3)
Grizzled Outrider - {4}{G} - (5/5)
Nema Siltlurker - {4}{G} - (3/5)
Alpha Tyrranax - {4}{G}{G} - (6/5)
Vorstclaw - {4}{G}{G} - (7/7)
Quilled Slagwurm - {4}{G}{G}{G} - (8/8)
Forest    17

Kobolds of Kher Keep - {0} - (0/1)
Dwarven Trader - {R} - (1/1)
Falkenrath Reaver - {1}{R} - (2/2)
Leopard-Spotted Jiao - {1}{R} - (3/1)
Swab Goblin - {1}{R} - (2/2)
Balduvian Barbarians - {1}{R}{R} - (3/2)
Frenzied Raptor - {2}{R} - (4/2)
Goblin Cavaliers - {2}{R} - (3/2)
Goblin Roughrider - {2}{R} - (3/2)
Canyon Minotaur - {3}{R} - (3/3)
Cyclops of One-Eyed Pass - {2}{R}{R} - (5/2)
Highland Giant - {2}{R}{R} - (3/4)
Lizard Warrior - {3}{R} - (4/2)
Lowland Giant - {2}{R}{R} - (4/3)
Ogre Resister - {2}{R}{R} - (4/3)
Orazca Raptor - {2}{R}{R} - (3/4)
Orazca Raptor - {2}{R}{R} - (3/4)
Orazca Raptor - {2}{R}{R} - (3/4)
Tor Giant - {3}{R} - (3/3)
Tor Giant - {3}{R} - (3/3)
Fire Elemental - {3}{R}{R} - (5/4)
Lathnu Sailback - {4}{R} - (5/4)
Obsidian Giant - {4}{R} - (4/4)
Scoria Elemental - {4}{R} - (6/1)
Mountain    16

Norwood Ranger - {G} - (1/2)
Barbary Apes - {1}{G} - (2/2)
Cylian Elf - {1}{G} - (2/2)
Swordwise Centaur - {G}{G} - (3/2)
Alpine Grizzly - {2}{G} - (4/2)
Alpine Grizzly - {2}{G} - (4/2)
Elvish Ranger - {2}{G} - (4/1)
Elvish Ranger - {2}{G} - (4/1)
Orazca Frillback - {2}{G} - (4/2)
Orazca Frillback - {2}{G} - (4/2)
Trained Armodon - {1}{G}{G} - (3/3)
Axebane Beast - {3}{G} - (3/4)
Ferocious Zheng - {2}{G}{G} - (4/4)
Feral Krushok - {4}{G} - (5/4)
Garruk's Gorehorn - {4}{G} - (7/3)
Gigantosaurus - {G}{G}{G}{G}{G} - (10/10)
Moss Monster - {3}{G}{G} - (3/6)
Pheres-Band Centaurs - {4}{G} - (3/7)
Pheres-Band Centaurs - {4}{G} - (3/7)
Rotted Hystrix - {4}{G} - (3/6)
Spined Wurm - {4}{G} - (5/4)
Thornhide Wolves - {4}{G} - (4/5)
Thornhide Wolves - {4}{G} - (4/5)
Craw Wurm - {4}{G}{G} - (6/4)
Nyxborn Colossus - {3}{G}{G}{G} - (6/7)
Whiptail Wurm - {6}{G} - (8/5)
Forest    14

Eager Cadet - {W} - (1/1)
Elite Vanguard - {W} - (2/1)
Isamaru, Hound of Konda - {W} - (2/2)
Blade of the Sixth Pride - {1}{W} - (3/1)
Blade of the Sixth Pride - {1}{W} - (3/1)
Cliffhaven Sell-Sword - {1}{W} - (3/1)
Devilthorn Fox - {1}{W} - (3/1)
Glory Seeker - {1}{W} - (2/2)
Knight of New Benalia - {1}{W} - (3/1)
Squire - {1}{W} - (1/2)
Squire - {1}{W} - (1/2)
Traveling Philosopher - {1}{W} - (2/2)
Alaborn Trooper - {2}{W} - (2/3)
Femeref Scouts - {2}{W} - (1/4)
Femeref Scouts - {2}{W} - (1/4)
Femeref Scouts - {2}{W} - (1/4)
Keepers of the Faith - {1}{W}{W} - (2/3)
Loxodon Wayfarer - {2}{W} - (1/5)
Dutiful Servants - {3}{W} - (2/5)
Foot Soldiers - {3}{W} - (2/4)
Loxodon Convert - {3}{W} - (4/2)
Great-Horn Krushok - {4}{W} - (3/5)
Plains    18

Woodland Druid - {G} - (1/2)
Balduvian Bears - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Bear Cub - {1}{G} - (2/2)
Forest Bear - {1}{G} - (2/2)
Grizzly Bears - {1}{G} - (2/2)
Kalonian Tusker - {G}{G} - (3/3)
Alpine Grizzly - {2}{G} - (4/2)
Colossodon Yearling - {2}{G} - (2/4)
Elvish Ranger - {2}{G} - (4/1)
Elvish Ranger - {2}{G} - (4/1)
Spined Karok - {2}{G} - (2/4)
Nettle Swine - {3}{G} - (4/3)
Southern Elephant - {3}{G} - (3/4)
Wild Ceratok - {3}{G} - (4/3)
Panther Warriors - {4}{G} - (6/3)
Plated Wurm - {4}{G} - (4/5)
Vorstclaw - {4}{G}{G} - (7/7)
Ancient Brontodon - {6}{G}{G} - (9/9)
Forest    21

Trained Jackal - {G} - (1/2)
Kalonian Tusker - {G}{G} - (3/3)
Kalonian Tusker - {G}{G} - (3/3)
Kalonian Tusker - {G}{G} - (3/3)
Swordwise Centaur - {G}{G} - (3/2)
Swordwise Centaur - {G}{G} - (3/2)
Treetop Warden - {1}{G} - (2/2)
Alpine Grizzly - {2}{G} - (4/2)
Centaur Courser - {2}{G} - (3/3)
Spined Karok - {2}{G} - (2/4)
Axebane Beast - {3}{G} - (3/4)
Golden Bear - {3}{G} - (4/3)
Nettle Swine - {3}{G} - (4/3)
Nema Siltlurker - {4}{G} - (3/5)
Pheres-Band Centaurs - {4}{G} - (3/7)
Redwood Treefolk - {4}{G} - (3/6)
Vastwood Gorger - {5}{G} - (5/6)
Axebane Stag - {6}{G} - (6/7)
Forest    22

@MaximeBastion MaximeBastion marked this pull request as ready for review August 27, 2023 17:15
@MaximeBastion MaximeBastion requested a review from gcoter August 27, 2023 17:15
Copy link
Copy Markdown
Owner

@gcoter gcoter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your code is very clean, well done! I made some comments but we can implement them later, so feel free to merge if you want 🙂

creature_color_filter = self.legal_creatures_df["color_identity"] == f"['{color}']"
creature_card_names = self.legal_creatures_df[creature_color_filter]["name"].unique()
land_color_filter = self.legal_lands_df["color_identity"] == f"['{color}']"
land_card_names = self.legal_lands_df[land_color_filter]["name"].unique()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These variables are relatively constant (they only vary depending on the color) so it would probably be more optimal to create them once and reuse them in add_a_card_at_random (currently if I understand well, they are recomputed at each iteration of the for loop above

from magic_the_gathering.players.random import RandomPlayer


def print_execution_time(func):
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of creating a decorator 👍


def _get_colors_of_deck(deck: OrderedDict[str, object]) -> list[str]:
color_identities = [card.color_identity for card in deck.values()]
return list(set([color for color_identity in color_identities for color in color_identity if color != "C"]))
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean when the color identity if "C"?

)
# print(
# f"Probability that decks are equivalently powerful with {wins_of_player_with_most_wins} wins out of {games_count}: {p_decks_are_equivalently_powerful}"
# )
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you think this print is not relevant anymore, you can remove it rather than commented

return 0 if wins_count / games_count >= 0.5 else 1


def simulate_one_game(decks: list[OrderedDict[str, object]]) -> int:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could group functions like this one in a module, because otherwise we might duplicate code in different scripts (for instance, run_competition_between_players.py has similar code I think)

return decks


def create_hands(game_state: GameState):
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced the MulliganPhase recently, it could replace this function

legal_lands_df,
legal_creatures_df,
deck_size=20,
lands_proportion=17 / 40,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 17/40?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants