Есть ли лучший способ создать игру в крестики-нолики в LaTeX? В настоящее время у меня есть следующий код:
\documentclass{beamer}
\mode<presentation>
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amsthm}
\usepackage{array}
\usepackage{graphicx}
\begin{document}
\begin{frame}
\frametitle{A game of noughts and crosses}
Here is a game of noughts and crosses. On the left we have the game, and on the right the commentary.\\
\begin{columns}
\begin{column}{0.1\textwidth}
\vspace{1.75cm}
\begin{tabular}{c|c|c}
& & \\ \hline
& & \\ \hline
& &
\end{tabular}
\end{column} \pause
\begin{column}{0.70\textwidth}
\begin{itemize}
\item crosses goes first, makes optimal move. A good player will never lose from this start.
\end{itemize}
\end{column}
\end{columns}
\end{frame}
\end{document}
Это разделяет слайд на две части, где с одной стороны у меня игра, а с другой — комментарии. Я хочу иметь возможность играть в игру, добавляя О и Х в сетку, слайд за слайдом, одновременно добавляя комментарии. Очевидно, что первый ход:
\begin{tabular}{c|c|c}
& & \\ \hline
& X & \\ \hline
& &
\end{tabular}
Мне просто интересно, есть ли лучший способ сделать это? Как мне заставить игру заполняться слайд за слайдом (т. е. X играет на слайде 1, O играет на слайде 2, добавить комментарий, X играет на слайде 3 и т. д.).
решение1
Когда планируется много повторяющегося кода (в данном случае для разных игр), я пишу макрос, чтобы упростить свой файл Latex и облегчить внесение изменений в дальнейшем, если это необходимо.
(Я)Первая мысль, пришедшая в голову, — написать макрос, который принимает в качестве «входных данных»:
- список позиций X, разделенных запятыми
- список позиций букв O, разделенных запятыми
- комментарий
Позиции X и O я бы закодировал как 1,2,...,9, читая слева направо и сверху вниз в сетке. Например, \NoughtsCrosses{5,6}{9}{Third move}
это даст
(II)Второй способ сделать это — предположить, что ходы чередуются как X,O,X,... и просто дать список ходов, разделенных запятыми, вместе с комментарием. Например, \NoughtsCrossesII{5,9,6,4}{Fourth move}
будет производить
Как говорит cfr в комментариях к вопросу, вы, вероятно, захотите заключить эти команды во что-то вроде того, \only<5>{\NoughtsCrossesII{5,9,6,4}{Fourth move}}
чтобы показывать по одному ходу за раз. Я делаю это в MWE ниже.
(III)Принимая во внимание последнее замечание и немного отступая от правил, более умный макрос просто взял бы список ходов и комментариев, а затем построил бы все слайды для игры в кадре. Все макросы до сих пор использовали tikz, а команда tikz \foreach
допускает несколько циклических переменных, разделенных слешем, поэтому самый простой способ реализовать это — использовать следующий синтаксис:
\NoughtsCrossesGame{%
5/First move,
9/Second move,
6/Third move,
4/Fouth move
}
Вывод "тот же", что и выше, за исключением того, что "игра" появляется на 4 слайдах. Макрос \NoughtsCrossesGame
принимает необязательный аргумент, который ведет себя аналогично необязательному аргументу команды \pause
. То есть, он действует как "смещение", контролируя, когда ходы в игре начинают появляться на слайдах для данного кадра.
Ниже приведен код, дающий определения трем макросам, приведенным выше, дополненный до MWE, чтобы показать, как их использовать:
\documentclass{beamer}
\usepackage{tikz}
% Helper macro for placing a node at "position" 1,2,...,9 into the grid
% \PlaceMarker[optional node styling]<position><X or O>
\usepackage{xparse}
\NewDocumentCommand\PlaceMarker{ O{}mm }{%
\ifnum#2>0
\def\markercol{#1}
\def\PlaceMakerNumber{#2}
\else
\def\markercol{red}
\def\PlaceMakerNumber{\numexpr-#2}
\fi
\ifcase\PlaceMakerNumber%
\or\node[\markercol] at (1,3) {#3}; % 1 = (3,1)
\or\node[\markercol] at (2,3) {#3}; % 2 = (3,2)
\or\node[\markercol] at (3,3) {#3}; % 3 = (3,3)
\or\node[\markercol] at (1,2) {#3}; % 4 = (2,1)
\or\node[\markercol] at (2,2) {#3}; % 5 = (2,2)
\or\node[\markercol] at (3,2) {#3}; % 6 = (2,3)
\or\node[\markercol] at (1,1) {#3}; % 7 = (1,1)
\or\node[\markercol] at (2,1) {#3}; % 8 = (1,2)
\or\node[\markercol] at (3,1) {#3}; % 9 = (1,3)
\fi
}
% Creates a noughts and cross game with commentary
%\NoughtsCrosses{x-positions}{y-positions}{Commentary}
\newcommand\NoughtsCrosses[3]{%
\begin{tikzpicture}
\foreach \x in {1.5,2.5} {
\draw[ultra thick](\x,0.5)--+(0,3);
\draw[ultra thick](0.5,\x)--+(3,0);
}
\foreach \x in {#1} {\PlaceMarker{\x}{X}}
\foreach \y in {#2} {\PlaceMarker{\y}{O}}
\node[text width=40mm,text ragged, anchor=west] at (5,3) {#3};
\end{tikzpicture}
}
% Creates a noughts and cross game with commentary
%\NoughtsCrossesII{move positions}{Commentary}
% Moves alternate as X,O,...
\newcommand\NoughtsCrossesII[2]{%
\begin{tikzpicture}
\foreach \x in {1.5,2.5} {
\draw[ultra thick](\x,0.5)--+(0,3);
\draw[ultra thick](0.5,\x)--+(3,0);
}
\foreach \move [count=\m] in {#1} {
\ifodd\m \PlaceMarker{\move}{X}
\else\PlaceMarker{\move}{O}
\fi
}
\node[text width=40mm,text ragged, anchor=west] at (5,3) {#2};% add comment
\end{tikzpicture}
}
% Creates a noughts and cross game with commentary
%\NoughtsCrossesII{move positions}{Commentary}
% Moves alternate as X,O,...
\makeatletter
\newcommand\NoughtsCrossesGame[2][0]{%
\begin{tikzpicture}
\foreach \x in {1.5,2.5} {
\draw[ultra thick](\x,0.5)--+(0,3);
\draw[ultra thick](0.5,\x)--+(3,0);
}
% count length of game
\foreach \move/\com [count=\lmove] in {#2} {}
\def\endgame{\the\numexpr\lmove+#1\relax}
\def\Endgame{\the\numexpr\endgame+1\relax}
\foreach \move/\com [count=\m,
evaluate=\m as \mm using int(\m+#1),
evaluate=\move as \mov using int(abs(-\move))] in {#2} {
\ifodd\m\def\Marker{X}
\else\def\Marker{O}
\fi
\def\mmm{\the\numexpr\mm+1\relax}
\only<\mm>{\PlaceMarker[blue]{\mov}{\Marker}}
\ifnum\move<0
\only<\mmm-\endgame>{\PlaceMarker{\mov}{\Marker}}
\only<\Endgame->{\PlaceMarker[blue]{\mov}{\Marker}}
\else
\only<\mmm->{\PlaceMarker{\mov}{\Marker}}
\fi
\only<\mm>{
\node[text width=40mm,text ragged, anchor=west] at (5,3){\com};
}
}
\end{tikzpicture}
}
\makeatother
\begin{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{frame}{A game of noughts and crosses}
Here is a game of noughts and crosses. On the left we have the
game, and on the right the commentary.
\medskip
\only<2>{\NoughtsCrosses{5}{}{First move}}
\only<3>{\NoughtsCrosses{5}{9}{Second move}}
\only<4>{\NoughtsCrosses{5,6}{9}{Third move}}
\only<5>{\NoughtsCrossesII{5,9,6,4}{Fourth move}}
\end{frame}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{frame}{Another game of noughts and crosses}
Here is a game of noughts and crosses. On the left we have the
game, and on the right the commentary.
\pause Here is a clever game
\medskip
\NoughtsCrossesGame[1]{% offset start of game because of \pause above
-5/First move,
6/Middle square,
7/Forcing,
3/Forced block,
-9/Forced block leading to a pincer!,
8/Forced defence!,
-1/Wins!
}
\end{frame}
\end{document}
Несколько комментариев:
- Все макросы используюттикздля рисования игр, так как это дает лучший контроль над размещением маркеров X/O и рисованием сетки
- Макрос
\PlaceMarker
представляет собой "вспомогательную функцию" для размещения маркера в указанной позиции. Используется\ifcase
для перевода индексов позиции1,2,...,9
в(x,y)
координаты. - Макросы используют
\foreach
для циклического обхода списка позиций маркеров, разделенных запятыми. Второй макрос использует, кроме того,\ifodd
для определения, следует ли разместить X или O. - "Комментарий" размещается внутри узла tikz как неровный левый текст шириной 40 мм. Возможно, вам придется точно настроить размещение (x,y)-координат и ширину текста.
- Как и просили в комментариях, я улучшил макросы, чтобы облегчить выделение некоторых ходов. Задание индекса "отрицательной позиции" теперь окрашивает соответствующий
X
илиO
. Например,\NoughtsCrosses{-5,-6}{9}{Third move}
сделает дваX
s красными. - Что еще забавнее,
\NoughtsCrossesGame
макрос теперь раскрашивает каждый ход, а затем в конце игры выделяет последнюю "выигрышную" полосу синим цветом - выигрышная полоса должна быть снова выделена с использованием отрицательных индексов позиции. В обоих случаях раскраска выполняется с помощью улучшения\PlaceMarker
, хотя автоматическая раскраска для\NoughtsCrosses
более сложна.
Для полноты картины приведем анимированную версию последнего слайда, созданного в этом \NoughtsCrossesGame
примере:
За исключением кадра, созданного beamer, эта коллекция слайдов по сути является выводом команды:
\NoughtsCrossesGame[1]{% offset start of game because of \pause above
-5/First move,
6/Middle square,
7/Forcing,
3/Forced block,
-9/Forced block leading to a pincer!,
8/Forced defence!,
-1/Wins!
}