Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- program SimpleInteractiveBook;
- const
- EXIT_ANSWER = 'bye';
- TAG_ERROR = '[ERROR] ';
- TAG_WARN = '[Warning] ';
- TAG_HINT = '[Hint] ';
- MSG_NO_INPUT =
- TAG_ERROR + 'Select book file. Example:' + LineEnding + '> SIB Book.book';
- MSG_WRONG_ORDER = TAG_WARN + 'Page answer before first page!';
- MSG_WRONG_PAGE_INDEX = TAG_ERROR + 'Wrong page index: ';
- MSG_WRONG_ANSWER_INDEX = TAG_HINT + 'Wrong answer index, pick another.';
- MSG_WRONG_ANSWER = TAG_HINT + 'To pick an answer, write it''s number.';
- MSG_INTERACTIVE_MODE =
- TAG_HINT + 'Interactive Mode. Type "' + EXIT_ANSWER + '" to exit.';
- MSG_EMPTY_BOOK = TAG_ERROR + 'Empty book, select another one!';
- MSG_LOST_PAGE = TAG_ERROR + 'Lost page or wrong name: ';
- MSG_BYE = 'See ya!';
- type
- TPageLink = record
- Index: Integer; // номер связанной страницы в массиве страниц
- Name: string; // имя связанной страницы
- end;
- TPageAnswer = record
- Text: string;
- Link: TPageLink; // связанная с ответом страница
- end;
- TPageAnswers = array of TPageAnswer;
- TBookPage = record
- Name: string; // внутреннее имя страницы (для связей)
- Text: string;
- Answer: TPageAnswers;
- end;
- TBookPages = array of TBookPage;
- TInteractiveBook = record
- Name: string;
- Page: TBookPages;
- Optimized: Boolean; // определены ли индексы связей?
- end;
- function LoadBookFrom(const FileName: string): TInteractiveBook;
- const
- // Символы синтаксиса книги:
- SYM_COMMENT = '#';
- SYM_PAGE = '!';
- SYM_ANSWER = '?';
- SYM_MARKER = ':';
- var
- BookFile: TextFile;
- Line, LinkName: string;
- MarkerPos: Integer;
- Pages: TBookPages;
- // Следующие субпроцедуры выделены для краткости основного кода:
- procedure StorePage;
- begin
- SetLength(Pages, Length(Pages) + 1); // новая страница
- with Pages[High(Pages)] do
- begin
- Name := Copy(Line, 1, MarkerPos - 2);
- Text := Copy(Line, MarkerPos + 2, High(Line));
- end;
- end;
- procedure StoreLine;
- begin
- with Pages[High(Pages)] do
- Text += LineEnding + Line;
- end;
- procedure StoreAnswer;
- begin
- if Length(Pages) > 0 then
- with Pages[High(Pages)] do
- begin
- SetLength(Answer, Length(Answer) + 1); // следующий вариант ответа
- with Answer[High(Answer)] do
- begin
- Link.Name := Copy(Line, 1, MarkerPos - 2);
- Text := Copy(Line, MarkerPos + 2, High(Line));
- end;
- end
- else
- WriteLn(MSG_WRONG_ORDER);
- end;
- //LoadBookFrom:
- begin
- AssignFile(BookFile, FileName);
- Reset(BookFile);
- SetLength(Pages, 0);
- repeat
- ReadLn(BookFile, Line);
- // Отбрасываем пустые строки и строки-комментарии:
- if (Length(Line) > 0) and (Line[1] <> SYM_COMMENT) then
- begin
- MarkerPos := Pos(SYM_MARKER, Line);
- if MarkerPos > 0 then // строка имеет маркер?
- begin
- // Предполагаем, что слева от маркера - имя связанной страницы:
- LinkName := Copy(Line, 1, MarkerPos - 1);
- // Последним символом LinkName может быть ? (ответ) или ! (страница):
- case LinkName[High(LinkName)] of
- SYM_PAGE: StorePage;
- SYM_ANSWER: StoreAnswer;
- else // Если символа ответа/страницы нет, то это строка страницы:
- StoreLine;
- end;
- end else // Если маркера нет, то это строка страницы:
- StoreLine;
- end;
- until Eof(BookFile);
- CloseFile(BookFile);
- Result.Name := FileName;
- Result.Page := Pages;
- end;
- function FindPageIndex(const Book: TInteractiveBook;
- const PageName: string): Integer;
- var I: Integer;
- begin
- Result := -1;
- for I := 0 to High(Book.Page) do
- if Book.Page[I].Name = PageName then
- begin
- Result := I;
- Break;
- end;
- if Result < 0 then // если не смогли найти
- begin
- WriteLn(MSG_LOST_PAGE, PageName);
- Halt(42); // ой всё, не хочу с exception связываться, пусть сразу падает
- end;
- end;
- // Пытается найти индексы страниц по их именам:
- function CalculateLinksIn(Book: TInteractiveBook): TInteractiveBook;
- var A, P: Integer;
- begin
- for P := 0 to High(Book.Page) do
- for A := 0 to High(Book.Page[P].Answer) do
- with Book.Page[P].Answer[A].Link do
- Index := FindPageIndex(Book, Name);
- Book.Optimized := true; // добавил для примера, использовать лень
- Result := Book;
- end;
- // Выводит страницу с ответами, с DebugMode выводит данные связей:
- procedure DisplayPage(const Book: TInteractiveBook; const PageIndex: Integer;
- const DebugMode: Boolean = false);
- var I: Integer;
- begin
- if (Length(Book.Page) > PageIndex) and (PageIndex >= 0) then
- with Book.Page[PageIndex] do
- begin
- if DebugMode then
- WriteLn('[[', PageIndex, ':', Name, ']]');
- WriteLn(Text);
- for I := 0 to High(Answer) do
- with Answer[I] do
- if DebugMode then
- WriteLn(I + 1, ': ', Text, ' [[', Link.Index, ':', Link.Name, ']]')
- else
- WriteLn(I + 1, ': ', Text);
- end
- else
- WriteLn(MSG_WRONG_PAGE_INDEX, PageIndex);
- end;
- procedure PlayInteractive(const Book: TInteractiveBook);
- var Answer: string; CurrentPage, AnswerIndex, Error: Integer;
- // Проверка корректности ввода занимает слишком много места:
- procedure PickAnswer;
- begin
- Write('> ');
- ReadLn(Answer);
- Val(Answer, AnswerIndex, Error);
- if Error = 0 then
- with Book.Page[CurrentPage] do
- if (High(Answer) >= (AnswerIndex - 1)) and (AnswerIndex > 0) then
- CurrentPage := Answer[AnswerIndex - 1].Link.Index
- else
- WriteLn(MSG_WRONG_ANSWER_INDEX)
- else
- if Answer <> EXIT_ANSWER then
- WriteLn(MSG_WRONG_ANSWER);
- end;
- //PlayInteractive:
- begin
- if Length(Book.Page) > 0 then
- begin
- WriteLn(MSG_INTERACTIVE_MODE);
- CurrentPage := 0;
- repeat
- WriteLn;
- DisplayPage(Book, CurrentPage);
- PickAnswer;
- until Answer = EXIT_ANSWER;
- WriteLn(MSG_BYE);
- end else
- WriteLn(MSG_EMPTY_BOOK);
- end;
- var
- InputFileName: string;
- begin
- InputFileName := ParamStr(1);
- if Length(InputFileName) = 0 then
- WriteLn(MSG_NO_INPUT)
- else
- PlayInteractive(CalculateLinksIn(LoadBookFrom(InputFileName)));
- end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement