БлогNot. Сапёр (Minesweeper) на PHP

Сапёр (Minesweeper) на PHP

Классическая игра "Сапёр", думаю, известна всем, а ниже показан её несложный исходник, сделанный на PHP, проверено на локальном сервере XAMPP с версией PHP 8.0.

Исходник публикуется на момент написания и может измениться в работающей игре. Предполагается, что он размещается как файл типа .php в кодировке Юникода UTF-8.

Немного комментариев в исходнике есть, он достаточно понятен, чтобы расширить игру, например, введя уровни сложности, изменяемые размеры поля, сохранение данных и т.п. На самом деле, нет никакой необходимости писать подобные вещи с использованием server-side языка, достаточно клиентского Javascript, но просто, чтоб не выкидывать код :)

 Посмотреть Сапёр (Minesweeper) на PHP в работе

<?php
 define('FIELD_WIDTH',    9);
 define('FIELD_HEIGHT',   9); //размерности сетки
 define('NOT_EXPLORED',  -1);
 define('MINE',          -2);
 define('FLAGGED',       -3);
 define('FLAGGED_MINE',  -4);
 define('ACTIVATED_MINE',-5); //состояния поля
 
 function check_mine ($field, $index) {
  if ($index < 1 or $index > FIELD_WIDTH * FIELD_HEIGHT) return false;
  return $field[$index] === MINE || $field[$index] === FLAGGED_MINE ? true : false;
 }
 
 function explore_field ($field) {
  if (!isset($_SESSION['minesweeper'][$field]) or !in_array($_SESSION['minesweeper'][$field], array(NOT_EXPLORED, FLAGGED))) {
   return;
  }
  $mines = 0;
  $fields  = &$_SESSION['minesweeper']; //ссылка на длинное имя
  //слева:
  if ($field % FIELD_WIDTH !== 1) {
   $mines += check_mine($fields,$field - FIELD_WIDTH - 1);
   $mines += check_mine($fields,$field - 1);
   $mines += check_mine($fields,$field + FIELD_WIDTH - 1);
  }
  //снизу и сверху:
  $mines += check_mine($fields,$field - FIELD_WIDTH);
  $mines += check_mine($fields,$field + FIELD_WIDTH);
  //справа:
  if ($field % FIELD_WIDTH !== 0) {
   $mines += check_mine($fields,$field - FIELD_WIDTH + 1);
   $mines += check_mine($fields,$field + 1);
   $mines += check_mine($fields,$field + FIELD_WIDTH + 1);
  }
  $fields[$field] = $mines;
  if ($mines === 0) {
   if ($field % FIELD_WIDTH !== 1) {
    explore_field($field - FIELD_WIDTH - 1);
    explore_field($field - 1);
    explore_field($field + FIELD_WIDTH - 1);
   }
   explore_field($field - FIELD_WIDTH);
   explore_field($field + FIELD_WIDTH);
   if ($field % FIELD_WIDTH !== 0) {
    explore_field($field - FIELD_WIDTH + 1);
    explore_field($field + 1);
    explore_field($field + FIELD_WIDTH + 1);
   }
  }
 }
 
 session_start(); //начать сессию для сохранения данных
 
 if (!isset($_SESSION['minesweeper'])) { //создать сетку, если нету
  $_SESSION['minesweeper'] = array_fill(1, FIELD_WIDTH * FIELD_HEIGHT, NOT_EXPLORED);
  $number_of_mines = (int) mt_rand (0.1 * FIELD_WIDTH * FIELD_HEIGHT, 0.2 * FIELD_WIDTH * FIELD_HEIGHT);
   //заполнить минами от 10 до 20% поля
  $random_keys = array_rand ($_SESSION['minesweeper'], $number_of_mines);
  foreach ($random_keys as $key) {
   $_SESSION['minesweeper'][$key] = MINE;
  }
  $_SESSION['numberofmines'] = $number_of_mines;
 }
 if (isset($_GET['explore'])) {
  if (isset($_SESSION['minesweeper'][$_GET['explore']])) {
   switch ($_SESSION['minesweeper'][$_GET['explore']]) {
    case NOT_EXPLORED:
     explore_field($_GET['explore']);
    break;
    case MINE:
     $lost = 1;
     $_SESSION['minesweeper'][$_GET['explore']] = ACTIVATED_MINE;
    break;
    default:
    break;
   }
  }
  else {
   die ('Недопустимый вызов с аргументом '.$_GET['explore']);
  }
 }
 elseif (isset($_GET['flag'])) {
  if (isset($_SESSION['minesweeper'][$_GET['flag']])) {
   $tile = &$_SESSION['minesweeper'][$_GET['flag']];
   switch ($tile) {
    case NOT_EXPLORED: $tile = FLAGGED; break;
    case MINE: $tile = FLAGGED_MINE; break;
    case FLAGGED: $tile = NOT_EXPLORED; break;
    case FLAGGED_MINE: $tile = MINE; break;
    default: break;
   }
  }
  else {
   die ('Недопустимый вызов с аргументом '.$_GET['flag']);
  }
 }
 if (!in_array(NOT_EXPLORED, $_SESSION['minesweeper']) and !in_array(FLAGGED, $_SESSION['minesweeper'])) {
  $won = true;
 }
?>
<!DOCTYPE html>
<html lang="ru">
 <head>
  <meta charset="utf-8">
  <title>Сапёр (Minesweeper) на PHP</title>
  <style>
   #minesweeperGame {
    width: 80%;
    margin: auto;
   }
   #minesweeperGame table { 
    border-collapse: collapse; 
    border:          1px solid black;
   }
   #minesweeperGame td {
    border-collapse: collapse;
    border:          1px solid black;
   }
   #minesweeperGame td, #minesweeperGame a {
    text-align:      center;
    width:           1.5em;
    height:          1.5em;
    margin:          0;
    padding:         0;
   }
   #minesweeperGame a {
    display:         block;
    color:           black;
    text-decoration: none;
    font-size:       1.5em;
   }
  </style>
 </head>
 <body>
  <div id="minesweeperGame">
   <script>
    function flag (number, e) {
     if (e.which === 2 || e.which === 3) {
      location = '?flag=' + number;
      return false;
     }
    }
    function showRools () {
     let div=document.getElementById('mineInstructionBox');
     div.style.display = div.style.display == "none" ? "block" : "none";
     document.getElementById('mineRools').innerHTML = (div.style.display == "block" ? 'Убрать правила' : 'Правила');
     return false;
    }
   </script>
   <noscript>
    <p>Для работы приложения нужен включённый Javascript!</p>
   </noscript>
<?php
 echo '<p>Всего мин: ',$_SESSION['numberofmines'],'; <span id="mineRools" onclick="showRools();">Правила</span></p>
  <div id="mineInstructionBox" style="display: none;">
   <p>Левая кнопка мыши - открываем поля, правая - ставим флажок "мина". 
   <br>Чтобы выиграть, нужно открыть все поля, при этом правильно указав положение всех мин. 
   <br>Цифры внутри полей показывают, сколько мин на восьми соседних клетках.</p>
  </div>'."\n";
 $mine_copy = $_SESSION['minesweeper'];
 echo '<table>'."\n";
 for ($x = 1; $x <= FIELD_HEIGHT; $x++) {
  echo '<tr>'."\n";
  for ($y = 1; $y <= FIELD_WIDTH; $y++) {
   echo '<td>';
   $number = array_shift  ($mine_copy);
   switch ($number) {
    case FLAGGED:
    case FLAGGED_MINE:
     if (!empty($lost) or !empty($won)) {
      echo '<a>'.($number === FLAGGED_MINE ? '*' : '.').'</a>';
     }
     else {
      echo '<a href="#" onmousedown="return flag(',($x - 1) * FIELD_WIDTH + $y,',event);"'.
       ' oncontextmenu="return false;">?</a>'."\n";
     }
    break;
    case ACTIVATED_MINE:
     echo '<a>:(</a>';
    break;
    case MINE:
    case NOT_EXPLORED:
     if (!empty($lost)) {
      echo '<a>'.($number === MINE ? '*' : '.').'</a>';
     }
     elseif (!empty($won)) {
      echo '<a>*</a>';
     }
     else {
      echo '<a href="?explore=',
       ($x - 1) * FIELD_WIDTH + $y, '" onmousedown="return flag(',
       ($x - 1) * FIELD_WIDTH + $y, ',event);"'.
       ' oncontextmenu="return false;">.</a>';
     }
    break;
    case 0:
     echo '<a></a>';
    break;
    default:
     echo '<a>', $number, '</a>';
    break;
   }
   echo '</td>'."\n";
  }
  echo '</tr>'."\n";
 }
 echo '</table>'."\n";
 if (!empty($lost)) {
  unset ($_SESSION['minesweeper']);
  echo '<p>К сожалению, Вы проиграли :(</p> <a href="?">Заново?</a>';
 }
 elseif (!empty($won)) {
  unset($_SESSION['minesweeper']);
  echo '<p>Поздравяю, Вы выиграли :)</p> <a href="?">Заново?</a>';
 }
?>
  </div>
 </body>
</html>

теги: программирование php игра

30.01.2021, 18:51; рейтинг: 59