Browse Source

Perfect IA

Gérald LEBAN 3 years ago
parent
commit
0442e04eaa
1 changed files with 126 additions and 0 deletions
  1. 126
    0
      marinbad/ai.py

+ 126
- 0
marinbad/ai.py View File

@@ -0,0 +1,126 @@
1
+from random import choice, randint
2
+
3
+
4
+class AI:
5
+    """Ordinateur pour le jeu de Marienbad"""
6
+    def __init__(self):
7
+        self.row = 0
8
+        self.removed = 0
9
+
10
+    @staticmethod
11
+    def _intToBin(integer):
12
+        """
13
+        Convertie un nombre en base 10 en base 2
14
+        :param integer: Le nombre à convertir
15
+        :type integer: int
16
+        :return: int
17
+        """
18
+        return int(bin(integer)[2:])
19
+
20
+    def _calculSomme(self, liste: list):
21
+        """
22
+        Convertie une liste de décimal en liste de binaire
23
+        Fait la somme des element binaire et la retranscrie en décimal
24
+        :param liste: une liste sur laquelle effectuer l'opération
25
+        :type liste: list
26
+        :return: list
27
+        """
28
+        array = [self._intToBin(el) for el in liste]  # int array => bin array
29
+        S = (list(str(sum(array))))  # Somme en base 2
30
+        S = [int(s) for s in S]  # str array => int array
31
+        return S
32
+
33
+    @staticmethod
34
+    def _sommeNIM(liste: list):
35
+        """
36
+        Remplace chaque digit par 0 si le digit est pair, par 1 si le digit est impair
37
+        :param liste: une liste sur laquelle effectuer l'opération
38
+        :type liste: list
39
+        :return: list
40
+        """
41
+        array = [str(el % 2) for el in liste]
42
+        return array
43
+
44
+    @staticmethod
45
+    def _isSafe(liste: list):
46
+        """
47
+        Retourne True si le nombre convertie en binaire vaut zéro.
48
+        Retourne False sinon
49
+        :param liste: une liste sur laquelle effectuer l'opération
50
+        :type liste: list
51
+        :return: int
52
+        """
53
+        string = ""
54
+        for el in liste:
55
+            string += str(el)
56
+        return int(string, 2) == 0
57
+
58
+    def randomDraw(self, array):
59
+        """
60
+        Retire une allumette d'une pile au hasard
61
+        :param array: Allumettes
62
+        :type array list
63
+        :return: list
64
+        """
65
+        rows = [i for i, row in enumerate(array) if row != 0]
66
+        row = choice(rows)
67
+        removed = randint(1, array[row])
68
+        array[row] = array[row] - removed
69
+        self.row = row + 1
70
+        self.removed = removed
71
+        return array
72
+
73
+    def _positionPerdante(self, array, debug=None):
74
+        """
75
+        Wrapper de méthodes
76
+        :param array: Allumettes
77
+        :type array list
78
+        :param debug: Affiche des info pour le debug
79
+        :type debug bool
80
+        :return: bool
81
+        """
82
+        S = self._calculSomme(array)
83
+        nimS = self._sommeNIM(S)
84
+        safe = self._isSafe(nimS)
85
+        if debug:
86
+            print("Somme: ", S)
87
+            print("NIM S: ", nimS)
88
+            print("Safe : ", safe)
89
+        return self._isSafe(nimS)
90
+
91
+    def _trouver_position_perdante(self, array: list):
92
+        """Brut force to find a solution"""
93
+        if self._positionPerdante(array):
94
+            print("Drawing one")
95
+            # Choisi une range au hasard où il reste des allumettes
96
+            return self.randomDraw(array)
97
+        else:
98
+            print("Computing")
99
+            # Tant que position pas perdante pour le joueur
100
+            while not self._positionPerdante(array):
101
+                # Choisi une range au hasard où il reste des allumettes
102
+                rows = [i for i, row in enumerate(array) if row > 0]
103
+                row = choice(rows)
104
+
105
+                # Créer une copie pour le brutforce
106
+                row_backup = array[row]
107
+                removed = 0
108
+                # Brutforce sur chaque rangée jusqu'a trouver une combinaison perdante
109
+                while not self._positionPerdante(array) and array[row] > 0:
110
+                    removed += 1
111
+                    # On retire une allumette
112
+                    array[row] -= 1
113
+                self.row = row + 1
114
+                self.removed = removed
115
+                self.removed = row_backup - array[row]
116
+
117
+                if not self._positionPerdante(array):
118
+                    array[row] = row_backup
119
+
120
+            return array
121
+
122
+    def compute(self, liste: list, *args, **kwargs):
123
+        """Method to call"""
124
+        board = self._trouver_position_perdante(liste)
125
+        print(f"{kwargs['name']}: L{self.row} => {self.removed}")
126
+        return board

Loading…
Cancel
Save