192 lines
6.8 KiB
Python
192 lines
6.8 KiB
Python
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() |