import time import random import numpy as np from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options from webdriver_manager.chrome import ChromeDriverManager URL_LOGIN = "http://localhost/Kelompok06_2048/Homepage.html" SEARCH_DEPTH = 4 DELAY_ANIMATION = 0.1 class GameAI: def __init__(self, url): print(">>> Menyiapkan Browser Mode HARDCORE (Depth 4)...") chrome_options = Options() chrome_options.add_argument("--log-level=3") chrome_options.add_experimental_option("detach", True) self.driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options) self.driver.set_window_size(1000, 1000) self.driver.get(url) print("\n" + "="*50) print(" SILAKAN LOGIN MANUAL -> KLIK PLAY ") print(" Bot Depth 4 siap menunggu di arena...") print("="*50 + "\n") try: WebDriverWait(self.driver, 3600).until( EC.presence_of_element_located((By.ID, "board")) ) print(">>> GAME START! Mengaktifkan Otak Jenius...") time.sleep(1) except: print("Gagal deteksi game, tapi browser tetap terbuka.") self.body = self.driver.find_element(By.TAG_NAME, 'body') def get_grid(self): matrix = np.zeros((4, 4), dtype=int) try: for r in range(4): for c in range(4): element_id = f"{r}-{c}" try: tile = self.driver.find_element(By.ID, element_id) text = tile.text.strip() if text.isdigit(): matrix[r][c] = int(text) else: matrix[r][c] = 0 except: matrix[r][c] = 0 except: pass return matrix def heuristic(self, grid): weights = np.array([ [0, 1, 10, 30], [150, 100, 80, 50], [200, 300, 500, 800], [20000, 8000, 3000, 1500] ]) score = np.sum(grid * weights) empty_count = len(np.where(grid == 0)[0]) score += empty_count * 30000 penalty = 0 for x in range(4): # Baris current = 0 next_val = 0 for y in range(3): current = grid[x][y] next_val = grid[x][y+1] if current > next_val: score += current - next_val else: score -= (next_val - current) * 5 for y in range(3): current = grid[y][x] next_val = grid[y+1][x] if current > next_val: score += current - next_val else: score -= (next_val - current) * 5 return score def simulate_move(self, grid, direction): temp_grid = grid.copy() if direction == 'UP': temp_grid = np.rot90(temp_grid, 1) elif direction == 'RIGHT': temp_grid = np.rot90(temp_grid, 2) elif direction == 'DOWN': temp_grid = np.rot90(temp_grid, 3) changed = False new_grid_vals = [] for row in temp_grid: new_row = [x for x in row if x != 0] for i in range(len(new_row)-1): if new_row[i] == new_row[i+1] and new_row[i] != 0: new_row[i] *= 2 new_row[i+1] = 0 new_row = [x for x in new_row if x != 0] new_row += [0] * (4 - len(new_row)) new_grid_vals.append(new_row) if new_row != list(row): changed = True temp_grid = np.array(new_grid_vals) if direction == 'UP': temp_grid = np.rot90(temp_grid, 3) elif direction == 'RIGHT': temp_grid = np.rot90(temp_grid, 2) elif direction == 'DOWN': temp_grid = np.rot90(temp_grid, 1) return temp_grid, changed def expectimax(self, grid, depth, is_player_turn): if depth == 0: return self.heuristic(grid) if is_player_turn: best_score = -float('inf') for move in ['DOWN', 'RIGHT', 'LEFT', 'UP']: new_grid, changed = self.simulate_move(grid, move) if changed: score = self.expectimax(new_grid, depth - 1, False) best_score = max(best_score, score) return best_score if best_score != -float('inf') else -10000000 else: empty_cells = list(zip(*np.where(grid == 0))) if not empty_cells: return self.heuristic(grid) sample_size = 4 if len(empty_cells) > sample_size: empty_cells = random.sample(empty_cells, sample_size) total_score = 0 for r, c in empty_cells: grid[r][c] = 2 total_score += self.expectimax(grid, depth - 1, True) grid[r][c] = 0 return total_score / len(empty_cells) def run(self): print(">>> Bot Mulai! (Mode: FULL DEPTH 4)") print(">>> Tips: Jangan minimize window browser biar lancar.") while True: try: grid = self.get_grid() if np.sum(grid) == 0: time.sleep(0.5) continue best_move = None max_val = -float('inf') valid_found = False empty = len(np.where(grid == 0)[0]) print(f"\rMikir... (Kosong: {empty})", end="") for move in ['DOWN', 'RIGHT', 'LEFT', 'UP']: sim_grid, changed = self.simulate_move(grid, move) if changed: valid_found = True val = self.expectimax(sim_grid, SEARCH_DEPTH, False) if val > max_val: max_val = val; best_move = move if best_move: self.body.send_keys(getattr(Keys, best_move)) time.sleep(DELAY_ANIMATION) else: if not valid_found: print("\nGAME OVER!") break else: self.body.send_keys(Keys.DOWN) except KeyboardInterrupt: break except Exception: time.sleep(1) if __name__ == "__main__": ai = GameAI(URL_LOGIN) ai.run()