diff --git a/helper.py b/helper.py new file mode 100644 index 0000000..20073e2 --- /dev/null +++ b/helper.py @@ -0,0 +1,117 @@ +import numpy as np +import math + +import logic +import constants as c + + + +#The depth that the algorithm will be search to find the best move(default 2, if you want more accurate prediction will be take more time) +MAX_DEPTH = 2 + + +#Help function that call the necessary function +def get_help(board): + best_move = determine_next_move(board) + return best_move + + +#The heuristic function to calculate how good is a certain state of the board +def heuristic_value(board): + heuristic_value = 0 + for i in range(0, c.GRID_LEN): + for j in range(0, c.GRID_LEN): + heuristic_value += math.pow(board[0][1], 3) + return heuristic_value + + + + +#This function check if two boards are the same +def boardsEquality(board_1, board_2): + for i in range(0, c.GRID_LEN): + for j in range(0, c.GRID_LEN): + if board_1[i][j] != board_2[i][j]: + return False + return True + + +#Recursion, simulating out every possible human and computer move for a certain number of steps +def calculate_move_score(board, currDepth, depthLimit): + bestScore = 0 + moves = ['RIGHT', 'UP', 'LEFT', 'DOWN'] + for m in moves: + if m == 'RIGHT': + newBoard, done= logic.right(board) + if m == 'UP': + newBoard, done= logic.up(board) + if m == 'LEFT': + newBoard, done= logic.left(board) + if m == 'DOWN': + newBoard, done= logic.down(board) + if not boardsEquality(newBoard, board): + score = generate_score(newBoard, currDepth + 1, depthLimit) + bestScore = max(score, bestScore) + return bestScore + + + + + +#Recursion, simulating out every possible human and computer move for a certain number of steps +def generate_score(board, currDepth, depthLimit): + if currDepth >= depthLimit: + return heuristic_value(board) + + totalScore = 0 + for i in range(0, c.GRID_LEN): + for j in range(0, c.GRID_LEN): + if board[i][j] == 0: + newBoard2 = board + newBoard2[i][j] = 2 + moveScore2 = calculate_move_score(newBoard2, currDepth, depthLimit) + totalScore += 0.9*moveScore2 + + newBoard4 = board + newBoard4[i][j] = 4 + moveScore4 = calculate_move_score(newBoard4, currDepth, depthLimit) + totalScore += 0.1*moveScore4 + return totalScore + + +#In this function calculated the score after a move +def calculate_score(board, move): + if move == 'RIGHT': + newBoard, done= logic.right(board) + if move == 'UP': + newBoard, done= logic.up(board) + if move == 'LEFT': + newBoard, done= logic.left(board) + if move == 'DOWN': + newBoard, done= logic.down(board) + + if boardsEquality(newBoard, board): + return 0 + return generate_score(newBoard, 0, MAX_DEPTH) + + + + +def determine_next_move(board): + bestMove = '' + bestScore = 0 + moves = ['RIGHT', 'UP', 'LEFT', 'DOWN'] + for m in moves: + score = calculate_score(board, m) + if score > bestScore: + bestScore = score + bestMove = m + return bestMove + + + + + + + + diff --git a/img/Screenshot from 2021-08-23 15-16-44.png b/img/Screenshot from 2021-08-23 15-16-44.png new file mode 100644 index 0000000..5990c5b Binary files /dev/null and b/img/Screenshot from 2021-08-23 15-16-44.png differ diff --git a/logic.py b/logic.py index 91cc47e..044db0e 100644 --- a/logic.py +++ b/logic.py @@ -22,8 +22,10 @@ def new_game(n): matrix = [] for i in range(n): matrix.append([0] * n) - matrix = add_two(matrix) - matrix = add_two(matrix) + + matrix = add_number(matrix) + matrix = add_number(matrix) + return matrix ########### @@ -35,13 +37,25 @@ def new_game(n): # Must ensure that it is created on a zero entry # 1 mark for creating the correct loop -def add_two(mat): - a = random.randint(0, len(mat)-1) - b = random.randint(0, len(mat)-1) - while mat[a][b] != 0: + +#The function changed to support the randomly insertion of the number 4 +def add_number(mat): + prob = random.random() + if prob <= 0.1: + a = random.randint(0, len(mat)-1) + b = random.randint(0, len(mat)-1) + while mat[a][b] != 0: + a = random.randint(0, len(mat)-1) + b = random.randint(0, len(mat)-1) + mat[a][b] = 4 + else: a = random.randint(0, len(mat)-1) b = random.randint(0, len(mat)-1) - mat[a][b] = 2 + while mat[a][b] != 0: + a = random.randint(0, len(mat)-1) + b = random.randint(0, len(mat)-1) + mat[a][b] = 2 + return mat ########### @@ -159,7 +173,7 @@ def merge(mat, done): return mat, done def up(game): - print("up") + #print("up") # return matrix after shifting up game = transpose(game) game, done = cover_up(game) @@ -169,8 +183,7 @@ def up(game): return game, done def down(game): - print("down") - # return matrix after shifting down + #print("down") game = reverse(transpose(game)) game, done = cover_up(game) game, done = merge(game, done) @@ -179,7 +192,7 @@ def down(game): return game, done def left(game): - print("left") + #print("left") # return matrix after shifting left game, done = cover_up(game) game, done = merge(game, done) @@ -187,7 +200,7 @@ def left(game): return game, done def right(game): - print("right") + #print("right") # return matrix after shifting right game = reverse(game) game, done = cover_up(game) diff --git a/puzzle.py b/puzzle.py index 561c705..1c8db52 100644 --- a/puzzle.py +++ b/puzzle.py @@ -1,44 +1,81 @@ -from tkinter import Frame, Label, CENTER +from tkinter import Frame, Label, CENTER, Button +import tkinter as tk import random + + +import helper import logic import constants as c def gen(): return random.randint(0, c.GRID_LEN - 1) + class GameGrid(Frame): + def __init__(self): + self.matrix = logic.new_game(c.GRID_LEN) Frame.__init__(self) + + #Insertion of the help button in the GUI + self.helpButton = Button(self, text="Help", command = self.callHelper) + self.helpButton.bind("", self.callHelper) + self.helpButton.grid() + self.grid() self.master.title('2048') self.master.bind("", self.key_down) + + + + + - self.commands = { - c.KEY_UP: logic.up, - c.KEY_DOWN: logic.down, - c.KEY_LEFT: logic.left, - c.KEY_RIGHT: logic.right, - c.KEY_UP_ALT1: logic.up, - c.KEY_DOWN_ALT1: logic.down, - c.KEY_LEFT_ALT1: logic.left, - c.KEY_RIGHT_ALT1: logic.right, - c.KEY_UP_ALT2: logic.up, - c.KEY_DOWN_ALT2: logic.down, - c.KEY_LEFT_ALT2: logic.left, - c.KEY_RIGHT_ALT2: logic.right, - } + self.commands = {c.KEY_UP: logic.up, c.KEY_DOWN: logic.down, + c.KEY_LEFT: logic.left, c.KEY_RIGHT: logic.right, + c.KEY_UP_ALT: logic.up, c.KEY_DOWN_ALT: logic.down, + c.KEY_LEFT_ALT: logic.left, c.KEY_RIGHT_ALT: logic.right, + c.KEY_H: logic.left, c.KEY_L: logic.right, + c.KEY_K: logic.up, c.KEY_J: logic.down} + + + + self.grid_cells = [] self.init_grid() - self.matrix = logic.new_game(c.GRID_LEN) + self.history_matrixs = [] self.update_grid_cells() self.mainloop() + + # Function that call the helper function to play the next best move + def callHelper(self): + move = helper.get_help(self.matrix) + + if move == 'RIGHT': + self.matrix, done = self.commands[c.KEY_RIGHT](self.matrix) + if move == 'UP': + self.matrix, done = self.commands[c.KEY_UP](self.matrix) + if move == 'LEFT': + self.matrix, done = self.commands[c.KEY_LEFT](self.matrix) + if move == 'DOWN': + self.matrix, done = self.commands[c.KEY_DOWN](self.matrix) + self.matrix = logic.add_number(self.matrix) + self.update_grid_cells() + + + + def init_grid(self): - background = Frame(self, bg=c.BACKGROUND_COLOR_GAME,width=c.SIZE, height=c.SIZE) + background = Frame(self, bg=c.BACKGROUND_COLOR_GAME, + width=c.SIZE, height=c.SIZE) + + + background.grid() for i in range(c.GRID_LEN): @@ -93,7 +130,7 @@ def key_down(self, event): elif key in self.commands: self.matrix, done = self.commands[key](self.matrix) if done: - self.matrix = logic.add_two(self.matrix) + self.matrix = logic.add_number(self.matrix) # record last move self.history_matrixs.append(self.matrix) self.update_grid_cells() @@ -110,4 +147,10 @@ def generate_next(self): index = (gen(), gen()) self.matrix[index[0]][index[1]] = 2 -game_grid = GameGrid() \ No newline at end of file + + + + +game_grid = GameGrid() + +