Slide 1

Slide 1 text

/ 58 symfony/panther Wordle 2 022 / 08 / 2 7 #phpcon_okinawa @ttskch 1

Slide 2

Slide 2 text

Kannade PHPer 10 Symfony頻 https://zenn.dev/topics/symfony @ttskch

Slide 3

Slide 3 text

/ 58 symfony/panther Wordle symfony/panther Wordle 💭 3

Slide 4

Slide 4 text

/ 58 symfony/panther Wordle symfony/panther Wordle 💭 4 😇

Slide 5

Slide 5 text

/ 58 https://github.com/ttskch/wordler 🐙 GitHub 5

Slide 6

Slide 6 text

/ 58 symfony/panther Wordle symfony/panther Wordle Wordle 6 🔖

Slide 7

Slide 7 text

/ 58 symfony/panther Wordle symfony/panther Wordle Wordle 7 🔖

Slide 8

Slide 8 text

/ 58 symfony/panther 8 https://github.com/symfony/panther

Slide 9

Slide 9 text

/ 58 PHP Web e 2 e Symfony WebDriver symfony/panther 9

Slide 10

Slide 10 text

/ 58 10 symfony/panther $ composer require symfony/panther

Slide 11

Slide 11 text

/ 58 symfony/panther 
 bdi browser-driver-installer 11 $ composer require dbrekelmans/bdi $ vendor/bin/bdi detect drivers

Slide 12

Slide 12 text

/ 58 request('GET', 'https://phpcon.okinawa.jp'); $client->waitForVisibility('#about img'); $client->takeScreenshot(__DIR__.'/screenshot.png'); 12 👨💻

Slide 13

Slide 13 text

/ 58 👨💻 13 screenshot.png 
 🎉

Slide 14

Slide 14 text

/ 58 14

Slide 15

Slide 15 text

/ 58 symfony/panther Wordle symfony/panther Wordle Wordle 15 🔖

Slide 16

Slide 16 text

/ 58 2022 頻 Web 
 Wordle 16

Slide 17

Slide 17 text

/ 58 5 6 1 ⬜ 🟨 🟩 ⾒ 🟩 🟩 🟩 🟩 🟩 Wordle 17

Slide 18

Slide 18 text

/ 58 Wordle 18 https://www.nytimes.com/games/wordle/index.html

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

symfony/panther

Slide 22

Slide 22 text

/ 58 symfony/panther Wordle symfony/panther Wordle Wordle 22 🔖

Slide 23

Slide 23 text

/ 58 23 🖱

Slide 24

Slide 24 text

/ 58 24 Wordle

Slide 25

Slide 25 text

/ 58 25 👨💻symfony/panther

Slide 26

Slide 26 text

/ 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.

Slide 27

Slide 27 text

/ 58 1. 27

Slide 28

Slide 28 text

/ 58 // ԿΒ͔ͷํ๏ͰଧͪࠐΉ୯ޠΛܾΊΔ $candidate = 'world'; // ୯ޠΛଧͪࠐΜͰEnterΩʔΛԡ͢ $client->getCrawler()->sendKeys($candidate) ->sendKeys(WebDriverKeys::ENTER); // ϑΟʔυόοΫͷද͕ࣔ׬ྃ͢Δ·Ͱ଴ͬͯεΫϦʔϯγϣοτΛࡱΔ sleep(3); $client->takeScreenshot(__DIR__.'/screenshot.png'); 28 2.

Slide 29

Slide 29 text

/ 58 2. 29

Slide 30

Slide 30 text

/ 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

Slide 31

Slide 31 text

/ 58 3. 6 31

Slide 32

Slide 32 text

/ 58 // ShareϘλϯ͕දࣔ͞ΕΔͷΛ଴ͬͯΫϦοΫ sleep(2); $client->getMouse()->clickTo('#share-button'); // ΫϦοϓϘʔυͷ಺༰Λద౰ͳWebϖʔδͷʹషΓ෇͚ͯऔಘ $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

Slide 33

Slide 33 text

/ 58 4 . Share 33

Slide 34

Slide 34 text

/ 58 34 🎉🎉🎉

Slide 35

Slide 35 text

/ 58 35 💪

Slide 36

Slide 36 text

/ 58 symfony/panther Wordle symfony/panther Wordle Wordle 36 🔖

Slide 37

Slide 37 text

/ 58 5 
 🧭 37 6

Slide 38

Slide 38 text

/ 58 
 頻 🧭 38 頻

Slide 39

Slide 39 text

/ 58 🖼 頻 39

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

/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͔Β͢΂ͯ࡟আ } }

Slide 42

Slide 42 text

/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); }

Slide 43

Slide 43 text

/ 58 https://github.com/kujirahand/EJDict 🤲 5 43

Slide 44

Slide 44 text

https://en.wikipedia.org/wiki/Letter_frequency

Slide 45

Slide 45 text

/xxx class Guesser { private array $characterScores = [ 'a' => 7.8, 'b' => 2, 'c' => 4, 'd' => 3.8, 'e' => 11, : : ]; }

Slide 46

Slide 46 text

/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']; }

Slide 47

Slide 47 text

/ 58 ⬜ 🟨 🟨 🟩 47

Slide 48

Slide 48 text

/ 58 ⬜ 🟨 🟨 🟩 48 skill sisal 🟩 🟨 ⬜ ⬜ 🟩 Wordle 3 ⬜ s skill 👨💻 s s i a l s

Slide 49

Slide 49 text

/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); }

Slide 50

Slide 50 text

/ 58 50

Slide 51

Slide 51 text

/ 58 51 🎉🎉🎉🎉🎉

Slide 52

Slide 52 text

/ 58 https://github.com/ttskch/wordler 🐙 GitHub 52

Slide 53

Slide 53 text

/ 58 53 🏃

Slide 54

Slide 54 text

/ 58 54

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

/ 58 56

Slide 57

Slide 57 text

/ 58 57 Happy Coding ❤

Slide 58

Slide 58 text

/ 58 58 Thanks! @ttskch