СоХабр закрыт.
С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.
Недавно я прочитал статью об играх, написанных на чистом CSS. Это было бомбой! Никогда мне в голову не пришла бы идея использовать язык описания таблиц стилей, как инструмент для разработки игр. Зато появилась другая — использовать для этого PHP.
Какую же игру выбрать для реализации? Воображение сразу же подсунуло целую кучу срелялок-леталок (спасибо 8 битному детству), но разум подсказывал — начни с простого. Поэтому я выбрал старые добрые крестики-нолики.
Для себя решил, что игра должна быть обязательно консольной (PHP игра, которая исполняется в браузере — слишком простая задача, а я не ищу легких путей). Все готово, пора начинать наш квест.
Поскольку наша игра будет консольной, то прорисовывать изображения и надписи на экране мы будем с помощью символов.
private static function showTitle() {
self::clearScreen();
echo <<<EOD
=========================================================
=========================================================
##### ## ## ##### #### #### ## # ##### ####
## ## ## ## ## ## ## ## ## ### ## ## ##
##### ###### ##### ## ### ###### ## # # #### ####
## ## ## ## ## ## ## ## ## # ## ##
## ## ## ## #### ## ## ## # ##### ####
=========================================================
=========================================================
1 | 2 | 3
--- --- ---
HOW TO PLAY: input number of cell you chose 4 | 5 | 6
--- --- ---
7 | 8 | 9
Chose 0 to exit the game.
=========================================================
=========================================================
EOD;
}
private static function gameOver($whoWin = -1) {
sleep(1);
self::clearScreen();
if ($whoWin == -1) {
echo <<<EOD
=========================================================
=========================================================
#### #### ## # ##### #### ## ## ##### #####
## ## ## ### ## ## ## ## ## ## ## ## ##
## ### ###### ## # # #### ## ## ## ## #### #####
## ## ## ## ## # ## ## ## #### ## ## ##
#### ## ## ## # ##### #### ## ##### ## ##
=========================================================
=========================================================
EOD;
}
if ($whoWin == 1) {
echo <<<EOD
=========================================================
=========================================================
## ## #### ## ## ## ## ###### ## ##
#### ## ## ## ## ## ## ## ### ##
## ## ## ## ## ## # ## ## ## # ##
## ## ## ## ## ####### ## ## ##
## #### #### ## ## ###### ## ##
=========================================================
=========================================================
EOD;
}
if ($whoWin == 0) {
echo <<<EOD
=========================================================
=========================================================
## ## #### ## ## ## #### #### #####
#### ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## #### ####
## ## ## ## ## ## ## ## ## ##
## #### #### ###### #### #### #####
=========================================================
=========================================================
EOD;
}
echo "\n\n Do you want play again (Y/N)? : ";
$userChoice = strtolower(substr(fgets(STDIN), 0, 1));
if ($userChoice == 'y') {
self::runGame();
} else {
exit();
}
}
Пользователь имеет возможность выбирать номер ячейки, куда будет поставлен крестик. Компьютер же играет ноликами. Каждый раз после того, как выбор сделан, игровое поле обновляется и выводится на экран.
private static function applyMove($field, $xStatrtPosition, $yStartPosition) {
foreach ($field as $fieldRow) {
$yCurrentPosition = $yStartPosition;
foreach ($fieldRow as $fieldCell) {
self::$gameField[$xStatrtPosition][$yCurrentPosition] = $fieldCell;
$yCurrentPosition++;
}
$xStatrtPosition++;
}
self::drawField();
}
private static function drawField() {
self::clearScreen();
self::showTitle();
echo "\n";
foreach (self::$gameField as $line) {
foreach ($line as $cell) {
echo $cell;
}
echo "\n";
}
}
При этом не забываем контролировать и проверять выбор игрока.
private static function checkUserChoice($userChoice) {
return (in_array($userChoice, [1,2,3,4,5,6,7,8,9,0]) &&
array_search($userChoice, array_merge(self::$playerMoves[0], self::$playerMoves[1])) === false);
}
Самый простой вариант — что называется "пальцем в небо", простая случайная выборка.
private static function applyComputerMove() {
$movesLeft = array_diff([1,2,3,4,5,6,7,8,9], array_merge(self::$playerMoves[0], self::$playerMoves[1]));
shuffle($movesLeft);
return array_pop($movesLeft);
}
Но давайте добавим нашем сопернику немного интеллекта.
private static function applyComputerMove() {
$movesLeft = array_diff([1,2,3,4,5,6,7,8,9], array_merge(self::$playerMoves[0], self::$playerMoves[1]));
foreach (self::$winnerFields as $winnerCombination) {
$availableMove = [];
if (count(array_intersect($winnerCombination, self::$playerMoves[0])) == 2) {
$availableMove = array_diff($winnerCombination, array_intersect($winnerCombination, self::$playerMoves[0]));
}
if (count(array_intersect($winnerCombination, self::$playerMoves[1])) == 2) {
$availableMove = array_diff($winnerCombination, array_intersect($winnerCombination, self::$playerMoves[1]));
}
if (count(array_intersect($availableMove, $movesLeft)) != 0) {
return array_pop($availableMove);
}
}
if (in_array(5, $movesLeft)) {
return 5;
}
shuffle($movesLeft);
$computerMove = array_pop($movesLeft);
return $computerMove;
}
После каждого сделанного шага (как игрока так и компьютера) следует проверять не появился ли победитель.
private static $winnerFields = [
[1, 2, 3], [4, 5, 6], [7, 8, 9], [1, 4, 7],
[2, 5, 8], [3, 6, 9], [1, 5, 9], [3, 5, 7],
];
private static function checkWinner($isPlayer) {
foreach (self::$winnerFields as $winnerCombination) {
if (count(array_intersect($winnerCombination, self::$playerMoves[(int) $isPlayer])) == 3) {
self::gameOver((int) $isPlayer);
}
}
if ((count(self::$playerMoves[0]) + count(self::$playerMoves[1])) == 9) {
self::gameOver();
}
}
Перед тем, как рисовать обновленное игровое поле, надо стереть старое. Казалось бы, что может быть проще — просто очищаем весь экран.
Но тут меня ждал неприятный сюрприз. Конструкция
system("cls");
не работает, если консоль запущена из-под Windows. Панику в сторону. Подход "гугл в помощь" в конце концов принес таки долгожданный результат.
private static function clearScreen() {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
popen('cls', 'w');
} else {
system("clear");
}
}
Объединяем все кусочки пазла в один класс — и вот, желаемая вершина достигнута GitHub. Однако оглянувшись я увидел, что это вершина не горы, а только маленького бугорка, а глаза уже смотрят туда, где вдалеке маячат консольные PHP экшены и игры на чистом sql.
комментарии (16)