💾 Archived View for code.lanterne.chilliet.eu › src › TicTacToe › RequestHandler.php captured on 2023-09-28 at 15:43:37.
⬅️ Previous capture (2021-12-17)
-=-=-=-=-=-=-
<?php declare(strict_types=1); namespace MCMic\Gemini\TicTacToe; use MCMic\Gemini; class RequestHandler extends Gemini\RequestHandler { /** * @var array<string,?string> */ protected static array $renderedChar = [ 'o' => '◯', '-' => null, 'x' => '⨯', ]; public function handle(Gemini\Request $request): Gemini\Response { $stateString = substr($request->path ?? '/', 1); if ($stateString === '') { /* Redirect to new game */ return new Gemini\Response\Redirect('---------'); } if (preg_match('/^[ox-]{9}$/', $stateString) !== 1) { throw new Gemini\Exception('Invalid request "' . $stateString . '"', 59); } $output = '# ❌⭕ Tic Tac Toe ⭕❌' . "\r\n"; $output .= "\r\n"; $legalMoves = []; $move = -1; while (($move = strpos($stateString, '-', $move + 1)) !== false) { $legalMoves[] = $move; } if (count($legalMoves) === 0) { $output .= '## Game has ended' . "\r\n"; $output .= "\r\n"; } elseif ($stateString !== '---------') { $randomMove = array_splice($legalMoves, random_int(0, (count($legalMoves) - 1)), 1)[0]; } $state = array_chunk(str_split($stateString), 3); [$winner, $highlights] = $this->testWinner($state); if (($winner === null) && isset($randomMove)) { $stateString[$randomMove] = 'o'; $state = array_chunk(str_split($stateString), 3); [$winner, $highlights] = $this->testWinner($state); } $output .= $this->renderBoard($state, $highlights); $output .= "\r\n"; if ($winner === 'x') { $output .= '## You won!' . "\r\n"; $output .= "\r\n"; $output .= '=> ' . $request->buildUrl('/---------') . ' Replay' . "\r\n"; } elseif ($winner === 'o') { $output .= '## You lost!' . "\r\n"; $output .= "\r\n"; $output .= '=> ' . $request->buildUrl('/---------') . ' Replay' . "\r\n"; } elseif (count($legalMoves) === 0) { $output .= '## Draw!' . "\r\n"; $output .= "\r\n"; $output .= '=> ' . $request->buildUrl('/---------') . ' Replay' . "\r\n"; } else { foreach ($legalMoves as $i) { $newStateString = $stateString; $newStateString[$i] = 'x'; $output .= '=> ' . $request->buildUrl('/' . $newStateString) . ' ' . chunk_split($newStateString, 3, '/') . ' ' . ($i + 1) . "\r\n"; } } $output .= $this->getFooter(); return new Gemini\Response\Success($output, 'text/gemini', 'en'); } protected function getFooter(): string { $footer = "\r\n"; $footer .= '## Information' . "\r\n"; $footer .= "\r\n"; $footer .= '* A game by MCMic' . "\r\n"; $footer .= '* Made with PHP' . "\r\n"; $footer .= '=> gemini://code.lanterne.chilliet.eu Source code' . "\r\n"; $footer .= '=> https://framagit.org/MCMic/gemini-games.git Git repository' . "\r\n"; $footer .= "\r\n"; return $footer; } /** * @param array<array<string>> $state * @param array<string> $highlights */ protected function renderBoard(array $state, array $highlights): string { $output = '```' . "\r\n"; $board = new Gemini\UnicodeBoard(3, 3); for ($l = 0; $l < 3; $l++) { for ($c = 0; $c < 3; $c++) { $board->setSquare($l, $c, static::$renderedChar[$state[$l][$c]] ?? mb_chr(0x2080 + ($l * 3 + $c) + 1, 'UTF-8'), in_array("$l$c", $highlights, true)); } } $output .= $board->render(); $output .= '```' . "\r\n"; return $output; } /** * @param array<array<string>> $state */ protected function isCaseEmpty(array $state, int $pos): bool { return (array_merge(...$state)[$pos] === '-'); } /** * @param array<array<string>> $state * @return array{?string, array<string>} */ protected function testWinner(array $state): array { for ($i = 0; $i < 3; $i++) { // Line if ( ($state[$i][0] === $state[$i][1]) && ($state[$i][0] === $state[$i][2]) && ($state[$i][0] !== '-') ) { return [$state[$i][0], [$i . '0',$i . '1',$i . '2']]; } // Column if ( ($state[0][$i] === $state[1][$i]) && ($state[0][$i] === $state[2][$i]) && ($state[0][$i] !== '-') ) { return [$state[0][$i], ['0' . $i,'1' . $i,'2' . $i]]; } } // Diagonals if ( ($state[0][0] === $state[1][1]) && ($state[0][0] === $state[2][2]) && ($state[0][0] !== '-') ) { return [$state[0][0], ['00','11','22']]; } if ( ($state[0][2] === $state[1][1]) && ($state[0][2] === $state[2][0]) && ($state[0][2] !== '-') ) { return [$state[0][2], ['02','11','20']]; } return [null, []]; } }