Skip to content

Commit 84350c5

Browse files
committed
compeleted Connect4 bot and fixed DiagonalLTRB bug
1 parent bd33a5c commit 84350c5

File tree

2 files changed

+31
-34
lines changed

2 files changed

+31
-34
lines changed

src/components/games/connectFour.ts

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -233,14 +233,13 @@ export class ConnectFourGame {
233233
left_pointer_y++;
234234
}
235235
while (
236-
right_pointer_x + 1 < CONNECT_FOUR_COLUMN_COUNT - 1 &&
236+
right_pointer_x + 1 < CONNECT_FOUR_COLUMN_COUNT && // used to be an extra -1, fixed bug
237237
right_pointer_y - 1 >= 0 &&
238238
state.columns[right_pointer_x + 1].tokens[right_pointer_y - 1] == sign
239239
) {
240240
right_pointer_x++;
241241
right_pointer_y--;
242242
}
243-
244243
if (right_pointer_x - left_pointer_x + 1 >= 4) {
245244
return true;
246245
}
@@ -259,6 +258,8 @@ export class ConnectFourGame {
259258
// newly placed token
260259
const horizontalIndex = columnIndex;
261260
const verticalIndex = state.columns[columnIndex].fill - 1;
261+
//console.log(horizontalIndex);
262+
//console.log(verticalIndex);
262263
const column = state.columns[columnIndex];
263264
const sign: ConnectFourGameSign = state.columns[columnIndex].tokens[verticalIndex];
264265

@@ -271,11 +272,11 @@ export class ConnectFourGame {
271272
state.winType = ConnectFourWinType.Horizontal;
272273
state.status = this.determineWinner(sign);
273274
} else if (this.checkForDiagonalLBRTWin(state, horizontalIndex, verticalIndex, sign)) {
274-
// Check for diagonal win (left top right bottom)
275+
// Check for diagonal win (left bottom right top)
275276
state.winType = ConnectFourWinType.DiagonalLBRT;
276277
state.status = this.determineWinner(sign);
277278
} else if (this.checkForDiagonalLTRBWin(state, horizontalIndex, verticalIndex, sign)) {
278-
// Check for diagonal win (left bottom right top)
279+
// Check for diagonal win (left top right bottom)
279280
state.winType = ConnectFourWinType.DiagonalLTRB;
280281
state.status = this.determineWinner(sign);
281282
} else if (state.columns.every((column) => column.fill === 6)) {
@@ -411,19 +412,19 @@ ${this.state.player2Username}: ${getEmojiFromSign(this.state.player2Sign)}
411412
// Player1TimeOut = 5,
412413
// Player2TimeOut = 6,
413414
// Unknown = 7,
414-
private updateState = (state : ConnectFourGameState, columnNumber : number, turn : number): ConnectFourGameState => {
415+
private updateState = (state : ConnectFourGameState, columnNumber : number, turn : ConnectFourGameSign): ConnectFourGameState => {
415416
const fill : number = state.columns[columnNumber].fill;
416-
if (turn === 1){ //means its Codeybot's turn
417-
state.columns[columnNumber].tokens[fill] = 3;
417+
if (turn === ConnectFourGameSign.Player2){
418+
state.columns[columnNumber].tokens[fill] = ConnectFourGameSign.Player2;
418419
} else{
419-
state.columns[columnNumber].tokens[fill] = 2;
420+
state.columns[columnNumber].tokens[fill] = ConnectFourGameSign.Player1;
420421
}
421422
state.columns[columnNumber].fill = state.columns[columnNumber].fill + 1;
422423
return state;
423424
}
424425

425426
// returns number of possible wins remaining
426-
private possibleWins = (state : ConnectFourGameState, opponentSign : number): number => {
427+
private possibleWins = (state : ConnectFourGameState, opponentSign : ConnectFourGameSign): number => {
427428
let possibleWins : number = 0;
428429
// check vertical
429430
for (let i = 0; i < CONNECT_FOUR_COLUMN_COUNT; i++){
@@ -463,51 +464,49 @@ ${this.state.player2Username}: ${getEmojiFromSign(this.state.player2Sign)}
463464

464465
// takes a ConnectFourGameState and evaluates it according to heuristic function
465466
private evaluate = (state : ConnectFourGameState): number => {
466-
let codeyPoints : number = this.possibleWins(state, 2); // 3 represents Codeybot sign
467-
let opponentPoints : number = this.possibleWins(state, 3); // 2 represents player1 sign
467+
let codeyPoints : number = this.possibleWins(state, ConnectFourGameSign.Player1); // 3 represents Codeybot sign
468+
let opponentPoints : number = this.possibleWins(state, ConnectFourGameSign.Player2); // 2 represents player1 sign
468469
return codeyPoints - opponentPoints;
469470
}
470471

471472
// from perspective of Codeybot, +infinity means Codeybot win, -infinity means Player1 wins
472-
// turn = 1, means it's Codeybot's turn, turn = -1 means it's opponent's turn
473473
// returns the best possible score that can be achieved, given that Player1 plays optimally
474-
private miniMax = (state : ConnectFourGameState, depth : number, turn : number): number =>{
475-
if (state.status === 2){ // means draw
474+
private miniMax = (state : ConnectFourGameState, depth : number, turn : ConnectFourGameSign): number =>{
475+
if (state.status === ConnectFourGameStatus.Draw){
476476
return 0;
477477
}
478-
if (state.status === 3){ // means Player1 wins
478+
if (state.status === ConnectFourGameStatus.Player1Win){
479479
return -Infinity;
480480
}
481-
if (state.status === 4){ // means Player 2 wins
481+
if (state.status === ConnectFourGameStatus.Player2Win){
482482
return Infinity;
483483
}
484484
if (depth === 0){
485485
return this.evaluate(state); //heuristic function to evaluate state of game
486486
}
487487

488488
// if it is Codeybot's turn, we want to find move that maximizes score
489-
if (turn === 1){
489+
const column_choices : number[] = [0, 1, 2, 3, 4, 5, 6];
490+
if (turn === ConnectFourGameSign.Player2){
490491
let value : number = -Infinity;
491-
const column_choices : number[] = [0, 1, 2, 3, 4, 5, 6];
492492
for (const column_choice of column_choices){
493493
if (state.columns[column_choice].fill < CONNECT_FOUR_ROW_COUNT){
494494
let newState : ConnectFourGameState = JSON.parse(JSON.stringify(state));
495495
this.updateState(newState, column_choice, turn);
496-
this.setStatus(newState, column_choice);
497-
value = Math.max(value, this.miniMax(newState, depth - 1, turn * -1));
496+
this.setStatus(newState, column_choice); // setStatus assumes newState has already been updated with chip
497+
value = Math.max(value, this.miniMax(newState, depth - 1, ConnectFourGameSign.Player1));
498498
}
499499
}
500500
return value;
501-
} else { // (turn = -1) it is Player 1's turn, so we want to find minimum score (this assumes Player 1 plays optimally)
501+
} else { //it is Player 1's turn, so we want to find minimum score (this assumes Player 1 plays optimally)
502502
let value : number = Infinity;
503-
const column_choices : number[] = [0, 1, 2, 3, 4, 5, 6];
504503
for (const column_choice of column_choices){
505504
// if selected column is not already full, recurse down the branch
506505
if (state.columns[column_choice].fill < CONNECT_FOUR_ROW_COUNT){
507506
let newState : ConnectFourGameState = JSON.parse(JSON.stringify(state));
508507
this.updateState(newState, column_choice, turn);
509508
this.setStatus(newState, column_choice);
510-
value = Math.min(value, this.miniMax(newState, depth - 1, turn * -1));
509+
value = Math.min(value, this.miniMax(newState, depth - 1, ConnectFourGameSign.Player2));
511510
}
512511
}
513512
return value;
@@ -519,9 +518,9 @@ ${this.state.player2Username}: ${getEmojiFromSign(this.state.player2Sign)}
519518
for (let i = 0; i < 7; i++){
520519
if (state.columns[i].fill < CONNECT_FOUR_ROW_COUNT){
521520
let newState : ConnectFourGameState = JSON.parse(JSON.stringify(state)); // make a deep copy of game state
522-
this.updateState(newState, i, 1);
521+
this.updateState(newState, i, ConnectFourGameSign.Player2);
523522
this.setStatus(newState, i);
524-
column_scores[i] = this.miniMax(newState, 4, -1);
523+
column_scores[i] = this.miniMax(newState, 4, ConnectFourGameSign.Player1);
525524
}
526525
}
527526
let value = -Infinity;
@@ -619,10 +618,6 @@ export const getStateAsString = (state: ConnectFourGameState): string => {
619618
return result;
620619
};
621620

622-
// export const getCodeyConnectFourSign = (): ConnectFourGameSign => {
623-
// return getRandomIntFrom1(7);
624-
// };
625-
626621
export const updateColumn = (column: ConnectFourColumn, sign: ConnectFourGameSign): boolean => {
627622
const fill: number = column.fill;
628623
if (fill < CONNECT_FOUR_ROW_COUNT) {

src/interaction-handlers/games/connectFour.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,11 @@ export class ConnectFourHandler extends InteractionHandler {
5959
} else {
6060
await interaction.deferUpdate();
6161
const status = await game.setStatus(game.state, result.sign - 1);
62-
if (status == ConnectFourGameStatus.Pending) {
62+
if (status === ConnectFourGameStatus.Pending) {
6363
if (!game.state.player2Id) {
6464
let bestMove = game.getBestMove(game.state);
65-
// while (!updateColumn(game.state.columns[codeySign - 1], game.state.player2Sign)) {
66-
// codeySign = getCodeyConnectFourSign(game.state);
67-
// }
6865
updateColumn(game.state.columns[bestMove], game.state.player2Sign);
6966
game.setStatus(game.state, bestMove);
70-
7167
}
7268
}
7369
}
@@ -77,3 +73,9 @@ export class ConnectFourHandler extends InteractionHandler {
7773
connectFourGameTracker.endGame(result.gameId);
7874
}
7975
}
76+
77+
//Debug function to enable program to allow program to pause
78+
// async function sleep(ms: number): Promise<void> {
79+
// return new Promise(
80+
// (resolve) => setTimeout(resolve, ms));
81+
// }

0 commit comments

Comments
 (0)