From ffd7577a65dcf48d86e84b560d01db6161e2eba5 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 4 Apr 2026 12:50:30 +0700 Subject: [PATCH] fix: Z-pattern logic, eliminate code duplication, fix tests - Fix Z-pattern to use proper 2-turn path (H-V-H and V-H-V scanning) instead of single midpoint which only duplicated L-pattern behavior - Eliminate ~390 lines of duplicated pattern-matching code from PikachuGame.js by delegating to PikachuGameLogic via shared board ref - Fix removeCards to reset type=0 so pattern matching works after removal - Fix Jest config: remove invalid preset, add ESM support for Windows - Rename log.js to log.cjs for CommonJS compatibility with ESM project - Fix test expectations to account for pattern priority and border routing --- jest.config.js | 8 - log.js => log.cjs | 0 package.json | 22 +- src/game/logic/PikachuGameLogic.js | 56 +++- src/game/scenes/PikachuGame.js | 404 +---------------------------- test/all-patterns.test.js | 141 +++++----- test/patterns/i-pattern.test.js | 106 +++++--- test/patterns/l-pattern.test.js | 115 ++++---- test/patterns/u-pattern.test.js | 119 +++++---- test/patterns/z-pattern.test.js | 179 +++++++------ 10 files changed, 399 insertions(+), 751 deletions(-) rename log.js => log.cjs (100%) diff --git a/jest.config.js b/jest.config.js index 45082b8..e7b0e99 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,12 +1,4 @@ export default { - // Use ES modules - preset: 'node', - extensionsToTreatAsEsm: ['.js'], - globals: { - 'ts-jest': { - useESM: true - } - }, transform: {}, // Test environment diff --git a/log.js b/log.cjs similarity index 100% rename from log.js rename to log.cjs diff --git a/package.json b/package.json index 14b3943..ab2f40c 100644 --- a/package.json +++ b/package.json @@ -22,19 +22,19 @@ "javascript" ], "scripts": { - "dev": "node log.js dev & next dev -p 8080", - "build": "node log.js build & next build", + "dev": "node log.cjs dev & next dev -p 8080", + "build": "node log.cjs build & next build", "dev-nolog": "next dev -p 8080", "build-nolog": "next build", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "test:i": "jest test/patterns/i-pattern.test.js", - "test:l": "jest test/patterns/l-pattern.test.js", - "test:u": "jest test/patterns/u-pattern.test.js", - "test:z": "jest test/patterns/z-pattern.test.js", - "test:verbose": "jest --verbose", - "test:silent": "jest --silent" + "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js", + "test:watch": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watch", + "test:coverage": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --coverage", + "test:i": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js test/patterns/i-pattern.test.js", + "test:l": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js test/patterns/l-pattern.test.js", + "test:u": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js test/patterns/u-pattern.test.js", + "test:z": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js test/patterns/z-pattern.test.js", + "test:verbose": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --verbose", + "test:silent": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --silent" }, "dependencies": { "next": "15.3.1", diff --git a/src/game/logic/PikachuGameLogic.js b/src/game/logic/PikachuGameLogic.js index 14f9473..0f7d438 100644 --- a/src/game/logic/PikachuGameLogic.js +++ b/src/game/logic/PikachuGameLogic.js @@ -295,14 +295,26 @@ export class PikachuGameLogic { } checkZPattern(start, end) { + // H-V-H: horizontal, vertical, horizontal (scan connecting columns) + for (let col = 0; col < this.matrixWidth; col++) { + if (col === start.col || col === end.col) continue; + if (this.board[start.row][col].type !== 0) continue; + if (this.board[end.row][col].type !== 0) continue; + if (this.isPathClear(start, {row: start.row, col}) && + this.isPathClear({row: start.row, col}, {row: end.row, col}) && + this.isPathClear({row: end.row, col}, end)) { + return true; + } + } + // V-H-V: vertical, horizontal, vertical (scan connecting rows) for (let row = 0; row < this.matrixHeight; row++) { - for (let col = 0; col < this.matrixWidth; col++) { - if (this.board[row][col].type !== 0) continue; - - const midPoint = {row, col}; - if (this.isPathClear(start, midPoint) && this.isPathClear(midPoint, end)) { - return true; - } + if (row === start.row || row === end.row) continue; + if (this.board[row][start.col].type !== 0) continue; + if (this.board[row][end.col].type !== 0) continue; + if (this.isPathClear(start, {row, col: start.col}) && + this.isPathClear({row, col: start.col}, {row, col: end.col}) && + this.isPathClear({row, col: end.col}, end)) { + return true; } } return false; @@ -465,14 +477,30 @@ export class PikachuGameLogic { } checkZPatternWithPath(start, end) { + // H-V-H: scan connecting columns + for (let col = 0; col < this.matrixWidth; col++) { + if (col === start.col || col === end.col) continue; + if (this.board[start.row][col].type !== 0) continue; + if (this.board[end.row][col].type !== 0) continue; + const corner1 = {row: start.row, col}; + const corner2 = {row: end.row, col}; + if (this.isPathClear(start, corner1) && + this.isPathClear(corner1, corner2) && + this.isPathClear(corner2, end)) { + return [start, corner1, corner2, end]; + } + } + // V-H-V: scan connecting rows for (let row = 0; row < this.matrixHeight; row++) { - for (let col = 0; col < this.matrixWidth; col++) { - if (this.board[row][col].type !== 0) continue; - - const midPoint = {row, col}; - if (this.isPathClear(start, midPoint) && this.isPathClear(midPoint, end)) { - return [start, midPoint, end]; - } + if (row === start.row || row === end.row) continue; + if (this.board[row][start.col].type !== 0) continue; + if (this.board[row][end.col].type !== 0) continue; + const corner1 = {row, col: start.col}; + const corner2 = {row, col: end.col}; + if (this.isPathClear(start, corner1) && + this.isPathClear(corner1, corner2) && + this.isPathClear(corner2, end)) { + return [start, corner1, corner2, end]; } } return null; diff --git a/src/game/scenes/PikachuGame.js b/src/game/scenes/PikachuGame.js index 5a2e13f..83c4d0c 100644 --- a/src/game/scenes/PikachuGame.js +++ b/src/game/scenes/PikachuGame.js @@ -1,5 +1,6 @@ import { EventBus } from '../EventBus'; import { Scene } from 'phaser'; +import { PikachuGameLogic } from '../logic/PikachuGameLogic'; export class PikachuGame extends Scene { @@ -18,6 +19,7 @@ export class PikachuGame extends Scene this.gameStarted = false; this.debugLines = []; this.debugMode = false; + this.logic = new PikachuGameLogic(this.boardWidth, this.boardHeight); } create () @@ -157,6 +159,7 @@ export class PikachuGame extends Scene } } + this.logic.board = this.board; this.renderBoard(); this.gameStarted = true; } @@ -221,7 +224,7 @@ export class PikachuGame extends Scene // Check if cards are of the same type if (firstCell.type === secondCell.type) { // Check if there's a valid path between them - const pathResult = this.hasValidPathWithDebug(first, second); + const pathResult = this.logic.hasValidPathWithDebug(first, second); if (pathResult.valid) { // Valid match - show green line and remove cards if (this.debugMode) { @@ -251,243 +254,6 @@ export class PikachuGame extends Scene } } - hasValidPath(start, end) - { - // Check I-pattern (straight line) - if (this.checkIPattern(start, end)) return true; - - // Check L-pattern (one turn) - if (this.checkLPattern(start, end)) return true; - - // Check U-pattern (two turns with border) - if (this.checkUPattern(start, end)) return true; - - // Check Z-pattern (two turns) - if (this.checkZPattern(start, end)) return true; - - return false; - } - - hasValidPathWithDebug(start, end) - { - // Check I-pattern (straight line) - const iPath = this.checkIPatternWithPath(start, end); - if (iPath) return { valid: true, path: iPath }; - - // Check L-pattern (one turn) - const lPath = this.checkLPatternWithPath(start, end); - if (lPath) return { valid: true, path: lPath }; - - // Check U-pattern (two turns with border) - const uPath = this.checkUPatternWithPath(start, end); - if (uPath) return { valid: true, path: uPath }; - - // Check Z-pattern (two turns) - const zPath = this.checkZPatternWithPath(start, end); - if (zPath) return { valid: true, path: zPath }; - - return { valid: false, path: null }; - } - - checkIPattern(start, end) - { - // Horizontal line - if (start.row === end.row) { - const minCol = Math.min(start.col, end.col); - const maxCol = Math.max(start.col, end.col); - for (let col = minCol + 1; col < maxCol; col++) { - if (this.board[start.row][col].visible) return false; - } - return true; - } - - // Vertical line - if (start.col === end.col) { - const minRow = Math.min(start.row, end.row); - const maxRow = Math.max(start.row, end.row); - for (let row = minRow + 1; row < maxRow; row++) { - if (this.board[row][start.col].visible) return false; - } - return true; - } - - return false; - } - - checkLineX(y1, y2, x) - { - // Find point have column max and min - const min = Math.min(y1, y2); - const max = Math.max(y1, y2); - - // Run column - for (let y = min + 1; y < max; y++) { - if (this.board[x][y].type !== 0) { // if see barrier then die - return false; - } - } - // Not die -> success - return true; - } - - checkLineY(x1, x2, y) - { - const min = Math.min(x1, x2); - const max = Math.max(x1, x2); - - for (let x = min + 1; x < max; x++) { - if (this.board[x][y].type !== 0) { - return false; - } - } - return true; - } - - checkLPattern(start, end) - { - // Try corner at start.row, end.col - if (this.isPathClear(start, {row: start.row, col: end.col}) && - this.isPathClear({row: start.row, col: end.col}, end) && - this.board[start.row][end.col].type === 0) { - return true; - } - - // Try corner at end.row, start.col - if (this.isPathClear(start, {row: end.row, col: start.col}) && - this.isPathClear({row: end.row, col: start.col}, end) && - this.board[end.row][start.col].type === 0) { - return true; - } - - return false; - } - - checkUPattern(start, end) - { - // Check more right - if (this.checkMoreLineX(start, end, 1)) return true; - // Check more left - if (this.checkMoreLineX(start, end, -1)) return true; - // Check more down - if (this.checkMoreLineY(start, end, 1)) return true; - // Check more up - if (this.checkMoreLineY(start, end, -1)) return true; - - return false; - } - - checkMoreLineX(start, end, type) - { - // Find point have y min - const pMinY = start.col < end.col ? start : end; - const pMaxY = start.col < end.col ? end : start; - - // Find line and y begin - let y = pMaxY.col + type; - let row = pMinY.row; - let colFinish = pMaxY.col; - - if (type === -1) { - colFinish = pMinY.col; - y = pMinY.col + type; - row = pMaxY.row; - } - - // Check if we can connect horizontally first - if ((this.board[row][colFinish].type === 0 || pMinY.col === pMaxY.col) && - this.checkLineX(pMinY.col, pMaxY.col, row)) { - - // Check extension beyond border - while (y >= 0 && y < this.matrixWidth && - this.board[pMinY.row][y].type === 0 && - this.board[pMaxY.row][y].type === 0) { - - if (this.checkLineY(pMinY.row, pMaxY.row, y)) { - return true; - } - y += type; - } - } - return false; - } - - checkMoreLineY(start, end, type) - { - // Find point have x min - const pMinX = start.row < end.row ? start : end; - const pMaxX = start.row < end.row ? end : start; - - let x = pMaxX.row + type; - let col = pMinX.col; - let rowFinish = pMaxX.row; - - if (type === -1) { - rowFinish = pMinX.row; - x = pMinX.row + type; - col = pMaxX.col; - } - - // Check if we can connect vertically first - if ((this.board[rowFinish][col].type === 0 || pMinX.row === pMaxX.row) && - this.checkLineY(pMinX.row, pMaxX.row, col)) { - - // Check extension beyond border - while (x >= 0 && x < this.matrixHeight && - this.board[x][pMinX.col].type === 0 && - this.board[x][pMaxX.col].type === 0) { - - if (this.checkLineX(pMinX.col, pMaxX.col, x)) { - return true; - } - x += type; - } - } - return false; - } - - checkZPattern(start, end) - { - // Check all possible two-turn paths within the matrix - for (let row = 0; row < this.matrixHeight; row++) { - for (let col = 0; col < this.matrixWidth; col++) { - if (this.board[row][col].type !== 0) continue; - - const midPoint = {row, col}; - if (this.isPathClear(start, midPoint) && this.isPathClear(midPoint, end)) { - return true; - } - } - } - return false; - } - - isPathClear(start, end) - { - if (start.row === end.row && start.col === end.col) return true; - - // Horizontal path - if (start.row === end.row) { - const minCol = Math.min(start.col, end.col); - const maxCol = Math.max(start.col, end.col); - for (let col = minCol + 1; col < maxCol; col++) { - if (this.board[start.row][col].type !== 0) return false; - } - return true; - } - - // Vertical path - if (start.col === end.col) { - const minRow = Math.min(start.row, end.row); - const maxRow = Math.max(start.row, end.row); - for (let row = minRow + 1; row < maxRow; row++) { - if (this.board[row][start.col].type !== 0) return false; - } - return true; - } - - return false; - } - removeCards(first, second) { const firstCell = this.board[first.row][first.col]; @@ -504,6 +270,8 @@ export class PikachuGame extends Scene secondCell.sprite.destroy(); firstCell.visible = false; secondCell.visible = false; + firstCell.type = 0; + secondCell.type = 0; firstCell.sprite = null; secondCell.sprite = null; } @@ -638,7 +406,7 @@ export class PikachuGame extends Scene if (row1 === row2 && col1 === col2) continue; if (this.board[row1][col1].type === this.board[row2][col2].type && - this.hasValidPath({row: row1, col: col1}, {row: row2, col: col2})) { + this.logic.hasValidPath({row: row1, col: col1}, {row: row2, col: col2})) { // Highlight the hint pair this.board[row1][col1].sprite.setTint(0xffff00); @@ -660,164 +428,6 @@ export class PikachuGame extends Scene } } - checkIPatternWithPath(start, end) - { - // Horizontal line - if (start.row === end.row) { - const minCol = Math.min(start.col, end.col); - const maxCol = Math.max(start.col, end.col); - for (let col = minCol + 1; col < maxCol; col++) { - if (this.board[start.row][col].type !== 0) return null; - } - return [start, end]; - } - - // Vertical line - if (start.col === end.col) { - const minRow = Math.min(start.row, end.row); - const maxRow = Math.max(start.row, end.row); - for (let row = minRow + 1; row < maxRow; row++) { - if (this.board[row][start.col].type !== 0) return null; - } - return [start, end]; - } - - return null; - } - - checkLPatternWithPath(start, end) - { - // Try corner at start.row, end.col - const corner1 = {row: start.row, col: end.col}; - if (this.isPathClear(start, corner1) && - this.isPathClear(corner1, end) && - this.board[start.row][end.col].type === 0) { - return [start, corner1, end]; - } - - // Try corner at end.row, start.col - const corner2 = {row: end.row, col: start.col}; - if (this.isPathClear(start, corner2) && - this.isPathClear(corner2, end) && - this.board[end.row][start.col].type === 0) { - return [start, corner2, end]; - } - - return null; - } - - checkUPatternWithPath(start, end) - { - // Check more right - let path = this.checkMoreLineXWithPath(start, end, 1); - if (path) return path; - - // Check more left - path = this.checkMoreLineXWithPath(start, end, -1); - if (path) return path; - - // Check more down - path = this.checkMoreLineYWithPath(start, end, 1); - if (path) return path; - - // Check more up - path = this.checkMoreLineYWithPath(start, end, -1); - if (path) return path; - - return null; - } - - checkMoreLineXWithPath(start, end, type) - { - // Find point have y min - const pMinY = start.col < end.col ? start : end; - const pMaxY = start.col < end.col ? end : start; - - // Find line and y begin - let y = pMaxY.col + type; - let row = pMinY.row; - let colFinish = pMaxY.col; - - if (type === -1) { - colFinish = pMinY.col; - y = pMinY.col + type; - row = pMaxY.row; - } - - // Check if we can connect horizontally first - if ((this.board[row][colFinish].type === 0 || pMinY.col === pMaxY.col) && - this.checkLineX(pMinY.col, pMaxY.col, row)) { - - // Check extension beyond border - while (y >= 0 && y < this.matrixWidth && - this.board[pMinY.row][y].type === 0 && - this.board[pMaxY.row][y].type === 0) { - - if (this.checkLineY(pMinY.row, pMaxY.row, y)) { - // Create path with the connecting point - const connectPoint1 = {row: pMinY.row, col: y}; - const connectPoint2 = {row: pMaxY.row, col: y}; - return [pMinY, connectPoint1, connectPoint2, pMaxY]; - } - y += type; - } - } - return null; - } - - checkMoreLineYWithPath(start, end, type) - { - // Find point have x min - const pMinX = start.row < end.row ? start : end; - const pMaxX = start.row < end.row ? end : start; - - let x = pMaxX.row + type; - let col = pMinX.col; - let rowFinish = pMaxX.row; - - if (type === -1) { - rowFinish = pMinX.row; - x = pMinX.row + type; - col = pMaxX.col; - } - - // Check if we can connect vertically first - if ((this.board[rowFinish][col].type === 0 || pMinX.row === pMaxX.row) && - this.checkLineY(pMinX.row, pMaxX.row, col)) { - - // Check extension beyond border - while (x >= 0 && x < this.matrixHeight && - this.board[x][pMinX.col].type === 0 && - this.board[x][pMaxX.col].type === 0) { - - if (this.checkLineX(pMinX.col, pMaxX.col, x)) { - // Create path with the connecting point - const connectPoint1 = {row: x, col: pMinX.col}; - const connectPoint2 = {row: x, col: pMaxX.col}; - return [pMinX, connectPoint1, connectPoint2, pMaxX]; - } - x += type; - } - } - return null; - } - - checkZPatternWithPath(start, end) - { - // Check all possible two-turn paths within the matrix - for (let row = 0; row < this.matrixHeight; row++) { - for (let col = 0; col < this.matrixWidth; col++) { - if (this.board[row][col].type !== 0) continue; - - const midPoint = {row, col}; - if (this.isPathClear(start, midPoint) && this.isPathClear(midPoint, end)) { - return [start, midPoint, end]; - } - } - } - return null; - } - shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { diff --git a/test/all-patterns.test.js b/test/all-patterns.test.js index faa4a2d..ac7df50 100644 --- a/test/all-patterns.test.js +++ b/test/all-patterns.test.js @@ -2,18 +2,17 @@ import { PikachuBaseTest } from './base/PikachuBaseTest.js'; describe('All Patterns Integration Tests', () => { let tester; - + beforeEach(() => { tester = new PikachuBaseTest(); }); describe('Pattern Priority', () => { test('should prefer I-pattern over all other patterns', () => { - // Simple horizontal line should use I-pattern const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 1); - + const testCase = tester.createTestCase( 'I-pattern priority', matrix, @@ -21,16 +20,15 @@ describe('All Patterns Integration Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); test('should prefer L-pattern over U and Z patterns', () => { - // Simple L-shape should use L-pattern const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'L-pattern priority', matrix, @@ -38,26 +36,25 @@ describe('All Patterns Integration Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); test('should use U-pattern when I and L patterns are blocked', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 3, 3, 1); - // Block L-pattern corners - tester.placeCard(matrix, 1, 3, 2); - tester.placeCard(matrix, 3, 1, 2); - + tester.placeCard(matrix, 4, 5, 1); + tester.placeCard(matrix, 4, 9, 1); + // Block I-pattern + tester.placeCard(matrix, 4, 7, 2); + const testCase = tester.createTestCase( - 'U-pattern when simpler patterns blocked', + 'U-pattern when I-pattern blocked', matrix, - 1, 1, 3, 3, + 4, 5, 4, 9, true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -65,23 +62,10 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 4, 4, 1); - // Block L-pattern + // Block L-pattern corners tester.placeCard(matrix, 2, 4, 2); tester.placeCard(matrix, 4, 2, 2); - // Block U-pattern by filling border extensions - for (let i = 1; i <= 20; i++) { - if (i !== 2 && i !== 4) { - tester.placeCard(matrix, 2, i, 2); - tester.placeCard(matrix, 4, i, 2); - } - } - for (let i = 1; i <= 8; i++) { - if (i !== 2 && i !== 4) { - tester.placeCard(matrix, i, 2, 2); - tester.placeCard(matrix, i, 4, 2); - } - } - + const testCase = tester.createTestCase( 'Z-pattern as last resort', matrix, @@ -89,7 +73,7 @@ describe('All Patterns Integration Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -99,7 +83,7 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 2); // different type - + const testCase = tester.createTestCase( 'Different card types should fail', matrix, @@ -108,7 +92,7 @@ describe('All Patterns Integration Tests', () => { null, 'Cards are different types' ); - + tester.expectTestCase(testCase); }); @@ -116,7 +100,7 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // No card at (1, 5) - + const testCase = tester.createTestCase( 'Empty positions should fail', matrix, @@ -125,14 +109,14 @@ describe('All Patterns Integration Tests', () => { null, 'One or both positions are empty' ); - + tester.expectTestCase(testCase); }); test('should handle same position selection', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); - + const testCase = tester.createTestCase( 'Same position should fail', matrix, @@ -141,17 +125,17 @@ describe('All Patterns Integration Tests', () => { null, 'Cannot select the same position' ); - + tester.expectTestCase(testCase); }); test('should handle out of bounds positions', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); - + tester.game.loadBoardFromMatrix(matrix); const result = tester.game.testMove(1, 1, 0, 0); // Out of bounds - + expect(result.valid).toBe(false); expect(result.error).toContain('out of bounds'); }); @@ -162,7 +146,7 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 3, 1); - + // Both I-pattern and L-pattern are possible, should choose I-pattern const testCase = tester.createTestCase( 'Multiple valid patterns - choose simplest', @@ -171,35 +155,32 @@ describe('All Patterns Integration Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); test('should handle dense board with limited connection options', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 8, 20, 1); - - // Fill most positions but leave some paths open - for (let row = 1; row <= 8; row++) { + tester.placeCard(matrix, 3, 3, 1); + // Block L-pattern corners + tester.placeCard(matrix, 1, 3, 2); + tester.placeCard(matrix, 3, 1, 2); + // Fill most other positions + for (let row = 4; row <= 8; row++) { for (let col = 1; col <= 20; col++) { - if (!((row === 1 && col === 1) || (row === 8 && col === 20))) { - // Leave some strategic positions empty for connection - if (!(row === 1 && col === 20) && !(row === 8 && col === 1) && - !(row === 4 && col === 10)) { - tester.placeCard(matrix, row, col, 2); - } - } + tester.placeCard(matrix, row, col, 2); } } - + + // Z-pattern should still work through col 2 const testCase = tester.createTestCase( 'Dense board with limited options', matrix, - 1, 1, 8, 20, - true // Should find some pattern + 1, 1, 3, 3, + true ); - + tester.expectTestCase(testCase); }); @@ -207,7 +188,7 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 8, 20, 1); - + // Block all possible connection paths for (let row = 1; row <= 8; row++) { for (let col = 1; col <= 20; col++) { @@ -216,14 +197,14 @@ describe('All Patterns Integration Tests', () => { } } } - + const testCase = tester.createTestCase( 'Impossible connections should fail', matrix, 1, 1, 8, 20, false ); - + tester.expectTestCase(testCase); }); }); @@ -235,17 +216,17 @@ describe('All Patterns Integration Tests', () => { tester.placeCard(matrix, 1, 20, 1); // top-right tester.placeCard(matrix, 8, 1, 1); // bottom-left tester.placeCard(matrix, 8, 20, 1); // bottom-right - + // Test all corner-to-corner connections const testCases = [ - [1, 1, 1, 20], // top-left to top-right - [1, 1, 8, 1], // top-left to bottom-left - [1, 1, 8, 20], // top-left to bottom-right - [1, 20, 8, 20], // top-right to bottom-right - [8, 1, 8, 20], // bottom-left to bottom-right - [1, 20, 8, 1] // top-right to bottom-left + [1, 1, 1, 20], // top-left to top-right (I-pattern) + [1, 1, 8, 1], // top-left to bottom-left (I-pattern) + [1, 1, 8, 20], // top-left to bottom-right (L-pattern) + [1, 20, 8, 20], // top-right to bottom-right (I-pattern) + [8, 1, 8, 20], // bottom-left to bottom-right (I-pattern) + [1, 20, 8, 1] // top-right to bottom-left (Z-pattern, L corners occupied) ]; - + testCases.forEach(([r1, c1, r2, c2]) => { const testCase = tester.createTestCase( `Corner connection (${r1},${c1}) to (${r2},${c2})`, @@ -253,7 +234,7 @@ describe('All Patterns Integration Tests', () => { r1, c1, r2, c2, true ); - + tester.expectTestCase(testCase); }); }); @@ -263,7 +244,7 @@ describe('All Patterns Integration Tests', () => { // Place cards along top edge tester.placeCard(matrix, 1, 5, 1); tester.placeCard(matrix, 1, 15, 1); - + const testCase = tester.createTestCase( 'Cards along board edge', matrix, @@ -271,7 +252,7 @@ describe('All Patterns Integration Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -281,8 +262,7 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 4, 4, 1); - - // This could potentially work with L, U, or Z patterns + // Should prefer L-pattern as it's simpler const testCase = tester.createTestCase( 'Multiple pattern possibilities', @@ -291,7 +271,7 @@ describe('All Patterns Integration Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -299,20 +279,21 @@ describe('All Patterns Integration Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 6, 6, 1); - + // Block L-pattern tester.placeCard(matrix, 2, 6, 2); tester.placeCard(matrix, 6, 2, 2); - - // Should fall back to U-pattern or Z-pattern + + // Should fall back to Z-pattern const testCase = tester.createTestCase( 'Cascading pattern fallbacks', matrix, 2, 2, 6, 6, - true + true, + 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); -}); \ No newline at end of file +}); diff --git a/test/patterns/i-pattern.test.js b/test/patterns/i-pattern.test.js index 49cea0d..07dcf58 100644 --- a/test/patterns/i-pattern.test.js +++ b/test/patterns/i-pattern.test.js @@ -2,7 +2,7 @@ import { PikachuBaseTest } from '../base/PikachuBaseTest.js'; describe('I-Pattern Tests', () => { let tester; - + beforeEach(() => { tester = new PikachuBaseTest(); }); @@ -12,7 +12,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 1); - + const testCase = tester.createTestCase( 'Horizontal line - clear path', matrix, @@ -20,23 +20,30 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); test('should not connect cards with blocked horizontal path', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 1, 3, 2); // blocking card - tester.placeCard(matrix, 1, 5, 1); - + // Fill entire board so no alternative patterns can route around + for (let row = 1; row <= 8; row++) { + for (let col = 1; col <= 20; col++) { + tester.placeCard(matrix, row, col, 2); + } + } + // Place target cards in the middle (away from border) + tester.placeCard(matrix, 4, 5, 1); + tester.placeCard(matrix, 4, 7, 1); + // (4,6) remains as blocker type 2 + const testCase = tester.createTestCase( 'Horizontal line - blocked path', matrix, - 1, 1, 1, 5, + 4, 5, 4, 7, false ); - + tester.expectTestCase(testCase); }); @@ -44,7 +51,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 2, 1); - + const testCase = tester.createTestCase( 'Adjacent cards - horizontal', matrix, @@ -52,7 +59,7 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -60,7 +67,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 20, 1); - + const testCase = tester.createTestCase( 'Long horizontal line', matrix, @@ -68,7 +75,7 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -78,7 +85,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 4, 1, 1); - + const testCase = tester.createTestCase( 'Vertical line - clear path', matrix, @@ -86,23 +93,30 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); test('should not connect cards with blocked vertical path', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 2, 1, 2); // blocking card - tester.placeCard(matrix, 4, 1, 1); - + // Fill entire board so no alternative patterns can route around + for (let row = 1; row <= 8; row++) { + for (let col = 1; col <= 20; col++) { + tester.placeCard(matrix, row, col, 2); + } + } + // Place target cards in the middle + tester.placeCard(matrix, 3, 10, 1); + tester.placeCard(matrix, 5, 10, 1); + // (4,10) remains as blocker type 2 + const testCase = tester.createTestCase( 'Vertical line - blocked path', matrix, - 1, 1, 4, 1, + 3, 10, 5, 10, false ); - + tester.expectTestCase(testCase); }); @@ -110,7 +124,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 2, 1, 1); - + const testCase = tester.createTestCase( 'Adjacent cards - vertical', matrix, @@ -118,7 +132,7 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -126,7 +140,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 8, 1, 1); - + const testCase = tester.createTestCase( 'Long vertical line', matrix, @@ -134,31 +148,37 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); describe('Edge Cases', () => { - test('should not connect diagonal cards', () => { + test('should not connect diagonal cards when all paths blocked', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 2, 2, 1); - + // Fill entire board to block all patterns + for (let row = 1; row <= 8; row++) { + for (let col = 1; col <= 20; col++) { + tester.placeCard(matrix, row, col, 2); + } + } + tester.placeCard(matrix, 4, 10, 1); + tester.placeCard(matrix, 5, 11, 1); + const testCase = tester.createTestCase( - 'Diagonal - should not work', + 'Diagonal - should not work when all paths blocked', matrix, - 1, 1, 2, 2, + 4, 10, 5, 11, false ); - + tester.expectTestCase(testCase); }); test('should not connect same position', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); - + const testCase = tester.createTestCase( 'Same position - should fail', matrix, @@ -167,7 +187,7 @@ describe('I-Pattern Tests', () => { null, 'Cannot select the same position' ); - + tester.expectTestCase(testCase); }); @@ -175,7 +195,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 2); // different card type - + const testCase = tester.createTestCase( 'Different card types', matrix, @@ -184,7 +204,7 @@ describe('I-Pattern Tests', () => { null, 'Cards are different types' ); - + tester.expectTestCase(testCase); }); @@ -192,7 +212,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // Don't place card at (1,5) - + const testCase = tester.createTestCase( 'Empty position', matrix, @@ -201,7 +221,7 @@ describe('I-Pattern Tests', () => { null, 'One or both positions are empty' ); - + tester.expectTestCase(testCase); }); }); @@ -211,7 +231,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // top-left corner tester.placeCard(matrix, 1, 20, 1); // top-right corner - + const testCase = tester.createTestCase( 'Cards at board edges', matrix, @@ -219,7 +239,7 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -227,7 +247,7 @@ describe('I-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // top-left tester.placeCard(matrix, 8, 1, 1); // bottom-left - + const testCase = tester.createTestCase( 'Cards at opposite corners', matrix, @@ -235,8 +255,8 @@ describe('I-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); -}); \ No newline at end of file +}); diff --git a/test/patterns/l-pattern.test.js b/test/patterns/l-pattern.test.js index ed84ea1..6b27f8a 100644 --- a/test/patterns/l-pattern.test.js +++ b/test/patterns/l-pattern.test.js @@ -2,7 +2,7 @@ import { PikachuBaseTest } from '../base/PikachuBaseTest.js'; describe('L-Pattern Tests', () => { let tester; - + beforeEach(() => { tester = new PikachuBaseTest(); }); @@ -12,7 +12,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'L-shape via first corner', matrix, @@ -20,7 +20,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -28,7 +28,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 3, 1); tester.placeCard(matrix, 3, 1, 1); - + const testCase = tester.createTestCase( 'L-shape via second corner', matrix, @@ -36,7 +36,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -45,7 +45,7 @@ describe('L-Pattern Tests', () => { tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 3, 2); // block first corner tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'L-shape with one corner blocked', matrix, @@ -53,43 +53,54 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); - test('should fail when both corners are blocked', () => { + test('should fail when both corners are blocked and no other path exists', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 1, 3, 2); // block first corner - tester.placeCard(matrix, 3, 1, 2); // block second corner - tester.placeCard(matrix, 3, 3, 1); - + // Fill entire board to block all patterns + for (let row = 1; row <= 8; row++) { + for (let col = 1; col <= 20; col++) { + tester.placeCard(matrix, row, col, 2); + } + } + // Place target cards in the middle + tester.placeCard(matrix, 4, 8, 1); + tester.placeCard(matrix, 6, 10, 1); + // L-corners (4,10) and (6,8) remain blocked by fill + const testCase = tester.createTestCase( 'L-shape with both corners blocked', matrix, - 1, 1, 3, 3, + 4, 8, 6, 10, false ); - + tester.expectTestCase(testCase); }); }); describe('Path Blocking', () => { - test('should fail when path to corner is blocked', () => { + test('should fail when all paths are completely blocked', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 1, 2, 2); // block horizontal path - tester.placeCard(matrix, 2, 1, 2); // block vertical path - tester.placeCard(matrix, 3, 3, 1); - + // Fill entire board to prevent any pattern + for (let row = 1; row <= 8; row++) { + for (let col = 1; col <= 20; col++) { + tester.placeCard(matrix, row, col, 2); + } + } + // Place target cards in the middle + tester.placeCard(matrix, 4, 8, 1); + tester.placeCard(matrix, 6, 10, 1); + const testCase = tester.createTestCase( - 'L-shape with blocked paths to corners', + 'L-shape with all paths blocked', matrix, - 1, 1, 3, 3, + 4, 8, 6, 10, false ); - + tester.expectTestCase(testCase); }); @@ -98,7 +109,7 @@ describe('L-Pattern Tests', () => { tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 2, 2); // block horizontal path to first corner tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'L-shape with one path blocked', matrix, @@ -106,7 +117,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -116,7 +127,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 4, 1, 1); tester.placeCard(matrix, 1, 4, 1); - + const testCase = tester.createTestCase( 'Up-then-right turn', matrix, @@ -124,7 +135,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -132,7 +143,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 4, 1); tester.placeCard(matrix, 4, 1, 1); - + const testCase = tester.createTestCase( 'Down-then-left turn', matrix, @@ -140,7 +151,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -148,7 +159,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 3, 5, 1); tester.placeCard(matrix, 1, 2, 1); - + const testCase = tester.createTestCase( 'Left-then-up turn', matrix, @@ -156,7 +167,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -164,7 +175,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 2, 1); tester.placeCard(matrix, 3, 5, 1); - + const testCase = tester.createTestCase( 'Right-then-down turn', matrix, @@ -172,7 +183,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -182,7 +193,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // top-left corner tester.placeCard(matrix, 8, 20, 1); // bottom-right corner - + const testCase = tester.createTestCase( 'L-shape at board edges', matrix, @@ -190,7 +201,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -198,7 +209,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); // top-left tester.placeCard(matrix, 1, 20, 1); // top-right - + // This should be I-pattern, not L-pattern const testCase = tester.createTestCase( 'Straight line at board boundary', @@ -207,7 +218,7 @@ describe('L-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -219,7 +230,7 @@ describe('L-Pattern Tests', () => { tester.placeCard(matrix, 2, 2, 2); // blocking card tester.placeCard(matrix, 3, 3, 2); // blocking card tester.placeCard(matrix, 5, 5, 1); - + const testCase = tester.createTestCase( 'L-shape with multiple blocking cards', matrix, @@ -227,7 +238,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -235,12 +246,12 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 4, 4, 1); - + // Fill some positions but leave L-path open tester.placeCard(matrix, 3, 3, 2); tester.placeCard(matrix, 1, 1, 2); tester.placeCard(matrix, 5, 5, 2); - + const testCase = tester.createTestCase( 'L-shape in dense board', matrix, @@ -248,7 +259,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -258,7 +269,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 2, 1); - + const testCase = tester.createTestCase( 'Adjacent cards should use I-pattern', matrix, @@ -266,7 +277,7 @@ describe('L-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -274,7 +285,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 10, 1); - + const testCase = tester.createTestCase( 'Straight line should use I-pattern', matrix, @@ -282,7 +293,7 @@ describe('L-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -292,7 +303,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'Corner at middle position', matrix, @@ -300,7 +311,7 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -308,7 +319,7 @@ describe('L-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 6, 10, 1); - + const testCase = tester.createTestCase( 'Asymmetric L-shape', matrix, @@ -316,8 +327,8 @@ describe('L-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); }); -}); \ No newline at end of file +}); diff --git a/test/patterns/u-pattern.test.js b/test/patterns/u-pattern.test.js index 16d4da3..3384c69 100644 --- a/test/patterns/u-pattern.test.js +++ b/test/patterns/u-pattern.test.js @@ -2,7 +2,7 @@ import { PikachuBaseTest } from '../base/PikachuBaseTest.js'; describe('U-Pattern Tests', () => { let tester; - + beforeEach(() => { tester = new PikachuBaseTest(); }); @@ -14,7 +14,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 3, 5, 1); // Block direct vertical path tester.placeCard(matrix, 2, 5, 2); - + const testCase = tester.createTestCase( 'U-pattern extending right', matrix, @@ -22,7 +22,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -32,7 +32,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 3, 15, 1); // Block direct vertical path tester.placeCard(matrix, 2, 15, 2); - + const testCase = tester.createTestCase( 'U-pattern extending left', matrix, @@ -40,7 +40,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -50,7 +50,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 1, 3, 1); // Block direct horizontal path tester.placeCard(matrix, 1, 2, 2); - + const testCase = tester.createTestCase( 'U-pattern extending down', matrix, @@ -58,7 +58,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -68,7 +68,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 8, 3, 1); // Block direct horizontal path tester.placeCard(matrix, 8, 2, 2); - + const testCase = tester.createTestCase( 'U-pattern extending up', matrix, @@ -76,7 +76,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -88,7 +88,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 1, 3, 1); // Block direct horizontal path tester.placeCard(matrix, 1, 2, 2); - + const testCase = tester.createTestCase( 'U-pattern at top-left corner', matrix, @@ -96,23 +96,25 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); - test('should handle U-pattern at opposite board corners', () => { + test('should handle U-pattern at opposite board corners (same col)', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 8, 20, 1); - + tester.placeCard(matrix, 8, 1, 1); + // Block direct vertical path + tester.placeCard(matrix, 4, 1, 2); + const testCase = tester.createTestCase( - 'U-pattern at opposite corners', + 'U-pattern at opposite corners same column', matrix, - 1, 1, 8, 20, + 1, 1, 8, 1, true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -120,7 +122,9 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 20, 1); tester.placeCard(matrix, 8, 20, 1); - + // Block direct vertical path + tester.placeCard(matrix, 4, 20, 2); + const testCase = tester.createTestCase( 'U-pattern along right edge', matrix, @@ -128,7 +132,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -142,7 +146,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 3, 5, 2); tester.placeCard(matrix, 4, 5, 2); tester.placeCard(matrix, 5, 5, 2); - + const testCase = tester.createTestCase( 'U-pattern with multiple blocking cards', matrix, @@ -150,7 +154,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -158,7 +162,7 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 4, 4, 1); - + // Block all possible extension paths for (let i = 1; i <= 20; i++) { if (i !== 2 && i !== 4) { @@ -172,14 +176,14 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, i, 4, 2); } } - + const testCase = tester.createTestCase( 'U-pattern impossible - all paths blocked', matrix, 2, 2, 4, 4, false ); - + tester.expectTestCase(testCase); }); }); @@ -191,7 +195,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 4, 2, 1); // Block direct vertical path tester.placeCard(matrix, 3, 2, 2); - + const testCase = tester.createTestCase( 'Horizontal extension to right', matrix, @@ -199,7 +203,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -209,7 +213,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 4, 18, 1); // Block direct vertical path tester.placeCard(matrix, 3, 18, 2); - + const testCase = tester.createTestCase( 'Horizontal extension to left', matrix, @@ -217,7 +221,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -227,7 +231,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 6, 4, 1); // Block direct horizontal path tester.placeCard(matrix, 6, 3, 2); - + const testCase = tester.createTestCase( 'Vertical extension upward', matrix, @@ -235,7 +239,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -245,7 +249,7 @@ describe('U-Pattern Tests', () => { tester.placeCard(matrix, 2, 4, 1); // Block direct horizontal path tester.placeCard(matrix, 2, 3, 2); - + const testCase = tester.createTestCase( 'Vertical extension downward', matrix, @@ -253,7 +257,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -261,17 +265,19 @@ describe('U-Pattern Tests', () => { describe('Long Distance Connections', () => { test('should handle long distance U-pattern', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 8, 20, 1); - + tester.placeCard(matrix, 4, 1, 1); + tester.placeCard(matrix, 4, 20, 1); + // Block direct horizontal path + tester.placeCard(matrix, 4, 10, 2); + const testCase = tester.createTestCase( 'Long distance U-pattern', matrix, - 1, 1, 8, 20, + 4, 1, 4, 20, true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -279,7 +285,9 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 4, 1, 1); tester.placeCard(matrix, 4, 20, 1); - + // Block direct horizontal path + tester.placeCard(matrix, 4, 5, 2); + const testCase = tester.createTestCase( 'U-pattern across full board width', matrix, @@ -287,7 +295,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); @@ -295,7 +303,9 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 10, 1); tester.placeCard(matrix, 8, 10, 1); - + // Block direct vertical path + tester.placeCard(matrix, 4, 10, 2); + const testCase = tester.createTestCase( 'U-pattern across full board height', matrix, @@ -303,7 +313,7 @@ describe('U-Pattern Tests', () => { true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -313,7 +323,7 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 1); - + const testCase = tester.createTestCase( 'Should prefer I-pattern', matrix, @@ -321,7 +331,7 @@ describe('U-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -329,7 +339,7 @@ describe('U-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'Should prefer L-pattern', matrix, @@ -337,27 +347,26 @@ describe('U-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); test('should use U-pattern when simpler patterns are blocked', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 2, 2, 1); - tester.placeCard(matrix, 4, 4, 1); - // Block L-pattern corners - tester.placeCard(matrix, 2, 4, 2); - tester.placeCard(matrix, 4, 2, 2); - + tester.placeCard(matrix, 4, 5, 1); + tester.placeCard(matrix, 4, 9, 1); + // Block I-pattern + tester.placeCard(matrix, 4, 7, 2); + const testCase = tester.createTestCase( - 'Should use U-pattern when L-pattern blocked', + 'Should use U-pattern when I-pattern blocked', matrix, - 2, 2, 4, 4, + 4, 5, 4, 9, true, 'U-pattern' ); - + tester.expectTestCase(testCase); }); }); -}); \ No newline at end of file +}); diff --git a/test/patterns/z-pattern.test.js b/test/patterns/z-pattern.test.js index db000cb..745948c 100644 --- a/test/patterns/z-pattern.test.js +++ b/test/patterns/z-pattern.test.js @@ -2,77 +2,77 @@ import { PikachuBaseTest } from '../base/PikachuBaseTest.js'; describe('Z-Pattern Tests', () => { let tester; - + beforeEach(() => { tester = new PikachuBaseTest(); }); describe('Basic Z-Shapes', () => { - test('should connect cards through intermediate point', () => { + test('should connect cards through two turns', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - // Block L-pattern possibilities + // Block L-pattern corners tester.placeCard(matrix, 1, 3, 2); tester.placeCard(matrix, 3, 1, 2); - + const testCase = tester.createTestCase( - 'Basic Z-pattern with intermediate point', + 'Basic Z-pattern with two turns', matrix, 1, 1, 3, 3, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); - test('should find multiple possible intermediate points', () => { + test('should find path with distant cards', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 5, 5, 1); - // Block L-pattern possibilities + // Block L-pattern corners tester.placeCard(matrix, 1, 5, 2); tester.placeCard(matrix, 5, 1, 2); - + const testCase = tester.createTestCase( - 'Z-pattern with multiple intermediate points', + 'Z-pattern with distant cards', matrix, 1, 1, 5, 5, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); - test('should work with intermediate point at border', () => { + test('should work with connecting column between cards', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 4, 4, 1); - // Block L-pattern possibilities + // Block L-pattern corners tester.placeCard(matrix, 2, 4, 2); tester.placeCard(matrix, 4, 2, 2); - + const testCase = tester.createTestCase( - 'Z-pattern with border intermediate point', + 'Z-pattern with connecting column', matrix, 2, 2, 4, 4, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); describe('Blocked Scenarios', () => { - test('should fail when no intermediate point is available', () => { + test('should fail when no path is available', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - - // Block all possible intermediate points + + // Block all possible paths for (let row = 1; row <= 8; row++) { for (let col = 1; col <= 20; col++) { if (!((row === 1 && col === 1) || (row === 3 && col === 3))) { @@ -80,35 +80,35 @@ describe('Z-Pattern Tests', () => { } } } - + const testCase = tester.createTestCase( - 'Z-pattern blocked - no intermediate point', + 'Z-pattern blocked - no path available', matrix, 1, 1, 3, 3, false ); - + tester.expectTestCase(testCase); }); - test('should fail when paths to intermediate point are blocked', () => { + test('should fail when all connecting columns and rows are blocked', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - - // Block paths to potential intermediate points + + // Block paths to potential connecting points tester.placeCard(matrix, 1, 2, 2); tester.placeCard(matrix, 2, 1, 2); tester.placeCard(matrix, 2, 3, 2); tester.placeCard(matrix, 3, 2, 2); - + const testCase = tester.createTestCase( - 'Z-pattern blocked - paths to intermediate blocked', + 'Z-pattern blocked - connecting paths blocked', matrix, 1, 1, 3, 3, false ); - + tester.expectTestCase(testCase); }); }); @@ -118,10 +118,10 @@ describe('Z-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 8, 20, 1); - // Block simpler patterns + // Block L-pattern corners tester.placeCard(matrix, 1, 20, 2); tester.placeCard(matrix, 8, 1, 2); - + const testCase = tester.createTestCase( 'Z-pattern at board edges', matrix, @@ -129,74 +129,74 @@ describe('Z-Pattern Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); - test('should handle Z-pattern along board boundaries', () => { + test('should handle Z-pattern with same-row cards and blocker', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 2, 1); tester.placeCard(matrix, 1, 8, 1); // Block direct horizontal path tester.placeCard(matrix, 1, 5, 2); - + const testCase = tester.createTestCase( - 'Z-pattern along board boundary', + 'Z-pattern with same-row cards', matrix, 1, 2, 1, 8, - true, - 'Z-pattern' + true ); - + tester.expectTestCase(testCase); }); }); - describe('Complex Intermediate Points', () => { - test('should use specific intermediate point', () => { + describe('Complex Connecting Points', () => { + test('should use specific connecting column', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 2, 2, 1); tester.placeCard(matrix, 6, 6, 1); - // Block L-pattern possibilities + // Block L-pattern corners tester.placeCard(matrix, 2, 6, 2); tester.placeCard(matrix, 6, 2, 2); - // Ensure intermediate point at (4,4) is available - + const testCase = tester.createTestCase( - 'Z-pattern with specific intermediate point', + 'Z-pattern with specific connecting column', matrix, 2, 2, 6, 6, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); - test('should work in dense board with limited intermediate points', () => { + test('should work in dense board with limited paths', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 4, 4, 1); - - // Fill most of the board but leave some Z-pattern paths + tester.placeCard(matrix, 2, 2, 1); + tester.placeCard(matrix, 5, 5, 1); + + // Fill most of the board but leave a Z-pattern corridor through col 3: + // (2,2) -> (2,3) -> (5,3) -> (5,5) for (let row = 1; row <= 8; row++) { for (let col = 1; col <= 20; col++) { - if (!((row === 1 && col === 1) || (row === 4 && col === 4) || - (row === 1 && col === 4) || (row === 4 && col === 1) || - (row === 2 && col === 2) || (row === 3 && col === 3))) { + if (!((row === 2 && col === 2) || (row === 5 && col === 5) || + (row === 2 && col === 3) || (row === 5 && col === 3) || + (row === 3 && col === 3) || (row === 4 && col === 3) || + (row === 5 && col === 4))) { tester.placeCard(matrix, row, col, 2); } } } - + const testCase = tester.createTestCase( 'Z-pattern in dense board', matrix, - 1, 1, 4, 4, + 2, 2, 5, 5, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -204,37 +204,35 @@ describe('Z-Pattern Tests', () => { describe('Same Row/Column Scenarios', () => { test('should handle Z-pattern with cards in same row', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 1, 10, 1); + tester.placeCard(matrix, 4, 1, 1); + tester.placeCard(matrix, 4, 10, 1); // Block direct horizontal path - tester.placeCard(matrix, 1, 5, 2); - + tester.placeCard(matrix, 4, 5, 2); + const testCase = tester.createTestCase( 'Z-pattern with cards in same row', matrix, - 1, 1, 1, 10, - true, - 'Z-pattern' + 4, 1, 4, 10, + true ); - + tester.expectTestCase(testCase); }); test('should handle Z-pattern with cards in same column', () => { const matrix = tester.createEmptyMatrix(); - tester.placeCard(matrix, 1, 1, 1); - tester.placeCard(matrix, 6, 1, 1); + tester.placeCard(matrix, 1, 5, 1); + tester.placeCard(matrix, 6, 5, 1); // Block direct vertical path - tester.placeCard(matrix, 3, 1, 2); - + tester.placeCard(matrix, 3, 5, 2); + const testCase = tester.createTestCase( 'Z-pattern with cards in same column', matrix, - 1, 1, 6, 1, - true, - 'Z-pattern' + 1, 5, 6, 5, + true ); - + tester.expectTestCase(testCase); }); }); @@ -244,7 +242,7 @@ describe('Z-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 1, 5, 1); - + const testCase = tester.createTestCase( 'Should prefer I-pattern over Z-pattern', matrix, @@ -252,7 +250,7 @@ describe('Z-Pattern Tests', () => { true, 'I-pattern' ); - + tester.expectTestCase(testCase); }); @@ -260,7 +258,7 @@ describe('Z-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - + const testCase = tester.createTestCase( 'Should prefer L-pattern over Z-pattern', matrix, @@ -268,7 +266,7 @@ describe('Z-Pattern Tests', () => { true, 'L-pattern' ); - + tester.expectTestCase(testCase); }); @@ -276,11 +274,10 @@ describe('Z-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 3, 3, 1); - // Block I-pattern (not applicable) // Block L-pattern corners tester.placeCard(matrix, 1, 3, 2); tester.placeCard(matrix, 3, 1, 2); - + const testCase = tester.createTestCase( 'Should use Z-pattern when L-pattern blocked', matrix, @@ -288,7 +285,7 @@ describe('Z-Pattern Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -301,7 +298,7 @@ describe('Z-Pattern Tests', () => { // Block L-pattern paths tester.placeCard(matrix, 1, 7, 2); tester.placeCard(matrix, 5, 1, 2); - + const testCase = tester.createTestCase( 'Z-pattern with two distinct turns', matrix, @@ -309,26 +306,26 @@ describe('Z-Pattern Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); - test('should handle Z-pattern with intermediate point far from both cards', () => { + test('should handle Z-pattern across full board', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 1, 1); tester.placeCard(matrix, 8, 20, 1); - // Block other patterns by placing strategic obstacles + // Block L-pattern corners tester.placeCard(matrix, 1, 20, 2); tester.placeCard(matrix, 8, 1, 2); - + const testCase = tester.createTestCase( - 'Z-pattern with distant intermediate point', + 'Z-pattern across full board', matrix, 1, 1, 8, 20, true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); @@ -341,7 +338,7 @@ describe('Z-Pattern Tests', () => { // Block L-pattern tester.placeCard(matrix, 2, 8, 2); tester.placeCard(matrix, 6, 3, 2); - + const testCase = tester.createTestCase( 'Asymmetric Z-pattern', matrix, @@ -349,7 +346,7 @@ describe('Z-Pattern Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); @@ -357,10 +354,10 @@ describe('Z-Pattern Tests', () => { const matrix = tester.createEmptyMatrix(); tester.placeCard(matrix, 1, 5, 1); tester.placeCard(matrix, 7, 15, 1); - // Block other patterns + // Block L-pattern corners tester.placeCard(matrix, 1, 15, 2); tester.placeCard(matrix, 7, 5, 2); - + const testCase = tester.createTestCase( 'Z-pattern with varying distances', matrix, @@ -368,8 +365,8 @@ describe('Z-Pattern Tests', () => { true, 'Z-pattern' ); - + tester.expectTestCase(testCase); }); }); -}); \ No newline at end of file +});