/**** * Classes ****/ var QRCode = Container.expand(function (string) { var self = Container.call(this); // Initialize QR code properties self.pixelSize = 32; self.modules = []; self.string = string; self.countBitLength = 10; self.dataCodeWord = ""; self.eccCodeWord = ""; self.size = 21; // Block = 8 bits self.maxDataBlock = 19; self.maxErrorBlock = 7; self.placePixel = function (x, y, isBlack) { if (x < 0 || x >= self.size || y < 0 || y >= self.size) { return; } // Initialize row if it doesn't exist if (!self.modules[y]) { self.modules[y] = []; } // Mark pixel in modules array self.modules[y][x] = isBlack; }; self.drawPositionPattern = function (centerX, centerY) { // Draw 8x8 position pattern for (var y = -4; y <= 4; y++) { for (var x = -4; x <= 4; x++) { var absX = Math.abs(x); var absY = Math.abs(y); // Separator if (absX === 4 || absY === 4) { self.placePixel(centerX + x, centerY + y, false); } else if (absX === 3 || absY === 3 || absX <= 1 && absY <= 1) { // Outer black ring, white ring, inner black square self.placePixel(centerX + x, centerY + y, true); } else if (absX === 2 || absY === 2) { // White middle ring self.placePixel(centerX + x, centerY + y, false); } } } }; self.drawToScreen = function () { // Draw all pixels from modules array for (var y = 0; y < self.size; y++) { if (self.modules[y]) { for (var x = 0; x < self.size; x++) { if (self.modules[y][x] !== undefined) { var pixel = self.attachAsset('QRCodePixel', { x: x * self.pixelSize, y: y * self.pixelSize, scaleX: self.pixelSize, scaleY: self.pixelSize, tint: self.modules[y][x] ? 0 : 0xFFFFFF }); } } } } }; self.createPattern = function () { // Draw position patterns at three corners self.drawPositionPattern(3, 3); // Top-left self.drawPositionPattern(self.size - 4, 3); // Top-right self.drawPositionPattern(3, self.size - 4); // Bottom-left // Hardcoded Format Bits var formatBits = [ // Bottom true, true, false, true, true, true, true, true, // Right true, false, true, false, true, false, true, false, // Left corner true, true, false, // Left Bottom true, true, true, true, true, false, // Left Side false, true, false, true, false, true]; for (var y = self.size - 8; y <= self.size; y++) { var i = y - (self.size - 8); self.placePixel(8, y, formatBits[i]); } for (var x = self.size - 8; x <= self.size; x++) { var i = x - (self.size - 8); self.placePixel(x, 8, formatBits[i + 8]); } self.placePixel(7, 8, formatBits[16]); self.placePixel(8, 8, formatBits[17]); self.placePixel(8, 7, formatBits[18]); for (var x = 0; x < 6; x++) { self.placePixel(x, 8, formatBits[x + 19]); } for (var y = 0; y < 6; y++) { self.placePixel(8, y, formatBits[y + 19 + 6]); } // timing pattern for (var i = 8; i <= self.size - 9; i++) { self.placePixel(i, 6, i % 2 == 0); self.placePixel(6, i, i % 2 == 0); } // Draw data and ECC codewords in zigzag pattern self.drawDataCodewords(); self.drawToScreen(); }; // Convert to hexadecimal and split into bytes self.binToHex = function (bin) { var hexBytes = ""; for (var i = 0; i < bin.length; i += 8) { var _byte = bin.substr(i, 8); var hexValue = parseInt(_byte, 2).toString(16).toUpperCase(); if (hexValue.length === 1) { hexValue = "0" + hexValue; } hexBytes += hexValue + " "; } return hexBytes; }; self.drawDataCodewords = function () { var allCodewords = self.dataCodeWord + self.eccCodeWord; var bitIndex = 0; var direction = -1; // -1 for up, 1 for down // Start from the right side, moving in pairs of columns for (var rightCol = self.size - 1; rightCol > 0; rightCol -= 2) { // Skip the timing column if (rightCol === 6) { rightCol = 5; } // Process the entire height for (var vert = 0; vert < self.size; vert++) { for (var c = 0; c < 2; c++) { var col = rightCol - c; var row = direction === -1 ? self.size - 1 - vert : vert; // Check if we've placed all bits if (bitIndex >= allCodewords.length) { return; } // Check if this position is already occupied if (self.modules[row] && self.modules[row][col] !== undefined) { continue; // Skip this position } // Place the bit var bit = allCodewords[bitIndex] === '1'; // Mask Pattern 2 if (col % 3 == 0) { bit = !bit; } if (bit) { self.placePixel(col, row, true); } bitIndex++; } } // Change direction for next column pair direction = -direction; } }; self.encodeECC = function () { var LOG_TABLE = new Array(256); var EXP_TABLE = new Array(256); var p = 1; for (var i = 0; i < 255; i++) { EXP_TABLE[i] = p; LOG_TABLE[p] = i; p = p << 1; if (p > 255) { p = p ^ 285; } } EXP_TABLE[255] = 1; var gf_multiply = function gf_multiply(a, b) { if (a === 0 || b === 0) { return 0; } return EXP_TABLE[(LOG_TABLE[a] + LOG_TABLE[b]) % 255]; }; var getGeneratorPolynomial = function getGeneratorPolynomial(numEccBytes) { var g = [1]; for (var i = 0; i < numEccBytes; i++) { var next_g = new Array(g.length + 1); for (var k = 0; k < next_g.length; k++) { next_g[k] = 0; } var alpha_pow_i = EXP_TABLE[i]; for (var j = 0; j < g.length; j++) { next_g[j] = g[j]; } for (var j = 0; j < g.length; j++) { next_g[j + 1] ^= gf_multiply(g[j], alpha_pow_i); } g = next_g; } return g; }; // 1. Convert dataCodeWord to bytes var dataBytes = []; for (var i = 0; i < self.dataCodeWord.length; i += 8) { var byteString = self.dataCodeWord.substr(i, 8); dataBytes.push(parseInt(byteString, 2)); } // 2. Get generator polynomial var generator = getGeneratorPolynomial(self.maxErrorBlock); // 3. Perform polynomial division to get ECC var dataLen = dataBytes.length; var eccLen = self.maxErrorBlock; var msg_out = new Array(dataLen + eccLen); for (var i = 0; i < msg_out.length; i++) { msg_out[i] = 0; } for (var i = 0; i < dataLen; i++) { msg_out[i] = dataBytes[i]; } for (var i = 0; i < dataLen; i++) { var coef = msg_out[i]; if (coef !== 0) { for (var j = 0; j < generator.length; j++) { msg_out[i + j] ^= gf_multiply(generator[j], coef); } } } var eccBytes = []; for (var i = 0; i < eccLen; i++) { eccBytes.push(msg_out[dataLen + i]); } // 4. Convert ECC bytes to binary string var eccBinaryString = ""; for (var i = 0; i < eccBytes.length; i++) { var binary = eccBytes[i].toString(2); while (binary.length < 8) { binary = "0" + binary; } eccBinaryString += binary; } self.eccCodeWord = eccBinaryString; console.log("eccCodeWord: " + self.binToHex(self.eccCodeWord)); }; // Encode numeric data method self.encodeNumeric = function () { // Split into groups of 3 digits var groups = []; for (var i = 0; i < self.string.length; i += 3) { var group = self.string.substr(i, Math.min(3, self.string.length - i)); groups.push(group); } console.log("Groups of 3: " + groups.join(", ")); // Convert each group to binary var binaryGroups = []; for (var j = 0; j < groups.length; j++) { var num = parseInt(groups[j], 10); var bitLength; // Determine bit length based on group size if (groups[j].length === 3) { bitLength = 10; // 3 digits need 10 bits (0-999) } else if (groups[j].length === 2) { bitLength = 7; // 2 digits need 7 bits (0-99) } else { bitLength = 4; // 1 digit needs 4 bits (0-9) } // Convert to binary with proper padding var binary = num.toString(2); while (binary.length < bitLength) { binary = "0" + binary; } binaryGroups.push(binary); console.log("Group " + num + " -> " + binary + " (" + bitLength + " bits)"); } console.log("Message Binary: " + binaryGroups.join(" ") + " (" + binaryGroups.join("").length + " bits)"); self.dataCodeWord += "0001"; var binary = string.length.toString(2); // Convert to binary with proper padding while (binary.length < self.countBitLength) { binary = "0" + binary; } self.dataCodeWord += binary; self.dataCodeWord += binaryGroups.join(""); self.dataCodeWord += "0000"; // Terminator // Add byte padding var currentBytes = self.dataCodeWord.length / 8; var bytesNeeded = self.maxDataBlock - currentBytes; // Pad to byte boundary first var bitsToNextByte = self.dataCodeWord.length % 8; if (bitsToNextByte > 0) { var padBits = 8 - bitsToNextByte; for (var p = 0; p < padBits; p++) { self.dataCodeWord += "0"; } } console.log("dataCodeWord: " + self.dataCodeWord + " (" + self.dataCodeWord.length / 8 + " bytes)"); // Add alternating padding bytes (11101100 and 00010001) var paddingBytes = ["11101100", "00010001"]; var paddingIndex = 0; while (self.dataCodeWord.length / 8 < self.maxDataBlock) { self.dataCodeWord += paddingBytes[paddingIndex]; paddingIndex = (paddingIndex + 1) % 2; } console.log("dataCodeWord with padding: " + self.binToHex(self.dataCodeWord)); }; self.encodeNumeric(); self.encodeECC(); self.createPattern(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xFFFFFF }); /**** * Game Code ****/ // Function to generate random number with specified digits function getRandomNumber(digits) { if (digits < 1) { return "0"; } var min = Math.pow(10, digits - 1); // Minimum value (e.g., 100 for 3 digits) var max = Math.pow(10, digits) - 1; // Maximum value (e.g., 999 for 3 digits) return Math.floor(Math.random() * (max - min + 1) + min).toString(); } // Create QR code container var num = getRandomNumber(16); var qrCode = new QRCode(num); qrCode.x = (2048 - qrCode.width) / 2; qrCode.y = (2732 - qrCode.height) / 2 - 100; game.addChild(qrCode); var numberText = new Text2("Scan the QR code then comment what you got! :D", { size: 86, fill: 0x000000 }); numberText.anchor.set(0.5, 0); numberText.x = 2048 / 2; numberText.y = qrCode.y + qrCode.size * qrCode.pixelSize + 100; game.addChild(numberText);
/****
* Classes
****/
var QRCode = Container.expand(function (string) {
var self = Container.call(this);
// Initialize QR code properties
self.pixelSize = 32;
self.modules = [];
self.string = string;
self.countBitLength = 10;
self.dataCodeWord = "";
self.eccCodeWord = "";
self.size = 21;
// Block = 8 bits
self.maxDataBlock = 19;
self.maxErrorBlock = 7;
self.placePixel = function (x, y, isBlack) {
if (x < 0 || x >= self.size || y < 0 || y >= self.size) {
return;
}
// Initialize row if it doesn't exist
if (!self.modules[y]) {
self.modules[y] = [];
}
// Mark pixel in modules array
self.modules[y][x] = isBlack;
};
self.drawPositionPattern = function (centerX, centerY) {
// Draw 8x8 position pattern
for (var y = -4; y <= 4; y++) {
for (var x = -4; x <= 4; x++) {
var absX = Math.abs(x);
var absY = Math.abs(y);
// Separator
if (absX === 4 || absY === 4) {
self.placePixel(centerX + x, centerY + y, false);
} else if (absX === 3 || absY === 3 || absX <= 1 && absY <= 1) {
// Outer black ring, white ring, inner black square
self.placePixel(centerX + x, centerY + y, true);
} else if (absX === 2 || absY === 2) {
// White middle ring
self.placePixel(centerX + x, centerY + y, false);
}
}
}
};
self.drawToScreen = function () {
// Draw all pixels from modules array
for (var y = 0; y < self.size; y++) {
if (self.modules[y]) {
for (var x = 0; x < self.size; x++) {
if (self.modules[y][x] !== undefined) {
var pixel = self.attachAsset('QRCodePixel', {
x: x * self.pixelSize,
y: y * self.pixelSize,
scaleX: self.pixelSize,
scaleY: self.pixelSize,
tint: self.modules[y][x] ? 0 : 0xFFFFFF
});
}
}
}
}
};
self.createPattern = function () {
// Draw position patterns at three corners
self.drawPositionPattern(3, 3); // Top-left
self.drawPositionPattern(self.size - 4, 3); // Top-right
self.drawPositionPattern(3, self.size - 4); // Bottom-left
// Hardcoded Format Bits
var formatBits = [
// Bottom
true, true, false, true, true, true, true, true,
// Right
true, false, true, false, true, false, true, false,
// Left corner
true, true, false,
// Left Bottom
true, true, true, true, true, false,
// Left Side
false, true, false, true, false, true];
for (var y = self.size - 8; y <= self.size; y++) {
var i = y - (self.size - 8);
self.placePixel(8, y, formatBits[i]);
}
for (var x = self.size - 8; x <= self.size; x++) {
var i = x - (self.size - 8);
self.placePixel(x, 8, formatBits[i + 8]);
}
self.placePixel(7, 8, formatBits[16]);
self.placePixel(8, 8, formatBits[17]);
self.placePixel(8, 7, formatBits[18]);
for (var x = 0; x < 6; x++) {
self.placePixel(x, 8, formatBits[x + 19]);
}
for (var y = 0; y < 6; y++) {
self.placePixel(8, y, formatBits[y + 19 + 6]);
}
// timing pattern
for (var i = 8; i <= self.size - 9; i++) {
self.placePixel(i, 6, i % 2 == 0);
self.placePixel(6, i, i % 2 == 0);
}
// Draw data and ECC codewords in zigzag pattern
self.drawDataCodewords();
self.drawToScreen();
};
// Convert to hexadecimal and split into bytes
self.binToHex = function (bin) {
var hexBytes = "";
for (var i = 0; i < bin.length; i += 8) {
var _byte = bin.substr(i, 8);
var hexValue = parseInt(_byte, 2).toString(16).toUpperCase();
if (hexValue.length === 1) {
hexValue = "0" + hexValue;
}
hexBytes += hexValue + " ";
}
return hexBytes;
};
self.drawDataCodewords = function () {
var allCodewords = self.dataCodeWord + self.eccCodeWord;
var bitIndex = 0;
var direction = -1; // -1 for up, 1 for down
// Start from the right side, moving in pairs of columns
for (var rightCol = self.size - 1; rightCol > 0; rightCol -= 2) {
// Skip the timing column
if (rightCol === 6) {
rightCol = 5;
}
// Process the entire height
for (var vert = 0; vert < self.size; vert++) {
for (var c = 0; c < 2; c++) {
var col = rightCol - c;
var row = direction === -1 ? self.size - 1 - vert : vert;
// Check if we've placed all bits
if (bitIndex >= allCodewords.length) {
return;
}
// Check if this position is already occupied
if (self.modules[row] && self.modules[row][col] !== undefined) {
continue; // Skip this position
}
// Place the bit
var bit = allCodewords[bitIndex] === '1';
// Mask Pattern 2
if (col % 3 == 0) {
bit = !bit;
}
if (bit) {
self.placePixel(col, row, true);
}
bitIndex++;
}
}
// Change direction for next column pair
direction = -direction;
}
};
self.encodeECC = function () {
var LOG_TABLE = new Array(256);
var EXP_TABLE = new Array(256);
var p = 1;
for (var i = 0; i < 255; i++) {
EXP_TABLE[i] = p;
LOG_TABLE[p] = i;
p = p << 1;
if (p > 255) {
p = p ^ 285;
}
}
EXP_TABLE[255] = 1;
var gf_multiply = function gf_multiply(a, b) {
if (a === 0 || b === 0) {
return 0;
}
return EXP_TABLE[(LOG_TABLE[a] + LOG_TABLE[b]) % 255];
};
var getGeneratorPolynomial = function getGeneratorPolynomial(numEccBytes) {
var g = [1];
for (var i = 0; i < numEccBytes; i++) {
var next_g = new Array(g.length + 1);
for (var k = 0; k < next_g.length; k++) {
next_g[k] = 0;
}
var alpha_pow_i = EXP_TABLE[i];
for (var j = 0; j < g.length; j++) {
next_g[j] = g[j];
}
for (var j = 0; j < g.length; j++) {
next_g[j + 1] ^= gf_multiply(g[j], alpha_pow_i);
}
g = next_g;
}
return g;
};
// 1. Convert dataCodeWord to bytes
var dataBytes = [];
for (var i = 0; i < self.dataCodeWord.length; i += 8) {
var byteString = self.dataCodeWord.substr(i, 8);
dataBytes.push(parseInt(byteString, 2));
}
// 2. Get generator polynomial
var generator = getGeneratorPolynomial(self.maxErrorBlock);
// 3. Perform polynomial division to get ECC
var dataLen = dataBytes.length;
var eccLen = self.maxErrorBlock;
var msg_out = new Array(dataLen + eccLen);
for (var i = 0; i < msg_out.length; i++) {
msg_out[i] = 0;
}
for (var i = 0; i < dataLen; i++) {
msg_out[i] = dataBytes[i];
}
for (var i = 0; i < dataLen; i++) {
var coef = msg_out[i];
if (coef !== 0) {
for (var j = 0; j < generator.length; j++) {
msg_out[i + j] ^= gf_multiply(generator[j], coef);
}
}
}
var eccBytes = [];
for (var i = 0; i < eccLen; i++) {
eccBytes.push(msg_out[dataLen + i]);
}
// 4. Convert ECC bytes to binary string
var eccBinaryString = "";
for (var i = 0; i < eccBytes.length; i++) {
var binary = eccBytes[i].toString(2);
while (binary.length < 8) {
binary = "0" + binary;
}
eccBinaryString += binary;
}
self.eccCodeWord = eccBinaryString;
console.log("eccCodeWord: " + self.binToHex(self.eccCodeWord));
};
// Encode numeric data method
self.encodeNumeric = function () {
// Split into groups of 3 digits
var groups = [];
for (var i = 0; i < self.string.length; i += 3) {
var group = self.string.substr(i, Math.min(3, self.string.length - i));
groups.push(group);
}
console.log("Groups of 3: " + groups.join(", "));
// Convert each group to binary
var binaryGroups = [];
for (var j = 0; j < groups.length; j++) {
var num = parseInt(groups[j], 10);
var bitLength;
// Determine bit length based on group size
if (groups[j].length === 3) {
bitLength = 10; // 3 digits need 10 bits (0-999)
} else if (groups[j].length === 2) {
bitLength = 7; // 2 digits need 7 bits (0-99)
} else {
bitLength = 4; // 1 digit needs 4 bits (0-9)
}
// Convert to binary with proper padding
var binary = num.toString(2);
while (binary.length < bitLength) {
binary = "0" + binary;
}
binaryGroups.push(binary);
console.log("Group " + num + " -> " + binary + " (" + bitLength + " bits)");
}
console.log("Message Binary: " + binaryGroups.join(" ") + " (" + binaryGroups.join("").length + " bits)");
self.dataCodeWord += "0001";
var binary = string.length.toString(2);
// Convert to binary with proper padding
while (binary.length < self.countBitLength) {
binary = "0" + binary;
}
self.dataCodeWord += binary;
self.dataCodeWord += binaryGroups.join("");
self.dataCodeWord += "0000"; // Terminator
// Add byte padding
var currentBytes = self.dataCodeWord.length / 8;
var bytesNeeded = self.maxDataBlock - currentBytes;
// Pad to byte boundary first
var bitsToNextByte = self.dataCodeWord.length % 8;
if (bitsToNextByte > 0) {
var padBits = 8 - bitsToNextByte;
for (var p = 0; p < padBits; p++) {
self.dataCodeWord += "0";
}
}
console.log("dataCodeWord: " + self.dataCodeWord + " (" + self.dataCodeWord.length / 8 + " bytes)");
// Add alternating padding bytes (11101100 and 00010001)
var paddingBytes = ["11101100", "00010001"];
var paddingIndex = 0;
while (self.dataCodeWord.length / 8 < self.maxDataBlock) {
self.dataCodeWord += paddingBytes[paddingIndex];
paddingIndex = (paddingIndex + 1) % 2;
}
console.log("dataCodeWord with padding: " + self.binToHex(self.dataCodeWord));
};
self.encodeNumeric();
self.encodeECC();
self.createPattern();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFFFFF
});
/****
* Game Code
****/
// Function to generate random number with specified digits
function getRandomNumber(digits) {
if (digits < 1) {
return "0";
}
var min = Math.pow(10, digits - 1); // Minimum value (e.g., 100 for 3 digits)
var max = Math.pow(10, digits) - 1; // Maximum value (e.g., 999 for 3 digits)
return Math.floor(Math.random() * (max - min + 1) + min).toString();
}
// Create QR code container
var num = getRandomNumber(16);
var qrCode = new QRCode(num);
qrCode.x = (2048 - qrCode.width) / 2;
qrCode.y = (2732 - qrCode.height) / 2 - 100;
game.addChild(qrCode);
var numberText = new Text2("Scan the QR code then comment what you got! :D", {
size: 86,
fill: 0x000000
});
numberText.anchor.set(0.5, 0);
numberText.x = 2048 / 2;
numberText.y = qrCode.y + qrCode.size * qrCode.pixelSize + 100;
game.addChild(numberText);