Python实现2048自动操作脚本时,如何在后台循环检测ESC键以随时终止程序
Hey there! Cool that you're challenging yourself with image recognition for 2048—props for skipping the easier web-scraping approach and going the visual route. Let's fix your problem of running the ESC key check in the background without blocking your auto-play logic.
The Core Issue
Your current ESC detection loop uses keyboard.read_key(), which is a blocking call—it stops execution until a key is pressed. That's why it can't run alongside your auto-move code. The fix here is to use threads to run the ESC listener in the background while your main thread handles the game operations.
Step-by-Step Fix
1. Import Required Modules
First, add the threading module to your imports (alongside your existing ones):
import pyautogui from PIL import ImageGrab, ImageChops import keyboard import threading
2. Use a Shared Termination Flag
Define a global endofgame flag so both the main thread and background thread can access and modify it:
endofgame = False
3. Create the Background ESC Listener
Write a function that runs in a separate thread to listen for the ESC key. It'll run continuously until ESC is pressed, then flip the termination flag:
def listen_for_esc(): global endofgame while not endofgame: if keyboard.read_key() == "esc": endofgame = True print("ESC pressed—terminating program...") break
4. Revised Auto-Play Logic
Start the background thread first, then adjust your main game loop to check the endofgame flag regularly. I've also cleaned up your move logic to avoid messy nested loops:
Here's the full revised code:
import pyautogui from PIL import ImageGrab, ImageChops import keyboard import threading endofgame = False def listen_for_esc(): global endofgame while not endofgame: if keyboard.read_key() == "esc": endofgame = True print("ESC pressed—terminating program...") break # Start ESC listener in a background daemon thread esc_thread = threading.Thread(target=listen_for_esc, daemon=True) esc_thread.start() # Main auto-play loop while not endofgame: # Check for game over using your pixel detection endgameim = ImageGrab.grab() pxcolour = endgameim.getpixel((818, 453)) print(f"Game over pixel colour: {pxcolour}") if pxcolour == (123, 110, 101): endofgame = True print("Game detected as over!") break # Try directions until a valid move is found im = ImageGrab.grab() moved = False # Try left first pyautogui.press("left") im2 = ImageGrab.grab() if ImageChops.difference(im2, im).getbbox() is not None: moved = True else: # Try up pyautogui.press("up") im2 = ImageGrab.grab() if ImageChops.difference(im2, im).getbbox() is not None: moved = True else: # Try down pyautogui.press("down") im2 = ImageGrab.grab() if ImageChops.difference(im2, im).getbbox() is not None: moved = True else: # Try right pyautogui.press("right") moved = True # Small delay to avoid spamming inputs (adjust as needed) pyautogui.sleep(0.1) print("Program terminated.")
Key Changes Explained
- Daemon Thread: The
daemon=Trueflag ensures the background listener dies automatically when the main thread ends (no leftover threads hanging around). - Simplified Move Logic: I removed the nested
while Trueloops to make the code cleaner and easier to debug. - Flag Checks: The main loop checks
endofgameon every iteration, so it'll exit quickly as soon as ESC is pressed or the game ends.
Quick Notes
- Make sure all dependencies are installed:
pip install pyautogui pillow keyboard - The 0.1-second delay prevents overwhelming the game with rapid inputs—tweak the value if the game doesn't register moves properly.
内容的提问来源于stack exchange,提问作者TotallyNotTeo

