-
Notifications
You must be signed in to change notification settings - Fork 0
Fix AI vertical threat detection blind spot on center columns #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
Co-authored-by: CodeKunalTomar <111980003+CodeKunalTomar@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request fixes a critical bug where the AI failed to detect vertical stacking threats on center columns (2, 3, 4), only monitoring edge columns (0, 1, 5, 6). The fix comprehensively improves the AI by increasing win score values to avoid overlap with evaluation scores, implementing forced move detection, replacing random move selection with deterministic tie-breaking, and enhancing threat detection across all columns.
Key changes:
- Win scores increased from ±4 to ±1,000,000 to prevent confusion with evaluation scores (which can reach 50,000+)
- Vertical threat detection expanded from edge columns only to all columns (0-6)
- Added forced move detection for immediate wins/blocks before consulting opening book or search
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
| Connect-4.js | Core AI fixes including win score constants, vertical threat detection for all columns, forced move detection, deterministic tie-breaking, depth bonuses, and enhanced opening book entries |
| ai-tests.js | Added three test cases specifically for vertical threat detection on center columns 2, 3, and 4 to verify the fix |
Comments suppressed due to low confidence (16)
Connect-4.js:30
- This property is duplicated in a later property.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:30
- This property is duplicated in a later property.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:30
- This property is duplicated in a later property.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:36
- This property is duplicated in a later property.
'313241': 2, '313251': 4, '313211': 2, '313221': 2, '313261': 4,
Connect-4.js:36
- This property is duplicated in a later property.
'313241': 2, '313251': 4, '313211': 2, '313221': 2, '313261': 4,
Connect-4.js:36
- This property is duplicated in a later property.
'313241': 2, '313251': 4, '313211': 2, '313221': 2, '313261': 4,
Connect-4.js:36
- This property is duplicated in a later property.
'313241': 2, '313251': 4, '313211': 2, '313221': 2, '313261': 4,
Connect-4.js:42
- This property is duplicated in a later property.
'313231': 3, // Continue center dominance
Connect-4.js:1076
- This expression assigns property score to itself.
node.score = node.score;
ai-tests.js:176
- Variable currentGameState is used like a local variable, but is missing a declaration.
currentGameState = new GameState();
Connect-4.js:470
- Unused variable opponent.
const opponent = player === 1 ? 2 : 1;
Connect-4.js:500
- Unused variable evenRows.
const evenRows = [1, 3, 5];
Connect-4.js:30
- This property is overwritten by another property in the same object literal.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:30
- This property is overwritten by another property in the same object literal.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:30
- This property is overwritten by another property in the same object literal.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
Connect-4.js:30
- This property is overwritten by another property in the same object literal.
'3132': 3, '0132': 3, '1132': 2, '2132': 2, '4132': 4, '5132': 4, '6132': 3,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| '414241': 4, // Continue blocking column 4 | ||
| '2122': 2, // Block column 2 stack | ||
| '5152': 5, // Block column 5 stack |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opening book entry '414241': 4 represents a game sequence where AI plays column 4 in response to human's first move on column 4. However, the opening book already defines that when human plays column 4 first ('41'), AI should respond with column 3 (line 27). This means the sequence '414241' would never occur if the AI follows its own opening book, making this entry unreachable dead code. Similar logic applies to entries '2122' and '5152' on lines 62-63.
| '414241': 4, // Continue blocking column 4 | |
| '2122': 2, // Block column 2 stack | |
| '5152': 5, // Block column 5 stack |
| // Human stacks center - AI contests by stacking same column | ||
| '3132': 3, // Contest center column | ||
| '313231': 3, // Continue center control | ||
| '31323132': 3, // Keep stacking center |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opening book entry '31323132': 3 on line 68 represents a sequence where human and AI alternate playing column 3 four times. However, this is an 8-character key representing 4 moves, which means it would only be consulted after move 4. But looking at the pattern: after '3132' (line 30/66), AI plays 3. Then if human plays 3 again, the board key is '313231', and the book says AI plays 3 (line 42/67). Then if human plays 3 again, the key would be '31323132', which is this entry. However, at this point, the column would likely be getting full (4 pieces in a 7-row board), and this represents a very specific scenario. Consider whether this entry adds value or if the search would handle this adequately.
| '31323132': 3, // Keep stacking center |
| if (score === COMPUTER_WIN_SCORE) { | ||
| score = score - recursionsRemaining; // Prefer faster AI wins | ||
| } else if (score === HUMAN_WIN_SCORE) { | ||
| score = score + recursionsRemaining; // Prefer slower human wins (delay loss) |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment says "prefer slower human wins (delay loss)" but the actual effect is achieved by adding recursionsRemaining to HUMAN_WIN_SCORE, which makes shallower human wins more negative (worse from AI's perspective). The comment is correct in intent but could be clearer. Consider rephrasing to: "Make immediate human wins worse (more negative) so AI prefers positions where losses are delayed".
| score = score + recursionsRemaining; // Prefer slower human wins (delay loss) | |
| score = score + recursionsRemaining; // Make immediate human wins worse (more negative) so AI prefers positions where losses are delayed |
| '5152': 5, // Block column 5 stack | ||
|
|
||
| // Human stacks center - AI contests by stacking same column | ||
| '3132': 3, // Contest center column |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opening book has duplicate key '3132' with different intended behaviors. Line 30 assigns it to respond to "human center (31), AI center (32)" scenario, while line 66 has a comment "Human stacks center - AI contests by stacking same column" with the same key. Both map to column 3, but the duplicate entry suggests confusion about what this key represents. The key '3132' means moves 31 (human column 3) and 32 (AI column 3), so lines 30 and 66 represent the same board state and should not be duplicated.
| '3132': 3, // Contest center column |
|
|
||
| // Human stacks center - AI contests by stacking same column | ||
| '3132': 3, // Contest center column | ||
| '313231': 3, // Continue center control |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The opening book has duplicate key '313231' on lines 42 and 67. Both entries map to column 3 but are listed under different comment sections. This duplication is unnecessary and makes the opening book harder to maintain. Consider removing one of these duplicate entries.
| '313231': 3, // Continue center control |
| if (childNodes[col] !== undefined && | ||
| ((player === 2 && childNodes[col].score === node.score) || | ||
| (player === 1 && childNodes[col].score === node.score))) { | ||
| ((player === 2 && childNodes[col].score === bestScore) || | ||
| (player === 1 && childNodes[col].score === bestScore))) { |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tie-breaking logic compares childNodes[col].score with bestScore, but these scores are from different perspectives. The childNodes[col].score contains the score from the child node's perspective (set by recursive think() calls), while bestScore contains the negated score from the parent's perspective (computed as -childNode.score at line 1163). This comparison will not correctly identify tied moves. Consider storing the computed scores in a separate array during the search loop, or tracking which moves achieved the best score using a different mechanism.
|
|
||
| // Comprehensive evaluation for near-perfect play | ||
| GameState.prototype.advancedEvaluate = function(player) { | ||
| const opponent = player === 1 ? 2 : 1; |
Copilot
AI
Dec 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable opponent.
| const opponent = player === 1 ? 2 : 1; |
The AI only detected vertical stacking threats on edge columns [0,1,5,6], allowing humans to win easily by building 4-in-a-row on center columns (2,3,4). Win scores of ±4 also overlapped with evaluation scores reaching 50k, causing the AI to confuse strong positions with actual wins.
Core Fix: Vertical Threat Detection
Changes
getForcedMove()to detect immediate wins/blocks before searchHelper Functions
Test Coverage
Added 3 test cases for vertical threats on center columns (2,3,4).
Original prompt
Complete AI Optimization for Connect-4
Executive Summary
The AI has multiple critical bugs that allow humans to easily win. This PR must fix ALL issues to create a near-unbeatable AI.
Board Configuration
Bug Evidence
In
, the human won with a vertical 4-in-a-row on column 4 (center-adjacent column). The AI failed to block this obvious threat.
Critical Bugs to Fix
Bug #1: Win Scores Too Small (MOST CRITICAL)
File:
Connect-4.js, Lines 4-5Why: Current values overlap with evaluation scores (5000+), causing AI to confuse wins with good positions.
Bug #2: Random Move Selection
File:
Connect-4.js, Lines 1143-1154Why: Random selection causes inconsistent play when moves appear "equal".
Bug #3: Early Search Termination
File:
Connect-4.js, Lines 924-927Why: Evaluation scores can exceed 8000 (DOUBLE_THREAT_WEIGHT = 5000), causing premature search termination.
Bug #4: Missing Forced Move Detection
File:
Connect-4.js, Add new function beforemakeComputerMove()Update
makeComputerMove()to use this:Bug #5: Minimum Search Depth Not Enforced
File:
Connect-4.js, Line 865Bug #6: Vertical Threat Detection Limited to Edge Columns
File:
Connect-4.js, Lines 575-604Replace
detectEdgeThreats()with comprehensive vertical threat detection for ALL columns: