Files
LerningCSW/MainPage/younes/Streichholzreatsel.js
El Haddoury Younes b483e5f73f mein code upload
2026-01-20 15:14:21 +01:00

332 lines
9.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const SEG = { a:0, b:1, c:2, d:3, e:4, f:5, g:6 };
const segNames = ["a","b","c","d","e","f","g"];
function maskFromSegments(list){
let m = 0;
for(const s of list) m |= (1 << SEG[s]);
return m;
}
const DIGIT_MASK = [
maskFromSegments(["a","b","c","d","e","f"]), // 0
maskFromSegments(["b","c"]), // 1
maskFromSegments(["a","b","d","e","g"]), // 2
maskFromSegments(["a","b","c","d","g"]), // 3
maskFromSegments(["b","c","f","g"]), // 4
maskFromSegments(["a","c","d","f","g"]), // 5
maskFromSegments(["a","c","d","e","f","g"]), // 6
maskFromSegments(["a","b","c"]), // 7
maskFromSegments(["a","b","c","d","e","f","g"]), // 8
maskFromSegments(["a","b","c","d","f","g"]) // 9
];
const MASK_TO_DIGIT = new Map(DIGIT_MASK.map((m,d)=>[m,d]));
function removableTargetsFromDigit(d){
const start = DIGIT_MASK[d];
const res = [];
for(let t=0;t<=9;t++){
const target = DIGIT_MASK[t];
if((target & start) === target){
const removed = popcount(start ^ target);
res.push({to:t, removed});
}
}
return res;
}
function popcount(x){
x = x >>> 0;
let c = 0;
while(x){ x &= (x-1); c++; }
return c;
}
function randInt(min, max){ // inclusive
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function pick(arr){ return arr[randInt(0, arr.length-1)]; }
let level = 1;
let targetRemove = 2;
let current = null; // {A,B,C} shown digits
let solution = null; // {A,B,C} target digits after removals
let removedSoFar = 0;
// Each segment element knows which digit/segment it belongs to
// We'll store: digitIndex (0..2 for A,B,C) and segName
let removedSet = new Set(); // keys like "0-a" meaning digit0 seg a removed
const elEq = document.getElementById("equation");
const elRemovedGrid = document.getElementById("removedGrid");
const elRemovedCount = document.getElementById("removedCount");
const elPileCount = document.getElementById("pileCount");
const elLvl = document.getElementById("lvl");
const elTarget = document.getElementById("target");
const elGoalN = document.getElementById("goalN");
const elTruthDot = document.getElementById("truthDot");
const elTruthText = document.getElementById("truthText");
const elHint = document.getElementById("hint");
document.getElementById("btnNew").addEventListener("click", () => {
level++;
generateLevel();
});
document.getElementById("btnReset").addEventListener("click", () => {
// reset current level state but keep the same puzzle
resetPlayState();
renderEquation();
updateTruthUI();
});
// Turn mask into a set of active segment names
function segmentsFromMask(mask){
const set = new Set();
for(let i=0;i<7;i++){
if(mask & (1<<i)) set.add(segNames[i]);
}
return set;
}
// Compute displayed digit masks after removals
function displayedMaskForDigit(digitValue, digitIndex){
let mask = DIGIT_MASK[digitValue];
for(const s of segNames){
const key = digitIndex + "-" + s;
if(removedSet.has(key)){
// remove the segment if it exists
mask &= ~(1 << SEG[s]);
}
}
return mask;
}
function displayedDigitValue(digitValue, digitIndex){
const mask = displayedMaskForDigit(digitValue, digitIndex);
return MASK_TO_DIGIT.has(mask) ? MASK_TO_DIGIT.get(mask) : null; // null = invalid digit shape
}
function equationIsTrue(){
// get displayed digits; if any invalid => false
const a = displayedDigitValue(current.A, 0);
const b = displayedDigitValue(current.B, 1);
const c = displayedDigitValue(current.C, 2);
if(a === null || b === null || c === null) return false;
return (a + b) === c;
}
function updateTruthUI(){
const ok = equationIsTrue();
elTruthDot.classList.toggle("ok", ok);
elTruthText.textContent = ok ? "Equation is TRUE" : "Equation is FALSE";
}
// Reset removals and removed pile
function resetPlayState(){
removedSet.clear();
removedSoFar = 0;
elRemovedGrid.innerHTML = "";
syncRemovedCounts();
}
function syncRemovedCounts(){
elRemovedCount.textContent = String(removedSoFar);
elPileCount.textContent = String(removedSoFar);
}
// Render a digit as a 7-seg container with clickable match segments
function renderDigit(digitValue, digitIndex, color){
const digit = document.createElement("div");
digit.className = "digit";
const baseMask = DIGIT_MASK[digitValue];
const baseSegs = segmentsFromMask(baseMask);
for(const s of segNames){
if(!baseSegs.has(s)) continue; // no stick here in the original digit
const seg = document.createElement("div");
seg.className = "seg " + s;
seg.style.background = color;
const key = digitIndex + "-" + s;
if(removedSet.has(key)) seg.classList.add("removed");
seg.addEventListener("click", () => {
if(removedSet.has(key)) return;
// If player already removed target count, block further removes
if(removedSoFar >= targetRemove) return;
removedSet.add(key);
removedSoFar++;
// visually remove from equation (turn white/invisible)
seg.classList.add("removed");
// add to removed pile as a small colored bar
const clone = document.createElement("div");
clone.className = "removedSeg";
clone.style.background = color;
elRemovedGrid.appendChild(clone);
syncRemovedCounts();
updateTruthUI();
// win condition
if(removedSoFar === targetRemove){
if(equationIsTrue()){
elHint.innerHTML = "✅ <b>Solved!</b> You removed exactly the target and made the equation true. Click <b>New level</b>.";
} else {
elHint.innerHTML = "❌ <b>Not solved.</b> You used all removals but the equation isnt true. Click <b>Reset level</b> to try again.";
}
}
});
digit.appendChild(seg);
}
return digit;
}
function renderEquation(){
elEq.innerHTML = "";
// color palette (A,B,C different)
const colors = [
"linear-gradient(180deg,#ff6b6b,#d64545)",
"linear-gradient(180deg,#6bcBff,#3a7bd5)",
"linear-gradient(180deg,#6bffb3,#1fae63)"
];
const g1 = document.createElement("div");
g1.className = "group";
g1.appendChild(renderDigit(current.A, 0, colors[0]));
const plus = document.createElement("div");
plus.className = "symbol";
plus.textContent = "+";
const g2 = document.createElement("div");
g2.className = "group";
g2.appendChild(renderDigit(current.B, 1, colors[1]));
const eq = document.createElement("div");
eq.className = "symbol";
eq.textContent = "=";
const g3 = document.createElement("div");
g3.className = "group";
g3.appendChild(renderDigit(current.C, 2, colors[2]));
elEq.appendChild(g1);
elEq.appendChild(plus);
elEq.appendChild(g2);
elEq.appendChild(eq);
elEq.appendChild(g3);
updateTruthUI();
}
// -------- Infinite level generation algorithm --------
//
// We generate a "solution equation" (A_s + B_s = C_s) valid in 0..9.
// Then we create the "shown equation" by turning each solution digit into a SUPERSET digit
// (i.e., add extra segments) so that the player can remove sticks to get back to the solution.
// We also ensure the shown equation is NOT already true.
// We finally choose a removal target N = total segments added across the 3 digits (or some bounded version).
//
// This guarantees solvable by removals only, for infinite levels.
// -----------------------------------------------------
function generateLevel(){
resetPlayState();
// Level difficulty curve: increase target removals slowly, but never explode.
// You can tweak this. Keeps it playable while still "infinite".
targetRemove = Math.min(2 + Math.floor(Math.log2(level + 1)), 6);
// Find a puzzle with exactly targetRemove removals needed to reach a true equation.
// We'll attempt random constructions until we hit the required N.
let tries = 0;
while(true){
tries++;
if(tries > 5000){
// fallback: relax target a bit if extremely unlucky
targetRemove = Math.max(2, targetRemove - 1);
tries = 0;
}
// 1) Pick a valid solution equation (A_s + B_s = C_s)
const As = randInt(0, 9);
const Bs = randInt(0, 9);
const Cs = As + Bs;
if(Cs < 0 || Cs > 9) continue;
// 2) For each digit, choose a "shown digit" that can be reduced to solution digit by removing sticks.
// i.e., shownMask is a superset of solutionMask.
const Achoices = superDigits(As);
const Bchoices = superDigits(Bs);
const Cchoices = superDigits(Cs);
// pick a random superset (could be itself)
const Ashow = pick(Achoices);
const Bshow = pick(Bchoices);
const Cshow = pick(Cchoices);
// Count how many removals required to go from shown -> solution (sum removed bits)
const need =
popcount(DIGIT_MASK[Ashow] ^ DIGIT_MASK[As]) +
popcount(DIGIT_MASK[Bshow] ^ DIGIT_MASK[Bs]) +
popcount(DIGIT_MASK[Cshow] ^ DIGIT_MASK[Cs]);
// Require exactly targetRemove removals
if(need !== targetRemove) continue;
// 3) Ensure the shown equation is not already true.
// (It might accidentally be true with different digit meanings.)
const shownTrue = (Ashow + Bshow) === Cshow;
if(shownTrue) continue;
// Accept puzzle
solution = { A: As, B: Bs, C: Cs };
current = { A: Ashow, B: Bshow, C: Cshow };
break;
}
// UI labels
elLvl.textContent = String(level);
elTarget.textContent = String(targetRemove);
elGoalN.textContent = String(targetRemove);
// Friendly hint (dont reveal exact digits)
elHint.innerHTML =
`Level <b>${level}</b>: Make the equation true by removing <b>${targetRemove}</b> match(es). ` +
`You can only remove sticks (click them).`;
renderEquation();
syncRemovedCounts();
updateTruthUI();
}
// Returns digits whose mask is a SUPERSET of base digit's mask (i.e., can remove to base)
function superDigits(baseDigit){
const base = DIGIT_MASK[baseDigit];
const res = [];
for(let d=0; d<=9; d++){
const m = DIGIT_MASK[d];
// m must contain all base segments
if((m & base) === base){
res.push(d);
}
}
return res;
}
// Start
generateLevel();