PROBABILITY_4 = 0.1  # probability of a new tile being a 4 rather than a 2
INITIAL_TILES = 2  # number of tiles on the board on a new game

DISPLAY_WIDTH = 296
DISPLAY_HEIGHT = 128

BOARD_LEFT = int(DISPLAY_WIDTH / 2) - 64

import ugfx
import random
import badge
import time
import appglue

badge.init()

# Apparently micropython's `random` library only gives us deterministic seeds natively.
# LET'S BLAST IT WITH SOME ENTROPY, BABY.
random.seed(badge.usb_volt_sense() << 10 ^ badge.battery_volt_sense())

ugfx.init()


board = None


def print_board():
	ugfx.clear(ugfx.WHITE)
	for y, row in enumerate(board):
		for x, tile in enumerate(row):
			if tile is None:
				ugfx.rounded_box(BOARD_LEFT + x * 32 + 2, y * 32 + 1, 30, 30, 2, ugfx.BLACK)
			else:
				ugfx.fill_rounded_box(BOARD_LEFT + x * 32 + 2, y * 32 + 1, 30, 30, 2, ugfx.BLACK)
				num = str(tile)
				if len(num) < 3:
					font = 'PermanentMarker22'
				elif len(num) < 5:
					font = 'Roboto_Regular12'
				else:
					font = 'pixelade13'
				ugfx.string_box(BOARD_LEFT + x * 32 + 2, y * 32 + 1, 30, 30, num, font, ugfx.WHITE, ugfx.justifyCenter)

	if not (can_move_up() or can_move_down() or can_move_left() or can_move_right()):
		ugfx.fill_rounded_box(BOARD_LEFT, 48, 128, 32, 2, ugfx.WHITE)
		ugfx.string_box(BOARD_LEFT, 48, 128, 20, "Game Over!", 'PermanentMarker22', ugfx.BLACK, ugfx.justifyCenter)
		ugfx.string_box(BOARD_LEFT, 48, 128, 48, "'start' to play again", 'pixelade13', ugfx.BLACK, ugfx.justifyCenter)

	ugfx.flush()


def add_tile():
	while True:
		x = random.randint(0, 3)
		y = random.randint(0, 3)
		if board[y][x] is None:
			board[y][x] = 4 if random.random() < PROBABILITY_4 else 2
			break


def start_game():
	global board
	board = [[None, None, None, None] for i in range(0, 4)]

	for i in range(0, INITIAL_TILES):
		add_tile()

	print_board()


def can_move_up():
	for x in range(0, 4):
		old_tiles = [board[y][x] for y in range(0, 4)]
		new_tiles = merge_tiles_left(old_tiles)
		if new_tiles != old_tiles:
			return True
	return False


def can_move_down():
	for x in range(0, 4):
		old_tiles = [board[y][x] for y in range(0, 4)]
		new_tiles = merge_tiles_right(old_tiles)
		if new_tiles != old_tiles:
			return True
	return False


def can_move_left():
	for y in range(0, 4):
		old_tiles = board[y]
		new_tiles = merge_tiles_left(old_tiles)
		if new_tiles != old_tiles:
			return True
	return False


def can_move_right():
	for y in range(0, 4):
		old_tiles = board[y]
		new_tiles = merge_tiles_right(old_tiles)
		if new_tiles != old_tiles:
			return True
	return False


def merge_tiles_left(old_tiles):
	new_tiles = []
	last_tile = None
	for tile in old_tiles:
		if tile is None:
			continue
		if tile == last_tile:
			new_tiles[-1] = tile * 2
			last_tile = None
		else:
			new_tiles.append(tile)
			last_tile = tile
	return (new_tiles + [None, None, None, None])[:4]


def merge_tiles_right(old_tiles):
	return merge_tiles_left(old_tiles[::-1])[::-1]


def move_up(pressed):
	if pressed and can_move_up():
		for x in range(0, 4):
			new_tiles = merge_tiles_left([board[y][x] for y in range(0, 4)])
			for y in range(0, 4):
				board[y][x] = new_tiles[y]

		add_tile()
		print_board()


def move_down(pressed):
	if pressed and can_move_down():
		for x in range(0, 4):
			new_tiles = merge_tiles_right([board[y][x] for y in range(0, 4)])
			for y in range(0, 4):
				board[y][x] = new_tiles[y]

		add_tile()
		print_board()


def move_left(pressed):
	if pressed and can_move_left():
		for y in range(0, 4):
			new_tiles = merge_tiles_left(board[y])
			for x in range(0, 4):
				board[y][x] = new_tiles[x]

		add_tile()
		print_board()


def move_right(pressed):
	if pressed and can_move_right():
		for y in range(0, 4):
			new_tiles = merge_tiles_right(board[y])
			for x in range(0, 4):
				board[y][x] = new_tiles[x]

		add_tile()
		print_board()


def restart(pressed):
	if pressed:
		start_game()


def quit(pressed):
	if pressed:
		appglue.home()


ugfx.input_init()
ugfx.input_attach(ugfx.JOY_UP, move_up)
ugfx.input_attach(ugfx.JOY_LEFT, move_left)
ugfx.input_attach(ugfx.JOY_DOWN, move_down)
ugfx.input_attach(ugfx.JOY_RIGHT, move_right)
ugfx.input_attach(ugfx.BTN_START, restart)
ugfx.input_attach(ugfx.BTN_SELECT, quit)

start_game()

while True:
	time.sleep(1)
