Advertisement
TimKruz

Simple Interactive Book Reader

Mar 24th, 2020
460
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 6.83 KB | None | 0 0
  1. program SimpleInteractiveBook;
  2.  
  3. const
  4.   EXIT_ANSWER = 'bye';
  5.  
  6.   TAG_ERROR = '[ERROR] ';
  7.   TAG_WARN = '[Warning] ';
  8.   TAG_HINT = '[Hint] ';
  9.  
  10.   MSG_NO_INPUT =
  11.     TAG_ERROR + 'Select book file. Example:' + LineEnding + '> SIB Book.book';
  12.   MSG_WRONG_ORDER = TAG_WARN + 'Page answer before first page!';
  13.   MSG_WRONG_PAGE_INDEX = TAG_ERROR + 'Wrong page index: ';
  14.   MSG_WRONG_ANSWER_INDEX = TAG_HINT + 'Wrong answer index, pick another.';
  15.   MSG_WRONG_ANSWER = TAG_HINT + 'To pick an answer, write it''s number.';
  16.   MSG_INTERACTIVE_MODE =
  17.     TAG_HINT + 'Interactive Mode. Type "' + EXIT_ANSWER + '" to exit.';
  18.   MSG_EMPTY_BOOK = TAG_ERROR + 'Empty book, select another one!';
  19.   MSG_LOST_PAGE = TAG_ERROR + 'Lost page or wrong name: ';
  20.   MSG_BYE = 'See ya!';
  21.  
  22. type
  23.   TPageLink = record
  24.     Index: Integer; // номер связанной страницы в массиве страниц
  25.     Name: string;   // имя связанной страницы
  26.   end;
  27.  
  28.   TPageAnswer = record
  29.     Text: string;
  30.     Link: TPageLink; // связанная с ответом страница
  31.   end;
  32.   TPageAnswers = array of TPageAnswer;
  33.  
  34.   TBookPage = record
  35.     Name: string; // внутреннее имя страницы (для связей)
  36.     Text: string;
  37.     Answer: TPageAnswers;
  38.   end;
  39.   TBookPages = array of TBookPage;
  40.  
  41.   TInteractiveBook = record
  42.     Name: string;
  43.     Page: TBookPages;
  44.     Optimized: Boolean; // определены ли индексы связей?
  45.   end;
  46.  
  47. function LoadBookFrom(const FileName: string): TInteractiveBook;
  48. const
  49.   // Символы синтаксиса книги:
  50.   SYM_COMMENT = '#';
  51.   SYM_PAGE = '!';
  52.   SYM_ANSWER = '?';
  53.   SYM_MARKER = ':';
  54. var
  55.   BookFile: TextFile;
  56.   Line, LinkName: string;
  57.   MarkerPos: Integer;
  58.   Pages: TBookPages;
  59.   // Следующие субпроцедуры выделены для краткости основного кода:
  60.   procedure StorePage;
  61.   begin
  62.     SetLength(Pages, Length(Pages) + 1); // новая страница
  63.     with Pages[High(Pages)] do
  64.     begin
  65.       Name := Copy(Line, 1, MarkerPos - 2);
  66.       Text := Copy(Line, MarkerPos + 2, High(Line));
  67.     end;
  68.   end;
  69.   procedure StoreLine;
  70.   begin
  71.     with Pages[High(Pages)] do
  72.       Text += LineEnding + Line;
  73.   end;
  74.   procedure StoreAnswer;
  75.   begin
  76.     if Length(Pages) > 0 then
  77.       with Pages[High(Pages)] do
  78.       begin
  79.         SetLength(Answer, Length(Answer) + 1); // следующий вариант ответа
  80.         with Answer[High(Answer)] do
  81.         begin
  82.           Link.Name := Copy(Line, 1, MarkerPos - 2);
  83.           Text := Copy(Line, MarkerPos + 2, High(Line));
  84.         end;
  85.       end
  86.     else
  87.       WriteLn(MSG_WRONG_ORDER);
  88.   end;
  89. //LoadBookFrom:
  90. begin
  91.   AssignFile(BookFile, FileName);
  92.   Reset(BookFile);
  93.   SetLength(Pages, 0);
  94.   repeat
  95.     ReadLn(BookFile, Line);
  96.     // Отбрасываем пустые строки и строки-комментарии:
  97.     if (Length(Line) > 0) and (Line[1] <> SYM_COMMENT) then
  98.     begin
  99.       MarkerPos := Pos(SYM_MARKER, Line);
  100.       if MarkerPos > 0 then // строка имеет маркер?
  101.       begin
  102.         // Предполагаем, что слева от маркера - имя связанной страницы:
  103.         LinkName := Copy(Line, 1, MarkerPos - 1);
  104.         // Последним символом LinkName может быть ? (ответ) или ! (страница):
  105.         case LinkName[High(LinkName)] of
  106.           SYM_PAGE: StorePage;
  107.           SYM_ANSWER: StoreAnswer;
  108.         else // Если символа ответа/страницы нет, то это строка страницы:
  109.           StoreLine;
  110.         end;
  111.       end else // Если маркера нет, то это строка страницы:
  112.         StoreLine;
  113.     end;
  114.   until Eof(BookFile);
  115.   CloseFile(BookFile);
  116.   Result.Name := FileName;
  117.   Result.Page := Pages;
  118. end;
  119.  
  120. function FindPageIndex(const Book: TInteractiveBook;
  121.   const PageName: string): Integer;
  122. var I: Integer;
  123. begin
  124.   Result := -1;
  125.   for I := 0 to High(Book.Page) do
  126.     if Book.Page[I].Name = PageName then
  127.     begin
  128.       Result := I;
  129.       Break;
  130.     end;
  131.   if Result < 0 then // если не смогли найти
  132.   begin
  133.     WriteLn(MSG_LOST_PAGE, PageName);
  134.     Halt(42); // ой всё, не хочу с exception связываться, пусть сразу падает
  135.   end;
  136. end;
  137.  
  138. // Пытается найти индексы страниц по их именам:
  139. function CalculateLinksIn(Book: TInteractiveBook): TInteractiveBook;
  140. var A, P: Integer;
  141. begin
  142.   for P := 0 to High(Book.Page) do
  143.     for A := 0 to High(Book.Page[P].Answer) do
  144.       with Book.Page[P].Answer[A].Link do
  145.         Index := FindPageIndex(Book, Name);
  146.   Book.Optimized := true; // добавил для примера, использовать лень
  147.   Result := Book;
  148. end;
  149.  
  150. // Выводит страницу с ответами, с DebugMode выводит данные связей:
  151. procedure DisplayPage(const Book: TInteractiveBook; const PageIndex: Integer;
  152.   const DebugMode: Boolean = false);
  153. var I: Integer;
  154. begin
  155.   if (Length(Book.Page) > PageIndex) and (PageIndex >= 0) then
  156.     with Book.Page[PageIndex] do
  157.     begin
  158.       if DebugMode then
  159.         WriteLn('[[', PageIndex, ':', Name, ']]');
  160.       WriteLn(Text);
  161.       for I := 0 to High(Answer) do
  162.         with Answer[I] do
  163.           if DebugMode then
  164.             WriteLn(I + 1, ': ', Text, ' [[', Link.Index, ':', Link.Name, ']]')
  165.           else
  166.             WriteLn(I + 1, ': ', Text);
  167.     end
  168.   else
  169.     WriteLn(MSG_WRONG_PAGE_INDEX, PageIndex);
  170. end;
  171.  
  172. procedure PlayInteractive(const Book: TInteractiveBook);
  173. var Answer: string; CurrentPage, AnswerIndex, Error: Integer;
  174.   // Проверка корректности ввода занимает слишком много места:
  175.   procedure PickAnswer;
  176.   begin
  177.     Write('> ');
  178.     ReadLn(Answer);
  179.     Val(Answer, AnswerIndex, Error);
  180.     if Error = 0 then
  181.       with Book.Page[CurrentPage] do
  182.         if (High(Answer) >= (AnswerIndex - 1)) and (AnswerIndex > 0) then
  183.           CurrentPage := Answer[AnswerIndex - 1].Link.Index
  184.         else
  185.           WriteLn(MSG_WRONG_ANSWER_INDEX)
  186.     else
  187.       if Answer <> EXIT_ANSWER then
  188.         WriteLn(MSG_WRONG_ANSWER);
  189.   end;
  190. //PlayInteractive:
  191. begin
  192.   if Length(Book.Page) > 0 then
  193.   begin
  194.     WriteLn(MSG_INTERACTIVE_MODE);
  195.     CurrentPage := 0;
  196.     repeat
  197.       WriteLn;
  198.       DisplayPage(Book, CurrentPage);
  199.       PickAnswer;
  200.     until Answer = EXIT_ANSWER;
  201.     WriteLn(MSG_BYE);
  202.   end else
  203.     WriteLn(MSG_EMPTY_BOOK);
  204. end;
  205.  
  206. var
  207.   InputFileName: string;
  208. begin
  209.   InputFileName := ParamStr(1);
  210.   if Length(InputFileName) = 0 then
  211.     WriteLn(MSG_NO_INPUT)
  212.   else
  213.     PlayInteractive(CalculateLinksIn(LoadBookFrom(InputFileName)));
  214. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement