Preskočiť na obsah

Tic Tac Toe s Laravelom: Pretože niekedy jednoducho potrebujete trochu zábavy

Tic Tac Toe s Laravelom: Pretože niekedy jednoducho potrebujete trochu zábavy

Buďme realisti: ako softvéroví inžinieri trávime veľa času programovaním riešení zložitých problémov. Ale občas potrebujete oddych od bežnej rutiny. Niekedy chcete jednoducho vytvoriť niečo zábavné a nesúvisiace s prácou – niečo, čo vám pripomenie, prečo ste sa do programovania vôbec zamilovali. Pre mňa bolo tým „niečím“ klasická hra Tic Tac Toe, vytvorená v PHP s Laravelom. A áno, je to tak nerdovské a zábavné, ako to znie!

Teším sa, že vám môžem predstaviť moju implementáciu, podeliť sa o to, čo som sa naučil, ako som ju vytvoril, a dokonca aj o niektoré úryvky kódu, ktoré si môžete vyskúšať sami.

Prečo Tic Tac Toe?

Prečo nie? Je to jednoduchá hra, ktorú všetci poznáme, ale je tiež klamlivo zložitá, keď sa ponoríte do detailov hernej logiky, stratégií umelej inteligencie a vytvárania elegantného, responzívneho rozhrania. Navyše je to skvelá zámienka na to, aby ste si pohrali s funkciami Laravel spôsobmi, ktoré by ste vo svojich každodenných pracovných projektoch možno nepoužívali.

Celkový obraz: Ako to všetko do seba zapadá

Tu je prehľad toho, čo som vytvoril:

  1. Herná doska: Trieda, ktorá spravuje stav dosky.
  2. Herná logika: Spravuje priebeh hry, podmienky výhry a ťahy.
  3. Hráč AI: Áno, počítač vás môže poraziť v Tic Tac Toe.
  4. Laravel Livewire: Robí hru interaktívnou bez otravného pre načítavania stránky.
  5. Enums a výnimky: Udržujú veci organizované a prehľadné (pretože aj zábavné projekty potrebujú štruktúru).

Nastavenie dosky

Najskôr som potreboval spôsob, ako znázorniť hernú dosku. Tu prichádza na rad trieda Board. Sleduje stav hry a kontroluje víťazov, remízy a platné ťahy.

Tu je úryvok z Board.php:

class Board
{
public array $tiles = [[null, null, null], [null, null, null], [null, null, null]];

public static function init(): self
{
return new Board;
}

public function markTile(int $row, int $column, PlayerEnum $currentPlayer): false|null|PlayerEnum
{
if ($this->tiles[$row][$column] === null) {
$this->tiles[$row][$column] = $currentPlayer;
}

return $this->checkWin($currentPlayer);
}

public function checkWin(PlayerEnum $player): false|null|PlayerEnum
{
// Skontroluj víťazstvo
}
}

Táto trieda je jednoduchá, ale výkonná. Udržuje veci jednoduché, čo je kľúčové pre niečo tak klasické ako Tic Tac Toe.

Vytvorenie hrateľnej hry

Trieda TicTacToeGame oživuje hru. Spracováva ťahy, kontroluje podmienky ukončenia hry a zabezpečuje plynulý priebeh hry.

Tu je malá ukážka:

class TicTacToeGame
{
public Board $board;

public int $scorePlayerOne = 0;

public int $scorePlayerTwo = 0;

public int $moves = 0;

public PlayerEnum $currentPlayer = PlayerEnum::CROSS;

public PlayerTypeEnum $opponentType = PlayerTypeEnum::HUMAN;


public function __construct(?PlayerTypeEnum $opponentType)
{
$this->board = new Board;
$this->opponentType = $opponentType ?? PlayerTypeEnum::HUMAN;
}

public function clearScore(): void
{
$this->scorePlayerOne = 0;
$this->scorePlayerTwo = 0;
}

public function resetGame(): void
{
$this->board = new Board;
$this->moves = 0;
$this->currentPlayer = PlayerEnum::CROSS;
}

/**
* @throws TicTacToeException
*/
public function setOpponentType(PlayerTypeEnum $opponentType): void
{
if ($this->moves > 0) {
throw new TicTacToeException(‚Cannot change opponent type after the game has started‘);
}

$this->opponentType = $opponentType;
}

public function markTile(int $row, int $column): false|null|PlayerEnum
{
$result = $this->board->markTile($row, $column, $this->currentPlayer);

if ($result === null) {
$this->moves++;
$this->changePlayer();
}

return $result;
}

public function changePlayer(): void
{
$this->currentPlayer = $this->moves % 2 === 0 ? PlayerEnum::CROSS : PlayerEnum::CIRCLE;
}

public function incrementScore(PlayerEnum $player): void
{
if ($player === PlayerEnum::CROSS) {
$this->scorePlayerOne++;
} elseif ($player === PlayerEnum::CIRCLE) {
$this->scorePlayerTwo++;
}
}

public function isAiOpponent(): bool
{
return $this->opponentType === PlayerTypeEnum::AI;
}

verejná funkcia getBoard(): pole
{
vráť $this->board->tiles ?? [];
}
}

Táto trieda riadi, kto bude hrať ako ďalší, a kontroluje, či niekto vyhral. Celkom fajn, čo? A áno, zahrnul som aj vlastné výnimky na riešenie prípadov, ako je pokus o ťah v už obsadenej bunke. Ide o to, aby všetko fungovalo spoľahlivo, aj keď sa len zabávate.

Umelá inteligencia: Pretože poraziť hlúpy počítač nie je zábava

Chcel som, aby umelá inteligencia bola náročná a nebolo ľahké ju prekabátiť (alebo nemožné?), tak som implementoval jedinú, výkonnú úroveň obtiažnosti pomocou algoritmu Minimax. Týmto spôsobom umelá inteligencia vždy hrá optimálne, čo robí hru skutočným testom vašej stratégie. Tu je pohľad na logiku umelej inteligencie:

class AiPlayer
{
private PlayerEnum $aiPlayer = PlayerEnum::CIRCLE;
private PlayerEnum $humanPlayer = PlayerEnum::CROSS;

private function findBestMove(array $board): ?array
{
// Nájdi najlepší ťah
}

private function minimax(array $board, int $depth, bool $isMaximizing)
{
$winner = $this->checkWinner($board);

if ($winner === $this->aiPlayer) {
return 10 - $depth;
} elseif ($winner === $this->humanPlayer) {
return $depth - 10;
} elseif ($this->isDraw($board)) {
return 0;
}

if ($isMaximizing) {
$bestScore = -INF;
for ($row = 0; $row < 3; $row++) {
for ($col = 0; $col < 3; $col++) {
if ($board[$row][$col] === null) {
$board[$row][$col] = $this->aiPlayer;
$score = $this->minimax($board, $depth + 1, false);
$board[$row][$col] = null;
$bestScore = max($bestScore, $score);
}
}
}
} else {
$bestScore = INF;
for ($row = 0; $row < 3; $row++) {
for ($col = 0; $col < 3; $col++) {
if ($board[$row][$col] === null) {
$board[$row][$col] = $this->humanPlayer;
$score = $this->minimax($board, $depth + 1, true);
$board[$row][$col] = null;
$bestScore = min($bestScore, $score);
}
}
}
}
return $bestScore;
}

private function checkWinner(array $board)
{
// Skontroluj víťaza
}

private function isDraw(array $board): bool
{
// Skontroluj remízu
}
}

Ponoriť sa do teórie hier a zistiť, ako dosiahnuť optimálnu hru umelej inteligencie, bola zábavná a zároveň náročná skúsenosť. Vytrhlo ma to z mojej komfortnej zóny a prinútilo ma premýšľať o stratégii úplne novým spôsobom. Ale výsledkom je umelá inteligencia, ktorú je ťažké poraziť a ktorá vás núti premýšľať niekoľko ťahov dopredu!

Interaktivita s Livewire

Laravel Livewire uľahčuje vytváranie interaktívnych komponentov. V TicTacToeComponent.php používam Livewire na spracovanie aktualizácií v reálnom čase, takže zakaždým, keď urobíte ťah, hracia doska sa okamžite aktualizuje bez nutnosti opätovného načítania stránky.

Tu je zjednodušená verzia:

use Livewire\Component;

class TicTacToeComponent extends Component {
public $game;

public function mount() {
$this->game = new TicTacToeGame();
}

public function makeMove($row, $col) {
$this->game->makeMove($row, $col);
}

public function render() {
return view(‚tic-tac-toe-component‘, [‚board‘ => $this->game->getBoard()]);
}
}

Šablóna Blade tic-tac-toe-component.blade.php oživuje hrací plán. Vďaka Livewire je responzívna a plynulá.

Zhrnutie: Čo som sa naučil

Tento projekt mi pripomenul, prečo milujem programovanie. Aj niečo tak jednoduché ako Tic Tac Toe môže byť priestorom pre učenie a kreativitu. Niekoľko kľúčových poznatkov:

Niekoľko poznatkov:

  • Udržujú kód čistý a udržiavateľný.
  • Livewire je revolučný nástroj (slovná hračka) pre interaktivitu v reálnom čase.
  • Napísanie inteligentného AI súpera bola zábavná výzva, ktorá ma veľa naučila o algoritmoch.

Ak sa niekedy cítite vyčerpaní z náročných pracovných projektov, vrelo odporúčam naprogramovať niečo zábavné. Možno to znovu rozníti vašu lásku k programovaniu!

Ak chcete vidieť hru v akcii alebo sa pokúsiť poraziť AI sami, pozrite si ju tu.

Čo si o tom myslíte? Vytvorili ste niekedy hru alebo zábavný projekt len tak, aby ste si oddýchli? Dajte mi vedieť!