Implement AI
This commit is contained in:
parent
9bbcedeb59
commit
14a8e8d546
@ -18,6 +18,7 @@ class Card():
|
|||||||
class Chasseresse(Card):
|
class Chasseresse(Card):
|
||||||
|
|
||||||
color = 91
|
color = 91
|
||||||
|
importance = 6
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -26,6 +27,7 @@ class Chasseresse(Card):
|
|||||||
class DragonCorail(Card):
|
class DragonCorail(Card):
|
||||||
|
|
||||||
color = 92
|
color = 92
|
||||||
|
importance = 7
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -34,6 +36,7 @@ class DragonCorail(Card):
|
|||||||
class Pearl(Card):
|
class Pearl(Card):
|
||||||
|
|
||||||
color = 93
|
color = 93
|
||||||
|
importance = 10
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -42,6 +45,7 @@ class Pearl(Card):
|
|||||||
class Witch(Card):
|
class Witch(Card):
|
||||||
|
|
||||||
color = 94
|
color = 94
|
||||||
|
importance = 7
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -50,6 +54,7 @@ class Witch(Card):
|
|||||||
class DragonPetrified(Card):
|
class DragonPetrified(Card):
|
||||||
|
|
||||||
color = 95
|
color = 95
|
||||||
|
importance = 9
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -58,6 +63,7 @@ class DragonPetrified(Card):
|
|||||||
class Golem(Card):
|
class Golem(Card):
|
||||||
|
|
||||||
color = 91
|
color = 91
|
||||||
|
importance = 8
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -66,6 +72,7 @@ class Golem(Card):
|
|||||||
class Guardian(Card):
|
class Guardian(Card):
|
||||||
|
|
||||||
color = 94
|
color = 94
|
||||||
|
importance = 5
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -74,6 +81,7 @@ class Guardian(Card):
|
|||||||
class Horser(Card):
|
class Horser(Card):
|
||||||
|
|
||||||
color = 92
|
color = 92
|
||||||
|
importance = 7
|
||||||
|
|
||||||
def __init__(self, score):
|
def __init__(self, score):
|
||||||
Card.__init__(self, score)
|
Card.__init__(self, score)
|
||||||
@ -82,6 +90,7 @@ class Horser(Card):
|
|||||||
class City(Card):
|
class City(Card):
|
||||||
|
|
||||||
color = 96
|
color = 96
|
||||||
|
importance = 6
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Card.__init__(self, 0)
|
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.carte import *
|
||||||
from opale.board import Board
|
from opale.board import Board
|
||||||
|
from opale.computer import Computer
|
||||||
from opale.player import Player
|
from opale.player import Player
|
||||||
|
|
||||||
class Game():
|
class Game():
|
||||||
@ -20,7 +21,10 @@ class Game():
|
|||||||
self.players = []
|
self.players = []
|
||||||
|
|
||||||
for pname in player_names:
|
for pname in player_names:
|
||||||
self.players.append(Player(pname))
|
if pname is not None:
|
||||||
|
self.players.append(Player(pname))
|
||||||
|
else:
|
||||||
|
self.players.append(Computer(self.board))
|
||||||
|
|
||||||
random.shuffle(self.players)
|
random.shuffle(self.players)
|
||||||
|
|
||||||
|
37
server.py
37
server.py
@ -8,9 +8,11 @@ import random
|
|||||||
import sys
|
import sys
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from opale.computer import Computer
|
import opale.computer
|
||||||
from opale.game import Game
|
from opale.game import Game
|
||||||
|
|
||||||
|
opale.computer.DEBUG = True
|
||||||
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
try:
|
try:
|
||||||
@ -58,6 +60,11 @@ while True:
|
|||||||
watchers.append(connP1)
|
watchers.append(connP1)
|
||||||
connP1.send(("Welcome! You'll watch futher games. Wait for one... :)\n").encode())
|
connP1.send(("Welcome! You'll watch futher games. Wait for one... :)\n").encode())
|
||||||
continue
|
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:
|
else:
|
||||||
connP1.send(("Welcome \033[93m%s\033[0m! We are waiting for a second player...\n" % nameP1).encode())
|
connP1.send(("Welcome \033[93m%s\033[0m! We are waiting for a second player...\n" % nameP1).encode())
|
||||||
|
|
||||||
@ -90,7 +97,8 @@ while True:
|
|||||||
break
|
break
|
||||||
|
|
||||||
connP1.send((chr(27) + "[2J").encode())
|
connP1.send((chr(27) + "[2J").encode())
|
||||||
connP2.send((chr(27) + "[2J").encode())
|
if nameP2 is not None:
|
||||||
|
connP2.send((chr(27) + "[2J").encode())
|
||||||
if choice not in games:
|
if choice not in games:
|
||||||
if len(choice) == 0:
|
if len(choice) == 0:
|
||||||
choice = str(uuid.uuid1())
|
choice = str(uuid.uuid1())
|
||||||
@ -98,7 +106,8 @@ while True:
|
|||||||
game = Game(nameP1, nameP2)
|
game = Game(nameP1, nameP2)
|
||||||
games[choice] = game
|
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())
|
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())
|
||||||
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())
|
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))
|
print("Play new game between %s and %s: %s" % (nameP1, nameP2, choice))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -109,15 +118,21 @@ while True:
|
|||||||
game.player1.name = nameP1
|
game.player1.name = nameP1
|
||||||
game.player2.name = nameP2
|
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())
|
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())
|
||||||
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())
|
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())
|
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())
|
||||||
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())
|
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.player1.print = functools.partial(writeSocket, connP1)
|
||||||
game.player2.print = functools.partial(writeSocket, connP2)
|
|
||||||
game.player1.input = functools.partial(readSocket, connP1)
|
game.player1.input = functools.partial(readSocket, connP1)
|
||||||
game.player2.input = functools.partial(readSocket, connP2)
|
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):
|
def printWatchers(*args):
|
||||||
for w in watchers:
|
for w in watchers:
|
||||||
@ -136,6 +151,9 @@ while True:
|
|||||||
printWatchers(*args)
|
printWatchers(*args)
|
||||||
|
|
||||||
def play_turn():
|
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 ""))
|
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_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 ""))
|
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,9 +247,10 @@ while True:
|
|||||||
del games[choice]
|
del games[choice]
|
||||||
|
|
||||||
connP1.send(("\n\nFinished! See you next time " + nameP1 + "!\n\n\n").encode())
|
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()
|
connP1.close()
|
||||||
connP2.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:
|
except BrokenPipeError as e:
|
||||||
print(e)
|
print(e)
|
||||||
finally:
|
finally:
|
||||||
|
Loading…
Reference in New Issue
Block a user