I hope the following does what you want. It creates an environment called randomhidetable that is a tabular in which each field with \hide is replaced by a random element of the sequence of first $n$ characters of the alphabet (each element is only used once). The table is evaluated twice, the first evaluation counts the number of \hide elements, the second typesets it. You can change \hide to any arbitrary macro using the second optional argument of the environment (that follows the mandatory argument).
The formatting of the hide labels can be customized by changing the definition of \__paul_hide_output_format:n (that currently is defined as \fbox{#1}).
You can output the hidden cells using the macro \hiddencells which takes four arguments:
- separator between label and value
- separator between the different labels if there are only two
- separator between the different labels if there are more than two (except the final two)
- separator between the final two labels if there are more than two
\documentclass[]{article}
\usepackage{xparse}
\ExplSyntaxOn
\tl_new:N \l__paul_reveal_separator_tl
\int_new:N \g__paul_hide_count_int
\seq_new:N \g__paul_hide_seq
\seq_new:N \g__paul_hidden_seq
\seq_new:N \g__paul_reveal_seq
\NewDocumentEnvironment { randomhidetable } { O{c} +m O{\hide} +b }
{
\group_begin:
\int_gzero:N \g__paul_hide_count_int
\cs_set:Npn #3 ##1 { \int_gincr:N \g__paul_hide_count_int }
\hbox_set:Nn \l_tmpa_box { \begin { tabular } { #2 } #4 \end { tabular } }
\group_end:
\seq_gclear:N \g__paul_hide_seq
\seq_gclear:N \g__paul_hidden_seq
\int_step_inline:nn \g__paul_hide_count_int { \seq_gpush:Nn \g__paul_hide_seq { ##1 } }
\seq_gshuffle:N \g__paul_hide_seq
\cs_set:Npn #3 ##1
{
\seq_gpop:NN \g__paul_hide_seq \l_tmpa_tl
\__paul_hide_output_format:x { \char_generate:nn { 64 + \l_tmpa_tl } { 11 } }
\seq_gpush:Nx \g__paul_hidden_seq { { \l_tmpa_tl } { ##1 } }
}
\begin { tabular } [ #1 ] { #2 } #4 \end { tabular }
\seq_gsort:Nn \g__paul_hidden_seq
{
\int_compare:nNnTF { \use_i:nn ##1 } > { \use_i:nn ##2 }
{ \sort_return_swapped: }
{ \sort_return_same: }
}
\seq_gclear:N \g__paul_reveal_seq
\seq_map_inline:Nn \g__paul_hidden_seq
{
\__paul_hide_output_hidden_values:nn ##1
}
}
{}
\cs_new:Npn \__paul_hide_output_hidden_values:nn #1 #2
{
\seq_gput_right:Nx \g__paul_reveal_seq
{
\exp_not:N \__paul_hide_output_format:n
{ \char_generate:nn { 64 + #1 } { 11 } }
\exp_not:n { \l__paul_reveal_separator_tl #2 }
}
}
\cs_new:Npn \__paul_hide_output_format:n #1
{
\fbox { #1 }
}
\cs_generate_variant:Nn \__paul_hide_output_format:n { x }
\NewDocumentCommand \hiddencells { m m m m }
{
\group_begin:
\tl_set:Nn \l__paul_reveal_separator_tl { #1 }
\seq_use:Nnnn \g__paul_reveal_seq { #2 } { #3 } { #4 }
\group_end:
}
\ExplSyntaxOff
\begin{document}
\makebox[2cm][l]{First call:}
\begin{randomhidetable}[b]{ccc}
\hide{5} & 4 & \hide{7} \\
2 & \hide{6} & 3
\end{randomhidetable}
The hidden fields were: \hiddencells{~}{, }{, }{, and }
\makebox[2cm][l]{Second call:}
\begin{randomhidetable}[t]{ccc}[\foo]
\foo{5} & 4 & \foo{7} \\
2 & \foo{6} & 3
\end{randomhidetable}
The hidden fields were: \hiddencells{~}{ and }{, }{, and }
\end{document}

EDIT: a version that uses the environ package as an alternative if you don't have a recent enough version of xparse. The environ based environment is called randomhidetable* and doesn't support the second optional argument. To still be able to set the used macro the new macro \sethidemacro is introduced (the old environment still works):
\documentclass[]{article}
\usepackage{xparse}
\usepackage{environ}
\ExplSyntaxOn
\tl_new:N \l__paul_reveal_separator_tl
\tl_new:N \l__paul_hide_macro_name_tl
\tl_set:Nn \l__paul_hide_macro_name_tl { \hide }
\int_new:N \g__paul_hide_count_int
\seq_new:N \g__paul_hide_seq
\seq_new:N \g__paul_hidden_seq
\seq_new:N \g__paul_reveal_seq
%\NewDocumentEnvironment { randomhidetable } { O{c} +m o +b }
% {
% \IfValueTF { #3 }
% { \__paul_randomhidetable_code:nnnn { #1 } { #2 } { #3 } { #4 } }
% {
% \__paul_randomhidetable_code:nnVn
% { #1 } { #2 } \l__paul_hide_macro_name_tl { #4 }
% }
% }
% {}
\cs_new:Npn \__paul_randomhidetable_code:nnnn #1 #2 #3 #4
{
\group_begin:
\int_gzero:N \g__paul_hide_count_int
\cs_set:Npn #3 ##1 { \int_gincr:N \g__paul_hide_count_int }
\hbox_set:Nn \l_tmpa_box { \begin { tabular } { #2 } #4 \end { tabular } }
\group_end:
\seq_gclear:N \g__paul_hide_seq
\seq_gclear:N \g__paul_hidden_seq
\int_step_inline:nn \g__paul_hide_count_int { \seq_gpush:Nn \g__paul_hide_seq { ##1 } }
\seq_gshuffle:N \g__paul_hide_seq
\cs_set:Npn #3 ##1
{
\seq_gpop:NN \g__paul_hide_seq \l_tmpa_tl
\__paul_hide_output_format:x { \char_generate:nn { 64 + \l_tmpa_tl } { 11 } }
\seq_gpush:Nx \g__paul_hidden_seq { { \l_tmpa_tl } { ##1 } }
}
\begin { tabular } [ #1 ] { #2 } #4 \end { tabular }
\seq_gsort:Nn \g__paul_hidden_seq
{
\int_compare:nNnTF { \use_i:nn ##1 } > { \use_i:nn ##2 }
{ \sort_return_swapped: }
{ \sort_return_same: }
}
\seq_gclear:N \g__paul_reveal_seq
\seq_map_inline:Nn \g__paul_hidden_seq
{
\__paul_hide_output_hidden_values:nn ##1
}
}
\cs_generate_variant:Nn \__paul_randomhidetable_code:nnnn { nnVV }
\cs_generate_variant:Nn \__paul_randomhidetable_code:nnnn { nnVn }
\cs_new:Npn \__paul_hide_output_hidden_values:nn #1 #2
{
\seq_gput_right:Nx \g__paul_reveal_seq
{
\exp_not:N \__paul_hide_output_format:n
{ \char_generate:nn { 64 + #1 } { 11 } }
\exp_not:n { \l__paul_reveal_separator_tl #2 }
}
}
\cs_new:Npn \__paul_hide_output_format:n #1
{
\fbox { #1 }
}
\cs_generate_variant:Nn \__paul_hide_output_format:n { x }
\NewDocumentCommand \hiddencells { m m m m }
{
\group_begin:
\tl_set:Nn \l__paul_reveal_separator_tl { #1 }
\seq_use:Nnnn \g__paul_reveal_seq { #2 } { #3 } { #4 }
\group_end:
}
\NewDocumentCommand \sethidemacro { m }
{
\tl_set:Nn \l__paul_hide_macro_name_tl { #1 }
}
\NewEnviron{randomhidetable*}[2][c]
{
\__paul_randomhidetable_code:nnVV
{ #1 } { #2 } \l__paul_hide_macro_name_tl \BODY
}
\ExplSyntaxOff
\begin{document}
\makebox[2cm][l]{First call:}
\begin{randomhidetable*}[b]{ccc}
\hide{5} & 4 & \hide{7} \\
2 & \hide{6} & 3
\end{randomhidetable*}
The hidden fields were: \hiddencells{~}{ and }{, }{, and }
\makebox[2cm][l]{Second call:}
\begingroup
\sethidemacro{\foo}
\begin{randomhidetable*}[t]{ccc}
\foo{5} & 4 & \foo{7} \\
2 & \foo{6} & 3
\end{randomhidetable*}
\endgroup
The hidden fields were: \hiddencells{~}{ and }{, }{, and }
\end{document}