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
This commit is contained in:
2026-04-04 12:50:30 +07:00
parent 73239c1e27
commit ffd7577a65
10 changed files with 399 additions and 751 deletions
+88 -91
View File
@@ -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);
});
});
});
});