4 Commits

Author SHA1 Message Date
  Gérald LEBAN 7a10317630 main game 3 years ago
  Gérald LEBAN 88ecb3a2e2 Board class 3 years ago
  Gérald LEBAN 0442e04eaa Perfect IA 3 years ago
  Gérald LEBAN b951a51d82 Renamed 3 years ago
5 changed files with 247 additions and 129 deletions
  1. 0
    55
      game.py
  2. 126
    0
      marinbad/ai.py
  3. 42
    0
      marinbad/board.py
  4. 0
    74
      marinbad/func.py
  5. 79
    0
      marinbad/game_numy.py

+ 0
- 55
game.py View File

@@ -1,55 +0,0 @@
1
-from player import get_player_move
2
-from random import shuffle
3
-
4
-
5
-drug = [3, 5, 7]
6
-
7
-playerType = {
8
-    "human": get_player_move
9
-}
10
-
11
-players = [
12
-    {
13
-        "name": "Joueur 1",
14
-        "type": "human"
15
-    },
16
-    {
17
-        "name": "Joueur 2",
18
-        "type": "human"
19
-    }
20
-]
21
-
22
-
23
-def show_current_game():
24
-    print("=============")
25
-    for index, val in enumerate(drug):
26
-        print(f"L{index + 1}: {'o' * val}")
27
-    print("=============")
28
-
29
-
30
-def execute_move(row, value, name):
31
-    drug[row] -= value
32
-    print(f"{name} a enlevé {value} allumettes(s) sur la ligne L{row + 1}")
33
-
34
-
35
-def play(name, type_):
36
-    show_current_game()
37
-    execute_move(*playerType[type_](name, drug), name)
38
-    if sum(drug) == 0:
39
-        show_current_game()
40
-        print(f"{name} à gangné !!")
41
-        return True
42
-    else:
43
-        return False
44
-
45
-
46
-# Choisi au hasard le premier joueur
47
-shuffle(players)
48
-
49
-# Affiche l'ordre de jeu
50
-print(f"Voici l'ordre des joueurs: {' => '.join([player['name'] for player in players])}")
51
-
52
-while True:
53
-    for player in players:
54
-        if play(player["name"], player["type"]):
55
-            exit(0)

+ 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

+ 42
- 0
marinbad/board.py View File

@@ -0,0 +1,42 @@
1
+class Board:
2
+    def __init__(self):
3
+        pass
4
+
5
+    @staticmethod
6
+    def show_board(board):
7
+        """
8
+        Display the board o screen
9
+        :param board: the board
10
+        :type board list
11
+        :return: None
12
+        """
13
+        for index, val in enumerate(board):
14
+            print(f"Row {index + 1}: {val}")
15
+
16
+    @staticmethod
17
+    def nb_allumettes(board: list):
18
+        """
19
+        Return the number of remaining allumettes
20
+        :param board: the board
21
+        :type board list
22
+        :return: int
23
+        """
24
+        return sum(board)
25
+
26
+    @staticmethod
27
+    def retirer(board, row, stick):
28
+        """
29
+        Computer player move on the board
30
+        :param board: the board
31
+        :type board list
32
+
33
+        :param row: the selected row to remove allumettes from
34
+        :type row: int
35
+
36
+        :param stick: the number of allumettes to remove
37
+        :type stick: int
38
+
39
+        :return: board
40
+        """
41
+        board[int(row) - 1] = board[int(row) - 1] - int(stick)
42
+        return board

+ 0
- 74
marinbad/func.py View File

@@ -1,74 +0,0 @@
1
-def toBin(integer):
2
-    """
3
-    Convert a decimal integer into a binary"
4
-    :param integer: the integer to be converted
5
-    :type integer int
6
-    :return: int
7
-    """
8
-    return int(str(bin(integer))[2:])
9
-
10
-
11
-def toDec(binVal):
12
-    """
13
-    Convert a binary value to a decimal value
14
-    :param binVal: The bin value to be converted
15
-    :type binVal: int, str
16
-    :return: int
17
-    """
18
-    return int(str(binVal), 2)
19
-
20
-
21
-def toBinArray(array):
22
-    """
23
-    Turn an array of decimal into an array of binary
24
-    :param array:
25
-    :return:
26
-    """
27
-    return [toBin(el) for el in array]
28
-
29
-
30
-def computeS(array):
31
-    """
32
-    Sum all values in an array
33
-    :param array: the array to compute on
34
-    :type array: list
35
-    :return: int
36
-    """
37
-    return sum(array)
38
-
39
-
40
-def nimSomme(integer):
41
-    """Return an array with 0 if element is even, 1 otherwise"""
42
-    return [int(el) % 2 for el in str(integer)]
43
-
44
-
45
-def isSafe(array):
46
-    """
47
-    Return True if value computed from the array is zero, False otherwise
48
-    :param array: the array to compute
49
-    :type array: list
50
-    :return: bool
51
-    """
52
-    def list2str(array):
53
-        a = ""
54
-        for el in array:
55
-            a += str(el)
56
-        return a
57
-
58
-    binString = list2str([int(el) % 2 for el in list2str(array)])
59
-    return toDec(binString) == 0
60
-
61
-
62
-array = [3, 5, 7]
63
-
64
-binArr = toBinArray(array)
65
-print("Bin Array: ", binArr)
66
-
67
-S = computeS(binArr)
68
-print("S: ", S)
69
-
70
-nimS = nimSomme(S)
71
-print("Somme de NIM: ", nimS)
72
-
73
-safe = isSafe(nimS)
74
-print("Safe: ", safe)

+ 79
- 0
marinbad/game_numy.py View File

@@ -0,0 +1,79 @@
1
+from AI import AI
2
+from Board import Board
3
+from os import system
4
+from random import shuffle
5
+
6
+
7
+def play(board: list, **kwargs):
8
+    def strArray(array):
9
+        """
10
+        Convert an array to a string array and add one to every value
11
+        Used to stringigy range() and start value from 1
12
+        :param array:
13
+        :return:
14
+        """
15
+        return [str(el + 1) for el in array]
16
+
17
+    def intable(value):
18
+        """
19
+        Return True if value can be cast to integer
20
+        :param value: The value to convert in integer
21
+        :type value str
22
+        :return: bool
23
+        """
24
+        try:
25
+            int(value)
26
+            return True
27
+        except ValueError:
28
+            return False
29
+
30
+    # Filter row input
31
+    row = "string"
32
+    strRange = strArray(range(len(board)))
33
+    while not(intable(row)) or not(row in strRange):
34
+        row = input(f"Choisissez une rangée entre 1 et {len(board)}: ")
35
+    row = int(row)
36
+
37
+    # Filter remove allumettes
38
+    remove = "0"
39
+    while not(intable(remove)) or not("1" <= remove <= str(board[row - 1])):
40
+        remove = input(f"Choisissez le nombre d'allumettes à retirer entre 1 et {board[row - 1]}: ")
41
+    remove = int(remove)
42
+
43
+    print(f"{kwargs['name']}: L{row} => {remove}")
44
+    # Return board after player move
45
+    return game_board.retirer(board, row, remove)
46
+
47
+
48
+game_board = Board()
49
+
50
+players = [
51
+    {
52
+        "name": "Jean Yves",
53
+        "func": play
54
+    },
55
+    {
56
+        "name": "Gérald",
57
+        "func": AI().compute
58
+    }
59
+]
60
+
61
+# Choisi le 1er joueur
62
+shuffle(players)
63
+
64
+# Creation du plateau de jeu
65
+print("Entrez une liste sous cette forme: X Y Z A B C")
66
+allumettes = [int(el) for el in input(">>> ").split(" ")]
67
+
68
+print(f"Voici l'ordre des joueurs: {' => '.join([player['name'] for player in players])}")
69
+
70
+while True:
71
+    for player in players:
72
+        game_board.show_board(allumettes)
73
+
74
+        allumettes = player["func"](allumettes, name=player["name"])
75
+
76
+        if game_board.nb_allumettes(allumettes) == 0:
77
+            print(f"Le {player['name']} à gagné !!")
78
+            exit(0)
79
+        print("")

Loading…
Cancel
Save