better testing for greedy solver. implement better handling of copies of cards

This commit is contained in:
Maximilian Keßler 2023-03-16 14:07:42 +01:00
parent 27055fd07f
commit 0756bba205
Signed by: max
GPG key ID: BCC5A619923C0BA5

56
greedy_solver.py Normal file → Executable file
View file

@ -1,8 +1,10 @@
#! /bin/python3
import collections import collections
from compress import DeckCard, Action, ActionType, link, decompress_deck from compress import DeckCard, Action, ActionType, link, decompress_deck
from enum import Enum from enum import Enum
from database import conn from database import conn
from time import sleep from time import sleep
import sys
COLORS = 'rygbp' COLORS = 'rygbp'
@ -149,7 +151,7 @@ class GameState():
return CardType.Dispensable return CardType.Dispensable
def is_over(self): def is_over(self):
return all(s == 5 for s in self.stacks) or (self.remaining_extra_turns == 0) return all(s == 5 for s in self.stacks) or (self.remaining_extra_turns == 0) or (self.is_known_lost())
def holding_players(self, card): def holding_players(self, card):
@ -200,17 +202,42 @@ class GreedyStrategy():
for dupe in dupes: for dupe in dupes:
dupe.card_type = CardType.Trash dupe.card_type = CardType.Trash
def hand_badness(states):
if any(state.card_type == CardType.Playable for state in states):
return 0
crits = [state for state in states if state.card_type == CardType.Critical]
crits_val = sum(map(lambda state: state.card.rank, crits))
if any(state.card_type == CardType.Playable for state in states):
return crits_val
def player_distance(f, t):
return ((t - f - 1) % self.game_state.num_players) + 1
for (player, states) in enumerate(hand_states): for (player, states) in enumerate(hand_states):
for state in states: for state in states:
if state.card_type == CardType.Playable: if state.card_type == CardType.Playable:
copy_holders = list(self.game_state.holding_players(state.card)) copy_holders = set(self.game_state.holding_players(state.card))
copy_holders.remove(player) copy_holders.remove(player)
connecting_holders = list(self.game_state.holding_players(DeckCard(state.card.suitIndex, state.card.rank + 1))) connecting_holders = set(self.game_state.holding_players(DeckCard(state.card.suitIndex, state.card.rank + 1)))
if len(copy_holders) == 0: if len(copy_holders) == 0:
state.weight = (3 if len(connecting_holders) > 0 else 1) * state.card.rank # card is unique, imortancy is based lexicographically on whether somebody has the conn. card and the rank
state.weight = (6 if len(connecting_holders) > 0 else 1) * (6 - state.card.rank)
else: else:
# TODO # copy is available somewhere else
state.weight = 0.5 * (5 - state.card.rank) if len(connecting_holders) == 0:
# card is not urgent
state.weight = 0.5 * (6 - state.card.rank)
else:
# there is a copy and there is a connecting card. check if they are out of order
turns_to_copy = min(map(lambda holder: player_distance(player, holder), copy_holders))
turns_to_conn = max(map(lambda holder: player_distance(player, holder), connecting_holders))
if turns_to_copy < turns_to_conn:
# our copy is not neccessary for connecting card to be able to play
state.weight = 0.5 * (6 - state.card.rank)
else:
# our copy is important, scale it little less than if it were unique
state.weight = 4 * (6 - state.card.rank)
elif state.card_type == CardType.Dispensable: elif state.card_type == CardType.Dispensable:
try: try:
# TODO: consider duplicate in hand # TODO: consider duplicate in hand
@ -279,25 +306,24 @@ def run_deck(seed, num_players, deck_str):
deck = decompress_deck(deck_str) deck = decompress_deck(deck_str)
gs = GameState(num_players, deck) gs = GameState(num_players, deck)
strat = GreedyStrategy(gs) strat = GreedyStrategy(gs)
try:
while not gs.is_over(): while not gs.is_over():
strat.make_move() strat.make_move()
if not gs.score() == 25: if not gs.score == 25:
losses.write("Seed {:10} {}:\n{}\n".format(seed, str(deck), link(gs.to_json()))) losses.write("Seed {:10} {}:\n{}\n".format(seed, str(deck), link(gs.to_json())))
lost += 1 lost += 1
else: else:
# wins.write("Seed {:10} {}:\n{}\n".format(seed, str(deck), link(gs.to_json())))
won += 1 won += 1
except ValueError:
crits.write("Seed {} {}lost crit:\n{}\n".format(seed, str(deck), link(gs.to_json())))
crits_lost += 1
if __name__ == "__main__": def run_samples(num_players, sample_size):
cur = conn.cursor() cur = conn.cursor()
cur.execute("SELECT seed, num_players, deck FROM seeds WHERE variant_id = 0 AND num_players = 5 limit 1000") cur.execute("SELECT seed, num_players, deck FROM seeds WHERE variant_id = 0 AND num_players = (%s) order by seed desc limit (%s)", (num_players, sample_size))
print()
for r in cur: for r in cur:
run_deck(*r) run_deck(*r)
print("won: {:4}, lost: {:4}, crits lost: {:3}".format(won, lost, crits_lost), end = "\r") print("won: {:4}, lost: {:4}, crits lost: {:3}".format(won, lost, crits_lost), end = "\r")
print() print()
print("Total wins: {}%".format(round(100 * won / (lost + won + crits_lost), 2))) print("Total wins: {}%".format(round(100 * won / (lost + won + crits_lost), 2)))
if __name__ == "__main__":
for p in range(2, 6):
print("Testing on {} players...".format(p))
run_samples(p, sys.argv[1])