“Sirtet” is a competitive speedrunning Tetris game with a unique reverse scoring system and comprehensive Firebase leaderboard integration. The game features a time-based challenge where players must clear lines to reduce their score to zero as quickly as possible, creating an innovative twist on the classic Tetris formula. Built with Google Analytics tracking and administrative controls, it provides a complete competitive gaming experience.
sirtet/
├── index.html # Complete Tetris game implementation (881 lines)
├── sirtetCnfg.js # Firebase configuration file (referenced but not analyzed)
├── tetrisfavicon.ico # Tetris-themed favicon (referenced but not analyzed)
└── loganWseal.png # Logan W seal image (referenced but not analyzed)
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-WPNF0DNSMX"></script>
<!-- Firebase SDK -->
<script src="https://www.gstatic.com/firebasejs/8.7.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.7.1/firebase-database.js"></script>
let score = 750; /* Starting score */
// Scoring based on lines cleared
if (linesCleared > 0) {
if (linesCleared === 1) {
score -= 100; /* Single line: -100 points */
} else if (linesCleared === 2) {
score -= 250; /* Double line: -250 points */
} else if (linesCleared === 3) {
score -= 500; /* Triple line: -500 points */
}
}
// Win condition: reach score of 0
if (score <= 0) {
showGameOver()
}
let totalSecondsPassed = 0;
let formattedTime = `00:00`;
function updateTimer() {
const minutes = Math.floor(totalSecondsPassed / 60);
const seconds = totalSecondsPassed % 60;
// Format as MM:SS
formattedTime = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
document.getElementById('timer').textContent = formattedTime;
}
function startTimer() {
timerInterval = setInterval(() => {
totalSecondsPassed++;
updateTimer();
}, 1000);
}
const tetrominos = {
'I': [
[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0]
],
'J': [
[1,0,0],
[1,1,1],
[0,0,0],
],
'L': [
[0,0,1],
[1,1,1],
[0,0,0],
],
'O': [
[1,1],
[1,1],
],
'S': [
[0,1,1],
[1,1,0],
[0,0,0],
],
'Z': [
[1,1,0],
[0,1,1],
[0,0,0],
],
'T': [
[0,1,0],
[1,1,1],
[0,0,0],
]
};
const colors = {
'I': '#43A3B7', /* Cyan for I-piece */
'O': '#F6BD44', /* Yellow for O-piece */
'T': '#6F41B1', /* Purple for T-piece */
'S': '#4CA84C', /* Green for S-piece */
'Z': '#E14331', /* Red for Z-piece */
'J': '#4D86F7', /* Blue for J-piece */
'L': '#DC6C33' /* Orange for L-piece */
};
function loop() {
rAF = requestAnimationFrame(loop);
context.clearRect(0, 0, canvas.width, canvas.height);
// Tetromino falls every 35 frames (approximately 0.583 seconds at 60 FPS)
if (++count > 35) {
tetromino.row++;
count = 0;
// Check for collision and place piece
if (!isValidMove(tetromino.matrix, tetromino.row, tetromino.col)) {
tetromino.row--;
placeTetromino();
}
}
}
// Primary game database
firebase.initializeApp(firebaseConfig);
const leaderboardRef = firebase.database().ref("leaderboard");
// Administrative control database
const switchFirebaseConfig = {
apiKey: "AIzaSyCRPBjKJfKoLO52TqROpQ3I9X-nEL-0btE",
databaseURL: "https://joshbruceonline-switch-default-rtdb.europe-west1.firebasedatabase.app/",
// ... additional configuration
};
const switchApp = firebase.initializeApp(switchFirebaseConfig, 'switch');
const switchPageRef = switchApp.database().ref('sirtet');
leaderboardRef.orderByChild("score").on("value", function (snapshot) {
const leaderboardEntries = {};
snapshot.forEach(function (childSnapshot) {
const entry = childSnapshot.val();
const playerName = entry.name;
const playerScore = entry.score;
// Keep only the best (lowest) score for each player
if (playerName in leaderboardEntries) {
if (playerScore < leaderboardEntries[playerName]) {
leaderboardEntries[playerName] = playerScore;
}
} else {
leaderboardEntries[playerName] = playerScore;
}
});
});
// Display leaderboard with medal emojis
sortedEntries.forEach(function (entry, index) {
const listItem = document.createElement("li");
const playerName = entry.name;
const playerScore = entry.score;
if (index === 0) {
listItem.innerHTML = `${playerName}: ${playerScore} 🥇`; /* Gold medal */
} else if (index === 1) {
listItem.innerHTML = `${playerName}: ${playerScore} 🥈`; /* Silver medal */
} else if (index === 2) {
listItem.innerHTML = `${playerName}: ${playerScore} 🥉`; /* Bronze medal */
} else {
listItem.innerHTML = `${playerName}: ${playerScore}`;
}
leaderboardList.appendChild(listItem);
});
function nameInput() {
const name = prompt("Game Over. Enter your name:");
const bannedNamesString = "cock,balls,ballz,pussy,fuck,shit,cunt,suck,bitch,nig,penis,france,french,fag,kill,uh,ass,arse,nonce,piss,pedo,slut,whore,twat,ret,ard,hate,gay,ligma,slay,tran,gun";
const bannedNames = bannedNamesString.split(",");
function isNameBanned(name, bannedNames) {
const lowercaseName = name.toLowerCase();
for (const bannedName of bannedNames) {
if (lowercaseName.includes(bannedName)) {
return true;
}
}
return false;
}
// Multiple validation layers
if (name.length > 5 && name !== "Logan W") {
alert("Names can't be more than 5 characters in length!");
nameInput();
return;
}
if (isNameBanned(name, bannedNames)) {
alert("Names will be displayed on the leaderboard. This name includes restricted words! Please enter another name.");
nameInput();
return;
}
}
// Delete entries with names longer than 5 characters
function deleteLongNames() {
const leaderboardRef = firebase.database().ref("leaderboard");
leaderboardRef.once("value", function (snapshot) {
snapshot.forEach(function (childSnapshot) {
const entry = childSnapshot.val();
const entryKey = childSnapshot.key;
if (entry.name.length > 5 && entry.name != 'Logan W') {
leaderboardRef.child(entryKey).remove();
}
});
});
}
let isToggled = false;
function togglePage() {
// Visual theme toggle
if (isToggled) {
document.body.style.backgroundColor = 'black';
} else {
document.body.style.backgroundColor = 'white';
}
// Modify tetromino shapes for increased difficulty
if (!isToggled) {
// Altered Z-piece becomes more complex
tetrominos['Z'] = [
[0, 1, 1, 1],
[1, 1, 0, 0],
[1, 1, 1, 1],
[0, 1, 0, 1],
];
// Altered O-piece becomes asymmetric
tetrominos['O'] = [
[0, 0],
[0, 1],
];
}
}
document.addEventListener('keydown', function(e) {
if (gameOver) return;
// Left and right arrow keys + A/D keys (move)
if (e.which === 37 || e.which === 65) { /* Left arrow or A */
const col = tetromino.col - 1;
if (isValidMove(tetromino.matrix, tetromino.row, col)) {
tetromino.col = col;
}
}
if (e.which === 39 || e.which === 68) { /* Right arrow or D */
const col = tetromino.col + 1;
if (isValidMove(tetromino.matrix, tetromino.row, col)) {
tetromino.col = col;
}
}
// Up arrow key + W key (rotate)
if (e.which === 38 || e.which === 87) {
const matrix = rotate(tetromino.matrix);
if (isValidMove(matrix, tetromino.row, tetromino.col)) {
tetromino.matrix = matrix;
}
}
// Down arrow key + S key (drop)
if(e.which === 40 || e.which === 83) {
const row = tetromino.row + 1;
if (!isValidMove(tetromino.matrix, row, tetromino.col)) {
tetromino.row = row - 1;
placeTetromino();
return;
}
tetromino.row = row;
}
});
canvas {
border: 3px solid white;
position: fixed;
top: 100px;
right: 120px;
}
.main-title {
text-align: center;
position: fixed;
top: 0px;
left: 0;
width: 100%;
font-size: 25px;
text-decoration: underline;
}
.leaderboard {
position: fixed;
top: 70px;
left: 80px;
width: 20%;
text-align: center;
}
.timer {
text-align: center;
position: fixed;
top: 10vh;
left: 0;
width: 100%;
font-size: 35px;
font-weight: bold;
}
<script async src="https://www.googletagmanager.com/gtag/js?id=G-WPNF0DNSMX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-WPNF0DNSMX');
</script>
switchPageRef.on('value', (snapshot) => {
const isPageActive = snapshot.val();
if (isPageActive === false) {
redirectLinkRef.once('value', (snapshot) => {
const redirectLink = snapshot.val();
if (redirectLink) {
window.location.href = redirectLink;
} else {
window.location.href = 'https://joshbruce.online/backrooms';
}
});
}
});
if (name === "Logan W") {
// Special handling for Logan W - bypass length restrictions
database.ref("leaderboard").push({
name: name,
score: formattedTime,
timestamp: serverTimestamp,
});
return;
}
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
// 60 FPS game loop with RequestAnimationFrame
function loop() {
rAF = requestAnimationFrame(loop);
context.clearRect(0, 0, canvas.width, canvas.height);
// Optimized rendering
for (let row = 0; row < 20; row++) {
for (let col = 0; col < 10; col++) {
if (playfield[row][col]) {
const name = playfield[row][col];
context.fillStyle = colors[name];
context.fillRect(col * grid, row * grid, grid - 1, grid - 1);
}
}
}
}
Sirtet represents an innovative take on classic Tetris gameplay, successfully combining competitive speedrunning mechanics with comprehensive online infrastructure. The reverse scoring system creates unique strategic challenges while the Firebase integration provides professional-grade leaderboard management.
Technical Rating: 8.7/10
The application successfully demonstrates how classic games can be enhanced with modern competitive features while maintaining the core gameplay that makes them timeless.