# les scripts d'IA peuvent importer ce script et utiliser librement tout ou partie de ce qui s'y trouve # par contre, ils ne doivent : # - ni le modifier (car ils seront evalues avec la script original non modifie) # - ni alterer son fonctionnement # si besoin de classes ou fonctions supplementaires, elles doivent etre definies dans les scripts d'IA #-------------------------- # LES CARTES DU 1000 BORNES #-------------------------- # entiers identifiants les 19 cartes differentes du jeu I_25_BORNES, I_50_BORNES, I_75_BORNES, I_100_BORNES, I_200_BORNES, I_FEU_VERT, I_FEU_ROUGE, I_VEHICULE_PRIORITAIRE, \ I_LIMITATION, I_FIN_LIMITATION, I_PANNE_ESSENCE, I_POMPE_ESSENCE, I_CAMION_CITERNE, I_CREVAISON, I_ROUE_SECOURS, \ I_VEHICULE_INCREVABLE, I_ACCIDENT, I_REPARATIONS, I_AS_VOLANT, N_CARTES = range(20) SABOT, MAIN = -1, -2 CODES_CARTES = { None : "?", I_25_BORNES : "25", I_50_BORNES : "50", I_75_BORNES : "75", I_100_BORNES : "100", I_200_BORNES : "200", I_FEU_VERT : "V", I_FEU_ROUGE : "R", I_VEHICULE_PRIORITAIRE : "VP", I_LIMITATION : "L", I_FIN_LIMITATION : "FL", I_PANNE_ESSENCE : "P", I_POMPE_ESSENCE : "E", I_CAMION_CITERNE : "CI", I_CREVAISON : "C", I_ROUE_SECOURS : "S", I_VEHICULE_INCREVABLE : "IN", I_ACCIDENT : "A", I_REPARATIONS : "R", I_AS_VOLANT : "AV", } def cartes_bornes(): return (I_25_BORNES, I_50_BORNES, I_75_BORNES, I_100_BORNES, I_200_BORNES) def cartes_bottes(): return (I_CAMION_CITERNE, I_VEHICULE_INCREVABLE, I_AS_VOLANT, I_VEHICULE_PRIORITAIRE) def cartes_attaque(): return (I_PANNE_ESSENCE, I_CREVAISON, I_ACCIDENT, I_LIMITATION, I_FEU_ROUGE) def cartes_parade(): return (I_POMPE_ESSENCE, I_ROUE_SECOURS, I_REPARATIONS, I_FIN_LIMITATION, I_FEU_VERT) def cartes_vitesse(): return (I_LIMITATION, I_FIN_LIMITATION) def est_carte_bornes(carte): return carte in cartes_bornes() def est_carte_botte(carte): return carte in cartes_bottes() def est_carte_attaque(carte): return carte in cartes_attaque() def est_carte_parade(carte): return carte in cartes_parade() def est_carte_vitesse(carte): return carte in cartes_vitesse() def valeur_carte_bornes(carte): if carte in cartes_bornes(): return {I_25_BORNES: 25, I_50_BORNES: 50, I_75_BORNES: 75, I_100_BORNES: 100, I_200_BORNES: 200}[carte] else: return 0 def bornes_pour(bornes): if bornes in (25, 50, 75, 100, 200): return {25: I_25_BORNES, 50: I_50_BORNES, 75: I_75_BORNES, 100: I_100_BORNES, 200: I_200_BORNES}[bornes] else: return None def botte_pour(carte): return carte in (I_ACCIDENT, I_REPARATIONS) and I_AS_VOLANT \ or carte in (I_CREVAISON, I_ROUE_SECOURS) and I_VEHICULE_INCREVABLE \ or carte in (I_PANNE_ESSENCE, I_POMPE_ESSENCE) and I_CAMION_CITERNE \ or carte in (I_FEU_VERT, I_FEU_ROUGE, I_LIMITATION, I_FIN_LIMITATION) and I_VEHICULE_PRIORITAIRE def attaque_pour(carte): return carte in (I_AS_VOLANT, I_REPARATIONS) and I_ACCIDENT \ or carte in (I_VEHICULE_INCREVABLE, I_ROUE_SECOURS) and I_CREVAISON \ or carte in (I_CAMION_CITERNE, I_POMPE_ESSENCE) and I_PANNE_ESSENCE \ or carte in (I_FEU_VERT, I_VEHICULE_PRIORITAIRE) and I_FEU_ROUGE \ or (I_FIN_LIMITATION, I_VEHICULE_PRIORITAIRE) and I_LIMITATION def parade_pour(carte): return carte in (I_AS_VOLANT, I_ACCIDENT) and I_REPARATIONS \ or carte in (I_VEHICULE_INCREVABLE, I_CREVAISON) and I_ROUE_SECOURS \ or carte in (I_CAMION_CITERNE, I_PANNE_ESSENCE) and I_POMPE_ESSENCE \ or carte in (I_VEHICULE_PRIORITAIRE , I_FEU_ROUGE) and I_FEU_VERT or carte == I_LIMITATION and I_FIN_LIMITATION #-------------- # classe c_coup #-------------- class c_coup: def __init__(self, origine, carte, destination): self.origine = origine self.carte = carte self.destination = destination def invalide(self, nbr_joueurs): nums_joueurs = tuple(range(nbr_joueurs)) coup_invalide = False if ( self.origine != SABOT and not self.origine in nums_joueurs or self.destination != SABOT and not self.destination in nums_joueurs or ( self.carte == None and self.origine != SABOT or self.carte != None and not self.carte in tuple(range(N_CARTES)) ) or self.origine != SABOT and self.destination != SABOT and ( self.origine == self.destination and est_carte_attaque(self.carte) or self.origine != self.destination and not est_carte_attaque(self.carte) ) ): coup_invalide = True return coup_invalide def to_str(self): return "{:s} {:s} -> {:s}".format(CODES_CARTES[self.carte], self.origine == SABOT and "Sabot(Pioche)" \ or "IA #{:d}(Main)".format(self.origine), self.destination == SABOT and "Sabot(Defausse)" \ or "IA #{:d}(Jeu)".format(self.destination)) #------------------- # classe c_infos_jeu #------------------- # cette classe librement utilisable est concue pour te permettre d'organiser et # acceder a toutes les infos utiles et autorises sur les IAs jouant la partie # et les mettre a jour automatiquement # workaround for HP Prime bug, since str([list]) has the side effect of showing the console screen def strlst(l): s = "" for v in l: s += " " + str(v) return s class c_infos_jeu(object): def __init__(self, num_joueur, nbr_joueurs, points_objectif, log=False): self.num_joueur = num_joueur # numero du joueur self.nbr_joueurs = nbr_joueurs self.nom_groupe = "" self.points = 0 self.points_objectif = points_objectif # nombre de points a atteindre pour gagner self.nums_joueurs = tuple(range(nbr_joueurs)) self.nums_joueurs_autres = list(self.nums_joueurs) self.nums_joueurs_autres.remove(self.num_joueur) self.log = log # prepare la classe pour une nouvelle manche def nouvelle_manche(self, nbr_cartes, taille_main, bornes_arrivee): self.nbr_cartes = nbr_cartes # nombre cartes dans la pioche self.taille_main = taille_main self.bornes_arrivee = bornes_arrivee # ligne d'arrivee self.bornes = 0 # bornes parcourues self.bornes100 = False # carte 100 bornes jouees self.bornes200 = 0 # nombre cartes 200 bornes jouees self.pile_bataille = [] # 2 dernieres cartes de la pile de bataille self.pile_vitesse = [] # pile de vitesse self.bottes = [] # bottes jouees self.joueur_allonge = None self.coups_fourres_autorises = [] # chaine identifiant l'IA def id_str(self): return "IA" + str(self.num_joueur) def rejoint_groupe(self, num_joueur, nom_groupe): if self.num_joueur == num_joueur: self.nom_groupe = nom_groupe # ajoute des points def change_score(self, pts, descr): self.points += pts if self.log: print(self.id_str() + " +{:d}pts ({:s})".format(pts, descr)) # ajoute les points de victoire en fin de manche def fin_manche(self, bornes_joueurs, len_pioche): if self.victoire_manche(): # victoire # bonus de victoire self.change_score(400, "victoire") # pas d'etapes de 200km if not self.bornes200: self.change_score(300, "pas d'etape de 200km") # capot nouvelle regle (pas d'etape de 100/200 km) if not self.bornes100: self.change_score(500, "capot2 - pas d'etape de 100+km") # capot regle originale (un autre joueur n'a pas pu jouer de carte bornes) for bornes_joueur in bornes_joueurs: if not bornes_joueur: self.change_score(500, "capot1 - un adversaire n'a pas roule") break # couronnement (victoire apres epuisement de la pioche) if not len_pioche: self.change_score(300, "couronnement - pioche epuisee") # allonge if self.joueur_allonge != None: self.change_score(200, "allonge") # indique si l'IA a gagne la manche def victoire_manche(self): return self.bornes == self.bornes_arrivee # indique si l'IA a gagne le tournoi def victoire_tournoi(self): return self.points >= self.points_objectif # retourne la derniere carte sur la pile de bataille de l'IA def carte_bataille(self): if len(self.pile_bataille): return self.pile_bataille[-1] return None # retourne la derniere carte sur la pile de vitesse de l'IA def carte_vitesse(self): if len(self.pile_vitesse): return self.pile_vitesse[-1] return None def to_str(self): s = "Etat " + self.id_str() + " :" s += "\n- bornes {:d}/{:d} km".format(self.bornes, self.bornes_arrivee) s += "\n- bataille : " + CODES_CARTES[self.carte_bataille()] s += "\n- vitesse : " + CODES_CARTES[self.carte_vitesse()] s += "\n- bottes :" + strlst([CODES_CARTES[carte] for carte in self.bottes]) return s # met a jour la classe en fonction du coup joue par une IA def traite_coup(self, coup): # mise a jour du nombre de cartes restantes apres cartes tireeĀ² if coup.origine == SABOT: self.nbr_cartes -= 1 if coup.destination == self.num_joueur and coup.origine != SABOT: # traitement specifique bottes et coups-fourres if coup.carte == I_VEHICULE_PRIORITAIRE or coup.carte != None and coup.carte in self.coups_fourres_autorises: botte_utile = False if attaque_pour(coup.carte) == self.carte_bataille(): self.pile_bataille = self.pile_bataille[:-1] botte_utile = True if coup.carte == I_VEHICULE_PRIORITAIRE and self.carte_vitesse() == I_LIMITATION: self.pile_vitesse = self.pile_vitesse[:-1] botte_utile = True if botte_utile and coup.carte in self.coups_fourres_autorises: self.change_score(300, "coup fourre") # fermeture de la fenetre pour realiser un coup fourre if coup.origine == self.num_joueur or coup.destination == self.num_joueur and coup.origine == SABOT: self.coups_fourres_autorises = [] if coup.destination == self.num_joueur and coup.origine != SABOT: # traitement normal du coup ## cas pile de vitesse if coup.carte in (I_LIMITATION, I_FIN_LIMITATION): self.pile_vitesse.append(coup.carte) ## cas pile de bornes elif est_carte_bornes(coup.carte): bornes = valeur_carte_bornes(coup.carte) self.bornes += bornes if coup.carte == I_200_BORNES: self.bornes200 += 1 elif bornes >= 100: self.bornes100 = True self.change_score(bornes, "bornes") # cas zone des bottes elif est_carte_botte(coup.carte): self.bottes.append(coup.carte) self.change_score(100, "botte") if len(self.bottes) == 4: self.change_score(300, "toutes bottes") # cas pile de bataille else: self.pile_bataille.append(coup.carte) self.pile_bataille = self.pile_bataille[-2:] if est_carte_attaque(coup.carte): self.coups_fourres_autorises.append(botte_pour(coup.carte)) # met a jour la classe avec un changement de la ligne d'arrivee def allonge(self, num_joueur, bornes_arrivee): self.bornes_arrivee = bornes_arrivee self.joueur_allonge = num_joueur # indique si l'IA est en capacite de rouler def vehicule_roulant(self): return ( I_VEHICULE_PRIORITAIRE in self.bottes and not est_carte_attaque(self.carte_bataille()) or not I_VEHICULE_PRIORITAIRE in self.bottes and self.carte_bataille() == I_FEU_VERT ) # indique si le coup est invalide def coup_invalide(self, coup): coup_invalide = False if not coup_invalide and ( coup.destination == self.num_joueur and ( coup.origine != SABOT and ( est_carte_bornes(coup.carte) and ( not self.vehicule_roulant() or valeur_carte_bornes(coup.carte) > 50 and self.carte_vitesse() == I_LIMITATION or coup.carte == I_200_BORNES and self.bornes200 >= 2 or self.bornes + valeur_carte_bornes(coup.carte) > self.bornes_arrivee) or coup.carte == I_FEU_VERT and ( self.vehicule_roulant() or not self.carte_bataille() in (I_FEU_ROUGE, None) and not est_carte_parade(self.carte_bataille())) or coup.carte == I_FIN_LIMITATION and ( I_VEHICULE_PRIORITAIRE in self.bottes or self.carte_vitesse() != I_LIMITATION) or est_carte_parade(coup.carte) and not coup.carte in (I_FEU_VERT, I_FIN_LIMITATION) \ and self.carte_bataille() != attaque_pour(coup.carte) or coup.carte == I_LIMITATION and ( I_VEHICULE_PRIORITAIRE in self.bottes or self.carte_vitesse() == I_LIMITATION) or est_carte_attaque(coup.carte) and coup.carte != I_LIMITATION and ( botte_pour(coup.carte) in self.bottes or not self.vehicule_roulant()) ) ) ): coup_invalide = True return coup_invalide #--------------------------- # classe c_infos_jeu_et_main #--------------------------- # cette classe reprend tout ce que fourni c_infos_jeu # et rajoute les cartes en main de l'IA # chaque IA ne peut la consulter que pour son propre etat # (pas question de connaitre les cartes en main des autres IA) class c_infos_jeu_et_main(c_infos_jeu): def nouvelle_manche(self, nbr_cartes, taille_main, bornes_arrivee): super().nouvelle_manche(nbr_cartes, taille_main, bornes_arrivee) self.main = [] # main du joueur def to_str(self): s = super().to_str() s += "\n- main : " + strlst([CODES_CARTES[carte] for carte in self.main]) return s def traite_coup(self, coup): super().traite_coup(coup) # mise a jour de la main if coup.origine == SABOT and coup.destination == self.num_joueur: self.main.append(coup.carte) elif coup.origine == self.num_joueur: self.main.remove(coup.carte) def coup_invalide(self, coup): coup_invalide = super().coup_invalide(coup) if not coup_invalide and ( coup.origine == self.num_joueur and not coup.carte in self.main or coup.destination == self.num_joueur and coup.origine == SABOT and len(self.main) >= self.taille_main or len(self.main) < self.taille_main and coup.origine == self.num_joueur and coup.carte != None \ and (not est_carte_botte(coup.carte) or not coup.carte in self.coups_fourres_autorises) ): coup_invalide = True return coup_invalide