\section{数值设计}

我们使用\texttt{SQLite}存储数据，这是因为\texttt{SQLite}在反序列化对象时的性能消耗可以接受，其相关生态也更加丰富和成熟。

\subsection{棋盘状态表示}

如前文所言，对于一个$H \times W$的2048游戏棋盘，其方块数字的最大值$N_{\text{max}} = 2^{(H \times W + 1)}$，
对数变化后的方块最大值即为$N'_{\text{max}} = H \times W + 1$。
对于$10 \times 10$的棋盘，$N'_{\text{max}} = 101$，\texttt{INT8}足以表示其范围。

因此，棋盘状态矩阵可以设计为一个\texttt{numpy.ndarray}，数据类型为\texttt{np.int8}。

\subsection{动作价值表示}

动作价值，实际上代表当前动作进行到终局时，棋盘的状态价值。

数字$N$的累计价值为 $V(N) = (\log_2(N) - 1) \cdot N$ 。
对于一个$H \times W$的棋盘，其方块数字的最大值$N_{\text{max}} = 2^{(H \times W + 1)}$，则该方块的最大累计价值为：
\begin{equation}
V(N_{\text{max}}) = (\log_2(2^{(H \times W + 1)}) - 1) \cdot 2^{(H \times W + 1)} = (H \times W) \cdot 2^{(H \times W + 1)}
\end{equation}

整个棋盘的最大累计价值理论上为：
\begin{align}
V_{\text{total}} &= \sum_{i=1}^{H} \sum_{j=1}^{W} V(i,j) \\
&= V(N_{\text{max}}) + V(N_{\text{max}}/2) + V(N_{\text{max}}/4) + \dots + V(8) + V(4) \\
&\lesssim V(2 \times N_{\text{max}}) \\
&= (H \times W + 1) \cdot 2^{(H \times W + 2)}
\end{align}

对于一个$4 \times 4$的棋盘，其最大累计价值为$17 \times 2^{18} = 4,456,448$，虽然在\texttt{INT32}范围内，但当拓展到$10 \times 10$的棋盘时，其累计价值上界为$101 \times 2^{102} \approx 5.15 \times 10^{32}$，远超\texttt{INT64}的表示范围（约$9.22 \times 10^{18}$）。

然而，在实际游戏过程中，$V_{\text{total}}$的取值具有高度的稀疏性：
由于2048游戏的合并规则，棋盘上的数字呈现指数级分布，大部分位置的数值相对较小，
只有少数位置达到较高数值。因此，实际的累计价值远小于理论上界。
同时，相同的分数差距，随着总分的增长，其在游戏能力上的区分度会逐渐降低。
通过对数变换$\log_2(V_{\text{total}})$，可以将数值范围压缩至$[0, \log_2(101 \times 2^{102})] = [0, 108.66]$，
完全在\texttt{FP32}的有效精度范围内，且能保持足够的数值精度用于价值函数的区分。

因此，动作价值矩阵可以设计为一个\texttt{numpy.ndarray}，数据类型为\texttt{np.float32}，存储对数变换后的价值。

\subsection{最大价值（\texttt{MAX\_VALUE}）}

这是一个64位高精度浮点数，它用于准确地代表当前状态-策略对的价值，即这个策略所模拟到的最高分。
它不参与训练和推理，当传入的状态-策略对与现有的缓存中的某个状态重复时，这个值用于判断是否更新缓存中的价值向量。

我们认为具有更高终局分数的状态-策略对更有价值。

\subsection{棋盘尺寸}

这是一个16位整数，用于代表棋盘的尺寸。其前8位二进制为代表棋盘的高度，后8位代表棋盘的宽度。

例如，一个$7 \times 5$的棋盘，其尺寸编码为\texttt{0b0000011100000101}，或是\texttt{0x0705}。

\subsection{注释}

游戏策略代码从设计之初就考虑到使用对数（0,1,2,3……）即$N'$表示棋盘上的数字，因此无需进行转换。

但是分数计算需要使用公式~\eqref{eq:log_value_formula}计算后取对数。
