From 1cc42703e00190ca1aa63183a3a2002e86549013 Mon Sep 17 00:00:00 2001 From: Khadija_mellak_codes Date: Mon, 4 May 2026 15:44:15 +0200 Subject: [PATCH] Improve stick game human mode and reporting Instead of the mean per move, the mean **per game** now is no longer negative. I also tried to add a responsive powershel sentences so to let the humain decide weither to play or to generate an automated game. Thanks for your valuable videos on youtube ! --- rl/sticks.py | 113 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 33 deletions(-) diff --git a/rl/sticks.py b/rl/sticks.py index dc34b7b..324c020 100644 --- a/rl/sticks.py +++ b/rl/sticks.py @@ -2,6 +2,7 @@ import random import numpy as np + class StickGame(object): """ StickGame. @@ -26,7 +27,7 @@ def reset(self): def display(self): # Display the state of the game - print ("| " * self.nb) + print("| " * self.nb) def step(self, action): # @action either 1, 2 or 3. Take an action into the environement @@ -36,6 +37,7 @@ def step(self, action): else: return self.nb, 0 + class StickPlayer(object): """ Stick Player @@ -47,11 +49,12 @@ def __init__(self, is_human, size, trainable=True): self.is_human = is_human self.history = [] self.V = {} - for s in range(1, size+1): + for s in range(1, size + 1): self.V[s] = 0. self.win_nb = 0. self.lose_nb = 0. self.rewards = [] + self.game_results = [] self.eps = 0.99 self.trainable = trainable @@ -60,6 +63,7 @@ def reset_stat(self): self.win_nb = 0 self.lose_nb = 0 self.rewards = [] + self.game_results = [] def greedy_step(self, state): # Greedy step @@ -79,10 +83,28 @@ def play(self, state): # Take random action if random.uniform(0, 1) < self.eps: action = randint(1, 3) - else: # Or greedy action + else: # Or greedy action action = self.greedy_step(state) else: - action = int(input("$>")) + while True: + try: + choice = input(f"$> Take 1, 2, or 3 sticks (remaining: {state}, Q to quit): ").strip() + if choice.lower() == "q": + raise SystemExit("Exiting the human game loop.") + action = int(choice) + except ValueError: + print("Please enter a whole number: 1, 2, or 3.") + continue + except (EOFError, KeyboardInterrupt): + raise SystemExit("\nInput cancelled. Exiting the human game loop.") + + if action not in (1, 2, 3): + print("Invalid move. Choose 1, 2, or 3.") + continue + if action > state: + print(f"Invalid move. You cannot take more than {state} stick(s).") + continue + break return action def add_transition(self, n_tuple): @@ -99,12 +121,25 @@ def train(self): for transition in reversed(self.history): s, a, r, sp = transition if r == 0: - self.V[s] = self.V[s] + 0.001*(self.V[sp] - self.V[s]) + self.V[s] = self.V[s] + 0.001 * (self.V[sp] - self.V[s]) else: - self.V[s] = self.V[s] + 0.001*(r - self.V[s]) + self.V[s] = self.V[s] + 0.001 * (r - self.V[s]) self.history = [] + def add_game_result(self, result): + self.game_results.append(result) + + +def should_play_human(): + try: + choice = input("Press P to play against the agent, or press Enter to run the automated evaluation: ") + except (EOFError, KeyboardInterrupt): + return False + + return choice.strip().lower() == "p" + + def play(game, p1, p2, train=True): state = game.reset() players = [p1, p2] @@ -112,27 +147,34 @@ def play(game, p1, p2, train=True): p = 0 while game.is_finished() is False: - if players[p%2].is_human: + if players[p % 2].is_human: game.display() - action = players[p%2].play(state) + action = players[p % 2].play(state) n_state, reward = game.step(action) - # Game is over. Ass stat - if (reward != 0): + # Game is over. Add stat + if reward != 0: # Update stat of the current player - players[p%2].lose_nb += 1. if reward == -1 else 0 - players[p%2].win_nb += 1. if reward == 1 else 0 + players[p % 2].lose_nb += 1. if reward == -1 else 0 + players[p % 2].win_nb += 1. if reward == 1 else 0 # Update stat of the other player - players[(p+1)%2].lose_nb += 1. if reward == 1 else 0 - players[(p+1)%2].win_nb += 1. if reward == -1 else 0 + players[(p + 1) % 2].lose_nb += 1. if reward == 1 else 0 + players[(p + 1) % 2].win_nb += 1. if reward == -1 else 0 + players[p % 2].add_game_result(reward) + players[(p + 1) % 2].add_game_result(-reward) + + if players[p % 2].is_human: + print("You lose. You took the last stick.") + elif players[(p + 1) % 2].is_human: + print("You win. The agent took the last stick.") # Add the reversed reward and the new state to the other player if p != 0: - s, a, r, sp = players[(p+1)%2].history[-1] - players[(p+1)%2].history[-1] = (s, a, reward * -1, n_state) + s, a, r, sp = players[(p + 1) % 2].history[-1] + players[(p + 1) % 2].history[-1] = (s, a, reward * -1, n_state) - players[p%2].add_transition((state, action, reward, None)) + players[p % 2].add_transition((state, action, reward, None)) state = n_state p += 1 @@ -141,7 +183,10 @@ def play(game, p1, p2, train=True): p1.train() p2.train() + if __name__ == '__main__': + play_human = should_play_human() + game = StickGame(12) # PLayers to train @@ -154,22 +199,24 @@ def play(game, p1, p2, train=True): # Train the agent for i in range(0, 10000): if i % 10 == 0: - p1.eps = max(p1.eps*0.996, 0.05) - p2.eps = max(p2.eps*0.996, 0.05) + p1.eps = max(p1.eps * 0.996, 0.05) + p2.eps = max(p2.eps * 0.996, 0.05) play(game, p1, p2) p1.reset_stat() - # Display the value function - for key in p1.V: - print(key, p1.V[key]) - print("--------------------------") - - # Play agains a random player - for _ in range(0, 1000): - play(game, p1, random_player, train=False) - print("p1 win rate", p1.win_nb/(p1.win_nb + p1.lose_nb)) - print("p1 win mean", np.mean(p1.rewards)) - - # Play agains us - while True: - play(game, p1, human, train=False) + if play_human: + while True: + play(game, p1, human, train=False) + else: + # Display the value function + for key in p1.V: + print(key, p1.V[key]) + print("--------------------------") + + # Play against a random player + for _ in range(0, 1000): + play(game, p1, random_player, train=False) + print("p1 win rate", p1.win_nb / (p1.win_nb + p1.lose_nb)) + print("p1 move reward mean", np.mean(p1.rewards)) + print("p1 game reward mean", np.mean(p1.game_results)) + print("Running automated mode. Press P at startup if you want to play yourself.")