I would like to credit Randy Olson for the template we used as this template is the key for evaluating a good position. Heuristic: def evalgrid(grid): return np.sum(np.array(grid) * TEMPLATE) TEMPLATE =, ,, ]
The best move function is the key to this code and all it does it recursively iterate through the game tree until it reaches its desires depth and then evaluates the grid based on a heuristic we have chosen and if its score if greater than the current greatest score, then we update a variable that holds the best grid with the current grid and then we use this grid for the next iteration of best move. import random import numpy as np import sys import time from itertools import product import matplotlib.pyplot as plt ROW_LENGTH = 4 STARTING_NUMBERS = 2 CHANCE_OF_TWO = 90 PLAYER_SCORE = 0 TOTAL_MOVES = 0 NUMBER_OF_RUNS = 7 POSSIBLE_MOVES = DEPTH = 2 POSSIBLE_ARRANGEMENTS = product(POSSIBLE_MOVES, repeat=DEPTH) TEMPLATE =, ,, ] BEST_DIRECTION = "" BEST_SCORE = 0 BEST_GRID = None PLT_SCORE = ,] def generateTile(): RANDOM_NUM = random.randint(1, 100) if RANDOM_NUM < CHANCE_OF_TWO: number = 2 else: number = 4 return number def createGrid(dimensions): grid = for j in range(dimensions)] for i in range(STARTING_NUMBERS): number = generateTile() grid = number displayGrid(grid) return grid def displayGrid(*grids): for grid in grids: print(np.array(grid)) def evalgrid(grid): return np.sum(np.array(grid) * TEMPLATE) def move(direction, grid, score): if direction = "left" or direction = "right": for i in range(ROW_LENGTH): if direction = "right": grid = grid for j in range(unt(0)): grid.append(grid.pop(grid.index(0))) for element in range(0, ROW_LENGTH - 1): if grid = grid: score += grid * 2 grid = grid * 2 grid.remove(grid) grid.append(0) if direction = "right": grid = grid return grid, score else: collection = for i in range(0, ROW_LENGTH) for j in range(0, ROW_LENGTH)] vGrid = for i in range(ROW_LENGTH)] for i in range(ROW_LENGTH): if direction = "down": vGrid = vGrid for j in range(unt(0)): vGrid.append(vGrid.pop(vGrid.index(0))) for element in range(0, ROW_LENGTH - 1): if vGrid = vGrid: score += grid * 2 vGrid = vGrid * 2 vGrid.remove(vGrid) vGrid.append(0) if direction = "down": vGrid = vGrid for row in range(ROW_LENGTH): for column in range(ROW_LENGTH): grid = vGrid return grid, score def check(grid): gridArr = for direction in POSSIBLE_MOVES: temp = for x in grid] gridArr.append(move(direction, temp, PLAYER_SCORE)) for potentialGrid in gridArr: if grid != potentialGrid: return False return True def updateGrid(grid, state): count = 0 for i in range(ROW_LENGTH): count += unt(0) if not count: if check(grid): return grid, True if state = True: while True: row = random.randint(0, ROW_LENGTH - 1) column = random.randint(0, ROW_LENGTH - 1) if grid = 0: grid = generateTile() return grid, False return grid, False def bestmove(grid, CURR_DEPTH, SET_OF_MOVES, currentscore): CURR_DEPTH += 1 placeholder = for x in grid] if CURR_DEPTH != DEPTH: for DIRECTION in POSSIBLE_MOVES: grid = move(DIRECTION, grid, currentscore) SET_OF_MOVES.append(DIRECTION) # print(f"Depth ") # displayGrid(grid) if grid != placeholder: grid = updateGrid(grid, True) bestmove(grid, CURR_DEPTH, SET_OF_MOVES, currentscore) SET_OF_MOVES.pop() grid = for x in placeholder] return else: global BEST_SCORE global BEST_DIRECTION global BEST_GRID SCORE = evalgrid(grid) if BEST_SCORE < SCORE: BEST_SCORE = SCORE BEST_DIRECTION = SET_OF_MOVES BEST_GRID = grid return return depth 10, I ran it for 2 hours and it hit max score of a 65,536 tile! You can see the final grid of depth 8 on the right of the grid on the pycharm terminal.