The symbol must tell a scanner which error-correction level and mask it used, in a 15-bit field protected by its own error-correcting code. Today you build that format information and place it beside the finders.
Encode the 15-bit format information for an error-correction level and mask, and place it.
The data codewords carry the message, but a scanner still needs two facts before it can read them: the error-correction level (to know the block structure) and the mask (to undo it). These live in the 15-bit format information. The first 5 bits are the payload - 2 bits of level (L=01, M=00, Q=11, H=10) and 3 bits of mask number - and the remaining 10 are BCH check bits so the format survives damage, computed by the same polynomial-remainder idea you used for Reed-Solomon, over a degree-10 binary generator. Finally the whole 15-bit value is XORed with a fixed mask pattern so that an all-light region cannot masquerade as valid format bits.
For level Q and mask 6 the payload is 11110, and the finished format information is 010111011011010. It is written twice for redundancy: one copy wraps the top-left finder along row 8 and column 8, and a second copy is split between the top-right and bottom-left finders - so a scanner can recover the format even if one copy is damaged. This is why lesson 27 reserved those exact modules. With format encoding in place, you can finally score all eight masks and choose.
// data = (levelBits<<3)|mask, 5 bits. Divide (data<<10) by the// BCH generator to get 10 check bits, then XOR the fixed mask.const bchGen = 0b10100110111 // degree-10 generatorconst fmtMask = 0b101010000010010 // applied so all-zero is invalid// fmt = ((data<<10) | remainder) XOR fmtMask -> 15 bits