You probably can use the package zref:
The following example could be a starting-point:
In all latex-runs retrieve values of pseudo-random-variables from zref-labels.
In the first latex-run create values for pseudo-random-variables via pseudo-random generator.
In consecutive latex-runs obtain values for pseudo-random-variables from zref-labels.
In all latex-runs store values of pseudo-random-variables in zref-labels.
In order to get everything right you need to compile at least twice without deleting aux-files between latex-runs.
With xlop to each character of a result/carry/operand/intermediary number/remainder an instance of the corresponding style-macro is applied.
Therefore in the first LaTeX-run, where zref-labels are not defined yet,
- style-macros
\undefinedstyle/\undefinedcarrystyle are used that deliver question-marks.
\zref@extractdefault defaults everything to 0 so that both operands and calculations/results yield 0, a digit-sequence which consists of a single character so that only one instance of the corresponding question-mark-delivering style-macro is applied.
In consecutive LaTeX-runs zref-labels are defined and values of pseudo-random-variables should be retrieved properly.
Where in consecutive LaTeX-runs answers are undesired resultstyle=\phantom instead of resultstyle=\color{white} is used and carries are done as \phantoms, too, via carrystyle=\phantomcarrystyle.
I am not quite sure if this is a good idea. But this way results are not in the pdf-file at all in the places where they shall not be seen, and therefore people cannot blindly copy-paste results from these places/cannot use GhostScript-trickery for making results visible in these places.
\documentclass{article}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{enumitem}
\usepackage{tikz}
\pgfmathsetseed{\number\pdfrandomseed}
\usepackage{xlop}
\usepackage{zref}
\makeatletter
\zref@newprop{AddOnea}{0}%
\zref@newprop{AddTwoa}{0}%
\zref@newprop{range}{0}%
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\Exchange[2]{#2#1}%
\makeatother
\usepackage{fancyhdr}
\newcommand{\DifficultyOne}{999}%
\newcommand{\DifficultyTwo}{99}%
%---------------------------------------%
\makeatletter
\newcommand\undefinedstyle[1]{\nfss@text{\reset@font\bfseries ??}}%
\newcommand\undefinedcarrystyle[1]{{\nfss@text{\reset@font\scriptsize\bfseries ??}}}%
\newcommand\phantomcarrystyle[1]{\phantom{\scriptsize{#1}}}%
\newcommand{\AddQuestions}[2]{%
\zref@setcurrent{range}{#1}%
\zref@labelbyprops{#2}{range}%
\foreach \i in {1,...,#1}{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{Question \i: }%
\zref@ifrefundefined{#2.\i}{%
\pgfmathtruncatemacro{\AddOnea}{random(\DifficultyOne)}%
\pgfmathtruncatemacro{\AddTwoa}{random(\DifficultyTwo)}%
\expandafter\PassFirstToSecond\expandafter{\AddOnea}{\zref@setcurrent{AddOnea}}%
\expandafter\PassFirstToSecond\expandafter{\AddTwoa}{\zref@setcurrent{AddTwoa}}%
}{%
\expandafter\expandafter\expandafter\PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddOnea}{0}%
}{\zref@setcurrent{AddOnea}}%
\expandafter\expandafter\expandafter\PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddTwoa}{0}%
}{\zref@setcurrent{AddTwoa}}%
}%
\zref@labelbyprops{#2.\i}{AddOnea, AddTwoa}%
\zref@refused{#2.\i}%
\zref@ifrefundefined{#2.\i}{%
% actually setting carrystyle is not needed as there are no carries with defaults 0, but ...
\opadd[carryadd=true, voperator=bottom, resultstyle=\undefinedstyle, operandstyle=\undefinedstyle, carrystyle=\undefinedcarrystyle]%
}{%
% Only operands shall be seen, so turn everything else into \phantom
\opadd[carryadd=true, voperator=bottom, resultstyle=\phantom, carrystyle=\phantomcarrystyle]%
}%
{\zref@extractdefault{#2.\i}{AddOnea}{0}}%
{\zref@extractdefault{#2.\i}{AddTwoa}{0}}%
}%
\par
}%
\newcommand{\AddAnswers}[1]{%
\zref@ifrefundefined{#1}{%
\zref@refused{#1}%
\par\noindent
{\normalfont\bfseries [Question-series ``#1'' undefined, probably re-run latex, see messages on console/in .log-file]}%
\par
}{%
\expandafter\PassFirstToSecond\expandafter{%
\romannumeral0%
\expandafter\expandafter\expandafter\Exchange
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#1}{range}{1}%
}{ 1,...,}%
}{\foreach \i in }{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{\rlap{Answer \i: }\phantom{Question \i: }}%
\zref@refused{#1.\i}%
\opadd[carryadd=true, voperator=bottom]%
{\zref@extractdefault{#1.\i}{AddOnea}{0}}%
{\zref@extractdefault{#1.\i}{AddTwoa}{0}}%
}%
}%
\par
}%
\makeatother
\begin{document}
\noindent Here come the questions with answers:
\AddAnswers{This question-series label}%
\newpage
\noindent Here come the questions without answers:
\AddQuestions{10}{This question-series label}%
\newpage
\noindent Here come the questions with answers again:
\AddAnswers{This question-series label}%
\end{document}

If you wish, e.g., to have 20 exam-sheets, each sheet holding another set of 15 randomly generated exercises, you can combine the above with a loop for replicating a set of tokens k times:
\DoKtimes{⟨TeX-number-quantity of value K⟩}{⟨tokens⟩}
On the console and in the .log-file obey the message
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.
, i.e., compile as often as needed without deleting aux-files between latex-runs for that message not occurring any more.
\documentclass{article}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{enumitem}
\usepackage{tikz}
\pgfmathsetseed{\number\pdfrandomseed}
\usepackage{xlop}
\usepackage[user]{zref}
\usepackage{fancyhdr}
\makeatletter
%%=============================================================================
%% \DoKtimes{<TeX-number-quantity of value K>}{<tokens>}
%%=============================================================================
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
\newcommand\DoKtimes[2]{%
\romannumeral
\expandafter\DoKtimesloop\expandafter{\romannumeral\number\number#1 000}{#2}{}%
}%
\newcommand\DoKtimesloop[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#3}{%
\expandafter\DoKtimesloop\expandafter{\UD@firstoftwo{}#1}{#2}{#3#2}%
}%
}%
%%=============================================================================
\zref@newprop{AddOnea}{0}%
\zref@newprop{AddTwoa}{0}%
\zref@newprop{range}{0}%
\newcommand\undefinedstyle[1]{\nfss@text{\reset@font\bfseries ??}}%
\newcommand\undefinedcarrystyle[1]{{\nfss@text{\reset@font\scriptsize\bfseries ??}}}%
\newcommand\phantomcarrystyle[1]{\phantom{\scriptsize{#1}}}%
\newcommand{\AddQuestions}[2]{%
\zref@setcurrent{range}{#1}%
\zref@labelbyprops{#2}{range}%
\foreach \i in {1,...,#1}{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{Question \i: }%
\zref@ifrefundefined{#2.\i}{%
\pgfmathtruncatemacro{\AddOnea}{random(\DifficultyOne)}%
\pgfmathtruncatemacro{\AddTwoa}{random(\DifficultyTwo)}%
\expandafter\UD@PassFirstToSecond\expandafter{\AddOnea}{\zref@setcurrent{AddOnea}}%
\expandafter\UD@PassFirstToSecond\expandafter{\AddTwoa}{\zref@setcurrent{AddTwoa}}%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddOnea}{0}%
}{\zref@setcurrent{AddOnea}}%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddTwoa}{0}%
}{\zref@setcurrent{AddTwoa}}%
}%
\zref@labelbyprops{#2.\i}{AddOnea, AddTwoa}%
\zref@refused{#2.\i}%
\zref@ifrefundefined{#2.\i}{%
% actually setting carrystyle is not needed as there are no carries with defaults 0, but ...
\opadd[carryadd=true, voperator=bottom, resultstyle=\undefinedstyle, operandstyle=\undefinedstyle, carrystyle=\undefinedcarrystyle]%
}{%
% Only operands shall be seen, so turn everything else into \phantom
\opadd[carryadd=true, voperator=bottom, resultstyle=\phantom, carrystyle=\phantomcarrystyle]%
}%
{\zref@extractdefault{#2.\i}{AddOnea}{0}}%
{\zref@extractdefault{#2.\i}{AddTwoa}{0}}%
}%
\par
}%
\newcommand{\AddAnswers}[1]{%
\zref@ifrefundefined{#1}{%
\zref@refused{#1}%
\par\noindent
{\normalfont\bfseries [Question-series ``#1'' undefined, probably re-run latex, see messages on console/in .log-file]}%
\par
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#1}{range}{1}%
}{\UD@stopromannumeral 1,...,}%
}{\foreach \i in }{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{\rlap{Answer \i: }\phantom{Question \i: }}%
\zref@refused{#1.\i}%
\opadd[carryadd=true, voperator=bottom]%
{\zref@extractdefault{#1.\i}{AddOnea}{0}}%
{\zref@extractdefault{#1.\i}{AddTwoa}{0}}%
}%
}%
\par
}%
\newcommand{\DifficultyOne}{999}%
\newcommand{\DifficultyTwo}{99}%
%%=============================================================================
%% A dirty hack for resetting page-numbers with each sheet
\newcommand\resetpagezreflabelnamenumber{0}%
\newcommand\resetpage{%
\newpage
\xdef\resetpagezreflabelnamenumber{\number\numexpr\resetpagezreflabelnamenumber+1\relax}%
\zref@labelbyprops{pagereset-\resetpagezreflabelnamenumber}{page}%
}%
\newcommand\obtainlastresetpage{%
\zref@extractdefault{pagereset-\resetpagezreflabelnamenumber}{page}{0}%
}%
\newcommand\ps@resetableplain{%
\let@mkboth@gobbletwo
\let@oddhead@empty
\def@oddfoot{\reset@font\hfil@arabic{\number\numexpr\value{page}-\obtainlastresetpage+1\relax}\hfil}%
\let@evenhead@empty
\let@evenfoot@oddfoot
}%
\makeatother
\pagestyle{resetableplain}
\begin{document}
%%%%%%%
%% Create 20 sheets of paper, each holding 15 randomly generated exercises without answers:
% Initialize the macro for counting sheets to 0:
\newcommand\sheetNumber{0}%
\DoKtimes{20}{%
\resetpage
% Increment \sheetNumber
\edef\sheetNumber{\number\numexpr\sheetNumber+1\relax}%
\section{Question sheet \sheetNumber (without answers)}
\AddQuestions{15}{This question-series-sheet-\sheetNumber-label}%
}%
%%%%%%%
%% Re-create the 20 sheets of paper from above, this time with answers:
% Initialize the macro for counting sheets to 0:
\renewcommand\sheetNumber{0}%
\DoKtimes{20}{%
\resetpage
% Increment \sheetNumber
\edef\sheetNumber{\number\numexpr\sheetNumber+1\relax}%
\section{Question sheet \sheetNumber (with answers)}
\AddAnswers{This question-series-sheet-\sheetNumber-label}%
}%
\end{document}

If you wish to individualize the sheets, e.g., to have the name of the examinee, you can, e.g., define a loop, which iterates on a list of undelimited arguments, defining a macro from each name and doing ⟨tokens⟩:
\DefineVariableWithEachAndDo{⟨(already defined) macro holding list of undelimited arguments⟩}%
{⟨macro (to be (re)defined in each iteration) holding current element of list⟩}%
{⟨tokens⟩}
On the console and in the .log-file obey the message
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.
, i.e., compile as often as needed without deleting aux-files between latex-runs for that message not occurring any more.
\documentclass{article}
\usepackage{amsmath}
\usepackage{mathtools}
\usepackage{enumitem}
\usepackage{tikz}
\pgfmathsetseed{\number\pdfrandomseed}
\usepackage{xlop}
\usepackage[user]{zref}
\usepackage{fancyhdr}
\makeatletter
%%=============================================================================
%% \DefineVariableWithEachAndDo{<macro holding list of undelimited arguments>}%
%% {<macro holding current element of list>}%
%% {<tokens>}
%%=============================================================================
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`^^00}%
\newcommand\DefineVariableWithEachAndDo[3]{%
\romannumeral
\expandafter\UD@Exchange
\expandafter{#1}{\DefineVariableWithEachAndDoloop{#2}{#3}{}}{\relax}%
}%
\newcommand\DefineVariableWithEachAndDoloop[4]{%
\ifx\relax#4\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@stopromannumeral#3}{%
\DefineVariableWithEachAndDoloop{#1}{#2}{#3\def#1{#4}#2}%
}%
}%
%%=============================================================================
\zref@newprop{AddOnea}{0}%
\zref@newprop{AddTwoa}{0}%
\zref@newprop{range}{0}%
\newcommand\undefinedstyle[1]{\nfss@text{\reset@font\bfseries ??}}%
\newcommand\undefinedcarrystyle[1]{{\nfss@text{\reset@font\scriptsize\bfseries ??}}}%
\newcommand\phantomcarrystyle[1]{\phantom{\scriptsize{#1}}}%
\newcommand{\AddQuestions}[2]{%
\zref@setcurrent{range}{#1}%
\zref@labelbyprops{#2}{range}%
\foreach \i in {1,...,#1}{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{Question \i: }%
\zref@ifrefundefined{#2.\i}{%
\pgfmathtruncatemacro{\AddOnea}{random(\DifficultyOne)}%
\pgfmathtruncatemacro{\AddTwoa}{random(\DifficultyTwo)}%
\expandafter\UD@PassFirstToSecond\expandafter{\AddOnea}{\zref@setcurrent{AddOnea}}%
\expandafter\UD@PassFirstToSecond\expandafter{\AddTwoa}{\zref@setcurrent{AddTwoa}}%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddOnea}{0}%
}{\zref@setcurrent{AddOnea}}%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#2.\i}{AddTwoa}{0}%
}{\zref@setcurrent{AddTwoa}}%
}%
\zref@labelbyprops{#2.\i}{AddOnea, AddTwoa}%
\zref@refused{#2.\i}%
\zref@ifrefundefined{#2.\i}{%
% actually setting carrystyle is not needed as there are no carries with defaults 0, but ...
\opadd[carryadd=true, voperator=bottom, resultstyle=\undefinedstyle, operandstyle=\undefinedstyle, carrystyle=\undefinedcarrystyle]%
}{%
% Only operands shall be seen, so turn everything else into \phantom
\opadd[carryadd=true, voperator=bottom, resultstyle=\phantom, carrystyle=\phantomcarrystyle]%
}%
{\zref@extractdefault{#2.\i}{AddOnea}{0}}%
{\zref@extractdefault{#2.\i}{AddTwoa}{0}}%
}%
\par
}%
\newcommand{\AddAnswers}[1]{%
\zref@ifrefundefined{#1}{%
\zref@refused{#1}%
\par\noindent
{\normalfont\bfseries [Question-series ``#1'' undefined, probably re-run latex, see messages on console/in .log-file]}%
\par
}{%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\zref@extractdefault{#1}{range}{1}%
}{\UD@stopromannumeral 1,...,}%
}{\foreach \i in }{%
\par\ifnum\i=1 \else\medskip\fi\noindent\hbox{\rlap{Answer \i: }\phantom{Question \i: }}%
\zref@refused{#1.\i}%
\opadd[carryadd=true, voperator=bottom]%
{\zref@extractdefault{#1.\i}{AddOnea}{0}}%
{\zref@extractdefault{#1.\i}{AddTwoa}{0}}%
}%
}%
\par
}%
\newcommand{\DifficultyOne}{999}%
\newcommand{\DifficultyTwo}{99}%
%%=============================================================================
%% A dirty hack for resetting page-numbers with each sheet
\newcommand\resetpagezreflabelnamenumber{0}%
\newcommand\resetpage{%
\newpage
\xdef\resetpagezreflabelnamenumber{\number\numexpr\resetpagezreflabelnamenumber+1\relax}%
\zref@labelbyprops{pagereset-\resetpagezreflabelnamenumber}{page}%
}%
\newcommand\obtainlastresetpage{%
\zref@extractdefault{pagereset-\resetpagezreflabelnamenumber}{page}{0}%
}%
\newcommand\ps@resetableplain{%
\let@mkboth@gobbletwo
\let@oddhead@empty
\def@oddfoot{\reset@font\hfil@arabic{\number\numexpr\value{page}-\obtainlastresetpage+1\relax}\hfil}%
\let@evenhead@empty
\let@evenfoot@oddfoot
}%
\makeatother
\pagestyle{resetableplain}
\newcommand\ExamineeNameList{%
{Coby}%
{Johan}%
{Rudy}%
{Rodrigo}%
{Alejandra}%
{Oswaldo}%
{Yusuf}%
{Luz}%
{Denzel}%
{Abigayle}%
{Bobby}%
{Margaret}%
{Eli}%
{Billy}%
{Jazmine}%
{Miguel}%
{Jon}%
{Valentin}%
{Lance}%
{Skyler}%
}%
\newcommand\ThisExamimeesName{}% <- This raises an error if the macro is already in use by some package/by some other code.
\begin{document}
%%%%%%%
%% Create a sheet of 15 ramdom questions for each examinee:
\DefineVariableWithEachAndDo{\ExamineeNameList}{\ThisExamimeesName}{%
\resetpage
\section{Question sheet without answers for \ThisExamimeesName}%
\AddQuestions{15}{This question-series-sheet-\ThisExamimeesName-label}%
}%
%%%%%%%
%% Re-create the sheets from above, this time with answers:
\DefineVariableWithEachAndDo{\ExamineeNameList}{\ThisExamimeesName}{%
\resetpage
\section{Question sheet with answers for \ThisExamimeesName}%
\AddAnswers{This question-series-sheet-\ThisExamimeesName-label}%
}%
\end{document}

gs --version) then there is a rendering parameter-dBlackTextthat you can use to make all text black in an existing pdf, for example the exercise sheets:gs -q -dNOPAUSE -dBATCH -dBlackText -sDEVICE=pdfwrite -sOutputFile=answers20.pdf exercise20.pdf. – Marijn Aug 03 '22 at 15:03carryadd=true, carrystyle=\scriptsize\color{white}in the questions. – Marijn Aug 03 '22 at 16:07