123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- ''' Programme Puissance 4 (IA !)
- '''
- import random
- nb_col = 7
- nb_li = 6
- vide = 0
- def detection_align(n, ligne, couleur):
- ''' Détecte une séquence de 4 contenant
- n pions de la couleur
- (n=1 ou 2 car 3 est à part...)
- et le reste vide.
- On renvoie les indices des cases vides.
- '''
- a_jouer = []
- for k in range(len(ligne)-n):
- if ligne[k:k+4].count(couleur)==n and ligne[k:k+4].count(vide)==4-n:
- for p,e in enumerate(ligne[k:k+4]):
- if e==vide:
- a_jouer.append(p+k)
- return a_jouer
- def diag_li(planche, i, j, sens):
- '''Retourne une liste des éléments de la diagonale
- qui part de (i,j)
- vers le bas à droite si sens vaut 1
- et vers le bas à gauche si sens vaut -1
- '''
- ligne = []
- ii, jj = i, j
- while ii<nb_li and 0<=jj<nb_col:
- ligne.append(planche[ii][jj])
- ii += 1
- jj = jj + sens
- return ligne
- def col_li(planche, j):
- ''' Transforme une colonne en ligne...'''
- return [planche[i][j] for i in range(nb_li)]
- def detection_coup_final(planche, couleur):
- """Différente des autres détection car il suffit de
- prendre la première qui vient en attaque puis
- sinon la première qui vient en défense,
- alors que pour les autres sont listées pour
- choisir les optimales...
- Le coup est donné dans un tuple d'un élément
- ou False sinon, ainsi on peut tester coup comme un booléen :
- 0 compterait comme faux mais (0,) compte comme vrai.
- """
- # Lignes de 3 :
- for i, ligne in enumerate(planche):
- detect_align = detection_align(3, ligne, couleur)
- for j in detect_align:
- if i==nb_li-1 or planche[i+1][j]!=vide:
- ## print('ligne 3, i, j couleur : ', i, j, couleur)
- return (j,)
- # Colonnes de 3 :
- for j in range(nb_col):
- detect_align = detection_align(3, col_li(planche, j), couleur)
- for i in detect_align:
- if i==nb_li-1 or planche[i+1][j]!=vide:
- ## print('colonne 3')
- return (j,)
- # Diagonales de 3 :
- for j in range(nb_col):
- for sens in (-1,1):
- detect_align = detection_align(3, diag_li(planche, 0, j, sens), couleur)
- for i in detect_align:
- k = j + sens*i
- if i==nb_li-1 or planche[i+1][k]!=vide:
- ## print('diag 3j', sens)
- return (k,)
- for i in range(nb_li):
- detect_align = detection_align(3, diag_li(planche, i, 0, 1), couleur)
- for j in detect_align:
- k = i + j
- if k==nb_li-1 or planche[k+1][j]!=vide:
- ## print('diag 3i', 1)
- return (j,)
- detect_align = detection_align(3, diag_li(planche, i, nb_col-1, -1), couleur)
- ## print('detect_align : ', detect_align)
- for j in detect_align:
- k = i + j
- ## print('k, j, i : ', k, j, i)
- if k==nb_li-1 or planche[k+1][nb_col-1-j]!=vide:
- ## print('diag 3i', -1)
- return (nb_col-1-j,)
- return False
- def detection(planche, couleur, nb, liste_a_jouer):
- ''' Détecte une position libre et possible qui amène à
- nb+1 l'effectif d'une séquence de nb parmi 4
- (pas nécessairement consécutifs).
- Ajoute les coups possibles dans liste_a_jouer (effet de bord).
- '''
-
- # Lignes de nb :
- for i, ligne in enumerate(planche):
- detect_align = detection_align(nb, ligne, couleur)
- for j in detect_align:
- if i==nb_li-1 or planche[i+1][j]!=vide:
- liste_a_jouer.append(j)
- # Colonnes de nb :
- for j in range(nb_col):
- detect_align = detection_align(nb, col_li(planche, j), couleur)
- for i in detect_align:
- if i==nb_li-1 or planche[i+1][j]!=vide:
- liste_a_jouer.append(j)
- # Diagonales de nb :
- for j in range(nb_col):
- for sens in (-1,1):
- detect_align = detection_align(nb, diag_li(planche, 0, j, sens), couleur)
- for i in detect_align:
- k = j + sens*i
- if i==nb_li-1 or planche[i+1][k]!=vide:
- liste_a_jouer.append(k)
- for i in range(nb_li):
- detect_align = detection_align(nb, diag_li(planche, i, 0, 1), couleur)
- for j in detect_align:
- k = i + j
- if k==nb_li-1 or planche[k+1][j]!=vide:
- liste_a_jouer.append(j)
- detect_align = detection_align(nb, diag_li(planche, i, nb_col-1, -1), couleur)
- for j in detect_align:
- k = i + j
- ## print('nb, k, j, i : ', nb, k, j, i)
- if k==nb_li-1 or planche[k+1][nb_col-1-j]!=vide:
- liste_a_jouer.append(nb_col-1-j)
- def duplique_planche(planche):
- copie_planche = []
- for ligne in planche:
- copie_planche.append([])
- for e in ligne:
- copie_planche[-1].append(e)
- ## print('copie_planche : ', copie_planche)
- return copie_planche
-
- def choix_coup_a_jouer(liste_a_jouer):
- """ Tire au sort une des colonnes de la liste
- dont la fréquence est maximale.
- """
-
- dico = {}
- for e in liste_a_jouer:
- dico[e] = dico.get(e,0) + 1
- maxi = 0
- liste_maxi = []
- for k, v in dico.items():
- if v>maxi:
- maxi = v
- liste_maxi = [k]
- elif v==maxi:
- liste_maxi.append(k)
- ## print('liste_a_jouer, dico, maxi : ', liste_a_jouer, dico, maxi)
- return random.choice(liste_maxi)
-
- def joueur_g(planche, couleur):
- couleur_adverse = 3 - couleur
- # Attaque ultime (3->4 !)
- coup = detection_coup_final(planche, couleur)
- if coup:
- return coup[0]
- # Défense ultime (3->4 !)
- coup = detection_coup_final(planche, couleur_adverse)
- if coup:
- ## print('défense')
- return coup[0]
-
- liste_a_jouer =[]
-
- # Attaque pour 2->3
- detection(planche, couleur, 2, liste_a_jouer)
-
- # Défense pour 2->3
- detection(planche, couleur_adverse, 2, liste_a_jouer)
- # On retire de la liste les coups
- # qui mèneraient à un coup de grâce par l'adversaire
- copie_liste_a_jouer = liste_a_jouer[:]
- for j in copie_liste_a_jouer:
- copie_planche = duplique_planche(planche)
- depot_jeton(copie_planche, j, couleur)
- coup = detection_coup_final(copie_planche, couleur_adverse)
- if coup:
- liste_a_jouer.remove(j)
- if liste_a_jouer:
- return choix_coup_a_jouer(liste_a_jouer)
- # Attaque pour 1->2
- detection(planche, couleur, 1, liste_a_jouer)
-
- # Défense pour 1->2
- detection(planche, couleur_adverse, 1, liste_a_jouer)
-
- # On retire encore de la liste les coups
- # qui mèneraient à un coup de grâce par l'adversaire
- # sauf le dernier car il faut bien jouer !
- # (on pourrait aussi encore essayer autre chose mais c'est compliqué)
- copie_liste_a_jouer = liste_a_jouer[:]
- for j in copie_liste_a_jouer:
- copie_planche = duplique_planche(planche)
- depot_jeton(copie_planche, j, couleur)
- coup = detection_coup_final(copie_planche, couleur_adverse)
- if coup:
- if len(liste_a_jouer)>1:
- liste_a_jouer.remove(j)
- else:
- break
- if liste_a_jouer:
- return choix_coup_a_jouer(liste_a_jouer)
- else: # Ici on pourrait essayer d'éviter les coups dangereux...
- return random.choice([i for i,e in enumerate(planche[0]) if e==vide])
- def depot_jeton(planche, j, couleur):
- ''' utilisée uniquement sur une copie de la planche !
- '''
- ## print('planche : ', planche)
- if not(planche[0][j]):
- i = nb_li-1
- ## print('i,j : ',i,j)
- while planche[i][j]:
- ## print('while, i, couleur : ', i, couleur)
- i = i-1
- ## print('i :', i)
- planche[i][j] = couleur
- deposer = True
- else:
- deposer = False
- return deposer
|