better testing for greedy solver. implement better handling of copies of cards
This commit is contained in:
parent
27055fd07f
commit
0756bba205
1 changed files with 47 additions and 21 deletions
68
greedy_solver.py
Normal file → Executable file
68
greedy_solver.py
Normal file → Executable 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:
|
won += 1
|
||||||
# wins.write("Seed {:10} {}:\n{}\n".format(seed, str(deck), link(gs.to_json())))
|
|
||||||
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])
|
||||||
|
|
Loading…
Reference in a new issue