diff --git a/app.js b/app.js index beeb94d..257d353 100644 --- a/app.js +++ b/app.js @@ -37,12 +37,24 @@ function initialiseArr() } } + +// return row where coin should be placed +var rowToBeFilled=function(col) +{ + for(var i=5;i>=0;i--) + { + if(arr[i][col]==-1)return i; + } + return -1; +} //----------------------------------------------------------- //MINIMAX ALGORITHM -function calculateByMatch(c,d,e,count){ +// Dibas Version of heuristic + +function calculateByMatchDibas(c,d,e,count){ // console.log(""+c+" "+d+" "+e+" "+count); e=0; - var tot = 0; + let tot = 0; if(count==4) { if(c==4) tot+=1000; else if(c==3) {if(d==1) tot+=100; else tot+=70;} @@ -66,8 +78,33 @@ function calculateByMatch(c,d,e,count){ //console.log(tot); return tot; } + +function calculateByMatchDeepanshu(c,d,e,count){ + // console.log(""+c+" "+d+" "+e+" "+count); e=0; + + let tot = 0; + if(count==4) { + if(c==4) console.log(arr); + if(c==4) tot+=50; + else if(c==3&&d==1) tot+=20; + else if(c==2&&d==2) tot+=2; + + //adding blocking moves + if(c==1&&e==3) tot+=19; + if(c==2&&d==2) tot+=3; + + if(e==3&&d==1) tot-=10; + if(e==4) tot-=40; + } + return tot; +} + +function calculateByMatch(c,d,e,count){ + return calculateByMatchDeepanshu(c,d,e,count); + //return calculateByMatchDibas(c,d,e,count); +} function findRowMatch(p1,count){ - var tot=0;var c=0,d=0,e=0; + let tot=0;var c=0,d=0,e=0; for(let i=0;i<6;++i){ for(let j=0;j<8-count;++j){ for(let k=0;k=count-1;--i){ + for(let j=0;j<8-count;++j){ + for(let k=0;k=0;i--) - { - if(arr[i][col]==-1)return i; +function findBestColumn(p1){ + var valid = getValidLocations(); + var ans=valid[0]; + var debug = []; + let score = -10000; + if(valid.length==1) return ans; + + for(var f=0;fscore) {score=newScore; ans=j;} + arr[i][j] = -1; + //console.log(arr); } - return -1; + console.log(debug); + return ans; } -// calculates if score is greater than or equal to four or not - var isScoreEnough=function(row,col) { var scoreLeft=0,scoreRight=0,scoreTop=0,scoreBottom=0,scoreLeftUpperDiagonal=0,scoreLeftLowerDiagonal=0,scoreRightUpperDiagonal=0,scoreRightLowerDiagonal=0; @@ -194,6 +282,105 @@ var isScoreEnough=function(row,col) return false; //if score is not enough } +function winningMove(){ + var ch = false; + for(let i=0;i<6;++i) for(let j=0;j<7;++j) if(arr[i][j]!=-1) { + ch|=isScoreEnough(i,j); + if(ch==true) break; + } + return ch; +} +function isTerminal(){ + + return (winningMove()||numberOfMovesComleted==42); +} + +function minimax(depth,alpha,beta,maxPlayer){ + + let valid = getValidLocations(); + let ansarr=[valid[Math.floor(Math.random()*valid.length)],0]; + //return ansarr; + let terminal = isTerminal(); + if((depth==0)||terminal){ + if(terminal){ + if(winningMove()){ + if(maxPlayer==currentPlayer) ansarr= [-1,-1000000000000]; + else { + ans=ansarr [-1,1000000000000]; + console.log("win"); + // debug + //console.log(arr); + } + } + else ansarr = [-1,0]; + } + else { + + //console.log(arr); + //var tmp =1-currentPlayer; + //currentPlayer = 1- maxPlayer; + //console.log(currentPlayer); + ansarr = [-1,calcScore()]; + //currentPlayer = 1-tmp; + } + return ansarr; + } + if(maxPlayer) { + //console.log("Hello"+depth+" "+maxPlayer); + numberOfMovesComleted++; + let value = -Infinity; + let col = valid[Math.floor(Math.random()*valid.length)]; + ansarr = [col,value]; + for(let g=0;g value) { + value = newScore; + col= valid[g]; + + ansarr = [col,value]; + } + alpha = Math.max(alpha,value); + if(alpha>=beta) break; + + } + + numberOfMovesComleted--; + } + else { + + numberOfMovesComleted++; + let value = Infinity; + let col = valid[Math.floor(Math.random()*valid.length)]; + ansarr = [col,value]; + for(let g=0;g=beta) break; + + } + numberOfMovesComleted--; + } + // console.log(ansarr); + return ansarr; + //currentPlayer = maxPlayer; +} +//----------------------------------------------------------- + +// calculates if score is greater than or equal to four or not + + //function to display coin in html //function to add event listener to each column Array.prototype.forEach.call(tableCell,(cell)=>{ @@ -221,15 +408,16 @@ function changeColor(e) if(currentPlayer==0) { row[0].style.backgroundColor='red'; - console.log(calcScore()); currentPlayer=1; + console.log(minimax(5,-Infinity,Infinity,1)[0]); } else { row[0].style.backgroundColor='yellow'; - console.log(calcScore()); currentPlayer=0; + +// console.log(findBestColumn(1)); } } diff --git a/connect4.py b/connect4.py new file mode 100644 index 0000000..f60c6e9 --- /dev/null +++ b/connect4.py @@ -0,0 +1,290 @@ +import numpy as np +import random +import pygame +import sys +import math + +BLUE = (0,0,255) +BLACK = (0,0,0) +RED = (255,0,0) +YELLOW = (255,255,0) + +ROW_COUNT = 6 +COLUMN_COUNT = 7 + +PLAYER = 0 +AI = 1 + +EMPTY = 0 +PLAYER_PIECE = 1 +AI_PIECE = 2 + +WINDOW_LENGTH = 4 + +def create_board(): + board = np.zeros((ROW_COUNT,COLUMN_COUNT)) + return board + +def drop_piece(board, row, col, piece): + board[row][col] = piece + +def is_valid_location(board, col): + return board[ROW_COUNT-1][col] == 0 + +def get_next_open_row(board, col): + for r in range(ROW_COUNT): + if board[r][col] == 0: + return r + +def print_board(board): + print(np.flip(board, 0)) + +def winning_move(board, piece): + # Check horizontal locations for win + for c in range(COLUMN_COUNT-3): + for r in range(ROW_COUNT): + if board[r][c] == piece and board[r][c+1] == piece and board[r][c+2] == piece and board[r][c+3] == piece: + return True + + # Check vertical locations for win + for c in range(COLUMN_COUNT): + for r in range(ROW_COUNT-3): + if board[r][c] == piece and board[r+1][c] == piece and board[r+2][c] == piece and board[r+3][c] == piece: + return True + + # Check positively sloped diaganols + for c in range(COLUMN_COUNT-3): + for r in range(ROW_COUNT-3): + if board[r][c] == piece and board[r+1][c+1] == piece and board[r+2][c+2] == piece and board[r+3][c+3] == piece: + return True + + # Check negatively sloped diaganols + for c in range(COLUMN_COUNT-3): + for r in range(3, ROW_COUNT): + if board[r][c] == piece and board[r-1][c+1] == piece and board[r-2][c+2] == piece and board[r-3][c+3] == piece: + return True + +def evaluate_window(window, piece): + score = 0 + opp_piece = PLAYER_PIECE + if piece == PLAYER_PIECE: + opp_piece = AI_PIECE + + if window.count(piece) == 4: + score += 100 + elif window.count(piece) == 3 and window.count(EMPTY) == 1: + score += 5 + elif window.count(piece) == 2 and window.count(EMPTY) == 2: + score += 2 + + if window.count(opp_piece) == 3 and window.count(EMPTY) == 1: + score -= 4 + + return score + +def score_position(board, piece): + score = 0 + + ## Score center column + center_array = [int(i) for i in list(board[:, COLUMN_COUNT//2])] + center_count = center_array.count(piece) + score += center_count * 3 + + ## Score Horizontal + for r in range(ROW_COUNT): + row_array = [int(i) for i in list(board[r,:])] + for c in range(COLUMN_COUNT-3): + window = row_array[c:c+WINDOW_LENGTH] + score += evaluate_window(window, piece) + + ## Score Vertical + for c in range(COLUMN_COUNT): + col_array = [int(i) for i in list(board[:,c])] + for r in range(ROW_COUNT-3): + window = col_array[r:r+WINDOW_LENGTH] + score += evaluate_window(window, piece) + + ## Score posiive sloped diagonal + for r in range(ROW_COUNT-3): + for c in range(COLUMN_COUNT-3): + window = [board[r+i][c+i] for i in range(WINDOW_LENGTH)] + score += evaluate_window(window, piece) + + for r in range(ROW_COUNT-3): + for c in range(COLUMN_COUNT-3): + window = [board[r+3-i][c+i] for i in range(WINDOW_LENGTH)] + score += evaluate_window(window, piece) + + return score + +def is_terminal_node(board): + return winning_move(board, PLAYER_PIECE) or winning_move(board, AI_PIECE) or len(get_valid_locations(board)) == 0 + +def minimax(board, depth, alpha, beta, maximizingPlayer): + valid_locations = get_valid_locations(board) + is_terminal = is_terminal_node(board) + if depth == 0 or is_terminal: + if is_terminal: + if winning_move(board, AI_PIECE): + return (None, 100000000000000) + elif winning_move(board, PLAYER_PIECE): + return (None, -10000000000000) + else: # Game is over, no more valid moves + return (None, 0) + else: # Depth is zero + return (None, score_position(board, AI_PIECE)) + if maximizingPlayer: + value = -1e12 + column = random.choice(valid_locations) + for col in valid_locations: + row = get_next_open_row(board, col) + b_copy = board.copy() + drop_piece(b_copy, row, col, AI_PIECE) + new_score = minimax(b_copy, depth-1, alpha, beta, False)[1] + if new_score > value: + value = new_score + column = col + alpha = max(alpha, value) + if alpha >= beta: + break + return column, value + + else: # Minimizing player + value = 1e12 + column = random.choice(valid_locations) + for col in valid_locations: + row = get_next_open_row(board, col) + b_copy = board.copy() + drop_piece(b_copy, row, col, PLAYER_PIECE) + new_score = minimax(b_copy, depth-1, alpha, beta, True)[1] + if new_score < value: + value = new_score + column = col + beta = min(beta, value) + if alpha >= beta: + break + return column, value + +def get_valid_locations(board): + valid_locations = [] + for col in range(COLUMN_COUNT): + if is_valid_location(board, col): + valid_locations.append(col) + return valid_locations + +def pick_best_move(board, piece): + + valid_locations = get_valid_locations(board) + best_score = -10000 + best_col = random.choice(valid_locations) + for col in valid_locations: + row = get_next_open_row(board, col) + temp_board = board.copy() + drop_piece(temp_board, row, col, piece) + score = score_position(temp_board, piece) + if score > best_score: + best_score = score + best_col = col + + return best_col + +def draw_board(board): + for c in range(COLUMN_COUNT): + for r in range(ROW_COUNT): + pygame.draw.rect(screen, BLUE, (c*SQUARESIZE, r*SQUARESIZE+SQUARESIZE, SQUARESIZE, SQUARESIZE)) + pygame.draw.circle(screen, BLACK, (int(c*SQUARESIZE+SQUARESIZE/2), int(r*SQUARESIZE+SQUARESIZE+SQUARESIZE/2)), RADIUS) + + for c in range(COLUMN_COUNT): + for r in range(ROW_COUNT): + if board[r][c] == PLAYER_PIECE: + pygame.draw.circle(screen, RED, (int(c*SQUARESIZE+SQUARESIZE/2), height-int(r*SQUARESIZE+SQUARESIZE/2)), RADIUS) + elif board[r][c] == AI_PIECE: + pygame.draw.circle(screen, YELLOW, (int(c*SQUARESIZE+SQUARESIZE/2), height-int(r*SQUARESIZE+SQUARESIZE/2)), RADIUS) + pygame.display.update() + +board = create_board() +print_board(board) +game_over = False + +pygame.init() + +SQUARESIZE = 100 + +width = COLUMN_COUNT * SQUARESIZE +height = (ROW_COUNT+1) * SQUARESIZE + +size = (width, height) + +RADIUS = int(SQUARESIZE/2 - 5) + +screen = pygame.display.set_mode(size) +draw_board(board) +pygame.display.update() + +myfont = pygame.font.SysFont("monospace", 75) + +turn = random.randint(PLAYER, AI) + +while not game_over: + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + if event.type == pygame.MOUSEMOTION: + pygame.draw.rect(screen, BLACK, (0,0, width, SQUARESIZE)) + posx = event.pos[0] + if turn == PLAYER: + pygame.draw.circle(screen, RED, (posx, int(SQUARESIZE/2)), RADIUS) + + pygame.display.update() + + if event.type == pygame.MOUSEBUTTONDOWN: + pygame.draw.rect(screen, BLACK, (0,0, width, SQUARESIZE)) + #print(event.pos) + # Ask for Player 1 Input + if turn == PLAYER: + posx = event.pos[0] + col = int(math.floor(posx/SQUARESIZE)) + + if is_valid_location(board, col): + row = get_next_open_row(board, col) + drop_piece(board, row, col, PLAYER_PIECE) + + if winning_move(board, PLAYER_PIECE): + label = myfont.render("Player 1 wins!!", 1, RED) + screen.blit(label, (40,10)) + game_over = True + + turn += 1 + turn = turn % 2 + + print_board(board) + draw_board(board) + + + # # Ask for Player 2 Input + if turn == AI and not game_over: + + #col = random.randint(0, COLUMN_COUNT-1) + #col = pick_best_move(board, AI_PIECE) + col, minimax_score = minimax(board, 5, -1e12, 1e12, True) + + if is_valid_location(board, col): + #pygame.time.wait(500) + row = get_next_open_row(board, col) + drop_piece(board, row, col, AI_PIECE) + + if winning_move(board, AI_PIECE): + label = myfont.render("Player 2 wins!!", 1, YELLOW) + screen.blit(label, (40,10)) + game_over = True + + print_board(board) + draw_board(board) + + turn += 1 + turn = turn % 2 + + if game_over: + pygame.time.wait(3000) \ No newline at end of file diff --git a/index.html b/index.html index a053fbd..bc15e38 100644 --- a/index.html +++ b/index.html @@ -73,7 +73,7 @@

Connect 4

-
+ \ No newline at end of file