Расширение своего опыта всегда положительно влияет на решение обычных задач. PHP изначально разрабатывался как язык для создания веб-сайтов, но существует и официальная поддержка режима CLI. По моему скромному опыту, могу сказать, что именно работа с PHP в командной строке дает возможность по-настоящему почувствовать язык. В качестве иллюстрации я написал cli-приложение, которое реализует некогда любимую игру всей детворы Крестики-Нолики.
Подготовка
Понятно, что на вашем компьютере должен быть установлен PHP. Отговорки вроде: Ой, да я использую OpenServer, не принимаются. Находите в меню Терминал, щелкаете и работаете.
Создайте, где вам удобно директорию xo. Пусть полный путь к этой директории будет, например, /home/vasya/php-apps/xo. Далее в консоли(терминале)
# переходите в эту директорию cd /home/vasya/php-apps/xo # проверяете версию PHP php -v
Я разрабатывал на PHP 7.4, думаю, на PHP 7.0 и новее все будет работать.
Без Hello, World! лучше даже и не начинать, поэтому создайте /home/vasya/php-apps/xo/hw.php со следующим содержимым
<?php echo "Hello, World!";
Далее в консоли(терминале)
php -f hw.php
Если вы видите вывод Hello, World!, то двигаемся дальше.
План приложения
Создаете /home/vasya/php-apps/xo/xo.php
<?php class CrossZero { private $size; // размер таблицы private $table; // сама таблица public const X = "x"; // крестик public const O = "o"; // нолик public function __construct(int $size = 3) { $this->size = $size; $this->initTable(); } private function initTable(): void { for ($i = 0; $i < $this->size; $i++) { for ($j = 0; $j < $this->size; $j++) { } } } // далее сюда следует поместить методы для работы с таблицей } class Helper { // здесь будет метод, реализующий ввод с клавиатуры } // последовательность работы приложения $ts = 3; // table size - размер таблицы $xo = new CrossZero($ts); while (true) { // ход человека, координаты х вводятся с клавиатуры // человек выиграл? если да, то цикл прерывается // таблица заполнена полностью? если да, то цикл прерывается // ход компьютера, координаты о генерируются случайным образом // компьютер выиграл? если да, то цикл прерывается // таблица заполнена полностью? если да, то цикл прерывается }
Четыре замечания:
— все помещено в один файл;
— константы можно использовать как внутри класса, например так, self::EMPTY, так и вне класса без создания экземпляра класса, например так, CrossZero::EMPTY;
— то, что не используется вне класса (свойства, методы) имеет область видимости private;
— таблица $table имеет область видимости private, это личное предпочтение, но и с точки зрения ООП, это правильнее.
Элементы логики работы(класс CrossZero)
Игра может закончится по 2-м причинам:
— таблица заполнена полностью,
— человек или компьютер одержал победу.
С реализацией заполнения все просто, нужно пробежаться по таблице и посмотреть, не осталось ли пустых ячеек.
public function tableIsFull(): bool { for ($i = 0; $i < $this->size; $i++) { for ($j = 0; $j < $this->size; $j++) { } } return true; }
А вот, чтобы проверить факт победы, нужно проверить каждую строку, столбец, диагональ на заполнение их self::X или self::O.
private function checkRows(string $simbol): bool { for ($row = 0; $row < $this->size; $row++) { $count = 0; for ($col = 0; $col < $this->size; $col++) { if ($this->table[$row][$col] == $simbol) $count++; } if ($count == $this->size) return true; } return false; } private function checkColumns(string $simbol): bool { for ($col = 0; $col < $this->size; $col++) { $count = 0; for ($row = 0; $row < $this->size; $row++) { if ($this->table[$row][$col] == $simbol) $count++; } if ($count == $this->size) return true; } return false; } private function checkDiagonal(string $simbol): bool { $count = 0; for ($i = 0; $i < $this->size; $i++) { if ($this->table[$i][$i] == $simbol) $count++; } if ($count == $this->size) return true; return false; } private function check2Diagonal(string $simbol): bool { $count = 0; for ($i = 0; $i < $this->size; $i++) { if ($this->table[$this->size - $i -1][$i] == $simbol) $count++; } if ($count == $this->size) return true; return false; } public function checkWin(string $simbol): bool { if ($this->checkRows($simbol) === true) return true; if ($this->checkColumns($simbol) === true) return true; if ($this->checkDiagonal($simbol) === true) return true; if ($this->check2Diagonal($simbol) === true) return true; return false; }
Чтобы записать нужный символ в приватную таблицу, реализован отдельный метод. Он не просто втыкает символ в указанное место, а проверяет, не занята ли ячейка. В этом случае запись не происходит, а метод возвращает false.
public function setSimbol(int $row, int $col, string $simbol): bool { $this->table[$row][$col] = $simbol; return true; }
Ну, и печать таблицы
public function printTable(): void { for ($i = 0; $i < $this->size; $i++) { $s = ""; for ($j = 0; $j < $this->size; $j++) { } $s.= "\n"; echo $s; } echo "\n"; }
На этом с классом CrossZero покончено.
Ввод с клавиатуры реализован методом класса Helper. Вводимые данные, кстати, проверяются на валидность
public static function readNumber(int $min, int $max): int { while (true) { echo "Должно быть введено число. Снова :"; continue; } $number = (int)$line; if ($number < $min || $number > $max) { echo "Число не принадлежит нужному диапазону. Снова :"; continue; } else { break; } } return $number; }
Наконец, реализация цикла while() из последовательности работы приложения
while (true) { // ход человека do { echo "Введите номер строки :"; $row = Helper::readNumber(1, $ts); echo "Введите номер столбца :"; $col = Helper::readNumber(1, $ts); } while ($xo->setSimbol($row - 1, $col - 1, CrossZero::X) === false); $xo->printTable(); // человек выиграл? if ($xo->checkWin(CrossZero::X) === true) { echo "Вы выиграли!\n"; break; } // таблица заполнена полностью? if ($xo->tableIsFull() === true) { echo "Таблица заполнена полностью.\n"; break; } // ход компьютера do { $row = random_int(0, $ts - 1); $col = random_int(0, $ts - 1); } while ($xo->setSimbol($row, $col, CrossZero::O) === false); $xo->printTable(); // компьютер выиграл? if ($xo->checkWin(CrossZero::O) === true) { echo "Компьютер выиграл!\n"; break; } // таблица заполнена полностью? if ($xo->tableIsFull() === true) { echo "Таблица заполнена полностью.\n"; break; } }
Если вы дочитали до этого места и не потеряли нить повествования, то можете проверить так ли это, собрав из кусочков полное приложение. Запускать из консоли
php -f xo.php
Если не получилось — не расстраивайтесь! Вот мой xo.php .
В данном приложении реализован минимальный функционал, при желании можно сделать действия компьютера более изощренными. Попробуйте запустить приложение с размером таблицы 4х4, 5х5. Можно устроить, что первыми будут ходить нолики. Играйтесь!
Вместо заключения
Если человек начинает писать на C, Rust, Go, Java, Scala, то он начинает этот процесс именно в консоли. Но в PHP, по крайней мере в русско-говорящем сегменте, как-то больше принято начинать с установки OpenServer, XAMPP… и сразу «брать быка за рога» — писать веб-приложение, называется это обычно «От новичка до профессионала». В результате такого подхода пациента сталкивают сразу с PHP, HTML, CSS, SQL, а особо продвинутые туда еще JavaScript добавляют. Поэтому немудрено, что вместо нескольких полезных навыков он(пациент) получает полную «кашу» в голове. Показано много всего и много действительно полезного, но применить это всё человек не в состоянии.
Но это мой взгляд. Как учиться вам — вам и виднее.
Полезная ссылка.
Реклама #
olegan 3 года назад #
Это нужно на сайте Instant?
IamB 3 года назад #
Признаться, я ожидал такого вопроса. Типичным cli-приложением в CMS является cron.php. И думаю, что, если посетители этого сайта станут играться в игрушки вроде моей, то тем на форуме, где ТС беспомощно разводит руками и стенает: А у меня cron не работает, не станет.
olegan 3 года назад #
Спасибо за разъяснение.Жаль я не программист и не могу оценить нужность всего этого.