6
\$\begingroup\$

I am working on an 2048 AI and this is my code so far.

In the game 2048 you have a 4x4 grid in that some random so named tiles spawn. Each tile has a number. The lowest number is 2. By using the left, right, up and down-key you can move all tiles into the specified direction. If two tiles with the same number "collide" they merge into one and sum up there numbers.

Example:

 ___ ___ ___ ___
|___|___|___|_2_|
|___|___|_2_|___|
|___|___|___|_2_|
|___|___|___|___|

DOWN
 ___ ___ ___ ___
|___|___|___|___|
|___|___|___|___|
|___|___|___|___|
|___|___|_2_|_4_|

You can get all needed files on Github. My AI uses the Minimax-algorithm to calculate the best move.

I've tested it here and got scores around 10,000 Points with getting the 1024-tile.

import pyautogui
import collections
import operator
import math
import time
import cv2
import numpy as np
import random

game  = [0] * 16

UP    = 0
DOWN  = 1
LEFT  = 2
RIGHT = 3

step  = 6

def printGame(game=game):
    print('')
    for i in range(3):
        print(end=' ')
        for j in range(3):
            print(str(game[i * 4 + j] if game[i * 4 + j] else '').center(4), '| ', end='')
        print(str(game[i * 4 + 3] if game[i * 4 + 3] else '').center(4))
        print('------|------|------|------')
    print(end=' ')
    for j in range(3):
        print(str(game[12 + j] if game[12 + j] else '').center(4), '| ', end='')
    print(str(game[12 + 3] if game[12 + 3] else '').center(4))
    print('')

def getTiles():
    tiles = {}
    screen = cv2.cvtColor(np.array(pyautogui.screenshot()), cv2.COLOR_BGR2GRAY)
    for tileNumber in [0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]:
        template = cv2.imread('tile' + str(tileNumber) + '.png', 0)
        w, h = template.shape[::-1]
        res = cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED)
        locations = np.where(res >= .8)
        for location in zip(*locations[::-1]):
            location = pyautogui.center((location[0], location[1], w, h))
            tiles[round(location[0] / 100), round(location[1] / 100)] = tileNumber
    od = collections.OrderedDict(sorted(tiles.items()))
    i = 0
    for k, v in od.items():
        game[i % 4 * 4 + math.floor(i / 4)] = v
        i += 1

def ij(direction, i, j, offset=0):
    if direction == UP:
        return (3 - j - offset) * 4 + i
    elif direction == DOWN:
        return (j + offset) * 4 + i
    elif direction == LEFT:
        return i * 4 + 3 - j - offset
    elif direction == RIGHT:
        return i * 4 + j + offset

def move(game, direction, step=step):
    tmpgame = game.copy()
    for i in range(4):
        lj = -1
        for j in range(4):
            if game[ij(direction, i, j)] != 0:
                if lj > -1 and game[ij(direction, i, j)] == game[lj]:
                    game[lj] = 0
                    game[ij(direction, i, j)] *= 2
                    lj = -1
                else:
                    lj = ij(direction, i, j)
    for i in range(4):
        l = []
        for j in range(4):
            l.append(game[ij(direction, 3 - i, 3 - j)])
        l = [x for x in l if x != 0]
        while len(l) < 4:
            l.append(0)
        for j in range(4):
            game[ij(direction, 3 - i, 3 - j)] = l[j]
    if tmpgame == game:
        return 0
    elif step > 0:
        return multispawn(game, step)
    else:
        return sum(game) * game.count(0)

def multimove(game, step):
    l = []
    for i in range(4):
        l.append(move(game.copy(), i, step))
    return max(l)

def spawn(game, tile, i, j, step=step):
    game[i * 4 + j] = 2
    if step > 0:
        return multimove(game, step - 1)
    else:
        return sum(game) * game.count(0)

def multispawn(game, step):
    l = []
    for i in range(4):
        for j in range(4):
            if game[i * 4 + j] == 0:
                l.append(spawn(game.copy(), 2, i, j, step - 1))
                l.append(spawn(game.copy(), 4, i, j, step - 1))
                if 0 in l:
                    return 0
    if len(l) == 0:
        if step > 0:
            return multimove(game, step - 1)
        else:
            return sum(game) * game.count(0)
    else:
        return min(l)

def main():
    time.sleep(2)
    while True:
        getTiles()
        printGame()

        count = {}

        count['up'] = move(game.copy(), UP)
        print('up', count['up'])

        count['down'] = move(game.copy(), DOWN)
        print('down', count['down'])

        count['left'] = move(game.copy(), LEFT)
        print('left', count['left'])

        count['right'] = move(game.copy(), RIGHT)
        print('right', count['right'])

        count = [x for x,y in count.items() if y == max(count.values())]

        pyautogui.press(count[random.randint(0, len(count) - 1)])

        time.sleep(0.2)

if __name__ == '__main__':
    main()
\$\endgroup\$
0

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.