Bounty Ended with 100 reputation awarded by Destructible Lemon
2 small optimization
source | link
from base import BasePlayer
import copy

class GrailThief(BasePlayer):
    class Stats:
        def __init__(self):
            self.deck = [1, 2, 3, 4, 5] * 2 + [6, 7, 9]
            self.items = {0, 1, 2, 3, 4, 5}
            self.dungeon_known = []
            self.dungeon_unknown = 0
            self.monsters_safe = {2, 4, 6, 7}
            self.update()

        def update(self):
            self.dungeon_total = len(self.dungeon_known) + self.dungeon_unknown
            deck_factor = float(self.dungeon_unknown) / len(self.deck) if len(self.deck) > 0 else 1.0
            self.dungeon_weighted = [(i, 0.0 if i in self.monsters_safe else 1.0) for i in self.dungeon_known] + [(i, 0.0 if i in self.monsters_safe else deck_factor) for i in self.deck]
            dungeon_weighted_sums = dict.fromkeys(self.dungeon_known + self.deck, 0.0)
            for i in self.dungeon_weighted:
                dungeon_weighted_sums[i[0]] += i[0] * i[1]
            self.vorpal = max(dungeon_weighted_sums, key = dungeon_weighted_sums.get)
            if 3 in self.items:
                self.dungeon_weighted = [(i[0], 0.0 if i[0] == self.vorpal else i[1]) for i in self.dungeon_weighted]

        def discard_item(self, item, card):
            new = copy.copy(self)
            new.items = {i for i in new.items if i != item}
            if item == 0:
                new.monsters_safe = {i for i in new.monsters_safe if i != 7}
            elif item == 2:
                new.monsters_safe = {i for i in new.monsters_safe if i not in {2, 4, 6}}
            if card is not None:
                new.deck = list(new.deck)
                new.deck.remove(card)
            new.update()
            return new

        def to_dungeon(self, card):
            new = copy.copy(self)
            if card is None:
                new.dungeon_unknown += 1
            else:
                new.deck = list(new.deck)
                new.deck.remove(card)
                new.dungeon_known = list(new.dungeon_known)
                new.dungeon_known.append(card)
            new.update()
            return new

        def effective_hp(self):
            hp = 3.0
            if 1 in self.items:
                hp += 3.0
                if self.dungeon_total > 0:
                    hp += sum([(i[0] - 1) * i[1] for i in self.dungeon_weighted]) / self.dungeon_total
            if 4 in self.items:
                hp += 3.0
            if 5 in self.items:
                hp += 5.0
            return hp

        def effective_damage(self):
            damage = sum([i[0] * i[1] for i in self.dungeon_weighted])
            if 0 in self.items:
                if self.dungeon_total > 1:
                    damage -= damage / (self.dungeon_total - 1)
            return damage

    def __init__(self):
        self.stats = self.Stats()

    def process_last_turn(self, last_turn):
        if last_turn in [0, 1, 2, 3, 4, 5]:
            self.stats = self.stats.discard_item(last_turn, None)
        elif last_turn == 6:
            self.stats = self.stats.to_dungeon(None)

    def start_turn(self, last_turn):
        self.process_last_turn(last_turn)
        if self.stats.effective_hp() > self.stats.effective_damage() + 1.5:
            return 1
        else:
            return 0

    def play(self, card):
        if 2 in self.stats.items:
            self.stats = self.stats.discard_item(2, card)
            return 2
        else:
            self.stats = self.stats.to_dungeon(card)
            return 6

    def vorpal_choice(self, last_turn):
        self.process_last_turn(last_turn)
        return self.stats.vorpal

    def result(self, bot, result, dungeon, vorped):
        self.stats = self.Stats()
from base import BasePlayer
import copy

class GrailThief(BasePlayer):
    class Stats:
        def __init__(self):
            self.deck = [1, 2, 3, 4, 5] * 2 + [6, 7, 9]
            self.items = {0, 1, 2, 3, 4, 5}
            self.dungeon_known = []
            self.dungeon_unknown = 0
            self.monsters_safe = {2, 4, 6, 7}
            self.update()

        def update(self):
            self.dungeon_total = len(self.dungeon_known) + self.dungeon_unknown
            deck_factor = float(self.dungeon_unknown) / len(self.deck) if len(self.deck) > 0 else 1.0
            self.dungeon_weighted = [(i, 0.0 if i in self.monsters_safe else 1.0) for i in self.dungeon_known] + [(i, 0.0 if i in self.monsters_safe else deck_factor) for i in self.deck]
            dungeon_weighted_sums = dict.fromkeys(self.dungeon_known + self.deck, 0.0)
            for i in self.dungeon_weighted:
                dungeon_weighted_sums[i[0]] += i[0] * i[1]
            self.vorpal = max(dungeon_weighted_sums, key = dungeon_weighted_sums.get)
            if 3 in self.items:
                self.dungeon_weighted = [(i[0], 0.0 if i[0] == self.vorpal else i[1]) for i in self.dungeon_weighted]

        def discard_item(self, item, card):
            new = copy.copy(self)
            new.items = {i for i in new.items if i != item}
            if item == 0:
                new.monsters_safe = {i for i in new.monsters_safe if i != 7}
            elif item == 2:
                new.monsters_safe = {i for i in new.monsters_safe if i not in {2, 4, 6}}
            if card is not None:
                new.deck = list(new.deck)
                new.deck.remove(card)
            new.update()
            return new

        def to_dungeon(self, card):
            new = copy.copy(self)
            if card is None:
                new.dungeon_unknown += 1
            else:
                new.deck = list(new.deck)
                new.deck.remove(card)
                new.dungeon_known = list(new.dungeon_known)
                new.dungeon_known.append(card)
            new.update()
            return new

        def effective_hp(self):
            hp = 3.0
            if 1 in self.items:
                hp += 3.0
                if self.dungeon_total > 0:
                    hp += sum([(i[0] - 1) * i[1] for i in self.dungeon_weighted]) / self.dungeon_total
            if 4 in self.items:
                hp += 3.0
            if 5 in self.items:
                hp += 5.0
            return hp

        def effective_damage(self):
            damage = sum([i[0] * i[1] for i in self.dungeon_weighted])
            if 0 in self.items:
                if self.dungeon_total > 1:
                    damage -= damage / (self.dungeon_total - 1)
            return damage

    def __init__(self):
        self.stats = self.Stats()

    def process_last_turn(self, last_turn):
        if last_turn in [0, 1, 2, 3, 4, 5]:
            self.stats = self.stats.discard_item(last_turn, None)
        elif last_turn == 6:
            self.stats = self.stats.to_dungeon(None)

    def start_turn(self, last_turn):
        self.process_last_turn(last_turn)
        if self.stats.effective_hp() > self.stats.effective_damage():
            return 1
        else:
            return 0

    def play(self, card):
        if 2 in self.stats.items:
            self.stats = self.stats.discard_item(2, card)
            return 2
        else:
            self.stats = self.stats.to_dungeon(card)
            return 6

    def vorpal_choice(self, last_turn):
        self.process_last_turn(last_turn)
        return self.stats.vorpal

    def result(self, bot, result, dungeon, vorped):
        self.stats = self.Stats()
from base import BasePlayer
import copy

class GrailThief(BasePlayer):
    class Stats:
        def __init__(self):
            self.deck = [1, 2, 3, 4, 5] * 2 + [6, 7, 9]
            self.items = {0, 1, 2, 3, 4, 5}
            self.dungeon_known = []
            self.dungeon_unknown = 0
            self.monsters_safe = {2, 4, 6, 7}
            self.update()

        def update(self):
            self.dungeon_total = len(self.dungeon_known) + self.dungeon_unknown
            deck_factor = float(self.dungeon_unknown) / len(self.deck) if len(self.deck) > 0 else 1.0
            self.dungeon_weighted = [(i, 0.0 if i in self.monsters_safe else 1.0) for i in self.dungeon_known] + [(i, 0.0 if i in self.monsters_safe else deck_factor) for i in self.deck]
            dungeon_weighted_sums = dict.fromkeys(self.dungeon_known + self.deck, 0.0)
            for i in self.dungeon_weighted:
                dungeon_weighted_sums[i[0]] += i[0] * i[1]
            self.vorpal = max(dungeon_weighted_sums, key = dungeon_weighted_sums.get)
            if 3 in self.items:
                self.dungeon_weighted = [(i[0], 0.0 if i[0] == self.vorpal else i[1]) for i in self.dungeon_weighted]

        def discard_item(self, item, card):
            new = copy.copy(self)
            new.items = {i for i in new.items if i != item}
            if item == 0:
                new.monsters_safe = {i for i in new.monsters_safe if i != 7}
            elif item == 2:
                new.monsters_safe = {i for i in new.monsters_safe if i not in {2, 4, 6}}
            if card is not None:
                new.deck = list(new.deck)
                new.deck.remove(card)
            new.update()
            return new

        def to_dungeon(self, card):
            new = copy.copy(self)
            if card is None:
                new.dungeon_unknown += 1
            else:
                new.deck = list(new.deck)
                new.deck.remove(card)
                new.dungeon_known = list(new.dungeon_known)
                new.dungeon_known.append(card)
            new.update()
            return new

        def effective_hp(self):
            hp = 3.0
            if 1 in self.items:
                hp += 3.0
                if self.dungeon_total > 0:
                    hp += sum([(i[0] - 1) * i[1] for i in self.dungeon_weighted]) / self.dungeon_total
            if 4 in self.items:
                hp += 3.0
            if 5 in self.items:
                hp += 5.0
            return hp

        def effective_damage(self):
            damage = sum([i[0] * i[1] for i in self.dungeon_weighted])
            if 0 in self.items:
                if self.dungeon_total > 1:
                    damage -= damage / (self.dungeon_total - 1)
            return damage

    def __init__(self):
        self.stats = self.Stats()

    def process_last_turn(self, last_turn):
        if last_turn in [0, 1, 2, 3, 4, 5]:
            self.stats = self.stats.discard_item(last_turn, None)
        elif last_turn == 6:
            self.stats = self.stats.to_dungeon(None)

    def start_turn(self, last_turn):
        self.process_last_turn(last_turn)
        if self.stats.effective_hp() > self.stats.effective_damage() + 1.5:
            return 1
        else:
            return 0

    def play(self, card):
        if 2 in self.stats.items:
            self.stats = self.stats.discard_item(2, card)
            return 2
        else:
            self.stats = self.stats.to_dungeon(card)
            return 6

    def vorpal_choice(self, last_turn):
        self.process_last_turn(last_turn)
        return self.stats.vorpal

    def result(self, bot, result, dungeon, vorped):
        self.stats = self.Stats()
1
source | link

GrailThief

An old and experienced dungeon crawler. He knows that most of the others hope, that the holy grail saves them, therefore he makes sure it disappears.

from base import BasePlayer
import copy

class GrailThief(BasePlayer):
    class Stats:
        def __init__(self):
            self.deck = [1, 2, 3, 4, 5] * 2 + [6, 7, 9]
            self.items = {0, 1, 2, 3, 4, 5}
            self.dungeon_known = []
            self.dungeon_unknown = 0
            self.monsters_safe = {2, 4, 6, 7}
            self.update()

        def update(self):
            self.dungeon_total = len(self.dungeon_known) + self.dungeon_unknown
            deck_factor = float(self.dungeon_unknown) / len(self.deck) if len(self.deck) > 0 else 1.0
            self.dungeon_weighted = [(i, 0.0 if i in self.monsters_safe else 1.0) for i in self.dungeon_known] + [(i, 0.0 if i in self.monsters_safe else deck_factor) for i in self.deck]
            dungeon_weighted_sums = dict.fromkeys(self.dungeon_known + self.deck, 0.0)
            for i in self.dungeon_weighted:
                dungeon_weighted_sums[i[0]] += i[0] * i[1]
            self.vorpal = max(dungeon_weighted_sums, key = dungeon_weighted_sums.get)
            if 3 in self.items:
                self.dungeon_weighted = [(i[0], 0.0 if i[0] == self.vorpal else i[1]) for i in self.dungeon_weighted]

        def discard_item(self, item, card):
            new = copy.copy(self)
            new.items = {i for i in new.items if i != item}
            if item == 0:
                new.monsters_safe = {i for i in new.monsters_safe if i != 7}
            elif item == 2:
                new.monsters_safe = {i for i in new.monsters_safe if i not in {2, 4, 6}}
            if card is not None:
                new.deck = list(new.deck)
                new.deck.remove(card)
            new.update()
            return new

        def to_dungeon(self, card):
            new = copy.copy(self)
            if card is None:
                new.dungeon_unknown += 1
            else:
                new.deck = list(new.deck)
                new.deck.remove(card)
                new.dungeon_known = list(new.dungeon_known)
                new.dungeon_known.append(card)
            new.update()
            return new

        def effective_hp(self):
            hp = 3.0
            if 1 in self.items:
                hp += 3.0
                if self.dungeon_total > 0:
                    hp += sum([(i[0] - 1) * i[1] for i in self.dungeon_weighted]) / self.dungeon_total
            if 4 in self.items:
                hp += 3.0
            if 5 in self.items:
                hp += 5.0
            return hp

        def effective_damage(self):
            damage = sum([i[0] * i[1] for i in self.dungeon_weighted])
            if 0 in self.items:
                if self.dungeon_total > 1:
                    damage -= damage / (self.dungeon_total - 1)
            return damage

    def __init__(self):
        self.stats = self.Stats()

    def process_last_turn(self, last_turn):
        if last_turn in [0, 1, 2, 3, 4, 5]:
            self.stats = self.stats.discard_item(last_turn, None)
        elif last_turn == 6:
            self.stats = self.stats.to_dungeon(None)

    def start_turn(self, last_turn):
        self.process_last_turn(last_turn)
        if self.stats.effective_hp() > self.stats.effective_damage():
            return 1
        else:
            return 0

    def play(self, card):
        if 2 in self.stats.items:
            self.stats = self.stats.discard_item(2, card)
            return 2
        else:
            self.stats = self.stats.to_dungeon(card)
            return 6

    def vorpal_choice(self, last_turn):
        self.process_last_turn(last_turn)
        return self.stats.vorpal

    def result(self, bot, result, dungeon, vorped):
        self.stats = self.Stats()