initial commit

This commit is contained in:
Joseph Montanaro 2022-01-20 09:26:40 -08:00
commit fcdfd667bd
4 changed files with 13145 additions and 0 deletions

17
.drone.yml Normal file
View File

@ -0,0 +1,17 @@
kind: pipeline
type: docker
name: solve
trigger:
event: [cron]
cron: [daily-solve]
steps:
- name: run
image: joyzoursky/python-chromedriver:3.9-selenium
environment:
SLACK_HOOK_URL:
from_secret: slack_hook_url
commands:
- pip install requests
- python bot.py

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
**/__pycache__*

155
bot.py Normal file
View File

@ -0,0 +1,155 @@
import collections
import datetime
import os
import pathlib
import time
import requests
from selenium import webdriver
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
EMOJI = {
'absent': ':black_large_square:',
'present': ':large_yellow_square:',
'correct': ':large_green_square:',
}
def filter_word(word, conditions):
# first, filter on correct
for char, idx in conditions['correct']:
if word[idx] != char:
return False
# we only want to consider characters in positions that haven't been spoken for already
positions = [0, 1, 2, 3, 4]
for _, i in conditions['correct']:
positions.remove(i)
# now filter on present-but-not-correct
if conditions['present']:
required_chars = set(c[0] for c in conditions['present'])
actual_chars = set(word[p] for p in positions)
overlap = required_chars & actual_chars
if len(overlap) == 0:
return False
to_remove = []
for cond in conditions['present']:
for p in positions:
char = word[p]
if cond[0] == char:
if cond[1] == p:
return False # this might break everything?
else:
to_remove.append(p)
# remove the characters that met our criteria above
for p in to_remove:
positions.remove(p)
# filter on absence
for p in positions:
char = word[p]
if char in conditions['absent']:
return False
return True
class Solver:
def __init__(self):
print('Launching web browser')
self.driver = self.setup_driver()
print('Navigating to page')
self.driver.get('https://www.powerlanguage.co.uk/wordle/')
time.sleep(2)
self.body = self.driver.find_element(By.TAG_NAME, 'body')
self.body.click()
self.words = pathlib.Path('dictionary.txt').read_text().splitlines()
def setup_driver(self):
options = ChromeOptions()
options.headless = True
options.add_argument('--no-sandbox')
options.add_argument('--remote-debugging-port=9222')
return webdriver.Chrome(options=options)
def input_word(self, word):
for char in word:
self.body.send_keys(char)
self.body.send_keys(Keys.ENTER)
def get_rows(self):
return self.driver.execute_script(
"""return Array.from(
document
.querySelector('game-app')
.shadowRoot
.querySelectorAll('game-row')
).map(e => e.shadowRoot.querySelectorAll('game-tile'))"""
)
def read_row(self, n):
result = {
'present': [],
'correct': [],
'absent': [],
}
rows = self.get_rows()
for i, tile in enumerate(rows[n]):
char = tile.get_attribute('letter')
status = tile.get_attribute('evaluation')
result[status].append((char, i))
result['absent'] = set(c[0] for c in result['absent'])
return result
def get_history(self, row_count):
rows = self.get_rows()
history = []
for i in range(row_count):
row_hist = []
for tile in rows[i]:
status = tile.get_attribute('evaluation')
row_hist.append(EMOJI[status])
history.append(row_hist)
return history
def solve(self):
print('Attempting to solve')
for i in range(6):
print('Trying word:', self.words[0])
self.input_word(self.words[0])
time.sleep(2)
conditions = self.read_row(i)
if len(conditions['correct']) == 5:
return {
'word': self.words[0],
'history': self.get_history(i + 1),
'iterations': i + 1
}
self.words = [w for w in self.words if filter_word(w, conditions)]
return None
if __name__ == '__main__':
solver = Solver()
try:
result = solver.solve()
finally:
solver.driver.close()
if result:
print(f'Success!')
wordle_num = (datetime.date.today() - datetime.date(2022, 1, 17)).days + 212
lines = [f"Wordle {wordle_num}: {result['iterations']}/6"]
lines = lines + [''.join(row) for row in result['history']]
requests.post(os.environ['SLACK_HOOK_URL'], json={'text': '\n'.join(lines)})
else:
print('Failed to find the word.')

12972
dictionary.txt Normal file

File diff suppressed because it is too large Load Diff