diff --git a/MainPage/younes/Streichholzreatsel.css b/MainPage/younes/Streichholzreatsel.css index e69de29..53f2aec 100644 --- a/MainPage/younes/Streichholzreatsel.css +++ b/MainPage/younes/Streichholzreatsel.css @@ -0,0 +1,276 @@ + :root{ + --bg:#0b1020; + --panel:#0f1733; + --text:#e9eeff; + --muted:#9fb0ff; + --shadow: 0 10px 30px rgba(0,0,0,.35); + --gap: 14px; + --matchW: 14px; + --matchL: 68px; + --matchT: 14px; + --radius: 12px; + } + *{box-sizing:border-box} + body{ + margin:0; + min-height:100vh; + background: radial-gradient(1200px 600px at 20% 20%, #1a2a7a 0%, transparent 60%), + radial-gradient(900px 500px at 85% 30%, #2b1a7a 0%, transparent 60%), + var(--bg); + color:var(--text); + font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; + display:flex; + align-items:center; + justify-content:center; + padding:24px; + } + .app{ + width:min(1100px, 100%); + display:grid; + grid-template-columns: 1.3fr .9fr; + gap:18px; + } + @media (max-width: 980px){ + .app{grid-template-columns:1fr} + } + + .card{ + background: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,.03)); + border: 1px solid rgba(255,255,255,.10); + border-radius: 18px; + box-shadow: var(--shadow); + overflow:hidden; + } + .card-header{ + padding:16px 18px; + border-bottom: 1px solid rgba(255,255,255,.10); + display:flex; + align-items:center; + justify-content:space-between; + gap:12px; + } + .title{ + display:flex; + flex-direction:column; + gap:4px; + } + .title h1{ + font-size:18px; + margin:0; + letter-spacing:.2px; + } + .title .sub{ + font-size:12px; + color:var(--muted); + } + .controls{ + display:flex; + gap:10px; + flex-wrap:wrap; + justify-content:flex-end; + } + button{ + border:none; + padding:10px 12px; + border-radius: 12px; + background: rgba(255,255,255,.12); + color:var(--text); + cursor:pointer; + font-weight:600; + letter-spacing:.2px; + transition: transform .05s ease, background .2s ease; + user-select:none; + } + button:hover{ background: rgba(255,255,255,.18); } + button:active{ transform: translateY(1px); } + button.primary{ + background: linear-gradient(180deg, rgba(102,168,255,.7), rgba(102,168,255,.35)); + } + button.primary:hover{ + background: linear-gradient(180deg, rgba(102,168,255,.85), rgba(102,168,255,.45)); + } + + .play{ + padding:18px; + display:flex; + flex-direction:column; + gap:14px; + } + + .statusRow{ + display:flex; + flex-wrap:wrap; + gap:10px; + align-items:center; + justify-content:space-between; + background: rgba(0,0,0,.18); + border: 1px solid rgba(255,255,255,.08); + border-radius: 14px; + padding:12px 14px; + } + .pill{ + display:inline-flex; + align-items:center; + gap:8px; + padding:8px 10px; + border-radius: 999px; + background: rgba(255,255,255,.08); + font-size:12px; + color:var(--text); + border:1px solid rgba(255,255,255,.08); + white-space:nowrap; + } + .dot{ + width:10px;height:10px;border-radius:50%; + background:#ff5c5c; + box-shadow: 0 0 0 3px rgba(255,92,92,.15); + } + .dot.ok{ + background:#29e07a; + box-shadow: 0 0 0 3px rgba(41,224,122,.15); + } + + /* Equation layout */ + .equationWrap{ + background: rgba(0,0,0,.18); + border: 1px solid rgba(255,255,255,.08); + border-radius: 16px; + padding:16px; + overflow:auto; + } + .equation{ + display:flex; + align-items:center; + gap: var(--gap); + padding:8px; + min-width: 740px; + } + .group{ + display:flex; + align-items:center; + gap: var(--gap); + } + .symbol{ + font-size: 34px; + font-weight: 900; + color: rgba(255,255,255,.85); + padding: 6px 10px; + border-radius: 14px; + background: rgba(255,255,255,.06); + border: 1px solid rgba(255,255,255,.08); + user-select:none; + } + + /* 7-seg digit container */ + .digit{ + position:relative; + width: 110px; + height: 170px; + border-radius: 16px; + background: rgba(255,255,255,.04); + border: 1px solid rgba(255,255,255,.07); + box-shadow: inset 0 0 0 1px rgba(0,0,0,.35); + flex:0 0 auto; + } + + /* Match segments */ + .seg{ + position:absolute; + width: var(--matchL); + height: var(--matchW); + border-radius: 999px; + cursor:pointer; + transition: filter .12s ease, transform .05s ease; + box-shadow: 0 10px 18px rgba(0,0,0,.25); + } + .seg:hover{ filter: brightness(1.1); } + .seg:active{ transform: translateY(1px); } + + /* removed (invisible/white) */ + .seg.removed{ + background: rgba(255,255,255,0) !important; + box-shadow:none; + cursor:not-allowed; + pointer-events:none; + } + + /* segment positions */ + /* a (top) */ + .seg.a{ top: 16px; left: 21px; } + /* b (top-right) */ + .seg.b{ top: 32px; left: 73px; width: var(--matchW); height: var(--matchL); } + /* c (bottom-right) */ + .seg.c{ top: 92px; left: 73px; width: var(--matchW); height: var(--matchL); } + /* d (bottom) */ + .seg.d{ top: 146px; left: 21px; } + /* e (bottom-left) */ + .seg.e{ top: 92px; left: 23px; width: var(--matchW); height: var(--matchL); } + /* f (top-left) */ + .seg.f{ top: 32px; left: 23px; width: var(--matchW); height: var(--matchL); } + /* g (middle) */ + .seg.g{ top: 81px; left: 21px; } + + .side{ + padding:18px; + display:flex; + flex-direction:column; + gap:14px; + } + .hint{ + background: rgba(0,0,0,.18); + border: 1px solid rgba(255,255,255,.08); + border-radius: 16px; + padding:14px; + color: rgba(255,255,255,.9); + font-size:13px; + line-height:1.4; + } + .hint b{ color:#fff; } + + .removedBox{ + background: rgba(0,0,0,.18); + border: 1px solid rgba(255,255,255,.08); + border-radius: 16px; + padding:14px; + min-height: 160px; + display:flex; + flex-direction:column; + gap:10px; + } + .removedHeader{ + display:flex; + align-items:center; + justify-content:space-between; + gap:10px; + } + .removedHeader .label{ + font-weight:800; + font-size:13px; + color: rgba(255,255,255,.92); + } + .removedHeader .count{ + font-size:12px; + color: var(--muted); + white-space:nowrap; + } + .removedGrid{ + display:flex; + flex-wrap:wrap; + gap:10px; + align-items:center; + } + .removedSeg{ + width: 44px; + height: 16px; + border-radius: 999px; + box-shadow: 0 8px 16px rgba(0,0,0,.25); + border: 1px solid rgba(255,255,255,.12); + } + .footerNote{ + font-size:12px; + color: rgba(255,255,255,.7); + line-height:1.35; + } + .mono{ + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono","Courier New", monospace; + } \ No newline at end of file diff --git a/MainPage/younes/Streichholzreatsel.html b/MainPage/younes/Streichholzreatsel.html index 4de0e9b..673c906 100644 --- a/MainPage/younes/Streichholzreatsel.html +++ b/MainPage/younes/Streichholzreatsel.html @@ -1,14 +1,76 @@ - + - - - Streichholzrätsel + + + + Allumettes (Matchstick) Equation Infinite Levels + -

Streichholzrätsel Game

- +
+
+
+
+

Allumettes Equation Infinite Levels

+
Klicke auf die Streichhölzer, um sie zu entfernen (sie werden weiß bzw. unsichtbar). +Mache die Gleichung wahr, indem du genau die vorgegebene Anzahl entfernst.
+
+
+ + +
+
- +
+
+
Level: 1
+
lösch target: 2
+
gelöscht 0
+
+ + Equation ist falsch +
+
+ +
+
+
+ +
+ Goal: lösch N sodass die Gleichung WAHR wird. +Es ist nur das Entfernen erlaubt (kein Hinzufügen). Die Ziffern bestehen aus 7 Segmenten. +Der Generator garantiert, dass jedes Level lösbar ist (unendlich viele Level). +
+
+
+ +
+
+
+

gelöschte matches

+
jede click clont den match hier.
+
+
+ +
+
+ +
+
+
gelöschte pile
+
0 sticks
+
+
+
+ +
+ Tip: Du kannst Streichhölzer von jeder Ziffer in der Gleichung entfernen. +Eine Ziffer ändert sich nur dann in eine andere Ziffer, wenn die verbleibenden Segmente einem gültigen Muster von 0 bis 9 entsprechen. +
+
+
+
+ - \ No newline at end of file + diff --git a/MainPage/younes/Streichholzreatsel.js b/MainPage/younes/Streichholzreatsel.js index e69de29..f0dbb40 100644 --- a/MainPage/younes/Streichholzreatsel.js +++ b/MainPage/younes/Streichholzreatsel.js @@ -0,0 +1,332 @@ + + + +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< 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 = "✅ Solved! You removed exactly the target and made the equation true. Click New level."; + } else { + elHint.innerHTML = "❌ Not solved. You used all removals but the equation isn’t true. Click Reset level 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 (don’t reveal exact digits) + elHint.innerHTML = + `Level ${level}: Make the equation true by removing ${targetRemove} 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(); \ No newline at end of file