コード例 #1
0
ファイル: genetic.py プロジェクト: eporat/QuoridorAI
    def run_games(self, generations):
        for generation in range(generations):
            print(f"Generation: {generation}")
            if generation == 0:
                self.genes = np.random.normal(0,1,(self.count, self.gene_size))
            else:
                # selection
                new_genes = np.zeros((self.count, self.gene_size))
                best_genes = self.get_best()
                new_genes[:len(best_genes)] = best_genes
                for i in range(len(best_genes), self.count):
                    new_genes[i] = self.create()
                self.genes = new_genes

            self.fitness = np.zeros(self.count)
            # crossover
            self.players = [Negamax(self.depth, scoring=self.scoring(gene), tt=TT()) for gene in self.genes]

            num_games = self.count * self.count
            game_count = 0

            for i, gene1 in enumerate(self.genes):
                for j, gene2 in enumerate(self.genes):

                    if game_count % 100 == 0:
                        self.tt.tofile(self.tt_file_name)
                        print(f"Played game: {game_count} / {num_games}")


                    game_count += 1
                    if i == j:
                        continue

                    result = self.game(self.players[i],self.players[j])

                    if result == 0:
                        self.fitness[i] += 0.5
                        self.fitness[j] += 0.5

                    if result == 1:
                        self.fitness[i] += 1

                    if result == 2:
                        self.fitness[j] += 1

            self.prob = self.fitness / self.fitness.sum()

            idx = np.argmax(self.prob)
            best_gene = self.genes[idx]
            print(f"Best player: {best_gene}")
            print(f"Best fitness: {np.max(self.fitness)/(2*(self.count - 1))}")

            negamax = Negamax(self.depth, tt=TT())
            negamax_best = self.players[idx]

            score1 = self.game(negamax, negamax_best)
            print(f"When normal Negamax started: {score1}")
            score2 = self.game(negamax_best, negamax)
            print(f"When best player started: {score2}")
コード例 #2
0
ファイル: genetic.py プロジェクト: eporat/QuoridorAI
    def __init__(self, depth, size, tt_file_name=None, count=1, mutation_rate=1e-2, best_percent=0.2, gene_size=7):
        self.count = count
        self.genes = None
        self.mutation_rate = mutation_rate
        self.best_percent = best_percent
        self.depth = depth
        self.size = size # Board size
        self.gene_size = gene_size
        self.tt_file_name = tt_file_name

        try:
            self.tt = TT.fromfile(self.tt_file_name)
            print("Loaded TT")
        except Exception as e:
            print("TT could not be found in memory")
            self.tt = TT()
コード例 #3
0
ファイル: Gomoku_optimized.py プロジェクト: csabagabor/easyAI
def play_game_transposition_table():
    ai_algo = Negamax(5, tt=TT())
    game = Gomoku_optimized([Human_Player(), AI_Player(ai_algo)], 5)
    game.play()
    if game.lose():
        print("Player %d wins!" % game.nopponent)
    else:
        print("Draw!")
コード例 #4
0
def play_game_transposition_table(size=6):
    ai_algo = Negamax(4, tt=TT())
    game = Gomoku_Strategic([Human_Player(), AI_Player(ai_algo)], size)
    game.play()
    if game.lose():
        print("Player %d wins!" % game.nopponent)
    else:
        print("Draw!")
コード例 #5
0
def create_players(parser):
    commands = parser.add_subparsers(title='sub-commands')
    parser.add_argument('--size', type=int)
    parser.add_argument('--wall_dist', type=str)

    for i in range(2):
        sub_parser = commands.add_parser(f'player{i+1}')
        sub_parser.add_argument('--type')
        sub_parser.add_argument('--depth', type=int)
        sub_parser.add_argument('--iterations', type=int)
        sub_parser.add_argument('--time_limit', type=int)
        sub_parser.add_argument('--tt', type=bool)
        sub_parser.add_argument('--n_playout', type=int)

    args = parse_args(parser, commands)
    players = [None] * 2
    args = vars(args)

    for i in range(2):
        args_player = args[f'player{i+1}']
        args_player = vars(args_player)
        player_type = args_player.pop("type")
        args_player = {k: v for k, v in args_player.items() if v is not None}
        if "tt" in args_player:
            args_player.pop("tt")
            player = players_dict[player_type](win_score=1000,
                                               tt=TT(),
                                               **args_player)
        else:
            player = players_dict[player_type](win_score=1000, **args_player)

        players[i] = player

    wall_dist = [int(x) for x in args['wall_dist'].split(",")
                 ] if args['wall_dist'] else [100, 100]
    return Game(args['size'], wall_dist=wall_dist), players
コード例 #6
0
def create_negamax_tt(depth):
    ai_algo_neg = Negamax(depth, tt=TT())
    return create_benchmark_game(ai_algo_neg)
コード例 #7
0
def create_dual_tt(depth):
    ai_algo_dual = DUAL(depth, tt=TT())
    return create_benchmark_game(ai_algo_dual)
コード例 #8
0
ファイル: webserver.py プロジェクト: asketak/gomoku-ai
              {{ttt.spot_string(j, i)}}
            </button>
          </td>
          {% endfor %}
        </tr>
        {% endfor %}
      </table>
      <button type="submit" name="reset">Start Over</button>
    </form>
  </body>
</html>
'''

app = Flask(__name__)
# ai_algo = Negamax(2 )
ai_algo = Negamax(3, tt = TT())
width = 15
@app.route("/", methods=['GET', 'POST'])
def play_game():
    global ttt 
    reset = False
    if "choice" in request.form:
        req = (request.form["choice"].split(','))
        coord = [ int(req[0]), int(req[1])]
        ttt.play_move(coord)
        if not ttt.is_over2():
            ai_move = ttt.get_move()
            ttt.play_move(ai_move)
    if "reset" in request.form:
        ttt.hboard = tuple(np.zeros((width, width), np.int8))
        ttt.htob()
コード例 #9
0
ファイル: algo_test.py プロジェクト: eporat/QuoridorAI
table1 = np.zeros((5,5))
table2 = np.zeros((5,5))
table3 = np.zeros((5,5)) # NOTICE THE SIZES!!!!

players_list = []

for i in range(MAX_DEPTH):
    # try:
    #     str = f'TT_folder/size{SIZE}/TT_depth{i + 1}/wall_score_0.0.data'
    #     print(str)
    #     tt_loaded = TT.fromfile(str)
    #     players_list.append(Negamax(i + 1, tt=tt_loaded))
    #     print("loaded")
    # except Exception as x:
    #     print(f"i dont have depth {i + 1} in memory")
    players_list.append(Negamax(i + 1, tt=TT()))


print("NO WALL RESTRICTION!!! DONT USE TT ")
for size in [5]:
    for depth1 in depths[size]:
        for depth2 in depths[size]:
                wins_white = 0
                draws = 0
                for i in range(TOTAL_GAMES):
                    players = [players_list[depth1 - 1], players_list[depth2 - 1]]

                    game = Game(size, wall_dist=[100, 100])
                    moves = []
                    index = 0
コード例 #10
0
ファイル: test.py プロジェクト: asketak/gomoku-ai
from easyAI import Human_Player, AI_Player, Negamax
from easyAI import TwoPlayersGame, Human_Player, AI_Player, DUAL
from negamax import Negamax
from pprint import pprint
from flask import Flask, render_template_string, request, make_response
from ai_negamax_faster import GomokuGame
from easyAI import TT

ai = Negamax(7, tt=TT())
game = GomokuGame([AI_Player(ai), Human_Player()], 15, 1)
import cProfile
cProfile.run("game.play(1)")
コード例 #11
0
ファイル: mcts_vs_negamax.py プロジェクト: eporat/QuoridorAI
import sys

sys.path.insert(0, '..')
from quoridor.game import Game
from mcts.mcts_pure import MCTSPlayer
from easyAI import Negamax, TT
from tqdm import tqdm

n_games = 10
tt = TT()
offset = 100
max_exp = 10
depth = 3


def f(offset, max_exp):
    if max_exp == 0:
        return offset

    for i in range(max_exp + 1):
        n_playout = offset + 2**i
        print(f"Number of playouts {n_playout}")

        draws = 0
        wins = [0, 0]

        player1 = MCTSPlayer(n_playout=n_playout, win_score=100)
        player2 = Negamax(depth=depth, tt=tt)
        players = [player1, player2]
        game = Game(9)
        while not game.is_terminal():
コード例 #12
0
 def __init__(self, timeout=5, name='AI_iterative'):
     self.name = name
     self.move = {}
     self.tt = TT()
     self.timeout = timeout
コード例 #13
0
ファイル: walls2_2.py プロジェクト: eporat/QuoridorAI
depth = 4
NUM_PLAYERS = 30
SIZE = 5
NUM_GAMES_EACH = 1 # has to be odd number
MIN_WALL_SCORE_BOUND = 1
MAX_WALL_SCORE_BOUND = 2


players_list = []
wall_scores = np.linspace(MIN_WALL_SCORE_BOUND, MAX_WALL_SCORE_BOUND, NUM_PLAYERS)
player_scores = [0] * NUM_PLAYERS

for i in range(NUM_PLAYERS):
    try:
        str = f'TT_folder/size5/TT_depth{depth}/wall_score_{wall_scores[i]}.data'
        tt_loaded = TT.fromfile(str)
        players_list.append(Negamax(depth, tt=tt_loaded))
        print("loaded")
    except Exception as x:
        print(f"i dont have {wall_scores[i]} in memory")
        players_list.append(Negamax(depth, tt=TT()))

start_time = time.time()
#score_func consideres both player1 and player2!!! not only one player
for i1, player1 in enumerate(players_list):
    for i2, player2 in enumerate(players_list):
        if player1 == player2:
            continue
        curr_time = time.time()
        players = [players_list[i1], players_list[i2]]
        for j in range(NUM_GAMES_EACH):
コード例 #14
0
def solve_game():
    tt = TT()
    r, d, m = id_solve(Gomoku_Strategic, range(2, 7), win_score=100, tt=tt)
    print r, d, m
コード例 #15
0
ファイル: mcts_pure.py プロジェクト: eporat/QuoridorAI
@author: Junxiao Song
"""
import sys
sys.path.insert(0, '..')
import numpy as np
import copy
from operator import itemgetter
from quoridor.game import Game
from tqdm import tqdm
from easyAI import Negamax, TT
from scipy.special import softmax
import random
# negamax = Negamax(depth=1, tt=TT(), win_score=100)
saved_games = {}

negamax = Negamax(depth=1, tt=TT())
def rollout_policy_fn(game):
    # """a coarse, fast version of policy_fn used in the rollout phase."""
    # return negamax(game)
    return min(game.availables, key=lambda move: game.move_score(move))

# def rollout_policy_fn(game):
#     """a coarse, fast version of policy_fn used in the rollout phase."""
#     # rollout randomly
#     return random.choice(game.availables)

def policy_value_fn(game):
    """a function that takes in a game and outputs a list of (action, probability)
    tuples and a score for the game"""
    # return uniform probabilities and 0 score for pure MCTS
    # action_probs = np.ones(len(game.availables))/len(game.availables)
コード例 #16
0
ファイル: walls2_1.py プロジェクト: eporat/QuoridorAI
# Constants. Notice that they are low.
depth = 4
NUM_PLAYERS = 5
SIZE = 5
NUM_GAMES_EACH = 1  # has to be odd number
MIN_WALL_SCORE_BOUND = 0
MAX_WALL_SCORE_BOUND = 3

players_list = []
wall_scores = np.linspace(MIN_WALL_SCORE_BOUND, MAX_WALL_SCORE_BOUND,
                          NUM_PLAYERS)
player_scores = [0] * NUM_PLAYERS

for _ in range(NUM_PLAYERS):
    players_list.append(Negamax(depth, tt=TT()))

#score_func consideres both player1 and player2!!! not only one player
for i1, player1 in enumerate(players_list):
    for i2, player2 in enumerate(players_list):
        if player1 == player2 or i2 <= i1:
            continue
        curr_time = time.time()
        players = [players_list[i1], players_list[i2]]
        for j in range(NUM_GAMES_EACH):
            game = Game(SIZE, wall_scores=[wall_scores[i1], wall_scores[i2]])
            moves = []
            index = 0

            while not game.is_terminal() and len(moves) < SIZE * 15:
                move = players[index](game)
コード例 #17
0
import argparse
from datetime import datetime
import json
import numpy as np
import create
from easyAI import Negamax, TT

# Constants. Notice that they are low.
DEPTH1 = 1
DEPTH2 = 3
SIZE = 5
NUM_GAMES_EACH = 45  # has to be odd number
WALL_SCORE = 0  # NOTICE THAT!!!!!!!!!!!!!!!!!!!!!!

players_list_no_shuffle = [
    Negamax(i + 1, tt=TT(), shuffle=False) for i in range(5)
]
players_list_yes_shuffle = [
    Negamax(i + 1, tt=TT(), shuffle=True) for i in range(5)
]

for depth1 in [1, 2, 3, 4, 5]:
    for depth2 in [1, 2, 3, 4, 5]:
        white_wins = 0
        draws = 0
        curr_time = time.time()
        players = [
            players_list_no_shuffle[depth1 - 1],
            players_list_yes_shuffle[depth2 - 1]
        ]
コード例 #18
0
def create_sss_tt(depth):
    ai_algo_sss = SSS(depth, tt=TT())
    return create_benchmark_game(ai_algo_sss)
コード例 #19
0
ファイル: tamir_tests.py プロジェクト: eporat/QuoridorAI
DEPTH = 1
SIZE = 5

player = create.Human()
pop = Population(DEPTH, SIZE)
#gene = [1.53276322, -0.31481973, 0.91032865, 0.10268689, 0.41397708, 0.93256447, 0.067119]
#gene = [2.17708616, 0.14143157, -0.39465167, 2.45134455, -1.29809957, 1.35924934, 0.1645196]
#gene = [ 2.17708616, 0.14143157, -0.39465167, 2.45134455, -1.29809957, -0.00416553, -0.38419192]
# gene = [2.17708616, 0.44166281, -0.39465167, 0.65901557, -1.29809957, -0.00416553, -0.38419192]
#gene = [ 2.17708616, 0.14143157, -0.39465167, 0.70410026, -1.29809957, -0.00416553, -0.38419192]
gene = [
    2.17708616, -1.0604653, -1.29051458, 0.70410026, -1.29809957, -0.00416553,
    -0.38419192
]
negamax_good = Negamax(depth=DEPTH, scoring=pop.scoring(gene), tt=TT())
gene = [1, 0, 0, 0, 0, 0, 0]
negamax_bad = Negamax(depth=3, scoring=pop.scoring(gene), tt=TT())


def main(size, game, players):
    root = tk.Tk()
    GUI(root, game, players)
    root.mainloop()


if __name__ == '__main__':
    game = Game(SIZE)
    #main(SIZE, game, [negamax_good, negamax_bad])
    main(SIZE, game, [Negamax(depth=1, tt=TT()), Negamax(depth=3, tt=TT())])
コード例 #20
0
ファイル: genetic.py プロジェクト: eporat/QuoridorAI
class Population:
    def __init__(self, depth, size, tt_file_name=None, count=1, mutation_rate=1e-2, best_percent=0.2, gene_size=7):
        self.count = count
        self.genes = None
        self.mutation_rate = mutation_rate
        self.best_percent = best_percent
        self.depth = depth
        self.size = size # Board size
        self.gene_size = gene_size
        self.tt_file_name = tt_file_name

        try:
            self.tt = TT.fromfile(self.tt_file_name)
            print("Loaded TT")
        except Exception as e:
            print("TT could not be found in memory")
            self.tt = TT()



    def calculate(self, game):
        average_degrees = self.average_degrees(game) # +
        jump = self.jump(game) # +
        center = self.center(game) # 0
        side = self.side(game) # +
        wall = self.wall(game) # +
        dist_player = self.dist_between_players(game) # -
        dist = self.distance(game) # +
        # [ 2.17708616, 0.14143157, -0.39465167, 2.45134455, -1.29809957, -0.00416553, -0.38419192]
        return np.array([dist, wall, jump, side, average_degrees, center, dist_player])[:self.gene_size]

    def walking_moves(self, game):
        return len(game.available_cells())

    def average_degrees(self, game):
        # average of possible walking moves
        moves = list(game.available_cells())
        sum_degrees = 0
        for cell in moves:
            sum_degrees += game.graph.degree(tuple(cell))

        return sum_degrees/len(moves)

    def jump(self, game):
        for cell in game.available_cells():
            if game.distance(game.index, cell) == 2:
                return 1

        return 0

    def center(self, game):
        CENTER = np.array([game.size // 2, game.size // 2])
        d1 = game.distance(game.index, CENTER)
        d2 = game.distance(1 - game.index, CENTER)
        return (d1 - d2)

    def side(self, game):
        score = 0

        if abs(game.players_loc[game.index][1] - game.end_loc[game.index]) <= game.size // 2:
            score += 1

        if abs(game.players_loc[1 - game.index][1] - game.end_loc[1 - game.index]) <= game.size // 2:
            score -= 1

        return score

    def wall(self, game):
        return game.wall_counts[game.index] - game.wall_counts[1 - game.index]

    def dist_between_players(self, game):
        locs = game.players_loc
        p0_loc = locs[game.index]
        p1_loc = locs[1 - game.index]
        return np.abs(p0_loc-p1_loc).sum()

    def distance(self, game):
        con1 = nx.node_connected_component(game.graph, tuple(game.players_loc[0]))
        con1 = filter(lambda x: x[1] == game.size - 1, con1)
        d1 = min(nx.shortest_path_length(game.graph, tuple(game.players_loc[0]), node) for node in con1)
        con2 = nx.node_connected_component(game.graph, tuple(game.players_loc[1]))
        con2 = filter(lambda x: x[1] == 0, con2)
        d2 = min(nx.shortest_path_length(game.graph, tuple(game.players_loc[1]), node) for node in con2)
        distance_score =  d2 - d1

        if game.index == 0:
            return distance_score
        else:
            return -distance_score

    def scoring(self, gene):
        def gene_scoring(game):
            if game.players_loc[0][1] == game.size - 1:
                if game.index == 0:
                    return 1000
                else:
                    return -1000

            if game.players_loc[1][1] == 0:
                if game.index == 0:
                    return -1000
                else:
                    return 1000

            lookup = self.tt.lookup(game)

            if lookup is None:
                score = self.calculate(game)
                self.tt.store(game=game, score=score)
                return score @ gene

            return lookup['score'] @ gene

        return gene_scoring

    def mutate(self, gene):
        new_gene = gene.copy()
        for i in range(self.gene_size):
            if np.random.random() > 1 - self.mutation_rate:
                new_gene[i] = np.random.normal(0, 1)

        return new_gene

    def crossover(self, i, j):
        fit1 = self.fitness[i]
        fit2 = self.fitness[j]
        gene1 = self.genes[i]
        gene2 = self.genes[j]

        fit = (fit1 + 1e-3) / (fit1 + fit2 + 2e-3)
        chosen = np.random.choice(2, self.gene_size, p=[fit, 1-fit])
        new_gene = np.zeros(self.gene_size)
        new_gene[chosen == 0] = gene1[chosen == 0]
        new_gene[chosen == 1] = gene2[chosen == 1]
        return new_gene

    def calculate_fitnesses(self):
        pass

    def create(self):
        a = self.selection()
        b = self.selection()
        return self.mutate(self.crossover(a,b))

    def get_best(self):
        best_idx = np.argsort(self.prob)[int(-self.best_percent * len(self.prob)):]
        return self.genes[best_idx]

    def selection(self):
        return np.random.choice(a=range(self.count), p=self.prob)

    def game(self, player1, player2):
        players = [player1, player2]

        game = Game(self.size)
        moves = []
        index = 0

        while not game.is_terminal() and len(moves) < self.size*50:
            move = players[index](game)
            end = time.time()
            game.play_game(*move)
            moves.append(move)
            index = 1 - index

        if len(moves) == self.size*50:
            return 0
        if index == 1:
            return 1
        else:
            return 2

    def run_games(self, generations):
        for generation in range(generations):
            print(f"Generation: {generation}")
            if generation == 0:
                self.genes = np.random.normal(0,1,(self.count, self.gene_size))
            else:
                # selection
                new_genes = np.zeros((self.count, self.gene_size))
                best_genes = self.get_best()
                new_genes[:len(best_genes)] = best_genes
                for i in range(len(best_genes), self.count):
                    new_genes[i] = self.create()
                self.genes = new_genes

            self.fitness = np.zeros(self.count)
            # crossover
            self.players = [Negamax(self.depth, scoring=self.scoring(gene), tt=TT()) for gene in self.genes]

            num_games = self.count * self.count
            game_count = 0

            for i, gene1 in enumerate(self.genes):
                for j, gene2 in enumerate(self.genes):

                    if game_count % 100 == 0:
                        self.tt.tofile(self.tt_file_name)
                        print(f"Played game: {game_count} / {num_games}")


                    game_count += 1
                    if i == j:
                        continue

                    result = self.game(self.players[i],self.players[j])

                    if result == 0:
                        self.fitness[i] += 0.5
                        self.fitness[j] += 0.5

                    if result == 1:
                        self.fitness[i] += 1

                    if result == 2:
                        self.fitness[j] += 1

            self.prob = self.fitness / self.fitness.sum()

            idx = np.argmax(self.prob)
            best_gene = self.genes[idx]
            print(f"Best player: {best_gene}")
            print(f"Best fitness: {np.max(self.fitness)/(2*(self.count - 1))}")

            negamax = Negamax(self.depth, tt=TT())
            negamax_best = self.players[idx]

            score1 = self.game(negamax, negamax_best)
            print(f"When normal Negamax started: {score1}")
            score2 = self.game(negamax_best, negamax)
            print(f"When best player started: {score2}")
コード例 #21
0
ファイル: less_walls.py プロジェクト: eporat/QuoridorAI
import numpy as np
import create
from easyAI import Negamax, TT

# Constants. Notice that they are low.
DEPTH1 = 1
DEPTH2 = 3
SIZE = 5
NUM_GAMES_EACH = 30 # has to be odd number
WALL_SCORE = 0

players_list_no_shuffle = []
players_list_yes_shuffle = []

for i in range(5):
    players_list_no_shuffle.append(Negamax(i+1, tt=TT(), shuffle=False))
    players_list_yes_shuffle.append(Negamax(i+1, tt=TT(), shuffle=True))


for depth1 in [1,2,3,4,5]:
    for depth2 in [1,2,3,4,5]:
        white_wins = 0
        draws = 0
        curr_time = time.time()

        player1 = players_list_no_shuffle[depth1 - 1]
        player2 = players_list_yes_shuffle[depth2 - 1]

        players = [player1, player2]

        for _ in range(NUM_GAMES_EACH):
コード例 #22
0
def solve_game_df():
    # to run this method we need to modify the max score to 100(check with self.win())
    ai_algo = Negamax(10, tt=TT())
    game = Gomoku_Strategic([Human_Player(), AI_Player(ai_algo)], 5)
    result = df_solve(game, win_score=100, maxdepth=10, tt=TT(), depth=0)
    print result
コード例 #23
0
ファイル: Gomoku_optimized.py プロジェクト: csabagabor/easyAI
def solve_game_df():
    ai_algo = Negamax(10, tt=TT())
    game = Gomoku_optimized([Human_Player(), AI_Player(ai_algo)], 5)
    result = df_solve(game, win_score=100, maxdepth=10, tt=TT(), depth=0)
    print result
コード例 #24
0
players_list = []
wall_scores = np.linspace(MIN_WALL_SCORE_BOUND, MAX_WALL_SCORE_BOUND,
                          NUM_PLAYERS)
player_scores = [0] * NUM_PLAYERS

for i in range(NUM_PLAYERS):
    players_list.append(Negamax(DEPTH1))

#score_func consideres both player1 and player2!!! not only one player
for i1, player1 in enumerate(players_list):
    for i2, player2 in enumerate(players_list):
        if player1 == player2 or i2 <= i1:
            continue
        curr_time = time.time()
        players = [Negamax(DEPTH1, tt=TT()), Negamax(DEPTH1, tt=TT())]
        for j in range(NUM_GAMES_EACH):
            game = Game(SIZE, wall_scores[i1], wall_scores[i2])
            moves = []
            index = 0

            while not game.is_terminal() and len(moves) < SIZE * 15:
                move = players[index](game)
                game.play_game(*move)
                moves.append(move)
                index = 1 - index

            if index == 1:
                player_scores[i1] += 1
            else:
                player_scores[i2] += 1
コード例 #25
0
ファイル: Gomoku_optimized.py プロジェクト: csabagabor/easyAI
def solve_game():
    tt = TT()
    r, d, m = id_solve(Gomoku_optimized, range(2, 20), win_score=100, tt=tt)
    print r, d, m
コード例 #26
0
def solve_game():
    #to run this method we need to modify the max score to 100(check with self.win())
    tt = TT()
    r, d, m = id_solve(Gomoku_Strategic, range(2, 20), win_score=100, tt=tt)
    print r, d, m