refactor: complete collision system rewrite — decomposed sub-signals

Replaced single monolithic blocked expression with 4 composable signals:
- blockedWall: piece-type aware bottom wall (T=18, others=19)
- blockedTop: grid[py+1] at px,px+1,px+2 (all pieces)
- blockedFoot: grid[py+2] at px+1 (T-piece foot only)
- blockedI4: grid[py+1] at px+3 (I-piece 4th cell only)
- blocked: OR combination of all 4

Fixed auto-drop cap from py<18 to py<19 so flat pieces
reach the bottom row. Fixed hard drop and keyboard per piece type.
This commit is contained in:
enzotar 2026-02-27 13:57:51 -08:00
parent d2cb302961
commit 9fb65e6a77

View file

@ -76,28 +76,45 @@ let lockTick = 0
on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowLeft" then (if px > 0 then px - 1 else px) else px))
on keydown(ev) -> px = if gameOver then px else (if paused then px else (if ev.key == "ArrowRight" then (if px < 7 then px + 1 else px) else px))
on keydown(ev) -> rotation = if gameOver then rotation else (if paused then rotation else (if ev.key == "ArrowUp" then (rotation + 1) % 4 else rotation))
on keydown(ev) -> py = if gameOver then py else (if paused then py else (if blocked then py else (if ev.key == "ArrowDown" then (if py < 18 then py + 1 else py) else py)))
on keydown(ev) -> py = if gameOver then py else (if paused then py else (if blocked then py else (if ev.key == " " then 18 else py)))
on keydown(ev) -> py = if gameOver then py else (if paused then py else (if blocked then py else (if ev.key == "ArrowDown" then (if py < 19 then py + 1 else py) else py)))
on keydown(ev) -> py = if gameOver then py else (if paused then py else (if blocked then py else (if ev.key == " " then (if piece == 6 then 18 else 19) else py)))
on keydown(ev) -> paused = if ev.key == "p" then (if paused then 0 else 1) else paused
-- ================================================================
-- LAYER 4: DERIVED SIGNALS — Collision, gravity, lock, line clear
-- ================================================================
-- COLLISION SYSTEM (decomposed into composable sub-signals)
-- ================================================================
-- Collision detection: can the piece move down?
-- All pieces: check grid[py+1] at px, px+1, px+2 (top row destination).
-- T-piece (6) only: also check grid[py+2] at px+1 (protruding bottom cell).
-- Bottom wall: py >= 18. Full grid coverage (py 0-17).
-- Sub-signal 1: Bottom wall (piece-type dependent)
-- T-piece stops at py=18 (foot at 19), all others at py=19
let blockedWall = 0
every 33 -> blockedWall = if piece == 6 then (if py >= 18 then 1 else 0) else (if py >= 19 then 1 else 0)
-- Sub-signal 2: Top row collision — check grid[py+1] at px, px+1, px+2
-- This is the core 3-cell check shared by ALL piece types
let blockedTop = 0
every 33 -> blockedTop = if py >= 19 then 0 else (if py == 18 then (if g19[px] > 0 then 1 else (if g19[px + 1] > 0 then 1 else (if g19[px + 2] > 0 then 1 else 0))) else (if py == 17 then (if g18[px] > 0 then 1 else (if g18[px + 1] > 0 then 1 else (if g18[px + 2] > 0 then 1 else 0))) else (if py == 16 then (if g17[px] > 0 then 1 else (if g17[px + 1] > 0 then 1 else (if g17[px + 2] > 0 then 1 else 0))) else (if py == 15 then (if g16[px] > 0 then 1 else (if g16[px + 1] > 0 then 1 else (if g16[px + 2] > 0 then 1 else 0))) else (if py == 14 then (if g15[px] > 0 then 1 else (if g15[px + 1] > 0 then 1 else (if g15[px + 2] > 0 then 1 else 0))) else (if py == 13 then (if g14[px] > 0 then 1 else (if g14[px + 1] > 0 then 1 else (if g14[px + 2] > 0 then 1 else 0))) else (if py == 12 then (if g13[px] > 0 then 1 else (if g13[px + 1] > 0 then 1 else (if g13[px + 2] > 0 then 1 else 0))) else (if py == 11 then (if g12[px] > 0 then 1 else (if g12[px + 1] > 0 then 1 else (if g12[px + 2] > 0 then 1 else 0))) else (if py == 10 then (if g11[px] > 0 then 1 else (if g11[px + 1] > 0 then 1 else (if g11[px + 2] > 0 then 1 else 0))) else (if py == 9 then (if g10[px] > 0 then 1 else (if g10[px + 1] > 0 then 1 else (if g10[px + 2] > 0 then 1 else 0))) else (if py == 8 then (if g9[px] > 0 then 1 else (if g9[px + 1] > 0 then 1 else (if g9[px + 2] > 0 then 1 else 0))) else (if py == 7 then (if g8[px] > 0 then 1 else (if g8[px + 1] > 0 then 1 else (if g8[px + 2] > 0 then 1 else 0))) else (if py == 6 then (if g7[px] > 0 then 1 else (if g7[px + 1] > 0 then 1 else (if g7[px + 2] > 0 then 1 else 0))) else (if py == 5 then (if g6[px] > 0 then 1 else (if g6[px + 1] > 0 then 1 else (if g6[px + 2] > 0 then 1 else 0))) else (if py == 4 then (if g5[px] > 0 then 1 else (if g5[px + 1] > 0 then 1 else (if g5[px + 2] > 0 then 1 else 0))) else (if py == 3 then (if g4[px] > 0 then 1 else (if g4[px + 1] > 0 then 1 else (if g4[px + 2] > 0 then 1 else 0))) else (if py == 2 then (if g3[px] > 0 then 1 else (if g3[px + 1] > 0 then 1 else (if g3[px + 2] > 0 then 1 else 0))) else (if py == 1 then (if g2[px] > 0 then 1 else (if g2[px + 1] > 0 then 1 else (if g2[px + 2] > 0 then 1 else 0))) else (if py == 0 then (if g1[px] > 0 then 1 else (if g1[px + 1] > 0 then 1 else (if g1[px + 2] > 0 then 1 else 0))) else 0)))))))))))))))))))
-- Sub-signal 3: T-piece foot collision — check grid[py+2] at px+1
-- Only applies to T-piece (piece==6)
let blockedFoot = 0
every 33 -> blockedFoot = if piece != 6 then 0 else (if py >= 18 then 0 else (if py == 17 then (if g19[px + 1] > 0 then 1 else 0) else (if py == 16 then (if g18[px + 1] > 0 then 1 else 0) else (if py == 15 then (if g17[px + 1] > 0 then 1 else 0) else (if py == 14 then (if g16[px + 1] > 0 then 1 else 0) else (if py == 13 then (if g15[px + 1] > 0 then 1 else 0) else (if py == 12 then (if g14[px + 1] > 0 then 1 else 0) else (if py == 11 then (if g13[px + 1] > 0 then 1 else 0) else (if py == 10 then (if g12[px + 1] > 0 then 1 else 0) else (if py == 9 then (if g11[px + 1] > 0 then 1 else 0) else (if py == 8 then (if g10[px + 1] > 0 then 1 else 0) else (if py == 7 then (if g9[px + 1] > 0 then 1 else 0) else (if py == 6 then (if g8[px + 1] > 0 then 1 else 0) else (if py == 5 then (if g7[px + 1] > 0 then 1 else 0) else (if py == 4 then (if g6[px + 1] > 0 then 1 else 0) else (if py == 3 then (if g5[px + 1] > 0 then 1 else 0) else (if py == 2 then (if g4[px + 1] > 0 then 1 else 0) else (if py == 1 then (if g3[px + 1] > 0 then 1 else 0) else (if py == 0 then (if g2[px + 1] > 0 then 1 else 0) else 0)))))))))))))))))))
-- Sub-signal 4: I-piece 4th cell collision — check grid[py+1] at px+3
-- Only applies to I-piece (piece==1)
let blockedI4 = 0
every 33 -> blockedI4 = if piece != 1 then 0 else (if py >= 19 then 0 else (if py == 18 then (if g19[px + 3] > 0 then 1 else 0) else (if py == 17 then (if g18[px + 3] > 0 then 1 else 0) else (if py == 16 then (if g17[px + 3] > 0 then 1 else 0) else (if py == 15 then (if g16[px + 3] > 0 then 1 else 0) else (if py == 14 then (if g15[px + 3] > 0 then 1 else 0) else (if py == 13 then (if g14[px + 3] > 0 then 1 else 0) else (if py == 12 then (if g13[px + 3] > 0 then 1 else 0) else (if py == 11 then (if g12[px + 3] > 0 then 1 else 0) else (if py == 10 then (if g11[px + 3] > 0 then 1 else 0) else (if py == 9 then (if g10[px + 3] > 0 then 1 else 0) else (if py == 8 then (if g9[px + 3] > 0 then 1 else 0) else (if py == 7 then (if g8[px + 3] > 0 then 1 else 0) else (if py == 6 then (if g7[px + 3] > 0 then 1 else 0) else (if py == 5 then (if g6[px + 3] > 0 then 1 else 0) else (if py == 4 then (if g5[px + 3] > 0 then 1 else 0) else (if py == 3 then (if g4[px + 3] > 0 then 1 else 0) else (if py == 2 then (if g3[px + 3] > 0 then 1 else 0) else (if py == 1 then (if g2[px + 3] > 0 then 1 else 0) else (if py == 0 then (if g1[px + 3] > 0 then 1 else 0) else 0))))))))))))))))))))
-- Combined blocked signal: any sub-signal triggers block
let blocked = 0
-- Piece-aware collision: check px+3 for I-piece, py+2 at px+1 for T-piece
every 33 -> blocked = if py > 17 then 1 else (if py == 17 then (if g18[px] > 0 then 1 else (if g18[px + 1] > 0 then 1 else (if g18[px + 2] > 0 then 1 else (if piece == 1 then (if g18[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g19[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 16 then (if g17[px] > 0 then 1 else (if g17[px + 1] > 0 then 1 else (if g17[px + 2] > 0 then 1 else (if piece == 1 then (if g17[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g18[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 15 then (if g16[px] > 0 then 1 else (if g16[px + 1] > 0 then 1 else (if g16[px + 2] > 0 then 1 else (if piece == 1 then (if g16[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g17[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 14 then (if g15[px] > 0 then 1 else (if g15[px + 1] > 0 then 1 else (if g15[px + 2] > 0 then 1 else (if piece == 1 then (if g15[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g16[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 13 then (if g14[px] > 0 then 1 else (if g14[px + 1] > 0 then 1 else (if g14[px + 2] > 0 then 1 else (if piece == 1 then (if g14[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g15[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 12 then (if g13[px] > 0 then 1 else (if g13[px + 1] > 0 then 1 else (if g13[px + 2] > 0 then 1 else (if piece == 1 then (if g13[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g14[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 11 then (if g12[px] > 0 then 1 else (if g12[px + 1] > 0 then 1 else (if g12[px + 2] > 0 then 1 else (if piece == 1 then (if g12[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g13[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 10 then (if g11[px] > 0 then 1 else (if g11[px + 1] > 0 then 1 else (if g11[px + 2] > 0 then 1 else (if piece == 1 then (if g11[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g12[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 9 then (if g10[px] > 0 then 1 else (if g10[px + 1] > 0 then 1 else (if g10[px + 2] > 0 then 1 else (if piece == 1 then (if g10[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g11[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 8 then (if g9[px] > 0 then 1 else (if g9[px + 1] > 0 then 1 else (if g9[px + 2] > 0 then 1 else (if piece == 1 then (if g9[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g10[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 7 then (if g8[px] > 0 then 1 else (if g8[px + 1] > 0 then 1 else (if g8[px + 2] > 0 then 1 else (if piece == 1 then (if g8[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g9[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 6 then (if g7[px] > 0 then 1 else (if g7[px + 1] > 0 then 1 else (if g7[px + 2] > 0 then 1 else (if piece == 1 then (if g7[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g8[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 5 then (if g6[px] > 0 then 1 else (if g6[px + 1] > 0 then 1 else (if g6[px + 2] > 0 then 1 else (if piece == 1 then (if g6[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g7[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 4 then (if g5[px] > 0 then 1 else (if g5[px + 1] > 0 then 1 else (if g5[px + 2] > 0 then 1 else (if piece == 1 then (if g5[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g6[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 3 then (if g4[px] > 0 then 1 else (if g4[px + 1] > 0 then 1 else (if g4[px + 2] > 0 then 1 else (if piece == 1 then (if g4[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g5[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 2 then (if g3[px] > 0 then 1 else (if g3[px + 1] > 0 then 1 else (if g3[px + 2] > 0 then 1 else (if piece == 1 then (if g3[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g4[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 1 then (if g2[px] > 0 then 1 else (if g2[px + 1] > 0 then 1 else (if g2[px + 2] > 0 then 1 else (if piece == 1 then (if g2[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g3[px + 1] > 0 then 1 else 0) else 0))))) else (if py == 0 then (if g1[px] > 0 then 1 else (if g1[px + 1] > 0 then 1 else (if g1[px + 2] > 0 then 1 else (if piece == 1 then (if g1[px + 3] > 0 then 1 else 0) else (if piece == 6 then (if g2[px + 1] > 0 then 1 else 0) else 0))))) else 0))))))))))))))))))
every 33 -> blocked = if blockedWall then 1 else (if blockedTop then 1 else (if blockedFoot then 1 else (if blockedI4 then 1 else 0)))
-- Gravity tick
every 33 -> gravityTick = if paused then gravityTick else (if gameOver then gravityTick else gravityTick + 1)
-- Auto-drop: only if NOT blocked
every 33 -> py = if paused then py else (if gameOver then py else (if blocked then py else (if gravityTick > dropInterval then (if py < 18 then py + 1 else py) else py)))
-- Auto-drop: py can go up to 19 (not 18) — blocked wall handles per-piece limits
every 33 -> py = if paused then py else (if gameOver then py else (if blocked then py else (if gravityTick > dropInterval then (if py < 19 then py + 1 else py) else py)))
every 33 -> gravityTick = if gravityTick > dropInterval then 0 else gravityTick
-- Lock detection: start counting when blocked
@ -406,7 +423,7 @@ view tetris_game = column [
}
button "Left" { click: px = if px > 0 then px - 1 else px, variant: "secondary" }
button "Right" { click: px = if px < 7 then px + 1 else px, variant: "secondary" }
button "Drop" { click: py = if blocked then py else 18, variant: "secondary" }
button "Drop" { click: py = if blocked then py else (if piece == 6 then 18 else 19), variant: "secondary" }
button "Rotate" { click: rotation = (rotation + 1) % 4, variant: "secondary" }
button "Reset" {
click: score = 0; lines = 0; level = 1; gameOver = 0; paused = 0; piece = 6; nextPiece = 1; px = 3; py = 0; rotation = 0; g16 = [0,0,0,0,0,0,0,0,0,0]; g17 = [0,0,0,0,0,0,0,0,0,0]; g18 = [0,0,0,0,0,0,0,0,0,0]; g19 = [0,0,0,0,0,0,0,0,0,0],