opale/opale/computer.py

138 lines
6.0 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 4 22:32:32 2020
@author: AlexandreMouchel
"""
DEBUG = False
import functools
from opale.carte import *
from opale.player import Player
class Computer(Player):
def __init__(self, board):
super().__init__("OpaleIA")
self.board = board
self.witch_played = 0
def base_importance(t):
return t.importance
def importance(self):
return {
Chasseresse: Computer.base_importance(Chasseresse) - len(self.board.chasseresse),
DragonCorail: Computer.base_importance(DragonCorail) + functools.reduce(lambda a,b: a + b, [0] + [p.score for p in self.board.pearl]),
Pearl: Computer.base_importance(Pearl) - len(self.board.pearl) // 2,
Witch: Computer.base_importance(Witch),
DragonPetrified: Computer.base_importance(DragonPetrified) - len(self.board.dragonPetrified) // 2,
Golem: Computer.base_importance(Golem) - len(self.board.golem) // 2,
Guardian: Computer.base_importance(Guardian) - len(self.board.guardian),
Horser: Computer.base_importance(Horser),
City: Computer.base_importance(City) - len(self.board.city),
}
def play_turn(self, play_round):
if DEBUG: print("HAND: %s" % self.hand)
cards = self.search_play()
if DEBUG: print("after search_play: %s" % cards)
if type(cards[0]) == Witch:
self.witch_played += len(cards)
play_round(*cards)
return cards
def search_play(self, hand=None):
if hand is None:
hand = self.hand
importance = self.importance()
# On regroupe les cartes identiques
cardgroup = {}
for card in hand:
if type(card) not in cardgroup:
cardgroup[type(card)] = []
cardgroup[type(card)].append(card)
# On trie les cartes, de sorte de toujours jouer les plus faibles d'abord
for t in cardgroup.keys():
cardgroup[t].sort(key=lambda c: c.score)
# Cas spécial pour les Witches : on veut récupérer les dragons à tout prix !
if Witch in cardgroup and not self.dragonPetrified:
if len(self.board.dragonPetrified) > 0:
if DEBUG: print("search_play RESOLUTION: taking the dragon is my priority")
return [cardgroup[Witch][0]]
# Cas spécial pour les Witches : on garde la dernière Witch pour récupérer un dragon
if self.witch_played >= 3 and Witch in cardgroup:
del cardgroup[Witch]
# On recherche le(s) groupe(s) de carte(s) qui rapporte(nt) le plus de points
scores = {}
score_max = None
for t in cardgroup.keys():
score = functools.reduce(lambda a,b: a + b, [0] + [c.score for c in self.board.try_cards(*cardgroup[t])])
if score > 0:
scores[t] = score
if score_max is None or scores[t] > scores[score_max]:
score_max = t
# Try by reducing the number of cards plays
if score_max is not None:
if DEBUG: print("search_play reduce with score=%d" % scores[score_max])
min_cards = 9
for t in [ty for ty in cardgroup.keys() if ty in scores and scores[ty] == scores[score_max]]:
if DEBUG: print("search_play reducing starting with %s" % cardgroup[t])
ok = len(cardgroup[t])
while ok > 1:
score = functools.reduce(lambda a,b: a + b, [0] + [c.score for c in self.board.try_cards(*cardgroup[t][0:ok - 1])])
if DEBUG: print("search_play reducing score=%d with %s" % (score, cardgroup[t][0:ok-1]))
if score >= scores[score_max]:
ok -= 1
else:
break
if ok != len(cardgroup[t]):
if t == Guardian:
cardgroup[t] = cardgroup[t][len(cardgroup[t]) - ok:]
else:
cardgroup[t] = cardgroup[t][0:ok]
if DEBUG: print("search_play reduce to %d %s" % (ok, cardgroup[t]))
if min_cards > ok:
min_cards = ok
# Return the first match
for t in sorted([ty for ty in cardgroup.keys() if ty in scores and scores[ty] == scores[score_max] and len(cardgroup[ty]) == min_cards], key=lambda t: importance[t], reverse=True):
if DEBUG: print("search_play RESOLUTION: play for score %d" % scores[score_max])
return cardgroup[t]
print("WHY am I here? score_max=%d min_cards=%d groups=[%s] sorted=%s" % (
scores[score_max],
min_cards,
','.join(["%s: %s" % (t.__name__, cardgroup[t]) for t in sorted([ty for ty in cardgroup.keys()], key=lambda t: importance[t], reverse=True)]),
[t.__name__ for t in sorted([ty for ty in cardgroup.keys() if ty in scores and scores[ty] == scores[score_max] and len(cardgroup[ty]) == min_cards], key=lambda t: importance[t], reverse=True)]
))
# Return the first match
for t in sorted(cardgroup.keys(), key=lambda t: importance[t], reverse=True):
if len(cardgroup[t]) > 1:
if t == Pearl and cardgroup[t][0].score + cardgroup[t][0].score < 4:
if DEBUG: print("search_play RESOLUTION: Pearl cards less than 4 points")
return cardgroup[t][0:2]
elif t == Pearl and len(cardgroup[t]) > 2:
if DEBUG: print("search_play RESOLUTION: too much Pearl cards in hand")
return cardgroup[t][0:2]
elif len(cardgroup[t]) > 4:
if DEBUG: print("search_play RESOLUTION: too much card type in hand")
return cardgroup[t][0:2]
if DEBUG: print("search_play RESOLUTION: fallback to default move")
return [cardgroup[t][0]]