Блог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>

30.01.2021, 18:51 [403 просмотра]


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