Dungeon of botdom
DevilWorshipper
My first attempt at a KOTH challenge:
from base import BasePlayer
#from random import randint
class DevilWorshipper(BasePlayer):
def reset(self):
self.items = [0, 1, 2, 3, 4, 5]
self.turns = 0
self.demon = False
self.dragon = False
def __init__(self):
self.reset()
def start_turn(self, last_turn):
if last_turn in self.items:
self.items.remove(last_turn)
if last_turn is not None:
#self.demon = True if randint(1, 13 - self.turns) <= 2 else False
self.turns += 1
if (((self.demon == True and not (0 in self.items)) or (self.dragon == True)) and not (3 in self.items)):
return 0
if (len(self.items) <= 1):
return 0
return 1
def play(self, card):
self.turns += 1
if (card == 9):
self.dragon = True
return 6
if (card == 7):
self.demon = True
return 6
for i in [3, 0, 2, 1, 5, 4]:
if (i in self.items):
self.items.remove(i)
return i
return 6
def vorpal_choice(self, last_turn):
return 5 #If it works for others maybe it will work for us
def result(self, bot, result, dungeon, vorped):
self.reset()
Essentially, we get rid of the pact and the vorpal dagger, wait for the demon to get into the deck, and pass. Every round the opponent may have drawn the demon, it has the % chance that the last card the opponent drew was a demon to just assume they played the demon already.
Let me know if there's anything I did wrong; I haven't messed with python in a while, this is my first KOTH, and it's 2 am, so there's bound to be something.
EDITS:
Taking out the randomness turns out to help it a lot. With the randomness in it is very dumb. Also, as said in the comments below, it tries to summon the demon or dragon.
Steve
from base import BasePlayer
from random import choice
class Steve(BasePlayer):
def reset(self):
self.items = [0, 1, 2, 3, 4, 5]
self.turns = 0
self.dungeon = []
self.possibledungeon = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 9]
self.lastDied = 0
def __init__(self):
self.TRIALS = 10 #How many dungeon runs to do each turn
self.PASS = 8 #How many dungeon runs have to have died to pass
self.SMASHITEMS = 4 #If less dungeon runs died, smash items.
self.reset()
def start_turn(self, last_turn):
if (last_turn is not None):
self.turns += 1
if (last_turn in self.items):
self.items.remove(last_turn)
else:
self.dungeon.append(-1)
#Check if the dungeon is lethal
died = 0
total_hp = 3
if (5 in self.items):
total_hp += 5
if (3 in self.items):
total_hp += 3
vorpal = self.vorpal_choice(None)
for i in range(self.TRIALS):
hp = total_hp
temppossible = self.possibledungeon.copy()
usedpotion = False
killedDemon = False
#Going for a crawl
for j in self.dungeon[::-1]:
if (killedDemon == True): #If last round we killed the Demon
killedDemon = False
j = 0
if (j == -1): #If we don't know what this card is
j = choice(temppossible)
temppossible.remove(j)
if (j == 7 and 0 in self.items): #If we kill demon with the pact
j = 0
killedDemon = True
if (j % 2 == 0 and 2 in self.items) or (j == vorpal): #If we vorpal or grail
j = 0
hp -= j
if (hp <= 0):
if (not usedpotion and 1 in self.items):
hp = 3
usedpotion = True
else:
died += 1
break
if (died >= self.PASS):
return 0
died = self.lastDied
return 1
def play(self, card):
self.possibledungeon.remove(card)
if (self.lastDied < self.SMASHITEMS):
if (7 in self.dungeon) and (0 in self.items):
self.items.remove(0)
return 0
if ( (9 in self.dungeon) or (5 in self.dungeon) ) and (3 in self.items):
self.items.remove(3)
return 3
for i in [2, 1, 5, 4, 3, 0]:
if (i in self.items):
self.items.remove(i)
return i
self.dungeon.append(card)
return 6
def vorpal_choice(self, last_turn):
if (last_turn is not None):
self.turns += 1
if (last_turn in self.items):
self.items.remove(last_turn)
else:
self.dungeon.append(-1)
if (self.dungeon.count(5) == 2):
return 5
if (9 in self.dungeon):
return 9
if (self.dungeon.count(4) == 2 and not 2 in self.items):
return 4
if (7 in self.dungeon and not 0 in self.items):
return 7
for i in range(6)[::-1]:
if (i+1 in self.dungeon):
return i+1
return 5
def result(self, bot, result, dungeon, vorped):
self.reset()
Steve tries to guess whether or not the dungeon is lethal. If he thinks it is, he passes. Other than that, I tried to make him get rid of items smartly. He used to adjust his PASS threshold depending on if he died in the dungeon or the opponent lived, but it ended up making him a lot dumber so I got rid of it.
He still doesn't beat GrailThief on my machine, but at least comes closer.
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() + 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()
SlapAndFlap
First time in KOTH, so slap me hard for mistakes.
This one simpleton is always trying to remove all good items with low-strength monsters, while keep powerful ones and then just forces opponent to play.
It beats RunAway and DumDum at least :D
My other bot in deleted answer for some time, I need to fix him by tommorow
from base import BasePlayer
class SlapAndFlap(BasePlayer):
def reset(self):
# Monsters that you pushed in
self.know_monster = list(self.deck)
# Items still in game
self.items_in_game = [True, True, True, True, True, True]
# List of items, sorted by value
self.valuables = [3,1,5,0,2,4]
# Counter
self.cards = 13
def __init__(self):
# Deck
self.deck = (1,1,2,2,3,3,4,4,5,5,6,7,9)
# Indexes of item cards
self.items = (0, 1, 2, 3, 4, 5)
self.reset()
def start_turn(self, last_turn):
if last_turn is not None:
self.cards -= 1
# Sneak peak at items removed by opponent
if last_turn is not None and last_turn < 6:
self.items_in_game[last_turn] = False
self.valuables.remove(last_turn)
# Flap!
if self.cards < 6:
return 0
return 1
def play(self, card):
if card < 6 and any(self.items_in_game):
self.know_monster.remove(card)
to_return = self.valuables[0] # remove the best of the rest
self.valuables = self.valuables[1:]
self.items_in_game[to_return] = False
return to_return
else:
return 6
def vorpal_choice(self, last_turn):
# We can just guess what monster will be there
# But we know ones, we removed
# If we have pact, no need to remove demon
if self.items_in_game[0]:
self.know_monster.remove(7)
# If we have grail, no need to remove even monsters (kinda)
if self.items_in_game[2]:
self.know_monster = [i for i in self.know_monster if i%2]
# Find what threatens us the most, counting its strength multiplied by number
weight = [i * self.know_monster.count(i) for i in self.know_monster]
return weight.index(max(weight)) + 1
def result(self, bot, result, dungeon, vorped):
self.reset() # we live for the thrill, not the result!