Upgrade to Pro — share decks privately, control downloads, hide ads and more …

symfony/pantherでWordleを解いてみた

 symfony/pantherでWordleを解いてみた

PHPカンファレンス沖縄2022 の発表資料です。
https://techplay.jp/event/866236
https://phpcon-okinawa.connpass.com/event/257992/

Takashi Kanemoto

August 27, 2022
Tweet

More Decks by Takashi Kanemoto

Other Decks in Programming

Transcript

  1. / 58 symfony/panther Wordle 2 022 / 08 / 2

    7 #phpcon_okinawa @ttskch 1
  2. / 58 symfony/panther 
 bdi browser-driver-installer 11 $ composer require

    dbrekelmans/bdi $ vendor/bin/bdi detect drivers
  3. / 58 <?php use Symfony\Component\Panther\Client; require_once __DIR__.'/vendor/autoload.php'; $client = Client::createFirefoxClient();

    $client->request('GET', 'https://phpcon.okinawa.jp'); $client->waitForVisibility('#about img'); $client->takeScreenshot(__DIR__.'/screenshot.png'); 12 👨💻
  4. / 58 5 6 1 ⬜ 🟨 🟩 ⾒ 🟩

    🟩 🟩 🟩 🟩 Wordle 17
  5. / 58 $client = Client::createFirefoxClient(); $client->request('GET', 'https://www.nytimes.com/games/wordle/index.html'); // ࠷ॳʹ։͍ͨϙοϓΞοϓΛด͡Δ $client->getMouse()

    ->clickTo('[class*="closeIcon"]'); // ϙοϓΞοϓ͕ด͡ΔͷΛ଴ͬͯεΫϦʔϯγϣοτΛࡱΔ sleep(1); $client->takeScreenshot(__DIR__.'/screenshot.png'); 26 1.
  6. / 58 // ԿΒ͔ͷํ๏ͰଧͪࠐΉ୯ޠΛܾΊΔ $candidate = 'world'; // ୯ޠΛଧͪࠐΜͰEnterΩʔΛԡ͢ $client->getCrawler()->sendKeys($candidate)

    ->sendKeys(WebDriverKeys::ENTER); // ϑΟʔυόοΫͷද͕ࣔ׬ྃ͢Δ·Ͱ଴ͬͯεΫϦʔϯγϣοτΛࡱΔ sleep(3); $client->takeScreenshot(__DIR__.'/screenshot.png'); 28 2.
  7. / 58 for ($i = 0; $i < 6; $i++)

    { $candidate = 'world'; $client->getCrawler() ->sendKeys($candidate) ->sendKeys(WebDriverKeys::ENTER); sleep(3); } $client->takeScreenshot(__DIR__.'/screenshot.png'); 30 3. 6
  8. / 58 // ShareϘλϯ͕දࣔ͞ΕΔͷΛ଴ͬͯΫϦοΫ sleep(2); $client->getMouse()->clickTo('#share-button'); // ΫϦοϓϘʔυͷ಺༰Λద౰ͳWebϖʔδͷ<textarea>ʹషΓ෇͚ͯऔಘ $client->request(‘GET', ‘https://getbootstrap.com/docs/5.1/forms/form-control/');

    $client->getMouse()->clickTo('textarea'); $client->getKeyboard()->pressKey(WebDriverKeys::COMMAND)->sendKeys('v'); $result = $client->getCrawler()->filter('textarea')->attr('value'); echo "{$result}\n"; 32 4 . Share
  9. /xxx class Guesser { private array $characterScores = [ 'a'

    => {είΞ}, 'b' => {είΞ}, ‘c' => {είΞ}, : : ]; public function guess(CandidateProvider $candidateProvider): string { // $candidateProvider͔Β࢒͍ͬͯΔީิ୯ޠΛड͚औΓ // ͢΂ͯͷ୯ޠͷείΞ߹ܭ஋Λܭࢉ͠ // είΞ߹ܭ஋͕࠷΋େ͖͍୯ޠΛฦ͢ // ͨͩ͠ɺείΞ߹ܭ஋Λࢉग़͢Δࡍɺ // ॏෳ͍ͯ͠Δจࣈʹ͍ͭͯ͸είΞΛՃࢉ͠ͳ͍ } }
  10. /xxx class CandidateProvider { public function __construct(private array $candidates) {

    } public function getCandidates(): array { return $this->candidates; } public function applyFeedback(string $word, array $states): void { // ࢼߦͨ͠୯ޠͱͦΕʹର͢ΔϑΟʔυόοΫ಺༰Λड͚औΓ // ਖ਼ղͰͳ͍ͱ൑ผͰ͖ͨ୯ޠΛ$this->candidates͔Β͢΂ͯ࡟আ } }
  11. /xxx // app.php for ($i = 0; $i < 6;

    $i++) { $candidate = $guesser->guess(); // ུ // ֤จࣈͷཁૉͷdata-stateʹϑΟʔυόοΫ಺༰͕͋ΔͷͰͦΕΛऔಘ // ⬜ "absent", 🟨 "present", 🟩 "correct" $states = []; for ($j = 0; $j < 5; $j++) { $states[] = $client->getCrawler() ->filter('CSSηϨΫλʢུʣ')->attr('data-state'); } $candidateProvider->applyFeedback($candidate, $sates); }
  12. /xxx class Guesser { private array $characterScores = [ 'a'

    => 7.8, 'b' => 2, 'c' => 4, 'd' => 3.8, 'e' => 11, : : ]; }
  13. /xxx public function guess(CandidateProvider $candidateProvider): string { $candidates = $candidateProvider->getCandidates();

    $primary = ['word' => null, 'score' => 0]; foreach ($candidates as $candidate) { $score = 0; $usedCharacters = []; foreach (str_split($candidate) as $ch) { if (!in_array($ch, $usedCharacters, true)) { $score += $this->characterScores[$ch]; $usedCharacters[] = $ch; } } if ($score > $primary['score']) { $primary['word'] = $candidate; $primary['score'] = $score; } } return $primary['word']; }
  14. / 58 ⬜ 🟨 🟨 🟩 48 skill sisal 🟩

    🟨 ⬜ ⬜ 🟩 Wordle 3 ⬜ s skill 👨💻 s s i a l s
  15. /xxx public function applyFeedback(string $word, array $states): void { $characters

    = str_split($word); foreach ($this->candidates as $i => $candidate) { $candidateCharacters = str_split($candidate); for ($j = 0; $j < 5; $j++) { if ($states[$j] === 'absent') { if (in_array($characters[$j], $candidateCharacters, true)) { for ($k = 0; $k < 5; $k++) { if (in_array($states[$k], ['correct', 'present']) && $characters[$k] === $characters[$j]) { continue 2; } } unset($this->candidates[$i]); break; } } elseif ($states[$j] === 'present') { if (!in_array($characters[$j], $candidateCharacters) || $characters[$j] === $candidateCharacters[$j]) { unset($this->candidates[$i]); break; } } else { // 'correct' if ($characters[$j] !== $candidateCharacters[$j]) { unset($this->candidates[$i]); break; } } } } $this->candidates = array_values($this->candidates); }