<jupyter_start><jupyter_text>3-PlyDer nächste Schritt ist es, sich zu allen Gegnerzügen auch noch alle eigenen Folgezüge anzuschauen und die Güte eines Zuges danach bewerten.Analog zu 2-ply wird der Wert der eigenen Folgezüge mit der Wahrscheinlichkeit der Würfe multipliziert. Man könnte dies auch weiterführen indem man sich danach wieder den Gegnerzug, danach weider den eigenen usw. ansieht. Das ist dann der sogenannte __Expectiminimax__ Algorithmus. Da die Rechenzeit für 2-ply schon sehr hoch war und 3-ply noch deutlich schlechter in der Performance ist, wird 3-ply die letzte Stufe sein.<jupyter_code>from Player import TwoPlyValuePlayer

class ThreePlyValuePlayer(TwoPlyValuePlayer):
    
    # Die Methode aus dem TwoPlyValuePlayer, nur die Value Funktion ist gegen 3-ply ausgetauscht
    def two_ply(self, game, player):
        # Alle möglichen Gegnerwürfe und dazugehörige Züge bewerten und mit der WS des Wurfes multiplizieren
        all_rolls = [(a,b) for a in range(1,7) for b in range(a,7)]
        value = 0
        for roll in all_rolls:
            probability = 1/18 if roll[0] != roll[1] else 1/36
            state = game.get_state()
            # Wir betrachten die Gegnerzüge
            moves = game.get_moves(roll, game.get_opponent(player))
            min_val = 1
            for move in moves:
                game.execute_moves(move, game.get_opponent(player))
                # Bewertet wird aber aus unserer Perspektive
                v = self.three_ply(game, player)
                if v < min_val:
                    min_val = v
                game.reset_to_state(state)
            value += probability * min_val
        # Wert zurückgeben
        return value
    
    # Wie two_ply nur das diesmal maximiert wird
    def three_ply(self, game, player):
        # Alle möglichen Würfe und dazugehörige Züge bewerten und mit der WS des Wurfes multiplizieren
        all_rolls = [(a,b) for a in range(1,7) for b in range(a,7)]
        value = 0
        for roll in all_rolls:
            probability = 1/18 if roll[0] != roll[1] else 1/36
            state = game.get_state()
            moves = game.get_moves(roll, player)
            max_val = 0
            for move in moves:
                game.execute_moves(move, player)
                # Bewertet wird aber aus unserer Perspektive
                v = self.value(game, player)
                if v > max_val:
                    max_val = v
                game.reset_to_state(state)
            value += probability * max_val
        # Wert zurückgeben
        return value
    
    def get_name(self):
        return "ThreePlyValuePlayer [" + self.value.__name__ + "]"
import Player
import PlayerTest

players = [ThreePlyValuePlayer('black', Player.singleton), Player.ValuePlayer('white', Player.singleton)]

PlayerTest.test(players, 1)<jupyter_output>Spiel 0 von 1 geht an ThreePlyValuePlayer [singleton] ( black )

{'white': 0, 'black': 1}
1 Spiele in  1493.3033773899078 Sekunden<jupyter_text>__25 Minuten__ für ein einziges Spiel...<jupyter_code>import Player
import PlayerTest
from FasterBackgammon import Game

players = [ThreePlyValuePlayer(Game.PLAYERS[0], Player.singleton), Player.ValuePlayer(Game.PLAYERS[1], Player.singleton)]

PlayerTest.test(players, 100)<jupyter_output>Spiel 0 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 1 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 2 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 3 von 100 geht an ValuePlayer [singleton] ( white )
Spiel 4 von 100 geht an ValuePlayer [singleton] ( white )
Spiel 5 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 6 von 100 geht an ValuePlayer [singleton] ( white )
Spiel 7 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 8 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 9 von 100 geht an ValuePlayer [singleton] ( white )
Spiel 10 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 11 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 12 von 100 geht an ValuePlayer [singleton] ( white )
Spiel 13 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 14 von 100 geht an ThreePlyValuePlayer [singleton] ( black )
Spiel 15 von 100 geht an ValuePlayer [singlet[...]
{'fraction_non_alphanumeric': 0.07041268360923292, 'fraction_numerical': 0.030776404756353463, 'mean_word_length': 2.9722222222222223, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '526227', 'license': 'MIT', 'n_tokens_mistral': 1481, 'n_tokens_neox': 1365, 'n_words': 460, 'path': 'TD-Gammon011-ThreePly.ipynb', 'repo_name': 'Lagiacrus24/TD-Gammon', 'size': '4289'}