有没有更好的方法在 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}
这会将幻灯片分成两部分,一边是游戏,另一边是评论。我希望能够通过在网格中逐张添加 O 和 X 来玩游戏,同时添加评论。显然,第一步是:
\begin{tabular}{c|c|c}
& & \\ \hline
& X & \\ \hline
& &
\end{tabular}
我只是想知道是否有更好的方法来实现这一点?如何让游戏逐张幻灯片填充(例如,X 在幻灯片 1 上播放,O 在幻灯片 2 上播放,添加评论,X 在幻灯片 3 上播放,等等)。
答案1
当有大量重复代码时(在本例中针对不同的游戏),我会编写一个宏来简化我的乳胶文件 - 并且使得以后需要时更容易更改内容。
(我)我想到的第一个想法是编写一个以“输入”为内容的宏:
- 以逗号分隔的 X 位置列表
- 以逗号分隔的 O 位置列表
- 评论
我将 X 和 O 位置编码为 1、2、...、9,从网格中的左到右、从上到下读取。例如,\NoughtsCrosses{5,6}{9}{Third move}
将产生
(二)第二种方法是假设动作交替为 X、O、X……,然后给出一个逗号分隔的动作列表以及评论。例如,\NoughtsCrossesII{5,9,6,4}{Fourth move}
将产生
正如 cfr 在问题评论中所说,您可能希望将这些命令包含在类似的东西中\only<5>{\NoughtsCrossesII{5,9,6,4}{Fourth move}}
,以便一次显示一个动作。我在下面的 MWE 中这样做了。
(三)考虑到最后一句话,并且稍微夸张一点,更智能的宏会简单地获取动作和评论的列表,然后在框架中构建游戏的所有幻灯片。到目前为止,所有宏都使用 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 节点内,作为宽度为 40mm 的参差不齐的左侧文本。您可能需要微调 (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!
}