9
\$\begingroup\$

I am working on an 2048 AI game, 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 keys, you can move all tiles in the specified direction. If two tiles with the same number "collide", they merge into one and sum up their 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()
toolic
14.4k5 gold badges29 silver badges201 bronze badges
asked Sep 9, 2018 at 14:49
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

Naming

The variable named l is not too descriptive:

 l = []

Also, l alone is easily confused with capital I and the number 1.

Since it is an array, it would be good to name it as a plural noun, like things.

lj should also be renamed.

The PEP 8 style guide recommends using snake_case for function names:

printGame

would become:

print_game

Documentation

PEP-8 also recommends adding docstrings for functions. Consider:

def ij(direction, i, j, offset=0):

Aside from changing the function name to be more descriptive, it would benefit from a docstring:

  • summarizing what it does
  • what type of inputs it has
  • what it returns

Import

The ruff tool can be used to analyze the code. It finds this:

import operator
= help: Remove unused import: `operator`
answered Jan 16 at 10:36
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.