Implement AI
This commit is contained in:
parent
9bbcedeb59
commit
14a8e8d546
@ -18,6 +18,7 @@ class Card():
|
||||
class Chasseresse(Card):
|
||||
|
||||
color = 91
|
||||
importance = 6
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -26,6 +27,7 @@ class Chasseresse(Card):
|
||||
class DragonCorail(Card):
|
||||
|
||||
color = 92
|
||||
importance = 7
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -34,6 +36,7 @@ class DragonCorail(Card):
|
||||
class Pearl(Card):
|
||||
|
||||
color = 93
|
||||
importance = 10
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -42,6 +45,7 @@ class Pearl(Card):
|
||||
class Witch(Card):
|
||||
|
||||
color = 94
|
||||
importance = 7
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -50,6 +54,7 @@ class Witch(Card):
|
||||
class DragonPetrified(Card):
|
||||
|
||||
color = 95
|
||||
importance = 9
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -58,6 +63,7 @@ class DragonPetrified(Card):
|
||||
class Golem(Card):
|
||||
|
||||
color = 91
|
||||
importance = 8
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -66,6 +72,7 @@ class Golem(Card):
|
||||
class Guardian(Card):
|
||||
|
||||
color = 94
|
||||
importance = 5
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -74,6 +81,7 @@ class Guardian(Card):
|
||||
class Horser(Card):
|
||||
|
||||
color = 92
|
||||
importance = 7
|
||||
|
||||
def __init__(self, score):
|
||||
Card.__init__(self, score)
|
||||
@ -82,6 +90,7 @@ class Horser(Card):
|
||||
class City(Card):
|
||||
|
||||
color = 96
|
||||
importance = 6
|
||||
|
||||
def __init__(self):
|
||||
Card.__init__(self, 0)
|
||||
|
134
opale/computer.py
Normal file
134
opale/computer.py
Normal file
@ -0,0 +1,134 @@
|
||||
#!/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):
|
||||
importance = self.importance()
|
||||
|
||||
# On regroupe les cartes identiques
|
||||
cardgroup = {}
|
||||
for card in self.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]]
|
@ -10,6 +10,7 @@ import random
|
||||
|
||||
from opale.carte import *
|
||||
from opale.board import Board
|
||||
from opale.computer import Computer
|
||||
from opale.player import Player
|
||||
|
||||
class Game():
|
||||
@ -20,7 +21,10 @@ class Game():
|
||||
self.players = []
|
||||
|
||||
for pname in player_names:
|
||||
if pname is not None:
|
||||
self.players.append(Player(pname))
|
||||
else:
|
||||
self.players.append(Computer(self.board))
|
||||
|
||||
random.shuffle(self.players)
|
||||
|
||||
|
25
server.py
25
server.py
@ -8,9 +8,11 @@ import random
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
from opale.computer import Computer
|
||||
import opale.computer
|
||||
from opale.game import Game
|
||||
|
||||
opale.computer.DEBUG = True
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
@ -58,6 +60,11 @@ while True:
|
||||
watchers.append(connP1)
|
||||
connP1.send(("Welcome! You'll watch futher games. Wait for one... :)\n").encode())
|
||||
continue
|
||||
elif nameP1 == "computer":
|
||||
nameP2 = None
|
||||
connP1.send(("Welcome! You'll play against the computer. Please choose your name:\n> ").encode())
|
||||
nameP1 = connP1.recv(1024).decode().strip()
|
||||
connP1.send("\n\nWould you like to load a saved game? (leave blank and press ENTER to load a new one)\n> ".encode())
|
||||
else:
|
||||
connP1.send(("Welcome \033[93m%s\033[0m! We are waiting for a second player...\n" % nameP1).encode())
|
||||
|
||||
@ -90,6 +97,7 @@ while True:
|
||||
break
|
||||
|
||||
connP1.send((chr(27) + "[2J").encode())
|
||||
if nameP2 is not None:
|
||||
connP2.send((chr(27) + "[2J").encode())
|
||||
if choice not in games:
|
||||
if len(choice) == 0:
|
||||
@ -98,6 +106,7 @@ while True:
|
||||
game = Game(nameP1, nameP2)
|
||||
games[choice] = game
|
||||
connP1.send(("\n\nYou entered a new game.\nSave the id of the room if you loose the connection for any reason: %s\n" % choice).encode())
|
||||
if nameP2 is not None:
|
||||
connP2.send(("\n\nYou entered a new game.\nSave the id of the room if you loose the connection for any reason: %s\n" % choice).encode())
|
||||
print("Play new game between %s and %s: %s" % (nameP1, nameP2, choice))
|
||||
|
||||
@ -109,14 +118,20 @@ while True:
|
||||
game.player1.name = nameP1
|
||||
game.player2.name = nameP2
|
||||
connP1.send(("You entered a saved game (%s); \033[93m%s\033[0m takes the place of \033[94m%s\033[0m, \033[93m%s\033[0m the place of \033[94m%s\033[0m.\n" % (choice, nameP1, game.player1.name, nameP2, game.player2.name)).encode())
|
||||
if nameP2 is not None:
|
||||
connP2.send(("You entered a saved game (%s); \033[93m%s\033[0m takes the place of \033[94m%s\033[0m, \033[93m%s\033[0m the place of \033[94m%s\033[0m.\n" % (choice, nameP1, game.player1.name, nameP2, game.player2.name)).encode())
|
||||
|
||||
connP1.send(("\n\nWelcome back! We are at round %d. \033[96m%d citie(s) discovered.\033[0m \033[93m%s\033[0m collected %d cards, you collected %d card(s).\n" % (game.round // 2, game.board.roundCity, game.player2.name, len(game.player2.défausse), len(game.player1.défausse))).encode())
|
||||
if nameP2 is not None:
|
||||
connP2.send(("\n\nWelcome back! We are at round %d. \033[96m%d citie(s) discovered.\033[0m \033[93m%s\033[0m collected %d cards, you collected %d card(s).\n" % (game.round // 2, game.board.roundCity, game.player1.name, len(game.player1.défausse), len(game.player2.défausse))).encode())
|
||||
|
||||
game.player1.print = functools.partial(writeSocket, connP1)
|
||||
game.player2.print = functools.partial(writeSocket, connP2)
|
||||
game.player1.input = functools.partial(readSocket, connP1)
|
||||
if type(game.player2) == opale.computer.Computer:
|
||||
game.player2.print = lambda *args: True
|
||||
game.player2.input = lambda *args: True
|
||||
else:
|
||||
game.player2.print = functools.partial(writeSocket, connP2)
|
||||
game.player2.input = functools.partial(readSocket, connP2)
|
||||
|
||||
def printWatchers(*args):
|
||||
@ -136,6 +151,9 @@ while True:
|
||||
printWatchers(*args)
|
||||
|
||||
def play_turn():
|
||||
if type(game.current_player) == opale.computer.Computer:
|
||||
return game.current_player.play_turn(game.play_round)
|
||||
|
||||
printWatchers("\n\033[1mCurrent player:\033[0m \033[93m%s\033[0m \033[95m%s\033[0m" % (game.current_player.name, "DRAGON" if game.current_player.dragonPetrified else ""))
|
||||
game.current_player.print("\n\033[1mCurrent player:\033[0m \033[93m%s\033[0m \033[95m%s\033[0m" % (game.current_player.name, "DRAGON" if game.current_player.dragonPetrified else ""))
|
||||
game.current_partner.print("\n\033[1mCurrent player:\033[0m \033[96m%s\033[0m \033[95m%s\033[0m" % (game.current_player.name, "DRAGON" if game.current_player.dragonPetrified else ""))
|
||||
@ -229,8 +247,9 @@ while True:
|
||||
del games[choice]
|
||||
|
||||
connP1.send(("\n\nFinished! See you next time " + nameP1 + "!\n\n\n").encode())
|
||||
connP2.send(("\n\nFinished! See you next time " + nameP2 + "!\n\n\n").encode())
|
||||
connP1.close()
|
||||
if nameP2 is not None:
|
||||
connP2.send(("\n\nFinished! See you next time " + nameP2 + "!\n\n\n").encode())
|
||||
connP2.close()
|
||||
except BrokenPipeError as e:
|
||||
print(e)
|
||||
|
Loading…
Reference in New Issue
Block a user