БлогNot. PHP: весёлый тест о шахматах :)

PHP: весёлый тест о шахматах :)

Смысл публикации данного шыдевра даже не в самом тесте, хотя он весёлый и подойдёт для детей-шахматистов, а в ещё одном способе быстрой организации онлайн-тестирования ("только написать текстовый файл, а скрипт чтоб был готовый"), на этот раз на PHP, так как на яваскрипте подобного уже много.

Работа скрипта сводится к тому, чтоб разобрать текстовый файл с помощью трёх функций:

read_test ($filename) //читает тест из файла $filename и формирует из него массив
show_test ($test) //показывает тест из массива $test
resume_test ($test) //выводит резюме по данным $_POST и массива $test 

"Тест о шахматах: в шутку и всерьёз" в работе

Технические замечания и листинги:

Кодировка файла данных и файлов скрипта - Юникод (UTF-8). В норме, конечно, пользователь не знает имя файла на сервере, которое передаётся функции read_test вместо data.txt, так что дополнительная защита данных не обязательна. Существенно и то, что начисляемые за ответы баллы не передаются через HTTP-соединение, присутствуя только в массиве $test. В исходнике страницы можно увидеть лишь то, что при выборе первой радиокнопки на сервер передаётся значение 1, второй - 2 и т.д.

Так как разбор данных очень простой, не нужно цифр 0-9 в тексте вопросов теста, цифры означают только номера вопросов, а буквы со скобками - варианты ответов.

Если максимум пяти вариантов ответа мало, исправьте в index.php строку А-Д в регулярном выражении на А-К, например.

Вот простейший пример файла теста:

1. Что родилось в лесу?
А) Ёлочка 2
Б) Медведь 1
В) Батут 0
Г) Таможенный союз 1
2. Куда уходит детство?
А) Неведомо куда 1
Б) Туда или сюда 0
В) В чужие поезда 1
Г) В какие города 2
RESULTS
1. 0-1 балл. Очень плохо :)
2. 2-3 балла. Ответы так себе
3. 4-4 балла. Вы - умный чувак, всё правильно

И массив $test для него:

Array
(
    [1] => Array
        (
            [number] => 1
            [question] => Что родилось в лесу?
            [variants] => 4
            [text] => Array
                (
                    [1] => Ёлочка
                    [2] => Медведь
                    [3] => Батут
                    [4] => Таможенный союз
                )

            [ball] => Array
                (
                    [1] => 2
                    [2] => 1
                    [3] => 0
                    [4] => 1
                )

        )

    [2] => Array
        (
            [number] => 2
            [question] => Куда уходит детство?
            [variants] => 4
            [text] => Array
                (
                    [1] => Неведомо куда
                    [2] => Туда или сюда
                    [3] => В чужие поезда
                    [4] => В какие города
                )

            [ball] => Array
                (
                    [1] => 1
                    [2] => 0
                    [3] => 1
                    [4] => 2
                )

        )

    [cnt] => 2
    [res] => Array
        (
            [1] => Array
                (
                    [result] => 0-1 балл. Очень плохо :)
                    [lo] => 0
                    [hi] => 1
                )

            [2] => Array
                (
                    [result] => 2-3 балла. Ответы так себе
                    [lo] => 2
                    [hi] => 3
                )

            [3] => Array
                (
                    [result] => 4-4 балла. Вы - умный чувак, всё правильно
                    [lo] => 4
                    [hi] => 4
                )

        )

    [rescnt] => 3
)

Во вложенную папку img можно дополнительно положить картинки в формате PNG, номера которых совпадают с номерами вопросов, например, 5.png для пятого. "Нечётные" картинки будут выводиться слева, "чётные" - справа. Аналогичную картинку для пятого варианта вывода следует назвать r5.png и поместить в ту же папку.

При пропуске вопросов приложение скажет, какие вопросы не отвечены и даст ответить повторно, не удалив уже выбранные варианты.

Стандартный элемент HTML <input type="radio"> не даёт возможности выбирать радиокнопку щелчком по сопровождающему тексту, а только по маленькому неудобному кружочку. В коде с помощью мини-скрипта script.js реализован выбор радиокнопки щелчком по тексту.

Скрипту не нужны внешние библиотеки вроде jquery.js.

Файл index.php
<!DOCTYPE html>
<html dir="ltr" lang="ru-RU">
<head>
 <meta charset="utf-8">
 <title>Весёлый тест о шахматах</title>
 <link rel="stylesheet" href="style.css">
</head>
<body><div id="content">
<h1>Тест о шахматах: в шутку и всерьёз :)</h1>
<?php

function read_test ($filename) { //читает тест из файла $filename и формирует из него массив
 $data = file_get_contents($filename);
 list ($questions,$results) = explode ("RESULTS",$data);
 $questions = preg_split ("/\d{1,2}\.\ /msiu", $questions);
 $cnt = 1;
 $test = array ();
 foreach ($questions as $q) {
  $q = trim($q); if (empty($q)) continue;
  list ($question,$variants) = preg_split ("/\r?\n+/msiu", $q);
  $question = trim($question);
  $parts = preg_match_all ("/([А-Д]{1}\)\ )(.*)/iu",$q,$matches);
  if (empty($question) or $parts===false or count($matches)<3) {
   echo '<div class="error">Ошибка теста в вопросе:<br><i>'.$q."</i></div>\n";
   exit;
  }
  $test[$cnt]['number']=$cnt;
  $test[$cnt]['question']=$question;
  $test[$cnt]['variants']=count($matches[2]);
  for ($i=1; $i<=$test[$cnt]['variants']; $i++) {
   $variant_string = trim($matches[2][$i-1]);
   $variant = preg_split ("/\ \d{1}/iu", $variant_string);
   $ball = array ();
   if (count($variant)!=2 or !preg_match("/\ \d{1}/iu",$variant_string,$ball)) {
    echo '<div class="error">Ошибка теста в строке:<br><i>'.$variant_string."</i></div>\n";
    exit;
   }
   $test[$cnt]['text'][$i]=$variant[0];
   $test[$cnt]['ball'][$i]=trim(''.$ball[0]);
  }
  $cnt++;
 }
 $test['cnt']=$cnt-1;
 $results = preg_split ("/\d+\.\ /msiu", $results);
 $rescnt = 1;
 foreach ($results as $r) {
  $r = trim($r); if (empty($r)) continue;
  $parts = preg_match_all ("/(\d+\-\d+)(\ +.*)/iu",$r,$matches);
  if ($parts===false or count($matches)<3) {
   echo '<div class="error">Ошибка теста в варианте вывода:<br><i>'.$r."</i></div>\n";
   exit;
  }
  $test['res'][$rescnt]['result']=$r;
  list ($lo,$hi) = preg_split ("/\-/msiu", trim($matches[1][0]));
  $test['res'][$rescnt]['lo']=$lo;
  $test['res'][$rescnt]['hi']=$hi; //не проверяем $lo<=$hi и т.п.
  $rescnt++;
 }
 $test['rescnt']=$rescnt-1;
 return $test;
}

function show_test ($test) { //показывает тест из массива $test
 echo '<script type="text/javascript" src="script.js"></script>
  <noscript>
   <div class="error">Для нормальной работы приложения нужен включённый Javascript!</div>
  </noscript>'."\n";
 echo '<form method="post" name="f1">'."\n";
 for ($cnt=1; $cnt<=$test['cnt']; $cnt++) {
  echo '<div class="question">'.$test[$cnt]['number'].'. '.$test[$cnt]['question']."\n"; 
  $filename = 'img/'.$cnt.'.png';
  $class = $cnt%2==0 ? 'odd_img' : 'even_img';
  if (file_exists($filename)) 
   echo '<img src="'.$filename.'" class="'.$class.'" />'."\n";
  $letter = 'A'; 
  for ($i=1; $i<=$test[$cnt]['variants']; $i++) {
   $text = $test[$cnt]['text'][$i];
   $ball = $test[$cnt]['ball'][$i];
   if (!empty($_POST['q'.$cnt])) $checker = intval($_POST['q'.$cnt]);
   else $checker = -1;
   echo '<div class="answer">'.
        '<input name="q'.$cnt.'" value="'.$i.'" type="radio"'.($checker==$i?' checked':'').'> '.
        '<span onclick="select_radio(\'q'.$cnt.'\','.($i-1).');">'.($letter++).'. '.$text.'</span>'.
        '</div>'."\n";
  }	   
  echo '</div>'."\n"; 
 }
 echo '<input type="hidden" name="cnt" value="'.$test['cnt'].'">'.
     '<input type="submit" name="action" value="Результаты &gt;&gt;">'.
     '</form>'."\n"; 
}

function resume_test ($test) { //выводит резюме по данным $_POST и массива $test
 $cnt = intval($_POST['cnt']);
 if ($cnt!=$test['cnt']) {
  echo '<div class="error">Неверное значение количества вопросов: <i>'.$cnt."</i></div>\n";
  exit;
 }
 $error_list = array ();
 $balls = 0;
 for ($i=1; $i<=$cnt; $i++) {
  if (!isset($_POST['q'.$i])) { $error_list[] = $i; continue; }
  else $balls += $test[$i]['ball'][intval($_POST['q'.$i])];
 }
 $errors = count($error_list);
 if ($errors>0) {
  echo '<div class="error">Вы не ответили на вопросы c номерами: <i>';
  for ($i=0; $i<$errors; $i++) echo $error_list[$i].' ';
  echo '</i>. Пожалуйста, дополните свой выбор :)</div>'."\n";
  show_test($test);
 }
 else {
  $found = false;
  for ($rescnt=1; $rescnt<=$test['rescnt']; $rescnt++) {
   if ($balls>=$test['res'][$rescnt]['lo'] and $balls<=$test['res'][$rescnt]['hi']) {
    echo '<div class="result">';
    $filename = 'img/r'.$test['res'][$rescnt]['lo'].'.png';
    $class = $test['res'][$rescnt]['lo']%2==0 ? 'odd_img' : 'even_img';
    if (file_exists($filename)) 
     echo '<img src="'.$filename.'" class="'.$class.'" />'."\n";
    echo 'Ваш результат: <b>'.$balls.'</b>.<p>Ваш вывод: '.$test['res'][$rescnt]['result'].'</p></div>'."\n";
    $found = true;
    break;
   }
  }
  if (!$found) {
   echo '<div class="error">Не найдено ответа для вашего количества баллов <b>'.
    $balls.'</b>. Обратитесь к разработчику :(</div>'."\n";
  }
  echo '<div class="question"><a href="'.$_SERVER['HTTP_REFERER'].'">Попробовать ещё раз!</a></div>'."\n";
 }	
}

$test = read_test("data.txt"); // print_r($test);
if (!empty($_POST['action'])) resume_test ($test); 
else show_test($test); //print_r($_POST);
?>
</div></body></html>
Файл style.css
* { padding: 0; margin: 0; }
h1 { font-size: 140%; }
input { background-color: white; border: 1px solid black; padding: 4px; }
#content { width: 90%; margin: 10px auto; }
.even_img { float: left; padding-left: 10px; padding-bottom: 10px; }
.odd_img { float: right; padding-left: 10px; padding-bottom: 10px; }
.question { padding: 10px; font-size: 120%; }
.answer { overflow: auto;  }
.error { width: 90%; margin: 10px auto; padding: 10px; font-size: 120%; border: 2px solid red;}
.result { width: 90%; margin: 10px auto; padding: 10px; font-size: 120%; border: 2px solid green; }
Файл script.js
function select_radio (myname, mynumber) {
 var btn = document.f1.elements[''+myname];
 for (var i=0; i<btn.length; i++) btn[i].checked = false;
 btn[mynumber].checked = true;
}

На самом деле, показанный код писался в учебных целях и далеко не совершенен, например, script.js можно успешно заменить тегом <label>, а файл читать сразу в массив функцией file и т.д.

27.10.2016, 11:25 [4284 просмотра]


теги: программирование шахматы php тест юмор

К этой статье пока нет комментариев, Ваш будет первым