mirror of
https://github.com/System-End/haxmas-day-8.git
synced 2026-04-19 15:28:18 +00:00
431 lines
13 KiB
Python
431 lines
13 KiB
Python
import re
|
|
import tkinter as tk
|
|
from datetime import datetime
|
|
from tkinter import messagebox, ttk
|
|
|
|
from selenium import webdriver
|
|
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
|
from selenium.webdriver.common.by import By
|
|
from selenium.webdriver.edge.options import Options as EdgeOptions
|
|
from selenium.webdriver.firefox.options import Options as FirefoxOptions
|
|
|
|
|
|
def detect_and_create_driver():
|
|
browsers = [
|
|
("Chrome", create_chrome_driver),
|
|
("Firefox", create_firefox_driver),
|
|
("Edge", create_edge_driver),
|
|
]
|
|
for browser_name, create_func in browsers:
|
|
try:
|
|
return create_func(), browser_name
|
|
except Exception:
|
|
continue
|
|
return None, None
|
|
|
|
|
|
def create_chrome_driver():
|
|
options = ChromeOptions()
|
|
options.add_argument("--disable-extensions")
|
|
options.add_argument("--disable-gpu")
|
|
options.add_argument("--no-sandbox")
|
|
options.add_argument("--disable-dev-shm-usage")
|
|
options.page_load_strategy = "eager"
|
|
options.add_experimental_option(
|
|
"excludeSwitches", ["enable-logging", "enable-automation"]
|
|
)
|
|
return webdriver.Chrome(options=options)
|
|
|
|
|
|
def create_firefox_driver():
|
|
options = FirefoxOptions()
|
|
options.set_preference("dom.webnotifications.enabled", False)
|
|
options.page_load_strategy = "eager"
|
|
return webdriver.Firefox(options=options)
|
|
|
|
|
|
def create_edge_driver():
|
|
options = EdgeOptions()
|
|
options.add_argument("--disable-extensions")
|
|
options.add_argument("--disable-gpu")
|
|
options.page_load_strategy = "eager"
|
|
return webdriver.Edge(options=options)
|
|
|
|
|
|
def parse_number(text):
|
|
text = text.lower().replace(",", "").strip()
|
|
multipliers = {
|
|
"million": 1e6,
|
|
"billion": 1e9,
|
|
"trillion": 1e12,
|
|
"quadrillion": 1e15,
|
|
"quintillion": 1e18,
|
|
}
|
|
for word, mult in multipliers.items():
|
|
if word in text:
|
|
return float(text.replace(word, "").strip()) * mult
|
|
try:
|
|
return float(text)
|
|
except:
|
|
return 0
|
|
|
|
|
|
class Bot:
|
|
def __init__(self, driver):
|
|
self.driver = driver
|
|
self.running = False
|
|
self.clicks = 0
|
|
self.start_time = None
|
|
self.cookie = None
|
|
self.cycle = 0
|
|
self.last_cps = 0
|
|
self.buildings_bought = 0
|
|
self.upgrades_bought = 0
|
|
|
|
def load(self):
|
|
self.driver.get("https://ozh.github.io/cookieclicker/")
|
|
|
|
def ready(self):
|
|
try:
|
|
c = self.driver.find_element(By.ID, "bigCookie")
|
|
if c.size["width"] > 0:
|
|
self.cookie = c
|
|
return True
|
|
except Exception:
|
|
pass
|
|
return False
|
|
|
|
def start(self):
|
|
self.start_time = datetime.now()
|
|
self.running = True
|
|
|
|
def click(self, times):
|
|
for _ in range(times):
|
|
try:
|
|
self.cookie.click()
|
|
self.clicks += 1
|
|
except Exception:
|
|
try:
|
|
self.cookie = self.driver.find_element(By.ID, "bigCookie")
|
|
except Exception:
|
|
pass
|
|
|
|
def golden(self):
|
|
try:
|
|
shimmers = self.driver.find_elements(By.CLASS_NAME, "shimmer")
|
|
for s in shimmers:
|
|
try:
|
|
s.click()
|
|
except:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
def get_building_info(self):
|
|
try:
|
|
products = self.driver.find_elements(By.CSS_SELECTOR, ".product.unlocked")
|
|
info = []
|
|
for p in products:
|
|
try:
|
|
enabled = "enabled" in p.get_attribute("class")
|
|
price_el = p.find_element(By.CLASS_NAME, "price")
|
|
price_text = price_el.text
|
|
price = parse_number(price_text)
|
|
info.append({"element": p, "price": price, "enabled": enabled})
|
|
except:
|
|
continue
|
|
return info
|
|
except:
|
|
return []
|
|
|
|
def buy_smart(self, remaining_time):
|
|
if remaining_time < 15:
|
|
return
|
|
|
|
buildings = self.get_building_info()
|
|
current_cps = self.get_cps_number()
|
|
cookies = self.get_cookies()
|
|
|
|
for b in buildings:
|
|
if not b["enabled"]:
|
|
continue
|
|
price = b["price"]
|
|
if price <= 0:
|
|
continue
|
|
|
|
estimated_cps_gain = max(current_cps * 0.1, 1)
|
|
|
|
if current_cps > 0:
|
|
breakeven = price / estimated_cps_gain
|
|
else:
|
|
breakeven = 5
|
|
|
|
if remaining_time > 25:
|
|
if price < cookies * 0.8:
|
|
try:
|
|
b["element"].click()
|
|
self.buildings_bought += 1
|
|
except:
|
|
pass
|
|
break
|
|
elif remaining_time > 20:
|
|
if breakeven < remaining_time * 0.5:
|
|
try:
|
|
b["element"].click()
|
|
self.buildings_bought += 1
|
|
except:
|
|
pass
|
|
break
|
|
|
|
def upgrade(self):
|
|
try:
|
|
upgrades = self.driver.find_elements(
|
|
By.CSS_SELECTOR, "#upgrades .upgrade.enabled"
|
|
)
|
|
for u in upgrades[:2]:
|
|
try:
|
|
u.click()
|
|
self.upgrades_bought += 1
|
|
except:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
def buy_cheapest(self, count=1):
|
|
try:
|
|
for _ in range(count):
|
|
products = self.driver.find_elements(
|
|
By.CSS_SELECTOR, ".product.unlocked.enabled"
|
|
)
|
|
if products:
|
|
products[0].click()
|
|
self.buildings_bought += 1
|
|
except Exception:
|
|
pass
|
|
|
|
def get_cookies(self):
|
|
try:
|
|
text = self.driver.find_element(By.ID, "cookies").text.split("\n")[0]
|
|
text = text.replace("cookies", "").strip()
|
|
return parse_number(text)
|
|
except Exception:
|
|
return 0
|
|
|
|
def get_cps_number(self):
|
|
try:
|
|
text = self.driver.find_element(By.ID, "cookies").text
|
|
if "per second" in text:
|
|
cps_text = text.split("per second:")[-1].strip()
|
|
self.last_cps = parse_number(cps_text)
|
|
return self.last_cps
|
|
return self.last_cps
|
|
except Exception:
|
|
return self.last_cps
|
|
|
|
def get_cps(self):
|
|
try:
|
|
text = self.driver.find_element(By.ID, "cookies").text
|
|
if "per second" in text:
|
|
return text.split("per second:")[-1].strip()
|
|
return "0"
|
|
except Exception:
|
|
return "0"
|
|
|
|
def elapsed(self):
|
|
if self.start_time:
|
|
return (datetime.now() - self.start_time).total_seconds()
|
|
return 0
|
|
|
|
def remaining(self):
|
|
return max(0, 60 - self.elapsed())
|
|
|
|
def stop(self):
|
|
self.running = False
|
|
|
|
def close(self):
|
|
try:
|
|
self.driver.quit()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
class GUI:
|
|
def __init__(self):
|
|
self.root = tk.Tk()
|
|
self.root.title("Cookie Speedrun Bot")
|
|
self.root.geometry("500x600")
|
|
self.bot = None
|
|
self.comp = True
|
|
self.build()
|
|
|
|
def build(self):
|
|
ttk.Label(
|
|
self.root, text="COOKIE SPEEDRUN BOT", font=("Arial", 22, "bold")
|
|
).pack(pady=20)
|
|
|
|
f1 = ttk.Frame(self.root)
|
|
f1.pack(pady=10)
|
|
self.mode = tk.StringVar(value="COMP")
|
|
ttk.Radiobutton(f1, text="Practice", variable=self.mode, value="PRACTICE").pack(
|
|
side="left", padx=20
|
|
)
|
|
ttk.Radiobutton(f1, text="COMPETITION", variable=self.mode, value="COMP").pack(
|
|
side="left", padx=20
|
|
)
|
|
|
|
f2 = ttk.Frame(self.root)
|
|
f2.pack(pady=10)
|
|
self.start_btn = ttk.Button(f2, text="START", command=self.start_bot)
|
|
self.start_btn.pack(side="left", padx=10)
|
|
self.stop_btn = ttk.Button(
|
|
f2, text="STOP", command=self.stop_bot, state="disabled"
|
|
)
|
|
self.stop_btn.pack(side="left", padx=10)
|
|
|
|
self.timer = tk.StringVar(value="60")
|
|
ttk.Label(
|
|
self.root,
|
|
textvariable=self.timer,
|
|
font=("Arial", 72, "bold"),
|
|
foreground="red",
|
|
).pack(pady=20)
|
|
|
|
self.phase = tk.StringVar(value="---")
|
|
ttk.Label(
|
|
self.root,
|
|
textvariable=self.phase,
|
|
font=("Arial", 16, "bold"),
|
|
foreground="blue",
|
|
).pack()
|
|
|
|
ttk.Separator(self.root).pack(fill="x", pady=20)
|
|
|
|
self.result = tk.StringVar(value="---")
|
|
ttk.Label(self.root, text="FINAL COOKIES:", font=("Arial", 14)).pack()
|
|
ttk.Label(
|
|
self.root,
|
|
textvariable=self.result,
|
|
font=("Arial", 32, "bold"),
|
|
foreground="green",
|
|
).pack(pady=5)
|
|
|
|
self.cps_var = tk.StringVar(value="CPS: ---")
|
|
ttk.Label(self.root, textvariable=self.cps_var, font=("Arial", 14)).pack()
|
|
|
|
self.clicks_var = tk.StringVar(value="Clicks: ---")
|
|
ttk.Label(self.root, textvariable=self.clicks_var, font=("Arial", 14)).pack()
|
|
|
|
self.buildings_var = tk.StringVar(value="Buildings: --- | Upgrades: ---")
|
|
ttk.Label(self.root, textvariable=self.buildings_var, font=("Arial", 10)).pack()
|
|
|
|
self.status = tk.StringVar(value="Click START")
|
|
ttk.Label(self.root, textvariable=self.status, font=("Arial", 10)).pack(pady=20)
|
|
|
|
def start_bot(self):
|
|
self.status.set("Starting browser...")
|
|
self.root.update()
|
|
driver, browser = detect_and_create_driver()
|
|
if not driver:
|
|
messagebox.showerror("Error", "No browser!")
|
|
return
|
|
self.status.set(f"{browser} - PICK LANGUAGE!")
|
|
self.comp = self.mode.get() == "COMP"
|
|
self.bot = Bot(driver)
|
|
self.start_btn.config(state="disabled")
|
|
self.stop_btn.config(state="normal")
|
|
self.result.set("---")
|
|
self.cps_var.set("CPS: ---")
|
|
self.clicks_var.set("Clicks: ---")
|
|
self.buildings_var.set("Buildings: --- | Upgrades: ---")
|
|
self.timer.set("--")
|
|
self.phase.set("PICK LANGUAGE")
|
|
self.bot.load()
|
|
self.wait()
|
|
|
|
def wait(self):
|
|
if not self.bot:
|
|
return
|
|
if self.bot.ready():
|
|
self.bot.start()
|
|
self.status.set("GO!")
|
|
self.loop()
|
|
else:
|
|
self.root.after(50, self.wait)
|
|
|
|
def loop(self):
|
|
if not self.bot or not self.bot.running:
|
|
return
|
|
|
|
sec = self.bot.elapsed()
|
|
remaining = self.bot.remaining()
|
|
self.bot.cycle += 1
|
|
|
|
if self.bot.cycle % 10 == 0:
|
|
left = max(0, 60 - sec)
|
|
self.timer.set(f"{int(left)}" if self.comp else f"{int(sec)}")
|
|
self.cps_var.set(f"CPS: {self.bot.get_cps()}")
|
|
self.buildings_var.set(
|
|
f"Buildings: {self.bot.buildings_bought} | Upgrades: {self.bot.upgrades_bought}"
|
|
)
|
|
|
|
if self.comp and sec >= 60:
|
|
final = self.bot.get_cookies()
|
|
cps = self.bot.get_cps()
|
|
clicks = self.bot.clicks
|
|
self.result.set(f"{final:,.0f}")
|
|
self.cps_var.set(f"CPS: {cps}")
|
|
self.clicks_var.set(f"Clicks: {clicks:,}")
|
|
self.timer.set("0")
|
|
self.phase.set("DONE!")
|
|
self.status.set("Competition finished!")
|
|
self.bot.stop()
|
|
self.start_btn.config(state="normal")
|
|
self.stop_btn.config(state="disabled")
|
|
return
|
|
|
|
if remaining > 45:
|
|
self.bot.click(300)
|
|
self.bot.golden()
|
|
self.bot.upgrade()
|
|
self.bot.buy_cheapest(3)
|
|
self.phase.set("PHASE 1: RUSH BUILD")
|
|
elif remaining > 25:
|
|
self.bot.click(300)
|
|
self.bot.golden()
|
|
self.bot.upgrade()
|
|
self.bot.buy_smart(remaining)
|
|
self.phase.set("PHASE 2: SMART BUY")
|
|
# elif remaining > 15:
|
|
# self.bot.click(100)
|
|
# self.bot.golden()
|
|
# self.bot.upgrade()
|
|
# self.phase.set("PHASE 3: UPGRADES ONLY")
|
|
else:
|
|
self.bot.click(600)
|
|
self.bot.golden()
|
|
self.phase.set("PHASE 3: ACCUMULATE")
|
|
|
|
self.root.after(1, self.loop)
|
|
|
|
def stop_bot(self):
|
|
if self.bot:
|
|
self.bot.stop()
|
|
self.bot.close()
|
|
self.bot = None
|
|
self.status.set("Stopped")
|
|
self.timer.set("--")
|
|
self.start_btn.config(state="normal")
|
|
self.stop_btn.config(state="disabled")
|
|
|
|
def close(self):
|
|
if self.bot:
|
|
self.bot.close()
|
|
self.root.destroy()
|
|
|
|
def run(self):
|
|
self.root.protocol("WM_DELETE_WINDOW", self.close)
|
|
self.root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
GUI().run()
|