diff --git a/2D/js/polyomino.js b/2D/js/polyomino.js index ad397c2..d74a159 100644 --- a/2D/js/polyomino.js +++ b/2D/js/polyomino.js @@ -199,7 +199,8 @@ export function getRandomColor() { export const SHAPES = { MONOMINO: [[1]], DOMINO: [[1, 1]], - TROMINO: [[1, 1, 1]], + TROMINO_I: [[1, 1, 1]], + TROMINO_L: [[1, 0], [1, 1]], TETROMINO_I: [[1, 1, 1, 1]], TETROMINO_O: [[1, 1], [1, 1]], TETROMINO_S: [[0, 1, 1], [1, 1, 0]], diff --git a/2D/js/popup/polyomino.js b/2D/js/popup/polyomino.js index d753152..415b889 100644 --- a/2D/js/popup/polyomino.js +++ b/2D/js/popup/polyomino.js @@ -25,7 +25,7 @@ export function showPolyominoPopup(toolbar) { ctx.strokeRect(10, y - shapeSize / 2, 180, shapeSize + 20); ctx.font = '20px Pixellari'; - ctx.fillStyle = '#0000c4'; + ctx.fillStyle = '#000'; ctx.fillText(shape.replace(/_/g, ' '), 15, y + 7); const polyomino = new Polyomino(SHAPES[shape].map(row => [...row]), 200, y - shapeSize / 2, getRandomColor(), toolbar.mainApp, shape.replace(/_/g, ' ')); diff --git a/2D/js/popup/solve.js b/2D/js/popup/solve.js index b0c7a3b..adf9061 100644 --- a/2D/js/popup/solve.js +++ b/2D/js/popup/solve.js @@ -8,20 +8,23 @@ export function showSolvePopup(toolbar) { const rows = [ { label: 'Auto tiling the Polyominoes blocks', title: true }, - { label: '1) Backtracking method :', icon: '../assets/ic_solution.png' }, - { label: '2) Brute force method :', icon: '../assets/ic_solution.png' }, - { label: '3) Random method :', icon: '../assets/ic_solution.png' }, - { label: '4) Random backtracking :', icon: '../assets/ic_solution.png' } + { label: '1) Backtracking method :', icon: '../assets/ic_solution.png', description: 'Backtracking places polyominoes on a grid, checking validity, and backtracks when stuck, ensuring no overlaps and full coverage. Usually runs in order of placing the larger blocks first, then the smaller.' }, + { label: '2) Brute force method :', icon: '../assets/ic_solution.png', description: 'Brute force tries all possible combinations of polyominoes on the grid to find a solution. It is guaranteed to find a solution if one exists but is computationally expensive and slow for large grids.' }, + { label: '3) Random method :', icon: '../assets/ic_solution.png', description: 'The random method places polyominoes randomly on the grid. It is fast but does not guarantee a solution or full coverage. It is useful for generating quick and varied patterns.' }, + { label: '4) Random backtracking :', icon: '../assets/ic_solution.png', description: 'Random backtracking combines random placement with backtracking to find a solution. It is more efficient than brute force but less predictable than pure backtracking.' } ]; const startY = 60; const rowHeight = 60; const colX = 30; + const maxWidth = 300; + + const dropdowns = {}; rows.forEach((row, index) => { const y = startY + index * rowHeight; ctx.font = '20px Pixellari'; - ctx.fillStyle = '#15159f'; + ctx.fillStyle = '#000'; ctx.fillText(row.label, colX, y + 20); if (row.icon) { @@ -32,6 +35,10 @@ export function showSolvePopup(toolbar) { }; attachSolveClickEvent(toolbar, popup, row, y); } + + if (row.description) { + dropdowns[index] = { description: row.description, expanded: false, y: y + 40 }; + } }); popup.addEventListener('mousemove', (e) => { @@ -42,12 +49,82 @@ export function showSolvePopup(toolbar) { rows.forEach((row, index) => { const y = startY + index * rowHeight; - if (row.icon && toolbar.isInside(mouseX, mouseY, { x: popup.width - 94, y: y - 14, width: 50, height: 50 })) { + if (toolbar.isInside(mouseX, mouseY, { x: colX, y: y, width: popup.width - colX - 100, height: rowHeight })) { cursor = 'pointer'; } }); popup.style.cursor = cursor; }); + + popup.addEventListener('click', (e) => { + const rect = popup.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + rows.forEach((row, index) => { + const y = startY + index * rowHeight; + if (toolbar.isInside(mouseX, mouseY, { x: colX, y: y, width: popup.width - colX - 100, height: rowHeight })) { + if (dropdowns[index]) { + dropdowns[index].expanded = !dropdowns[index].expanded; + redrawPopup(); + } + } + }); + }); + + function wrapText(ctx, text, x, y, maxWidth, lineHeight) { + const words = text.split(' '); + let line = ''; + const lines = []; + + for (let n = 0; n < words.length; n++) { + let testLine = line + words[n] + ' '; + let metrics = ctx.measureText(testLine); + let testWidth = metrics.width; + if (testWidth > maxWidth && n > 0) { + lines.push(line); + line = words[n] + ' '; + } else { + line = testLine; + } + } + lines.push(line); + lines.forEach((line, index) => { + ctx.fillText(line, x, y + index * lineHeight); + }); + return lines.length; + } + + function redrawPopup() { + ctx.clearRect(0, 0, popup.width, popup.height); + ctx.fillStyle = '#a0a0a0'; + ctx.fillRect(0, 0, popup.width, popup.height); + + let yOffset = 0; + rows.forEach((row, index) => { + const y = startY + index * rowHeight + yOffset; + ctx.font = '20px Pixellari'; + ctx.fillStyle = '#000'; + ctx.fillText(row.label, colX, y + 20); + + if (row.icon) { + const icon = new Image(); + icon.src = row.icon; + icon.onload = () => { + ctx.drawImage(icon, popup.width - 94, y - 14, 50, 50); + }; + } + + if (dropdowns[index] && dropdowns[index].expanded) { + ctx.font = '16px Pixellari'; + ctx.fillStyle = '#000'; + const linesCount = wrapText(ctx, dropdowns[index].description, colX + 20, y + 50, maxWidth, 20); + yOffset += linesCount * 20; + } + }); + } + + redrawPopup(); }; function attachSolveClickEvent(toolbar, popup, row, y) {