1
\$\begingroup\$

The project outline:

2048 is a simple game where you combine tiles by sliding them up, down, left, or right with the arrow keys. You can actually get a fairly high score by repeatedly sliding in an up, right, down, and left pattern over and over again. Write a program that will open the game at https://gabrielecirulli.github.io/2048/ and keep sending up, right, down, and left keystrokes to automatically play the game.

My solution:

from selenium import webdriver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

def main():
    service = Service(r"C:\Program Files\WebDrivers\geckdriver\geckodriver.exe")
    options = Options()
    options.binary_location = r"C:\Program Files\Mozilla Firefox\firefox.exe"
    driver = webdriver.Firefox(service=service, options=options)
    driver.get("https://gabrielecirulli.github.io/2048/")
    try:
        cookie_elem = driver.find_element(By. XPATH, '//*[@id="ez-accept-all"]')
        html_elem = driver.find_element(By.TAG_NAME, "html")
    except:
        print("Was not able to find an element with that name.")
    cookie_elem.click()
    time.sleep(1)
    while True:
        html_elem.send_keys(Keys.UP)
        time.sleep(0.1)
        html_elem.send_keys(Keys.RIGHT)
        time.sleep(0.1)
        html_elem.send_keys(Keys.DOWN)
        time.sleep(0.1)
        html_elem.send_keys(Keys.LEFT)
        time.sleep(0.1)
    
if __name__ == '__main__':
    main()
\$\endgroup\$
4
  • \$\begingroup\$ "I couldn't figure out how to make the program end on the game over screen" -- this isn't the place to ask about how to implement new features, it's only for reviewing working code. \$\endgroup\$
    – ggorlen
    Commented Nov 3, 2022 at 14:11
  • 2
    \$\begingroup\$ @ggorlen It's not a question. It's a statement expressing thoughts that informed my choices in omitting features, not asking to how to implement one. I thought it was useful for a reviewer as well as because many newbies are recommended the book and look for answers here. I'll remove it if it's still inappropriate, but the code works as specified by the outline. \$\endgroup\$
    – Javana
    Commented Nov 3, 2022 at 16:41
  • \$\begingroup\$ thanks for clarifying. Often, people ask questions without actually using a question mark, so the statement can be interpreted to imply "how can I implement this?" in many contexts, usually Stack Overflow, but also here as well. \$\endgroup\$
    – ggorlen
    Commented Nov 3, 2022 at 23:58
  • 1
    \$\begingroup\$ time.sleep is generally frowned on in selenium. Have a look at using explicit waits (see the selenium docs) \$\endgroup\$
    – 2e0byo
    Commented Nov 4, 2022 at 21:52

1 Answer 1

2
\$\begingroup\$

This code looks straight-forward and simple enough to me.

The 1 change request I'd make is around the bare except. With exception handling you should always try to be as specific as possible so ideally you'd have something like:

from selenium.common.exceptions import NoSuchElementException

...
try:
    driver.find_element(...)
except NoSuchElementException as e:
    print(e)
...

or at the very least use except Exception: instead of except because bare excepts are almost always not what you want because you'll ignore all exceptions even things like KeyboardInterrupt which means you can't stop the script from running with Ctrl+C.

Also even with just except Exception, you should really try to capture exception information instead of swallowing it so here's a nasty one - you'll catch NameErrors so in a fresh file try these out:

try:
    some_var_that_doesnt_exist_at_all
    this_should_totally_not_work_and_break
except:
    print("gonna ignore whatever I just caught")

# vs 
some_var_that_doesnt_exist  # you get an obvious NameError & stacktrace

# vs
try:
    some_var_that_doesnt_exist_at_all
except Exception as e:
    print(e)  # prints 'name some_var_that_doesnt_exist_at_all is not defined'

print will print to stdout and typically you want errors going out into stderr. In a production-ready app, you should leverage logging in favor of print and you can easily just logging.exception("a message") to capture stack traces and error information within an except block much better than just print(e).

Finally a suggestion (or what could be considered an extra feature).

Swappable configuration

1 thing that sticks out immediately to me is how this code is tightly coupled to a few things that would be nice if they were configurable at runtime dynamically and/or statically at startup. Specifically the driver to use (i.e. FireFox vs Chrome), the path to the driver binary, and the path to your browser executable. This would let you change these variables without having to go and edit code each time. Also you wouldn't have to hardcode the path to your binary which means if someone else tried to run your script on their machine, the code wouldn't just break. You could do this in a number of ways, but here are some:

Dynamic configuration at runtime

aka can be changed after script startup

Use something like sys.argv or argparse or typer to read input from a user.

Static configuration at startup

aka can configure without code changes before running the script

Read from a config file like json or use a .env file which is a bit more conventional. python-dotenv makes using a .env file super simple so I'd recommend that.

\$\endgroup\$
1
  • \$\begingroup\$ Personally I don't think the link you provided for exception handling to be too helpful. Otherwise nice answer. The article doesn't really explain the exception hierarchy. And so doesn't explain what the difference between BaseException and Exception are. And wouldn't leave the user with the tools to know which exceptions to use. For example LookupError is one I use fairly often but, AFAIK, is never raised. \$\endgroup\$
    – Peilonrayz
    Commented Nov 8, 2022 at 1:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.