Update
This commit is contained in:
parent
6a89ece792
commit
1736e2f4ab
63
2048.css
63
2048.css
@ -533,69 +533,6 @@ h1 {
|
|||||||
75% { transform: translateX(10px); }
|
75% { transform: translateX(10px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tile Colors */
|
|
||||||
.tile-2 {
|
|
||||||
background: #00eaff55;
|
|
||||||
box-shadow: 0 0 12px #00eaff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-4 {
|
|
||||||
background: #00ff9955;
|
|
||||||
box-shadow: 0 0 12px #00ff99;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-8 {
|
|
||||||
background: #ff00ff55;
|
|
||||||
box-shadow: 0 0 12px #ff00ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-16 {
|
|
||||||
background: #ff006655;
|
|
||||||
box-shadow: 0 0 12px #ff0066;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-32 {
|
|
||||||
background: #ffaa0055;
|
|
||||||
box-shadow: 0 0 12px #ffaa00;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-64 {
|
|
||||||
background: #ff000055;
|
|
||||||
box-shadow: 0 0 12px #ff0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-128 {
|
|
||||||
background: #5f00ff55;
|
|
||||||
box-shadow: 0 0 12px #5f00ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-256 {
|
|
||||||
background: #00ffea55;
|
|
||||||
box-shadow: 0 0 12px #00ffea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-512 {
|
|
||||||
background: #ff00aa55;
|
|
||||||
box-shadow: 0 0 12px #ff00aa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-1024 {
|
|
||||||
background: #00ffaa55;
|
|
||||||
box-shadow: 0 0 12px #00ffaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile-2048 {
|
|
||||||
background: #ffd70066;
|
|
||||||
box-shadow: 0 0 18px #ffd700;
|
|
||||||
animation: goldShimmer 2.6s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes goldShimmer {
|
|
||||||
0% { box-shadow: 0 0 12px #ffd70066; transform: translateZ(0); }
|
|
||||||
50% { box-shadow: 0 0 30px #ffd700aa; transform: translateY(-2px); }
|
|
||||||
100% { box-shadow: 0 0 12px #ffd70066; transform: translateZ(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced Merge Animation */
|
/* Enhanced Merge Animation */
|
||||||
.tile.merge {
|
.tile.merge {
|
||||||
animation: mergeBounce 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
animation: mergeBounce 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||||
|
|||||||
18
2048.html
18
2048.html
@ -4,14 +4,10 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
<title>2048</title>
|
<title>2048</title>
|
||||||
<link rel="stylesheet" href="2048_Base.css"/>
|
|
||||||
<link rel="stylesheet" href="2048_Animation.css"/>
|
|
||||||
<link rel="stylesheet" href="2048_Layout.css"/>
|
|
||||||
<link rel="stylesheet" href="2048_Buttons.css"/>
|
|
||||||
<link rel="stylesheet" href="2048_Modals.css"/>
|
|
||||||
<link rel="stylesheet" href="2048_Background_Effects.css"/>
|
<link rel="stylesheet" href="2048_Background_Effects.css"/>
|
||||||
<link rel="stylesheet" href="2048_Floating_Particles.css"/>
|
<link rel="stylesheet" href="2048_Floating_Particles.css"/>
|
||||||
<link rel="stylesheet" href="2048.css"/>
|
<link rel="stylesheet" href="2048.css"/>
|
||||||
|
<link rel="stylesheet" href="2048_Tiles_Colors.css"/>
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
|
||||||
<link
|
<link
|
||||||
@ -320,13 +316,13 @@
|
|||||||
|
|
||||||
<script src="Score_Request.js"></script>
|
<script src="Score_Request.js"></script>
|
||||||
<script src="2048.js"></script>
|
<script src="2048.js"></script>
|
||||||
<script src="Audio_2048.js"></script>
|
<script src="2048_Audio.js"></script>
|
||||||
<script src="Visual_Effects_2048.js"></script>
|
<script src="2048_Background_Effects.css"></script>
|
||||||
<script src="Floating_Particles_2048.js"></script>
|
<script src="2048_Floating_Particles.js"></script>
|
||||||
<script src="User_Interface_2048.js"></script>
|
<script src="2048_User_Interface.js"></script>
|
||||||
<script src="2048_Logic.js"></script>
|
<script src="2048_Logic.js"></script>
|
||||||
<script src="2048_Controls.js"></script>
|
<script src="2048_Controls.js"></script>
|
||||||
<script src="Main_2048.js"></script>
|
<script src="2048_Main.js"></script>
|
||||||
<script src="Tutorial_Logic.js"></script>
|
<script src="2048_Tutorial_Logic.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
295
2048_Tiles_Colors.css
Normal file
295
2048_Tiles_Colors.css
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
.tile-2 {
|
||||||
|
background: #00eaff55;
|
||||||
|
box-shadow: 0 0 12px #00eaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-4 {
|
||||||
|
background: #00ff9955;
|
||||||
|
box-shadow: 0 0 12px #00ff99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-8 {
|
||||||
|
background: #ff00ff55;
|
||||||
|
box-shadow: 0 0 12px #ff00ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-16 {
|
||||||
|
background: #ff006655;
|
||||||
|
box-shadow: 0 0 12px #ff0066;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-32 {
|
||||||
|
background: #ffaa0055;
|
||||||
|
box-shadow: 0 0 12px #ffaa00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-64 {
|
||||||
|
background: #ff000055;
|
||||||
|
box-shadow: 0 0 12px #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-128 {
|
||||||
|
background: #5f00ff55;
|
||||||
|
box-shadow: 0 0 12px #5f00ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-256 {
|
||||||
|
background: rgba(75, 0, 130, 0.6);
|
||||||
|
box-shadow: 0 0 20px #4b0082;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-512 {
|
||||||
|
background: rgba(255, 69, 0, 0.6);
|
||||||
|
box-shadow: 0 0 25px #ff4500;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-1024 {
|
||||||
|
background: rgba(212, 115, 130, 0.65);
|
||||||
|
box-shadow: 0 0 25px #d47382;
|
||||||
|
color: white !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-2048 {
|
||||||
|
background: #ffd70066;
|
||||||
|
box-shadow: 0 0 18px #ffd700;
|
||||||
|
animation: goldShimmer 2.6s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes goldShimmer {
|
||||||
|
0% { box-shadow: 0 0 12px #ffd70066; transform: translateZ(0); }
|
||||||
|
50% { box-shadow: 0 0 30px #ffd700aa; transform: translateY(-2px); }
|
||||||
|
100% { box-shadow: 0 0 12px #ffd70066; transform: translateZ(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-4096 {
|
||||||
|
background: #ff220066;
|
||||||
|
box-shadow: 0 0 20px #ff2200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-8192 {
|
||||||
|
background: #0044ff66;
|
||||||
|
box-shadow: 0 0 20px #0044ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-16384 {
|
||||||
|
background: #ccff0066;
|
||||||
|
box-shadow: 0 0 20px #ccff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-32768 {
|
||||||
|
background: #7700ff66;
|
||||||
|
box-shadow: 0 0 25px #7700ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-65536 {
|
||||||
|
background: #000000aa;
|
||||||
|
box-shadow: 0 0 30px #ffffff, inset 0 0 10px #ffffff;
|
||||||
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
|
animation: pulseWhite 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulseWhite {
|
||||||
|
0% { box-shadow: 0 0 20px #ffffff; }
|
||||||
|
50% { box-shadow: 0 0 40px #ffffff, 0 0 10px #00eaff; }
|
||||||
|
100% { box-shadow: 0 0 20px #ffffff; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-131072 {
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgba(255, 0, 0, 0.5),
|
||||||
|
rgba(0, 255, 0, 0.5),
|
||||||
|
rgba(0, 0, 255, 0.5)
|
||||||
|
);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
color: white !important;
|
||||||
|
text-shadow: 0 1px 3px rgba(0,0,0,0.8);
|
||||||
|
box-shadow: 0 0 30px rgba(255, 255, 255, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
|
animation: holoMove 3s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes holoMove {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
box-shadow: 0 0 25px #ff00ff;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
box-shadow: 0 0 25px #00ffff;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
box-shadow: 0 0 25px #ff00ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-262144 {
|
||||||
|
background: linear-gradient(-45deg, #ff0080, #ff8c00, #40e0d0, #9932cc);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
color: white !important;
|
||||||
|
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||||
|
box-shadow:
|
||||||
|
0 0 30px rgba(255, 255, 255, 0.6),
|
||||||
|
inset 0 0 20px rgba(255, 255, 255, 0.4);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||||
|
animation: auroraFlow 5s ease infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes auroraFlow {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-524288 {
|
||||||
|
background: radial-gradient(circle at center, #ffffac 0%, #ffeb3b 30%, #ff9800 60%, #d50000 100%);
|
||||||
|
background-size: 200% 200%;
|
||||||
|
color: white !important;
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.8);
|
||||||
|
box-shadow:
|
||||||
|
0 0 30px #ff5722,
|
||||||
|
0 0 60px #ffc107,
|
||||||
|
inset 0 0 20px rgba(255, 255, 255, 0.5);
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.6);
|
||||||
|
animation: sunBurn 3s infinite alternate ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sunBurn {
|
||||||
|
0% {
|
||||||
|
background-position: 50% 50%;
|
||||||
|
box-shadow: 0 0 30px #ff5722, 0 0 50px #ff9800;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 60% 40%;
|
||||||
|
box-shadow: 0 0 50px #ff5722, 0 0 80px #ffeb3b;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-1048576 {
|
||||||
|
background: radial-gradient(circle, #ffffff 10%, #00ffff 30%, #9400d3 70%, #000000 100%);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
color: white !important;
|
||||||
|
box-shadow:
|
||||||
|
0 0 40px #00ffff,
|
||||||
|
0 0 80px #9400d3,
|
||||||
|
inset 0 0 30px #ffffff;
|
||||||
|
border: 2px solid #ffffff;
|
||||||
|
animation: supernovaPulse 0.5s infinite alternate ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes supernovaPulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 40px #00ffff, 0 0 80px #9400d3;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 60px #00ffff, 0 0 100px #ff00ff;
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-2097152 {
|
||||||
|
background: linear-gradient(
|
||||||
|
45deg,
|
||||||
|
#240b36 0%,
|
||||||
|
#c31432 25%,
|
||||||
|
#240b36 50%,
|
||||||
|
#00d2ff 75%,
|
||||||
|
#240b36 100%
|
||||||
|
);
|
||||||
|
background-size: 400% 400%;
|
||||||
|
color: #ffffff !important;
|
||||||
|
text-shadow: 0 0 5px #00d2ff, 0 0 10px #c31432;
|
||||||
|
box-shadow:
|
||||||
|
0 0 20px rgba(138, 43, 226, 0.8),
|
||||||
|
inset 0 0 15px rgba(0, 210, 255, 0.3);
|
||||||
|
border: 1px solid #00d2ff;
|
||||||
|
animation: galaxyFlow 6s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes galaxyFlow {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
border-color: #00d2ff;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
border-color: #c31432;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
border-color: #00d2ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-4194304 {
|
||||||
|
background-color: #330000;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle at 50% 50%, transparent 20%, #000000 120%),
|
||||||
|
linear-gradient(45deg, #ff4500 25%, #ff8c00 50%, #ff4500 75%);
|
||||||
|
color: #fffacd !important;
|
||||||
|
text-shadow: 0 0 5px #ff0000;
|
||||||
|
box-shadow:
|
||||||
|
0 0 20px #ff4500,
|
||||||
|
inset 0 0 10px #8b0000;
|
||||||
|
border: 2px solid #ff8c00;
|
||||||
|
font-size: clamp(22px, 1.6vmin, 14px);
|
||||||
|
animation: magmaPulse 0.8s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes magmaPulse {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 15px #ff4500;
|
||||||
|
border-color: #ff4500;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 35px #ff8c00;
|
||||||
|
border-color: #ffff00;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tile-8388608 {
|
||||||
|
background: linear-gradient(
|
||||||
|
125deg,
|
||||||
|
#000000 0%,
|
||||||
|
#3a0000 40%,
|
||||||
|
#800000 50%,
|
||||||
|
#3a0000 60%,
|
||||||
|
#000000 100%
|
||||||
|
);
|
||||||
|
background-size: 300% 300%;
|
||||||
|
color: white !important;
|
||||||
|
text-shadow: 0 0 10px #ff0000;
|
||||||
|
box-shadow:
|
||||||
|
0 0 30px #800000,
|
||||||
|
inset 0 0 20px rgba(255, 0, 0, 0.2);
|
||||||
|
border: 1px solid #ff0000;
|
||||||
|
font-size: clamp(21px, 1.6vmin, 14px);
|
||||||
|
animation: eclipseMove 4s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes eclipseMove {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
box-shadow: 0 0 20px #800000;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
box-shadow: 0 0 50px #ff0000;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
box-shadow: 0 0 20px #800000;
|
||||||
|
}
|
||||||
|
}
|
||||||
192
Bot_2048.py
Normal file
192
Bot_2048.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
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()
|
||||||
@ -97,8 +97,8 @@
|
|||||||
|
|
||||||
// Redirect after 1.5 seconds
|
// Redirect after 1.5 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = 'index.html';
|
window.location.href = 'Homepage.html';
|
||||||
}, 1500);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SHOW SUCCESS MODAL ====================
|
// ==================== SHOW SUCCESS MODAL ====================
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user